diff --git a/src/codemodder/scripts/generate_docs.py b/src/codemodder/scripts/generate_docs.py index dc1c58d3..bcd2e426 100644 --- a/src/codemodder/scripts/generate_docs.py +++ b/src/codemodder/scripts/generate_docs.py @@ -29,7 +29,7 @@ class DocMetadata: ), "enable-jinja2-autoescape": DocMetadata( importance="High", - guidance_explained="This codemod protects your applications against XSS attacks. We believe using the default behavior is unsafe.", + guidance_explained="This codemod protects your applications against XSS attacks. However, it's possible you would like to set the `autoescape` parameter to a custom callable.", ), "fix-mutable-params": DocMetadata( importance="Medium", diff --git a/src/core_codemods/enable_jinja2_autoescape.py b/src/core_codemods/enable_jinja2_autoescape.py index d4426ade..7d245328 100644 --- a/src/core_codemods/enable_jinja2_autoescape.py +++ b/src/core_codemods/enable_jinja2_autoescape.py @@ -11,7 +11,7 @@ class EnableJinja2Autoescape(SimpleCodemod): metadata = Metadata( name="enable-jinja2-autoescape", summary="Enable Jinja2 Autoescape", - review_guidance=ReviewGuidance.MERGE_WITHOUT_REVIEW, + review_guidance=ReviewGuidance.MERGE_AFTER_REVIEW, references=[ Reference(url="https://owasp.org/www-community/attacks/xss/"), Reference( @@ -28,12 +28,12 @@ class EnableJinja2Autoescape(SimpleCodemod): - patterns: - pattern: jinja2.Environment(...) - pattern-not: jinja2.Environment(..., autoescape=True, ...) + - pattern-not: jinja2.Environment(..., autoescape=jinja2.select_autoescape(...), ...) - pattern-inside: | import jinja2 ... - patterns: - - pattern: aiohttp_jinja2.setup(...) - - pattern-not: aiohttp_jinja2.setup(..., autoescape=True, ...) + - pattern: aiohttp_jinja2.setup(..., autoescape=False, ...) - pattern-inside: | import aiohttp_jinja2 ... diff --git a/tests/codemods/test_enable_jinja2_autoescape.py b/tests/codemods/test_enable_jinja2_autoescape.py index 5998e5b3..3c3f423d 100644 --- a/tests/codemods/test_enable_jinja2_autoescape.py +++ b/tests/codemods/test_enable_jinja2_autoescape.py @@ -1,3 +1,4 @@ +import pytest from core_codemods.enable_jinja2_autoescape import EnableJinja2Autoescape from tests.codemods.base_codemod_test import BaseSemgrepCodemodTest @@ -99,10 +100,26 @@ def test_autoescape_enabled(self, tmpdir): expexted_output = input_code self.run_and_assert(tmpdir, input_code, expexted_output) + @pytest.mark.parametrize( + "code", + [ + """ + import jinja2 + env = jinja2.Environment(autoescape=jinja2.select_autoescape()) + """, + """ + import jinja2 + env = jinja2.Environment(autoescape=jinja2.select_autoescape(disabled_extensions=('txt',), default_for_string=True, default=True)) + """, + ], + ) + def test_autoescape_callable(self, tmpdir, code): + self.run_and_assert(tmpdir, code, code) + def test_aiohttp_import_setup(self, tmpdir): input_code = """ import aiohttp_jinja2 - aiohttp_jinja2.setup(app) + aiohttp_jinja2.setup(app, autoescape=False) """ expected_output = """ import aiohttp_jinja2 @@ -113,7 +130,7 @@ def test_aiohttp_import_setup(self, tmpdir): def test_aiohttp_import_from_setup(self, tmpdir): input_code = """ from aiohttp_jinja2 import setup - setup(app) + setup(app, autoescape=False) """ expected_output = """ from aiohttp_jinja2 import setup @@ -124,7 +141,7 @@ def test_aiohttp_import_from_setup(self, tmpdir): def test_aiohttp_import_alias(self, tmpdir): input_code = """ from aiohttp_jinja2 import setup as setup_jinja2 - setup_jinja2(app) + setup_jinja2(app, autoescape=False) """ expected_output = """ from aiohttp_jinja2 import setup as setup_jinja2 @@ -141,13 +158,24 @@ def test_aiohttp_import_alias_no_change(self, tmpdir): """ self.run_and_assert(tmpdir, input_code, expected_output) - def test_aiohttp_set_false(self, tmpdir): + def test_aiohttp_autoescape_default(self, tmpdir): input_code = """ import aiohttp_jinja2 - aiohttp_jinja2.setup(app, autoescape=False) + aiohttp_jinja2.setup(app) """ - expected_output = """ + self.run_and_assert(tmpdir, input_code, input_code) + + def test_aiohttp_autoescape_True(self, tmpdir): + input_code = """ import aiohttp_jinja2 aiohttp_jinja2.setup(app, autoescape=True) """ - self.run_and_assert(tmpdir, input_code, expected_output) + self.run_and_assert(tmpdir, input_code, input_code) + + def test_aiohttp_autoescape_callable(self, tmpdir): + input_code = """ + import aiohttp_jinja2 + import jinja + aiohttp_jinja2.setup(app, autoescape=jinja2.select_autoescape()) + """ + self.run_and_assert(tmpdir, input_code, input_code)