Skip to content

feat: report attacker input reaching eval and exec as code injection - #153

Merged
SizzleUnrlsd merged 3 commits into
mainfrom
feat/code-injection
Sep 25, 2026
Merged

SizzleUnrlsd merged 3 commits into
mainfrom
feat/code-injection

Conversation

@SizzleUnrlsd

@SizzleUnrlsd SizzleUnrlsd commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Closes #152.

eval and exec were CODE sinks, yet no rule reported CODE flows. The only finding came from dangerous-eval, which reports every call whatever it runs, so eval(request.args["expr"]) looked the same as eval("1 + 1").

What changes

  • New rule. code-injection is a TaintDetector of the CODE kind, like command-injection (severity HIGH). It reports attacker input reaching a CODE sink, with the usual refutation verdicts, and through project functions too (through, sink_line).
  • Narrower sinks. The eval and exec sinks of python-stdlib-models now read their first argument only, the code. Attacker data in the globals or locals dictionaries is data, not code, e.g. eval('x + 1', {'x': input()}). No rule reported CODE before, so no earlier result changes.
  • dangerous-eval is unchanged. It still reports every call to eval or exec, and the rules table says how the two relate.
  • Groundwork for Jinja2 and Mako. CoreTrace-Entreprise/coretrace-python-analyzer-plugins#3 adds their template sources as CODE sinks, and those only produce findings with this rule.

Regression corpus

Three new findings and none removed. I reviewed each one against the source, and all three are true positives:

Project Location Code
pygoat introduction/mitre.py:218, mitre_lab_25_api eval(request.POST.get('expression'))
pygoat introduction/views.py:460, cmd_lab2 eval(request.POST.get('val'))
vulnerable-flask fixture app.py:71, code_injection eval(request.args["expression"])

The first two are guarded only by tests on request.method or is_authenticated, which do not examine the value. Their verdict is therefore vulnerability.

Tests

  • tests/test_code_injection.py:
    • eval, exec, exec(compile(...)), and code built by concatenation are reported;
    • constant code, attacker data only in globals or locals, and ast.literal_eval are not;
    • dangerous-eval stays next to code-injection;
    • a flow through a project function is reported.
  • A mutation that removes the first-argument restriction makes two tests fail.
  • Tests that list or count the bundled plugins now include the new one (35 → 36): the shipped manifests, one detector per taint kind, documentation of every rule, the packaged plugins and the wheel. The wheel test is skipped without build, so the first CI run caught the count it still held; it now runs locally too.
  • Full suite: 1122 passed. Regression under Python 3.12: 19 passed, after updating the two snapshots above. mypy and ruff: clean.

eval and exec were CODE sinks with no rule reporting their flows, so the
only finding was dangerous-eval, whatever the call ran. code-injection
reports the calls attacker input reaches, with the refutation verdicts,
and the sinks now read the code argument only: the globals and locals
are data. The regression corpus gains three reviewed true positives.
@SizzleUnrlsd SizzleUnrlsd added the enhancement New feature or request label Sep 25, 2026
@SizzleUnrlsd SizzleUnrlsd self-assigned this Sep 25, 2026
@SizzleUnrlsd
SizzleUnrlsd merged commit fb57d90 into main Sep 25, 2026
10 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the feat/code-injection branch September 25, 2026 21:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Detection: a code-injection rule for attacker input reaching eval, exec and other CODE sinks

1 participant