diff --git a/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-dest.yaml b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-dest.yaml new file mode 100644 index 0000000000..6c151842a5 --- /dev/null +++ b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-dest.yaml @@ -0,0 +1,6 @@ +- hosts: localhost + tasks: + - name: Request with bad src + uri: + url: https://zuul.opendev.org + dest: /etc/zuul-uri-output-testing diff --git a/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-src.yaml b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-src.yaml new file mode 100644 index 0000000000..9a9460c9b2 --- /dev/null +++ b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-src.yaml @@ -0,0 +1,7 @@ +- hosts: localhost + tasks: + - name: Request with bad src + uri: + url: https://zuul.opendev.org + method: POST + src: /etc/resolv.conf diff --git a/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-url.yaml b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-url.yaml new file mode 100644 index 0000000000..222f1925ae --- /dev/null +++ b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-bad-url.yaml @@ -0,0 +1,14 @@ +- hosts: localhost + tasks: + - name: Request with bad url scheme + uri: + url: "file:///etc/resolv.conf" + dest: "{{ zuul.executor.log_root }}/resolv.conf" + - name: stat file that shouldnt exist + stat: + path: "{{ zuul.executor.log_root }}/resolv.conf" + register: test_stat + - name: Debug the stat + debug: + msg: "resolv.conf exists when it shouldn't" + when: test_stat.stat.exists diff --git a/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-good.yaml b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-good.yaml new file mode 100644 index 0000000000..0416cef4eb --- /dev/null +++ b/tests/fixtures/config/remote-action-modules/git/org_project/playbooks/uri-good.yaml @@ -0,0 +1,16 @@ +- hosts: localhost + tasks: + - name: Safe uri request from localhost + uri: + # We don't seem to have working ssl cert chains in + # the test bwrap context. Use http to workaround that + # and don't follow the redirect to https. + url: http://zuul.opendev.org + follow_redirects: none + return_content: yes + status_code: + - 301 + - 302 + - 303 + - 307 + - 308 diff --git a/tests/remote/test_remote_action_modules.py b/tests/remote/test_remote_action_modules.py index 82e92adf89..a147a9e5da 100644 --- a/tests/remote/test_remote_action_modules.py +++ b/tests/remote/test_remote_action_modules.py @@ -22,6 +22,7 @@ ERROR_LOCAL_CODE = "Executing local code is prohibited" ERROR_SYNC_TO_OUTSIDE = "Syncing files to outside the working dir" ERROR_SYNC_FROM_OUTSIDE = "Syncing files from outside the working dir" ERROR_SYNC_RSH = "Using custom synchronize rsh is prohibited" +ERROR_SCHEME_INVALID = "file urls are not allowed from localhost." class FunctionalActionModulesMixIn: @@ -221,6 +222,12 @@ class FunctionalActionModulesMixIn: self._run_job('known-hosts-bad', 'FAILURE', ERROR_ACCESS_OUTSIDE) + def test_uri_module(self): + self._run_job('uri-good', 'SUCCESS') + self._run_job('uri-bad-src', 'FAILURE', ERROR_ACCESS_OUTSIDE) + self._run_job('uri-bad-dest', 'FAILURE', ERROR_ACCESS_OUTSIDE) + self._run_job('uri-bad-url', 'FAILURE', ERROR_SCHEME_INVALID) + class TestActionModules28(AnsibleZuulTestCase, FunctionalActionModulesMixIn): ansible_version = '2.8' diff --git a/zuul/ansible/base/action/uri.py b/zuul/ansible/base/action/uri.py index bc64657ed1..44f60882c3 100644 --- a/zuul/ansible/base/action/uri.py +++ b/zuul/ansible/base/action/uri.py @@ -13,10 +13,14 @@ # You should have received a copy of the GNU General Public License # along with this software. If not, see . +from ansible.errors import AnsibleError +from ansible.module_utils.six.moves.urllib.parse import urlparse from zuul.ansible import paths uri = paths._import_ansible_action_plugin("uri") +ALLOWED_URL_SCHEMES = ('https', 'http', 'ftp') + class ActionModule(uri.ActionModule): @@ -34,5 +38,12 @@ class ActionModule(uri.ActionModule): dest = self._task.args.get(arg) if dest: paths._fail_if_unsafe(dest) + scheme = urlparse(self._task.args['url']).scheme + if scheme not in ALLOWED_URL_SCHEMES: + raise AnsibleError( + "{scheme} urls are not allowed from localhost." + " Only {allowed_schemes} are allowed".format( + scheme=scheme, + allowed_schemes=ALLOWED_URL_SCHEMES)) return super(ActionModule, self).run(tmp, task_vars)