PyPI / jinja2 / 3.1.6

jinja2 3.1.6

Source
https://github.com/pallets/jinja
Tag scanned
3.1.6
Compared with
3.1.5 (0 signals introduced since)
Files
83 scanned, 21 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 12 info
Scan time
9.3 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 10 of 10 warn and critical signals; 10 demoted to info.
Disclude version
4.0.0
Scanned (UTC)
2026-10-14 18:16

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info warn → info by review dynamic-execution docs/api.rst:817:1

    [markup prose] `eval` called on a string literal (ast pass, confidence 0.85)

    eval context object itself.
    dismissed LLM, confidence 0.95 Documentation prose mentioning eval, not executable code.
    Reasoning

    The match is the word 'eval' in reStructuredText prose ('eval context object itself'), not a call. The AST pass flagged a non-code file, so this is a false positive.

  2. info unicode-mixed-script src/jinja2/_identifier.py:5:27

    identifier `ׇֽֿׁׂׅׄؐ` mixes scripts: Hebrew + Arabic (in string/comment) (raw pass, confidence 0.40)

        r"[\w·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ
  3. info unicode-mixed-script src/jinja2/_identifier.py:5:70

    identifier `ܑۭܰ` mixes scripts: Arabic + Other (in string/comment) (raw pass, confidence 0.40)

    �ׇׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூ… (340 bytes total)
  4. info critical → info by review unicode-invisible src/jinja2/_identifier.py:5:1101

    4 invisible tag characters on this line (possible encoded payload) (raw pass, confidence 0.99)

    ��ꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ⟨U+FE00⟩-⟨U+FE0F⟩︠-︯︳︴﹍-﹏_𐇽𐋠𐍶-𐍺𐨁-𐨃𐨅𐨆𐨌-𐨏𐨸-𐨿𐨺𐫦𐫥𐴤-𐽆𐴧-𐽐𑀀-𑀂𑀸-𑁆𑁿-𑂂𑂰-𑂺𑄀-𑄂𑄧-𑄴𑅅𑅆𑅳𑆀-𑆂𑆳-𑇀𑇉-𑇌𑈬-𑈷𑈾𑋟-𑋪𑌀-𑌃𑌻𑌼𑌾-𑍄𑍇𑍈𑍋-𑍍𑍗… (339 bytes total)
    likely benign LLM, confidence 0.85 A generated Unicode character-class table for Python identifier matching.
    Reasoning

    The line is a large Unicode range table used to build the identifier regex (ID_Start/ID_Continue). Invisible tag characters appear because the table enumerates Unicode code points. The raw pass has a high false-positive rate, and this is a known data table, not a hidden payload.

  5. info warn → info by review dynamic-execution src/jinja2/bccache.py:79:25

    `marshal.load` deserializes Python bytecode from bytes — code-object sink (ast pass, confidence 0.75)

            try:
                self.code = marshal.load(f)
            except (EOFError, ValueError, TypeError):
                self.reset()
    likely benign LLM, confidence 0.75 Loads cached compiled template bytecode from disk via marshal.
    Reasoning

    marshal.load on a bytecode cache file is a real code-object sink, and loading untrusted cache files would be a risk. In Jinja2 it is the intended bytecode cache mechanism for compiled templates, with errors caught and the cache reset. It is a documented design choice, not obfuscation.

  6. info critical → info by review dynamic-execution src/jinja2/debug.py:105:22

    `compile` called on a constructed string (ast pass, confidence 0.80)

        # Raise an exception at the correct line number.
        code: CodeType = compile(
            "\n" * (lineno - 1) + "raise __jinja_exception__", filename, "exec"
        )
    likely benign LLM, confidence 0.90 Compiles a padding stub that raises an exception at the template's line number.
    Reasoning

    The compile call builds a string of newlines followed by a raise statement so the traceback reports the correct line number. This is a deliberate, well-known technique in Jinja's traceback rewriting, and the string is not attacker-controlled.

  7. info warn → info by review dynamic-execution src/jinja2/debug.py:145:9

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

        try:
            exec(code, globals, locals)
        except BaseException:
            return sys.exc_info()[2].tb_next  # type: ignore
    likely benign LLM, confidence 0.85 Executes compiled template code to capture a traceback for error rewriting.
    Reasoning

    exec runs a compiled template code object inside a try block to produce a real traceback frame for rewriting. This is the template execution path, not an unexpected dynamic execution.

  8. info warn → info by review dynamic-execution src/jinja2/environment.py:709:16

    `compile` called on a non-literal expression (ast pass, confidence 0.70)

            """
            return compile(source, filename, "exec")
    
        @typing.overload
    likely benign LLM, confidence 0.90 Compiles generated template source into a Python code object.
    Reasoning

    compile is the template compiler turning generated Python source into a code object. Compiling generated code is the core function of a template engine.

  9. info warn → info by review dynamic-execution src/jinja2/environment.py:1228:9

    `exec` called on a non-literal expression (ast pass, confidence 0.70)

            namespace = {"environment": environment, "__file__": code.co_filename}
            exec(code, namespace)
            rv = cls._from_namespace(environment, namespace, globals)
            rv._uptodate = uptodate
    likely benign LLM, confidence 0.90 Executes compiled template code to build a Template class namespace.
    Reasoning

    exec runs the compiled template code inside a namespace to create the template class. This is the expected execution step of a template engine.

  10. info critical → info by review dynamic-import src/jinja2/loaders.py:680:23

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

                try:
                    mod = __import__(module, None, None, ["root"])
                except ImportError as e:
                    raise TemplateNotFound(name) from e
    likely benign LLM, confidence 0.85 Imports a precompiled Jinja template module by name.
    Reasoning

    The __import__ call loads a precompiled template module by name in ModuleLoader. The module name comes from the loader's configured path, which is the designed behaviour of this loader.

  11. info critical → info by review dynamic-import src/jinja2/utils.py:157:20

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

            else:
                return __import__(import_name)
            return getattr(__import__(module, None, None, [obj]), obj)
        except (ImportError, AttributeError):
    likely benign LLM, confidence 0.90 Dynamic import helper that resolves a dotted name to a module attribute.
    Reasoning

    This is the standard import_string helper, which resolves a dotted path to an object. It is a common utility pattern, and the argument is supplied by the caller, not fetched from untrusted input.

  12. info critical → info by review dynamic-import src/jinja2/utils.py:158:24

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

                return __import__(import_name)
            return getattr(__import__(module, None, None, [obj]), obj)
        except (ImportError, AttributeError):
            if not silent:
    likely benign LLM, confidence 0.90 Dynamic getattr-based import of a named object from a module.
    Reasoning

    This is the second half of the same import_string helper, resolving an object from a module with getattr. It is ordinary dynamic-import logic with no sign of obfuscation.

Reproduce This Scan

Run disclude locally to verify. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.0.0
git clone https://github.com/pallets/jinja jinja && cd jinja
git checkout 3.1.6
disclude scan --severity info --diff 3.1.5 .