Skip to content

fix(analyzers): resolve import aliases in AST and taint analyzers#115

Open
zied-jlassi wants to merge 1 commit into
NVIDIA:mainfrom
zied-jlassi:fix/import-alias-evasion-ast-taint
Open

fix(analyzers): resolve import aliases in AST and taint analyzers#115
zied-jlassi wants to merge 1 commit into
NVIDIA:mainfrom
zied-jlassi:fix/import-alias-evasion-ast-taint

Conversation

@zied-jlassi

Copy link
Copy Markdown

Closes #114.

Problem

behavioral_ast and behavioral_taint_tracking resolved call names purely
syntactically, so dangerous calls imported under an alias were missed — a skill could
evade detection just by renaming the import:

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 / taint flow missed

Fix

  • Expose the existing import-alias scan as build_import_aliases() — a thin wrapper over
    _build_import_aliases, which build_type_map already uses.
  • Add an apply_import_aliases(name, aliases) helper.
  • Thread an optional aliases argument through resolve_call_name() and
    resolve_call_name_typed() (backward compatible — defaults to None).
  • Both analyzers compute the alias map once per file and pass it through.
  • 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).

Tests

  • New TestImportAliasEvasion in both analyzer test modules: aliased dangerous forms
    are now detected (AST4/AST5/AST8, TT3/TT5), while aliased-but-safe imports
    produce no findings.
  • The existing behavioral suites continue to pass (33 AST + 46 taint).

Scope / follow-ups (intentionally out of this PR)

  • Reflection-based calls such as getattr(os, "system")(...) remain out of scope
    (pre-existing; AST7 only flags non-constant attribute access).
  • The alias map is module-level (no per-scope shadowing analysis). It can over-detect in
    contrived variable-shadowing cases, which errs on the safe side for a security tool.

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>
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.

[BUG] Import-alias evasion: behavioral_ast & behavioral_taint_tracking miss dangerous calls imported via 'from X import Y' / 'import X as Y'

1 participant