From 27eea75f3c7a215e230567ab3e7e26e29670c3fa Mon Sep 17 00:00:00 2001 From: Rob Fletcher Date: Tue, 24 Feb 2015 10:02:52 -0800 Subject: [PATCH] Add mako templating plugin and XSS profile Adds plugin to warn on Mako templates. Since mako does not have template wide autoescaping (that we can verify is turned on), a developer is responsible to escape each variable individually in the templates. This is higher than info because the *only* way to escape is via the variables, but not set to ERROR because we can't know for sure if any of those variables are used and/or malicious. Also add an XSS profile. Change-Id: I65515f9584d67d8b7b58b71b6ddb447c307675d1 --- .gitignore | 1 + bandit.yaml | 5 +++++ bandit/plugins/jinja2_templates.py | 2 +- bandit/plugins/mako_templates.py | 33 ++++++++++++++++++++++++++++++ examples/mako_templating.py | 11 ++++++++++ tests/test_functional.py | 3 +++ 6 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 bandit/plugins/mako_templates.py create mode 100644 examples/mako_templating.py diff --git a/.gitignore b/.gitignore index 8cd5fc99..36a192df 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ venv* .DS_Store *.egg *.egg-info +.eggs/ .tox .testrepository build/* diff --git a/bandit.yaml b/bandit.yaml index 326b11e5..30e5f4c9 100644 --- a/bandit.yaml +++ b/bandit.yaml @@ -28,6 +28,11 @@ include: exclude_dirs: profiles: + XSS: + include: + - jinja2_autoescape_false + - use_of_mako_templates + ShellInjection: include: - subprocess_popen_with_shell_equals_true diff --git a/bandit/plugins/jinja2_templates.py b/bandit/plugins/jinja2_templates.py index 805018c6..36f8b489 100644 --- a/bandit/plugins/jinja2_templates.py +++ b/bandit/plugins/jinja2_templates.py @@ -21,7 +21,7 @@ from bandit.core.test_properties import * @checks('Call') -def autoescape_false(context): +def jinja2_autoescape_false(context): # check type just to be safe if type(context.call_function_name_qual) == str: qualname_list = context.call_function_name_qual.split('.') diff --git a/bandit/plugins/mako_templates.py b/bandit/plugins/mako_templates.py new file mode 100644 index 00000000..dc6f2136 --- /dev/null +++ b/bandit/plugins/mako_templates.py @@ -0,0 +1,33 @@ +# -*- coding:utf-8 -*- +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import bandit +from bandit.core.test_properties import * + + +@checks('Call') +def use_of_mako_templates(context): + # check type just to be safe + if type(context.call_function_name_qual) == str: + qualname_list = context.call_function_name_qual.split('.') + func = qualname_list[-1] + if 'mako' in qualname_list and func == 'Template': + # unlike Jinja2, mako does not have a template wide autoescape + # feature and thus each variable must be carefully sanitized. + return(bandit.WARN, "Mako templates allow HTML/JS rendering" + " by default and are inherently open to XSS attacks." + " Ensure variables in all templates are properly" + " sanitized via the 'n', 'h' or 'x' flags (depending on" + " context). For example, to HTML escape the variable 'data'" + " do ${ data |h }") diff --git a/examples/mako_templating.py b/examples/mako_templating.py new file mode 100644 index 00000000..29dd38c9 --- /dev/null +++ b/examples/mako_templating.py @@ -0,0 +1,11 @@ +from mako.template import Template +import mako + +from mako import template + +Template("hello") + +# XXX(fletcher): for some reason, bandit is missing the one below. keeping it +# in for now so that if it gets fixed inadvertitently we know. +mako.template.Template("hern") +template.Template("hern") diff --git a/tests/test_functional.py b/tests/test_functional.py index 77f30bd5..4684c92b 100644 --- a/tests/test_functional.py +++ b/tests/test_functional.py @@ -218,3 +218,6 @@ class FunctionalTests(unittest.TestCase): def test_secret_config_option(self): '''Test for `secret=True` in Oslo's config.''' self.check_example('secret-config-option.py', info=1, warn=2) + + def test_mako_templating(self): + self.check_example('mako_templating.py', warn=3)