Skip to content

fix(refutation): prove only the allowlists that are constant and stable - #247

Merged
SizzleUnrlsd merged 2 commits into
mainfrom
fix/constant-allowlists
Oct 13, 2026
Merged

SizzleUnrlsd merged 2 commits into
mainfrom
fix/constant-allowlists

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Summary

Implements the approach agreed in #239.

What proves a value:

  • A membership test proves the value when every member is a constant that cannot change before the test:
    • a tuple, list or set display, or a dict display by its keys, tested directly or through .keys();
    • frozenset, set, tuple or list of one of these;
    • a module-level tuple, or frozenset of a display, of constants, bound once and never rebound.
  • The module-level case is a new ModuleCollectionsAnalysis. It shares the "bound once, never rebound" rule with ModuleStringsAnalysis through module_bindings.
  • A name the semantic tables know as an instance (ALLOWED = frozenset(...)) is loaded as the Symbol of frozenset. The refutation therefore recognises it by the span of the name in the function's HIR.
  • A lookup in a dict display ({...}[key], .get(key[, default])) carries the taint of its values, of what was stored in it and of the default, never the taint of the key.

What does not prove a value:

  • A list, set or dict display of the function counts only while the function only reads it. This closes a false negative on main: allowed = ["ls"]; allowed.append(x); if cmd in allowed was refuted and is now a hotspot. The same holds for a display passed to a call, or with an unpacked element (("ls", *extra)).
  • Module-level lists, sets and dicts keep today's verdict: a hotspot for a membership test, a vulnerability for a lookup.
  • As for module-level strings, rebinding a module-level name from another module is not seen.

Verification

  • tests/test_allowlist_shapes.py, new: 34 tests through the bundled command-injection rule. 13 of them fail on main. No existing test changed.
  • Full suite green, apart from test_packaging.py::test_the_version_has_one_source_of_truth, which also fails on main (locally installed metadata). ruff check and mypy clean.
  • Regression suite: 18 of 19 repositories match their snapshots. full-stack-fastapi-template fails on coverage (43 files and 198 functions observed, 42 and 194 expected) on main too; its findings are identical with and without this change.
  • healthchecks and mitmproxy: identical findings, and no measurable time difference.

@SizzleUnrlsd SizzleUnrlsd added the bug Something isn't working label Oct 13, 2026
@SizzleUnrlsd SizzleUnrlsd self-assigned this Oct 13, 2026
…t keys out of lookups

A list or set built by list() or set() proves a membership test only while the function only reads it; a lookup in a dict display carries the taint of its values, unpacked mappings and later stores, never of its keys.
@SizzleUnrlsd
SizzleUnrlsd merged commit ca3b215 into main Oct 13, 2026
10 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the fix/constant-allowlists branch October 13, 2026 05:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant