From c47a2128ca8f1811e0f7eb5384fe0ed729238c63 Mon Sep 17 00:00:00 2001 From: Andrew Klychkov Date: Thu, 7 May 2020 12:49:53 +0300 Subject: [PATCH] postgresql_query: add trust_input parameter (#294) * postgresql_query: add trust_input parameter * add changelog fragment * fix CI --- ...gresql_query_add_trust_input_parameter.yml | 2 + .../database/postgresql/postgresql_query.py | 16 +++++ .../tasks/postgresql_query_initial.yml | 65 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 changelogs/fragments/294-postgresql_query_add_trust_input_parameter.yml diff --git a/changelogs/fragments/294-postgresql_query_add_trust_input_parameter.yml b/changelogs/fragments/294-postgresql_query_add_trust_input_parameter.yml new file mode 100644 index 0000000000..9beddbc960 --- /dev/null +++ b/changelogs/fragments/294-postgresql_query_add_trust_input_parameter.yml @@ -0,0 +1,2 @@ +minor_changes: +- postgresql_query - add the ``trust_input`` parameter (https://github.com/ansible-collections/community.general/pull/294). diff --git a/plugins/modules/database/postgresql/postgresql_query.py b/plugins/modules/database/postgresql/postgresql_query.py index 589b8e2564..feb8409711 100644 --- a/plugins/modules/database/postgresql/postgresql_query.py +++ b/plugins/modules/database/postgresql/postgresql_query.py @@ -73,6 +73,12 @@ options: - Set the client encoding for the current session (e.g. C(UTF-8)). - The default is the encoding defined by the database. type: str + trust_input: + description: + - If C(no), check whether a value of I(session_role) is potentially dangerous. + - It does make sense to use C(yes) only when SQL injections via I(session_role) are possible. + type: bool + default: yes seealso: - module: postgresql_db author: @@ -186,6 +192,9 @@ except ImportError: pass from ansible.module_utils.basic import AnsibleModule +from ansible_collections.community.general.plugins.module_utils.database import ( + check_input, +) from ansible_collections.community.general.plugins.module_utils.postgres import ( connect_to_db, get_conn_params, @@ -248,6 +257,7 @@ def main(): path_to_script=dict(type='path'), autocommit=dict(type='bool', default=False), encoding=dict(type='str'), + trust_input=dict(type='bool', default=True), ) module = AnsibleModule( @@ -262,6 +272,12 @@ def main(): path_to_script = module.params["path_to_script"] autocommit = module.params["autocommit"] encoding = module.params["encoding"] + session_role = module.params["session_role"] + trust_input = module.params["trust_input"] + + if not trust_input: + # Check input for potentially dangerous elements: + check_input(module, session_role) if autocommit and module.check_mode: module.fail_json(msg="Using autocommit is mutually exclusive with check_mode") diff --git a/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml b/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml index d7ac010664..5b75e74ebb 100644 --- a/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml +++ b/tests/integration/targets/postgresql_query/tasks/postgresql_query_initial.yml @@ -3,22 +3,26 @@ become: true shell: psql postgres -U "{{ pg_user }}" -t -c "DROP TABLE IF EXISTS test_table;" ignore_errors: true + - name: postgresql_query - create test table called test_table become_user: '{{ pg_user }}' become: true shell: psql postgres -U "{{ pg_user }}" -t -c "CREATE TABLE test_table (id int, story text);" ignore_errors: true + - name: postgresql_query - insert some data into test_table become_user: '{{ pg_user }}' become: true shell: psql postgres -U "{{ pg_user }}" -t -c "INSERT INTO test_table (id, story) VALUES (1, 'first'), (2, 'second'), (3, 'third');" ignore_errors: true + - name: postgresql_query - remove SQL script if exists become: true file: path: ~{{ pg_user}}/test.sql state: absent ignore_errors: true + - name: postgresql_query - create an empty file to check permission become: true file: @@ -29,6 +33,7 @@ mode: '0644' register: sql_file_created ignore_errors: true + - name: postgresql_query - prepare SQL script become_user: '{{ pg_user }}' become: true @@ -48,6 +53,7 @@ query: ANALYZE test_table register: result ignore_errors: true + - assert: that: - result is changed @@ -55,6 +61,7 @@ - result.rowcount == 0 - result.statusmessage == 'ANALYZE' - result.query_result == {} + - name: postgresql_query - run queries from SQL script become_user: '{{ pg_user }}' become: true @@ -68,6 +75,7 @@ register: result ignore_errors: true when: sql_file_created + - assert: that: - result is not changed @@ -76,6 +84,7 @@ - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT' - result.query_result[0].story == 'first' when: sql_file_created + - name: postgresql_query - simple select query to test_table become_user: '{{ pg_user }}' become: true @@ -85,6 +94,7 @@ query: SELECT * FROM test_table register: result ignore_errors: true + - assert: that: - result is not changed @@ -97,6 +107,7 @@ - result.query_result[0].story == 'first' - result.query_result[1].story == 'second' - result.query_result[2].story == 'third' + - name: postgresql_query - select query with named args become_user: '{{ pg_user }}' become: true @@ -109,6 +120,7 @@ story_val: first register: result ignore_errors: true + - assert: that: - result is not changed @@ -116,6 +128,7 @@ - result.rowcount == 1 - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT' - result.query_result[0].id == 1 + - name: postgresql_query - select query with positional arguments become_user: '{{ pg_user }}' become: true @@ -128,6 +141,7 @@ - second register: result ignore_errors: true + - assert: that: - result is not changed @@ -135,6 +149,7 @@ - result.rowcount == 1 - result.statusmessage == 'SELECT 1' or result.statusmessage == 'SELECT' - result.query_result[0].story == 'second' + - name: postgresql_query - simple update query become_user: '{{ pg_user }}' become: true @@ -144,6 +159,7 @@ query: UPDATE test_table SET story = 'new' WHERE id = 3 register: result ignore_errors: true + - assert: that: - result is changed @@ -151,6 +167,7 @@ - result.rowcount == 1 - result.statusmessage == 'UPDATE 1' - result.query_result == {} + - name: check the previous update become_user: '{{ pg_user }}' become: true @@ -159,9 +176,11 @@ db: postgres query: SELECT * FROM test_table WHERE story = 'new' AND id = 3 register: result + - assert: that: - result.rowcount == 1 + - name: postgresql_query - simple update query in check_mode become_user: '{{ pg_user }}' become: true @@ -171,6 +190,7 @@ query: UPDATE test_table SET story = 'CHECK_MODE' WHERE id = 3 register: result check_mode: true + - assert: that: - result is changed @@ -178,6 +198,7 @@ - result.rowcount == 1 - result.statusmessage == 'UPDATE 1' - result.query_result == {} + - name: check the previous update that nothing has been changed become_user: '{{ pg_user }}' become: true @@ -186,9 +207,11 @@ db: postgres query: SELECT * FROM test_table WHERE story = 'CHECK_MODE' AND id = 3 register: result + - assert: that: - result.rowcount == 0 + - name: postgresql_query - try to update not existing row become_user: '{{ pg_user }}' become: true @@ -198,6 +221,7 @@ query: UPDATE test_table SET story = 'new' WHERE id = 100 register: result ignore_errors: true + - assert: that: - result is not changed @@ -205,6 +229,7 @@ - result.rowcount == 0 - result.statusmessage == 'UPDATE 0' - result.query_result == {} + - name: postgresql_query - insert query become_user: '{{ pg_user }}' become: true @@ -217,6 +242,7 @@ - fourth register: result ignore_errors: true + - assert: that: - result is changed @@ -224,6 +250,7 @@ - result.rowcount == 1 - result.statusmessage == 'INSERT 0 1' - result.query_result == {} + - name: postgresql_query - truncate test_table become_user: '{{ pg_user }}' become: true @@ -233,6 +260,7 @@ query: TRUNCATE test_table register: result ignore_errors: true + - assert: that: - result is changed @@ -240,6 +268,7 @@ - result.rowcount == 0 - result.statusmessage == 'TRUNCATE TABLE' - result.query_result == {} + - name: postgresql_query - alter test_table become_user: '{{ pg_user }}' become: true @@ -249,12 +278,14 @@ query: ALTER TABLE test_table ADD COLUMN foo int register: result ignore_errors: true + - assert: that: - result is changed - result.query == "ALTER TABLE test_table ADD COLUMN foo int" - result.rowcount == 0 - result.statusmessage == 'ALTER TABLE' + - name: postgresql_query - vacuum without autocommit must fail become_user: '{{ pg_user }}' become: true @@ -264,9 +295,11 @@ query: VACUUM register: result ignore_errors: true + - assert: that: - result.failed == true + - name: postgresql_query - autocommit in check_mode must fail become_user: '{{ pg_user }}' become: true @@ -278,10 +311,12 @@ check_mode: true register: result ignore_errors: true + - assert: that: - result.failed == true - result.msg == "Using autocommit is mutually exclusive with check_mode" + - name: postgresql_query - vacuum with autocommit become_user: '{{ pg_user }}' become: true @@ -291,6 +326,7 @@ query: VACUUM autocommit: true register: result + - assert: that: - result is changed @@ -298,6 +334,7 @@ - result.rowcount == 0 - result.statusmessage == 'VACUUM' - result.query_result == {} + - name: postgresql_query - create test table for issue 59955 become_user: '{{ pg_user }}' become: true @@ -308,6 +345,7 @@ columns: - arr_col int[] when: postgres_version_resp.stdout is version('9.4', '>=') + - set_fact: my_list: - 1 @@ -315,6 +353,7 @@ - 3 my_arr: '{1, 2, 3}' when: postgres_version_resp.stdout is version('9.4', '>=') + - name: postgresql_query - insert array into test table by positional args become_user: '{{ pg_user }}' become: true @@ -326,11 +365,13 @@ - '{{ my_list }}' register: result when: postgres_version_resp.stdout is version('9.4', '>=') + - assert: that: - result is changed - result.query == "INSERT INTO test_array_table (arr_col) VALUES ('{1, 2, 3}')" when: postgres_version_resp.stdout is version('9.4', '>=') + - name: postgresql_query - select array from test table by passing positional_args become_user: '{{ pg_user }}' become: true @@ -342,12 +383,14 @@ - '{{ my_list }}' register: result when: postgres_version_resp.stdout is version('9.4', '>=') + - assert: that: - result is not changed - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'" - result.rowcount == 1 when: postgres_version_resp.stdout is version('9.4', '>=') + - name: postgresql_query - select array from test table by passing named_args become_user: '{{ pg_user }}' become: true @@ -360,12 +403,14 @@ - '{{ my_list }}' register: result when: postgres_version_resp.stdout is version('9.4', '>=') + - assert: that: - result is not changed - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'" - result.rowcount == 1 when: postgres_version_resp.stdout is version('9.4', '>=') + - name: postgresql_query - select array from test table by passing positional_args as a string become_user: '{{ pg_user }}' become: true @@ -375,14 +420,34 @@ query: SELECT * FROM test_array_table WHERE arr_col = %s positional_args: - '{{ my_arr|string }}' + trust_input: yes register: result when: postgres_version_resp.stdout is version('9.4', '>=') + - assert: that: - result is not changed - result.query == "SELECT * FROM test_array_table WHERE arr_col = '{1, 2, 3}'" - result.rowcount == 1 when: postgres_version_resp.stdout is version('9.4', '>=') + +- name: postgresql_query - test trust_input parameter + become_user: '{{ pg_user }}' + become: true + postgresql_query: + login_user: '{{ pg_user }}' + login_db: postgres + session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --' + query: SELECT version() + trust_input: no + ignore_errors: yes + register: result + +- assert: + that: + - result is failed + - result.msg is search('is potentially dangerous') + - name: postgresql_query - clean up become_user: '{{ pg_user }}' become: true