Merge "Fix plugin injection vulnerability"

This commit is contained in:
Zuul 2018-03-16 18:09:14 +00:00 committed by Gerrit Code Review
commit 18ded8798d
25 changed files with 242 additions and 1 deletions

View File

@ -0,0 +1,17 @@
- pipeline:
name: check
manager: independent
post-review: true
trigger:
gerrit:
- event: patchset-created
success:
gerrit:
Verified: 1
failure:
gerrit:
Verified: -1
- job:
name: base
parent: null

View File

@ -0,0 +1 @@
test

View File

@ -0,0 +1,14 @@
import subprocess
def my_cool_test(string):
shell_output = subprocess.check_output(['hostname'])
return 'hostname: %s' % shell_output.decode('utf-8')
class FilterModule(object):
def filters(self):
return {
'my_cool_test': my_cool_test
}

View File

@ -0,0 +1,3 @@
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,4 @@
- hosts: all
roles:
- bare-role

View File

@ -0,0 +1 @@
symlink: ../filter-plugin-playbook/filter_plugins

View File

@ -0,0 +1,5 @@
- hosts: all
tasks:
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,14 @@
import subprocess
def my_cool_test(string):
shell_output = subprocess.check_output(['hostname'])
return 'hostname: %s' % shell_output.decode('utf-8')
class FilterModule(object):
def filters(self):
return {
'my_cool_test': my_cool_test
}

View File

@ -0,0 +1,5 @@
- hosts: all
tasks:
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,14 @@
import subprocess
def my_cool_test(string):
shell_output = subprocess.check_output(['hostname'])
return 'hostname: %s' % shell_output.decode('utf-8')
class FilterModule(object):
def filters(self):
return {
'my_cool_test': my_cool_test
}

View File

@ -0,0 +1,3 @@
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,4 @@
- hosts: all
roles:
- local-role

View File

@ -0,0 +1,4 @@
- hosts: all
roles:
- shared-bare-role

View File

@ -0,0 +1,4 @@
- hosts: all
roles:
- shared-role

View File

@ -0,0 +1,4 @@
- project:
check:
jobs:
- noop

View File

@ -0,0 +1,14 @@
import subprocess
def my_cool_test(string):
shell_output = subprocess.check_output(['hostname'])
return 'hostname: %s' % shell_output.decode('utf-8')
class FilterModule(object):
def filters(self):
return {
'my_cool_test': my_cool_test
}

View File

@ -0,0 +1,3 @@
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,14 @@
import subprocess
def my_cool_test(string):
shell_output = subprocess.check_output(['hostname'])
return 'hostname: %s' % shell_output.decode('utf-8')
class FilterModule(object):
def filters(self):
return {
'my_cool_test': my_cool_test
}

View File

@ -0,0 +1,3 @@
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,4 @@
- hosts: all
roles:
- project-role

View File

@ -0,0 +1,14 @@
import subprocess
def my_cool_test(string):
shell_output = subprocess.check_output(['hostname'])
return 'hostname: %s' % shell_output.decode('utf-8')
class FilterModule(object):
def filters(self):
return {
'my_cool_test': my_cool_test
}

View File

@ -0,0 +1,3 @@
- name: Test filter plugin
debug:
msg: "{{ 'ignore me' | my_cool_test }}"

View File

@ -0,0 +1,11 @@
- tenant:
name: tenant-one
source:
gerrit:
config-projects:
- common-config
untrusted-projects:
- org/project
- org/project2
- org/project3
- org/projectrole

View File

@ -3429,3 +3429,48 @@ class TestJobOutput(AnsibleZuulTestCase):
log_output = output.getvalue()
self.assertIn('Final playbook failed', log_output)
self.assertIn('Failure test', log_output)
class TestPlugins(AnsibleZuulTestCase):
tenant_config_file = 'config/speculative-plugins/main.yaml'
def _run_job(self, job_name, project='org/project', roles=''):
# Output extra ansible info so we might see errors.
self.executor_server.verbose = True
conf = textwrap.dedent(
"""
- job:
name: {job_name}
run: playbooks/{job_name}/test.yaml
nodeset:
nodes:
- name: controller
label: whatever
{roles}
- project:
check:
jobs:
- {job_name}
""".format(job_name=job_name, roles=roles))
file_dict = {'zuul.yaml': conf}
A = self.fake_gerrit.addFakeChange(project, 'master', 'A',
files=file_dict)
self.fake_gerrit.addEvent(A.getPatchsetCreatedEvent(1))
self.waitUntilSettled()
message = A.messages[0]
self.assertIn('ERROR Ansible plugin dir', message)
self.assertIn('found adjacent to playbook', message)
self.assertIn('in non-trusted repo', message)
def test_filter_plugin(self):
self._run_job('filter-plugin-playbook')
self._run_job('filter-plugin-playbook-symlink')
self._run_job('filter-plugin-bare-role')
self._run_job('filter-plugin-role')
self._run_job('filter-plugin-repo-role', project='org/projectrole')
self._run_job('filter-plugin-shared-role',
roles="roles: [{zuul: 'org/project2'}]")
self._run_job('filter-plugin-shared-bare-role',
roles="roles: [{zuul: 'org/project3', name: 'shared'}]")

View File

@ -1028,6 +1028,7 @@ class AnsibleJob(object):
'''
for entry in os.listdir(path):
entry = os.path.join(path, entry)
if os.path.isdir(entry) and entry.endswith('_plugins'):
raise ExecutorError(
"Ansible plugin dir %s found adjacent to playbook %s in "
@ -1036,8 +1037,40 @@ class AnsibleJob(object):
def findPlaybook(self, path, trusted=False):
if os.path.exists(path):
if not trusted:
# Plugins can be defined in multiple locations within the
# playbook's subtree.
#
# 1. directly within the playbook:
# block playbook_dir/*_plugins
#
# 2. within a role defined in playbook_dir/<rolename>:
# block playbook_dir/*/*_plugins
#
# 3. within a role defined in playbook_dir/roles/<rolename>:
# block playbook_dir/roles/*/*_plugins
playbook_dir = os.path.dirname(os.path.abspath(path))
self._blockPluginDirs(playbook_dir)
paths_to_check = []
def addPathsToCheck(root_dir):
if os.path.isdir(root_dir):
for entry in os.listdir(root_dir):
entry = os.path.join(root_dir, entry)
if os.path.isdir(entry):
paths_to_check.append(entry)
# handle case 1
paths_to_check.append(playbook_dir)
# handle case 2
addPathsToCheck(playbook_dir)
# handle case 3
addPathsToCheck(os.path.join(playbook_dir, 'roles'))
for path_to_check in paths_to_check:
self._blockPluginDirs(path_to_check)
return path
raise ExecutorError("Unable to find playbook %s" % path)