Skip to content

fix: resolve module-level names bound through another instance - #156

Merged
SizzleUnrlsd merged 2 commits into
mainfrom
fix/module-level-instances
Sep 25, 2026
Merged

SizzleUnrlsd merged 2 commits into
mainfrom
fix/module-level-instances

Conversation

@SizzleUnrlsd

Copy link
Copy Markdown
Contributor

Summary

A module-level name now denotes what its value denotes in a function body, so the calls made through it resolve. Previously only a call of an imported symbol bound a name (app = Flask(__name__)). A name bound through another module-level name stayed unknown, and so did every call made through it.

Closes #155.

Module-level binding Now denotes
cursor = conn.cursor() after conn = sqlite3.connect(...) python.sqlite3.connect.cursor
same = conn python.sqlite3.connect
run = os.system python.os.system
home = os.environ['HOME'] python.os.environ: an item carries its container's symbol, as derive_symbols does
raw = connections['default'].cursor() python.django.db.connections.cursor

Names resolve in statement order. A binding that uses a name before the module binds it stays unresolved.

Scope

  • The derivation stays in analyze_symbols, through a private _denoted. SymbolTable.resolve_expression is unchanged: it also resolves annotations, decorators and class bases, where an item such as list[str] must not become list.
  • A name bound to a project function or class still resolves through its own mechanisms (factories, known functions), as before.
  • pymongo (CoreTrace-Entreprise/coretrace-python-analyzer-plugins#3) keeps its database and collection in such names (db = client.shop). They now resolve, as python.pymongo.MongoClient.shop.users.find_one. Modelling the dynamic database and collection names is a separate change.

Tests

  • test_module_level_names_bound_through_an_instance_denote_what_a_function_derives: alias, attribute, item, call chain, statement order, a constant.
  • test_a_query_through_a_module_level_cursor_is_an_injection: end to end, a Flask route executing a query through a module-level sqlite3 cursor is a sql-injection.

Both failed before the fix. Locally: 1124 passed; regression snapshots 19 passed, unchanged; ruff check and mypy clean.

A module-level name denotes what its value denotes in a function body: an alias, an attribute, an item or a call of an import or of an earlier module-level name, resolved in statement order.
@SizzleUnrlsd SizzleUnrlsd added the bug Something isn't working label Sep 25, 2026
@SizzleUnrlsd SizzleUnrlsd self-assigned this Sep 25, 2026
@SizzleUnrlsd
SizzleUnrlsd merged commit 574f032 into main Sep 25, 2026
10 checks passed
@SizzleUnrlsd
SizzleUnrlsd deleted the fix/module-level-instances branch September 25, 2026 23:30
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.

Detection: module-level names bound through another instance resolve in functions

1 participant