Merge pull request #7584 from ryanpetrello/jinja2-sandbox

use jinja2.sandbox for credential type injectors

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot]
2020-07-08 20:53:02 +00:00
committed by GitHub
2 changed files with 15 additions and 8 deletions

View File

@@ -7,8 +7,8 @@ import json
import re import re
import urllib.parse import urllib.parse
from jinja2 import Environment, StrictUndefined from jinja2 import sandbox, StrictUndefined
from jinja2.exceptions import UndefinedError, TemplateSyntaxError from jinja2.exceptions import UndefinedError, TemplateSyntaxError, SecurityError
# Django # Django
from django.contrib.postgres.fields import JSONField as upstream_JSONBField from django.contrib.postgres.fields import JSONField as upstream_JSONBField
@@ -940,7 +940,7 @@ class CredentialTypeInjectorField(JSONSchemaField):
self.validate_env_var_allowed(key) self.validate_env_var_allowed(key)
for key, tmpl in injector.items(): for key, tmpl in injector.items():
try: try:
Environment( sandbox.ImmutableSandboxedEnvironment(
undefined=StrictUndefined undefined=StrictUndefined
).from_string(tmpl).render(valid_namespace) ).from_string(tmpl).render(valid_namespace)
except UndefinedError as e: except UndefinedError as e:
@@ -950,6 +950,10 @@ class CredentialTypeInjectorField(JSONSchemaField):
code='invalid', code='invalid',
params={'value': value}, params={'value': value},
) )
except SecurityError as e:
raise django_exceptions.ValidationError(
_('Encountered unsafe code execution: {}').format(e)
)
except TemplateSyntaxError as e: except TemplateSyntaxError as e:
raise django_exceptions.ValidationError( raise django_exceptions.ValidationError(
_('Syntax error rendering template for {sub_key} inside of {type} ({error_msg})').format( _('Syntax error rendering template for {sub_key} inside of {type} ({error_msg})').format(

View File

@@ -11,7 +11,7 @@ import tempfile
from types import SimpleNamespace from types import SimpleNamespace
# Jinja2 # Jinja2
from jinja2 import Template from jinja2 import sandbox
# Django # Django
from django.db import models from django.db import models
@@ -514,8 +514,11 @@ class CredentialType(CommonModelNameNotUnique):
# If any file templates are provided, render the files and update the # If any file templates are provided, render the files and update the
# special `tower` template namespace so the filename can be # special `tower` template namespace so the filename can be
# referenced in other injectors # referenced in other injectors
sandbox_env = sandbox.ImmutableSandboxedEnvironment()
for file_label, file_tmpl in file_tmpls.items(): for file_label, file_tmpl in file_tmpls.items():
data = Template(file_tmpl).render(**namespace) data = sandbox_env.from_string(file_tmpl).render(**namespace)
_, path = tempfile.mkstemp(dir=private_data_dir) _, path = tempfile.mkstemp(dir=private_data_dir)
with open(path, 'w') as f: with open(path, 'w') as f:
f.write(data) f.write(data)
@@ -537,14 +540,14 @@ class CredentialType(CommonModelNameNotUnique):
except ValidationError as e: except ValidationError as e:
logger.error('Ignoring prohibited env var {}, reason: {}'.format(env_var, e)) logger.error('Ignoring prohibited env var {}, reason: {}'.format(env_var, e))
continue continue
env[env_var] = Template(tmpl).render(**namespace) env[env_var] = sandbox_env.from_string(tmpl).render(**namespace)
safe_env[env_var] = Template(tmpl).render(**safe_namespace) safe_env[env_var] = sandbox_env.from_string(tmpl).render(**safe_namespace)
if 'INVENTORY_UPDATE_ID' not in env: if 'INVENTORY_UPDATE_ID' not in env:
# awx-manage inventory_update does not support extra_vars via -e # awx-manage inventory_update does not support extra_vars via -e
extra_vars = {} extra_vars = {}
for var_name, tmpl in self.injectors.get('extra_vars', {}).items(): for var_name, tmpl in self.injectors.get('extra_vars', {}).items():
extra_vars[var_name] = Template(tmpl).render(**namespace) extra_vars[var_name] = sandbox_env.from_string(tmpl).render(**namespace)
def build_extra_vars_file(vars, private_dir): def build_extra_vars_file(vars, private_dir):
handle, path = tempfile.mkstemp(dir = private_dir) handle, path = tempfile.mkstemp(dir = private_dir)