Skip to content

feat: analyse module bodies and class bodies as functions - #114

Merged
SizzleUnrlsd merged 3 commits into
mainfrom
feat/module-level-code
Sep 24, 2026
Merged

SizzleUnrlsd merged 3 commits into
mainfrom
feat/module-level-code

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Closes #113.

What

  • analyzable_functions now yields the module body as a synthetic function <module> and each top-level class's body as Cls.<body>, in source order, before the definitions they contain. They carry the module's / class's span, so scope_for resolves their names in the right scope (the module scope is now registered by its span).
  • Module variables are the body's locals: found on the regression corpus, where module-level for loops and x = input(); os.system(x) were unsupported or disconnected when every name was a global. Functions, classes and imports stay globals, which is what the call graph and the symbol resolution key on; other functions still read module variables through Global. Wildcard imports lower to nothing.
  • The call graph names a lambda made by the module body without a prefix, matching its scope. _enclosing ignores the synthetic bodies so a module-level lambda is not "nested" in <module>.
  • Project symbols sanitise <module> / <body>; CACHE_FORMAT 4 → 5 so cached modules gain their module-level findings.

Tests

tests/test_module_level.py (9): synthetic functions and their order, empty module, module-level os.system(input()) reported in <module>, an advisory entry point called at module level and in a class body reachable, a top-level def called from the body found through it, module variables as locals (taint connects, for supported), functions still read module variables as globals. One ceiling documented: a lambda bound to a module-level name and called through that name is not resolved.

Expectations that counted or listed functions were updated (<module> appears; coverage +1 per module with statements); two lower() test helpers skip the body function. 926 passed; ruff and mypy clean.

Regression snapshots

Re-recorded and reviewed: every new entry is in <module> — 13 missing-timeout, 2 ssrf (Vulnerable_fastapi exploit script: response JSON into a URL), 1 command-injection (httpie docs/contributors/fetch.py: sys.argv through main to check_output), 1 reachable-vulnerability (pygoat lab_code/test.py: module-level yaml.load). One removal: a syntax-error on deps.py that main already no longer produced (stale snapshot). Coverage function counts rose accordingly.

Downstream

The two xfail(strict=True) Django integrations in the enterprise repo (#17) will start passing and need their marks removed with the pin bump.

The module's statements outside its definitions are lowered as a function
named <module>, and a top-level class's statements outside its methods as
Cls.<body>, both carrying the span of what they stand for so their names
resolve in the right scope. Their calls are call sites, their values carry
taint and an advisory entry point called at import time is reachable.

Inside the module body, the variables the module assigns are the body's
locals: x = input(); os.system(x) connects in SSA and a module-level for
loop has a target, while functions, classes and imports stay globals for
the call graph and the symbol resolution. Wildcard imports lower to
nothing. Coverage counts the bodies; the cache format is bumped.

Regression snapshots re-recorded: 13 missing-timeout, 2 ssrf, 1
command-injection and 1 reachable-vulnerability at module level, and one
stale syntax-error entry main no longer produced.

Closes #113
…preter

full-stack-fastapi-template's deps.py uses 'except A, B:' (PEP 758), which
only Python 3.14 parses; the snapshot keeps its syntax-error note and the
coverage the CI observes.
@SizzleUnrlsd SizzleUnrlsd self-assigned this Sep 24, 2026
@SizzleUnrlsd
SizzleUnrlsd merged commit 9b39063 into main Sep 24, 2026
10 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the feat/module-level-code branch September 24, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analyse module-level and class-body code: call sites, taint and reachability

1 participant