fix(analyzers): resolve import aliases in AST and taint analyzers#115
Merged
rng1995 merged 1 commit intoJun 22, 2026
Merged
Conversation
behavioral_ast and behavioral_taint_tracking resolved call names purely
syntactically, so dangerous calls imported under an alias were missed:
from os import system # system("id") -> AST5 missed
import os as o # o.system("id") -> AST5 missed
from subprocess import run # run([...]) -> AST4 missed
import subprocess as sp # sp.run(secret) -> AST4 / TT missed
A skill could evade detection simply by importing the primitive under
another name. This reuses the existing import-alias scan
(_build_import_aliases), exposes it as build_import_aliases(), adds an
apply_import_aliases() helper, and threads an optional `aliases` argument
through resolve_call_name() and resolve_call_name_typed(). Aliases are
normalized before the type-map lookup so already-canonical names are not
re-expanded (e.g. `from socket import socket` must not yield
socket.socket.socket.recv).
Adds TestImportAliasEvasion coverage to both analyzers: aliased dangerous
forms are now detected, while aliased-but-safe imports produce no findings.
Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com>
rng1995
approved these changes
Jun 21, 2026
rng1995
left a comment
Collaborator
There was a problem hiding this comment.
Nice, well-scoped fix for import-alias evasion. Approving.
build_import_aliases()is a clean public wrapper over the existing_build_import_aliasesalready used bybuild_type_map, and threading an optionalaliasesarg throughresolve_call_name()/resolve_call_name_typed()keeps this fully backward compatible (defaults toNone).- The ordering fix in
resolve_call_name_typed— normalize aliases before the type-map lookup — is the subtle-but-correct part, and the inline comment about avoidingsocket.socket.socket.recvre-expansion explains why.apply_import_aliaseshandles bothfrom x import yandimport x as y(root-of-dotted-name) forms. - Both
from ... importandimport ... asare covered for AST4/AST5/AST8 and TT3/TT5, including the aliased credential→network and aliased-subscript (o.environ[...]) flows, plus negative tests that aliased-but-safe imports don't fire.
The acknowledged limitations (no per-scope shadowing; reflection like getattr(os, "system")(...) still out of scope) are reasonable and err on the safe side for a security tool. LGTM.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #114.
Problem
behavioral_astandbehavioral_taint_trackingresolved call names purelysyntactically, so dangerous calls imported under an alias were missed — a skill could
evade detection just by renaming the import:
Fix
build_import_aliases()— a thin wrapper over_build_import_aliases, whichbuild_type_mapalready uses.apply_import_aliases(name, aliases)helper.aliasesargument throughresolve_call_name()andresolve_call_name_typed()(backward compatible — defaults toNone).not re-expanded (e.g.
from socket import socketmust not yieldsocket.socket.socket.recv).Tests
TestImportAliasEvasionin both analyzer test modules: aliased dangerous formsare now detected (
AST4/AST5/AST8,TT3/TT5), while aliased-but-safe importsproduce no findings.
Scope / follow-ups (intentionally out of this PR)
getattr(os, "system")(...)remain out of scope(pre-existing;
AST7only flags non-constant attribute access).contrived variable-shadowing cases, which errs on the safe side for a security tool.