diff --git a/changelogs/fragments/307-postgresql_table_add_trust_input_parameter.yml b/changelogs/fragments/307-postgresql_table_add_trust_input_parameter.yml new file mode 100644 index 0000000000..3f105d95ba --- /dev/null +++ b/changelogs/fragments/307-postgresql_table_add_trust_input_parameter.yml @@ -0,0 +1,2 @@ +minor_changes: +- postgresql_table - add the ``trust_input`` parameter (https://github.com/ansible-collections/community.general/pull/307). diff --git a/plugins/modules/database/postgresql/postgresql_table.py b/plugins/modules/database/postgresql/postgresql_table.py index 1449f2b69d..95703471d8 100644 --- a/plugins/modules/database/postgresql/postgresql_table.py +++ b/plugins/modules/database/postgresql/postgresql_table.py @@ -99,6 +99,12 @@ options: Used with I(state=absent) only. type: bool default: no + trust_input: + description: + - If C(no), check whether values of parameters are potentially dangerous. + - It makes sense to use C(yes) only when SQL injections are possible. + type: bool + default: yes notes: - If you do not pass db parameter, tables will be created in the database named postgres. @@ -244,7 +250,10 @@ except ImportError: pass from ansible.module_utils.basic import AnsibleModule -from ansible_collections.community.general.plugins.module_utils.database import pg_quote_identifier +from ansible_collections.community.general.plugins.module_utils.database import ( + check_input, + pg_quote_identifier, +) from ansible_collections.community.general.plugins.module_utils.postgres import ( connect_to_db, exec_sql, @@ -365,7 +374,7 @@ class Table(object): query += " WITH (%s)" % params if tblspace: - query += " TABLESPACE %s" % pg_quote_identifier(tblspace, 'database') + query += ' TABLESPACE "%s"' % tblspace if exec_sql(self, query, return_bool=True): changed = True @@ -412,7 +421,7 @@ class Table(object): query += " WITH (%s)" % params if tblspace: - query += " TABLESPACE %s" % pg_quote_identifier(tblspace, 'database') + query += ' TABLESPACE "%s"' % tblspace if exec_sql(self, query, return_bool=True): changed = True @@ -432,8 +441,7 @@ class Table(object): return exec_sql(self, query, return_bool=True) def set_owner(self, username): - query = "ALTER TABLE %s OWNER TO %s" % (pg_quote_identifier(self.name, 'table'), - pg_quote_identifier(username, 'role')) + query = 'ALTER TABLE %s OWNER TO "%s"' % (pg_quote_identifier(self.name, 'table'), username) return exec_sql(self, query, return_bool=True) def drop(self, cascade=False): @@ -446,8 +454,7 @@ class Table(object): return exec_sql(self, query, return_bool=True) def set_tblspace(self, tblspace): - query = "ALTER TABLE %s SET TABLESPACE %s" % (pg_quote_identifier(self.name, 'table'), - pg_quote_identifier(tblspace, 'database')) + query = 'ALTER TABLE %s SET TABLESPACE "%s"' % (pg_quote_identifier(self.name, 'table'), tblspace) return exec_sql(self, query, return_bool=True) def set_stor_params(self, params): @@ -464,7 +471,7 @@ def main(): argument_spec = postgres_common_argument_spec() argument_spec.update( table=dict(type='str', required=True, aliases=['name']), - state=dict(type='str', default="present", choices=["absent", "present"]), + state=dict(type='str', default='present', choices=['absent', 'present']), db=dict(type='str', default='', aliases=['login_db']), tablespace=dict(type='str'), owner=dict(type='str'), @@ -477,24 +484,32 @@ def main(): storage_params=dict(type='list', elements='str'), session_role=dict(type='str'), cascade=dict(type='bool', default=False), + trust_input=dict(type='bool', default=True), ) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, ) - table = module.params["table"] - state = module.params["state"] - tablespace = module.params["tablespace"] - owner = module.params["owner"] - unlogged = module.params["unlogged"] - like = module.params["like"] - including = module.params["including"] - newname = module.params["rename"] - storage_params = module.params["storage_params"] - truncate = module.params["truncate"] - columns = module.params["columns"] - cascade = module.params["cascade"] + table = module.params['table'] + state = module.params['state'] + tablespace = module.params['tablespace'] + owner = module.params['owner'] + unlogged = module.params['unlogged'] + like = module.params['like'] + including = module.params['including'] + newname = module.params['rename'] + storage_params = module.params['storage_params'] + truncate = module.params['truncate'] + columns = module.params['columns'] + cascade = module.params['cascade'] + 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, table, tablespace, owner, like, including, + newname, storage_params, columns, session_role) if state == 'present' and cascade: module.warn("cascade=true is ignored when state=present") diff --git a/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml b/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml index aceee5fa60..c06403a4ac 100644 --- a/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml +++ b/tests/integration/targets/postgresql_table/tasks/postgresql_table_initial.yml @@ -53,7 +53,7 @@ postgresql_query: db: postgres login_user: "{{ pg_user }}" - query: "SELECT 1 FROM pg_stat_all_tables WHERE relname ='test1'" + query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test1'" ignore_errors: yes register: result @@ -92,7 +92,7 @@ postgresql_query: db: postgres login_user: "{{ pg_user }}" - query: "SELECT 1 FROM pg_stat_all_tables WHERE relname ='test1'" + query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test1'" ignore_errors: yes register: result @@ -146,7 +146,7 @@ postgresql_query: db: postgres login_user: "{{ pg_user }}" - query: "SELECT 1 FROM pg_stat_all_tables WHERE relname ='test2'" + query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'" ignore_errors: yes register: result @@ -183,7 +183,7 @@ postgresql_query: db: postgres login_user: "{{ pg_user }}" - query: "SELECT 1 FROM pg_stat_all_tables WHERE relname ='test2'" + query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'" ignore_errors: yes register: result @@ -224,7 +224,7 @@ postgresql_query: db: postgres login_user: "{{ pg_user }}" - query: "SELECT 1 FROM pg_stat_all_tables WHERE relname ='test2'" + query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'" ignore_errors: yes register: result @@ -257,7 +257,7 @@ postgresql_query: db: postgres login_user: "{{ pg_user }}" - query: "SELECT 1 FROM pg_stat_all_tables WHERE relname ='test2'" + query: "SELECT 1 FROM pg_stat_all_tables WHERE relname = 'test2'" ignore_errors: yes register: result @@ -846,6 +846,7 @@ login_user: "{{ pg_user }}" name: public.test_schema_table rename: new_test_schema_table + trust_input: yes register: result - assert: @@ -853,6 +854,24 @@ - result is changed - result.queries == ['ALTER TABLE "public"."test_schema_table" RENAME TO "new_test_schema_table"'] +############################ +# Test trust_input parameter +- name: postgresql_table - check trust_input + postgresql_table: + db: postgres + login_user: "{{ pg_user }}" + name: postgres.acme.test_schema_table + state: absent + trust_input: no + session_role: 'curious.anonymous"; SELECT * FROM information_schema.tables; --' + register: result + ignore_errors: yes + +- assert: + that: + - result is failed + - result.msg is search('is potentially dangerous') + # # Clean up #