Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jinja2 autoescape codemod should allow for setting autoescape to select_autoescape callable #285

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/codemodder/scripts/generate_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 3 additions & 3 deletions src/core_codemods/enable_jinja2_autoescape.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
...
Expand Down
42 changes: 35 additions & 7 deletions tests/codemods/test_enable_jinja2_autoescape.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytest
from core_codemods.enable_jinja2_autoescape import EnableJinja2Autoescape
from tests.codemods.base_codemod_test import BaseSemgrepCodemodTest

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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)
Loading