Skip to content

Commit 414acff

Browse files
author
mnbplus
committed
test: enable interprocedural taint propagation cases
1 parent 5988249 commit 414acff

File tree

1 file changed

+106
-8
lines changed

1 file changed

+106
-8
lines changed

tests/test_interprocedural.py

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,29 @@
44
Some tests are skipped pending full API alignment.
55
"""
66
import ast
7-
import pytest
7+
import os
8+
import textwrap
89

910
from pyaegis.core.call_graph import GlobalSymbolTable, InterproceduralAnalyzer
11+
from pyaegis.core.parser import PyASTParser
12+
from pyaegis.core.taint import TaintTracker
13+
14+
15+
def _write(tmp_path, rel: str, code: str):
16+
p = tmp_path / rel
17+
p.parent.mkdir(parents=True, exist_ok=True)
18+
p.write_text(textwrap.dedent(code).lstrip("\n"), encoding="utf-8")
19+
return p
20+
21+
22+
def _first_call(tree: ast.AST) -> ast.Call:
23+
for node in ast.walk(tree):
24+
if isinstance(node, ast.Call):
25+
return node
26+
raise AssertionError("No ast.Call found")
27+
28+
29+
# --- existing symbol table tests ---
1030

1131

1232
def test_global_symbol_table_build_empty():
@@ -45,13 +65,91 @@ def test_symbol_table_registers_imports():
4565
assert "helper" in st.imports["app.py"]
4666

4767

48-
@pytest.mark.skip(reason="API alignment pending - tracked in ROADMAP P0")
49-
def test_interprocedural_basic():
50-
pass
68+
# --- inter-procedural taint tests ---
69+
70+
71+
def test_interprocedural_basic(tmp_path):
72+
"""Taint should flow across modules into a sink inside the callee."""
73+
utils = _write(
74+
tmp_path,
75+
"utils.py",
76+
"""
77+
def run_cmd(cmd):
78+
import os
79+
os.system(cmd)
80+
""",
81+
)
82+
app = _write(
83+
tmp_path,
84+
"app.py",
85+
"""
86+
from utils import run_cmd
87+
88+
def handler(request):
89+
cmd = request.args.get("cmd")
90+
run_cmd(cmd)
91+
""",
92+
)
93+
94+
gst = GlobalSymbolTable.build([str(utils), str(app)], root_dir=str(tmp_path))
95+
parser = PyASTParser(str(app))
96+
parser.parse()
97+
cfg = parser.extract_cfg()
98+
99+
tracker = TaintTracker(
100+
sources=["request", "request.args", "input"],
101+
sinks=["os.system", "eval"],
102+
sanitizers=[],
103+
symbol_table=gst,
104+
)
105+
tracker.analyze_cfg(cfg, filepath=str(app))
106+
107+
findings = tracker.get_findings()
108+
assert any(
109+
f.sink_name == "os.system"
110+
and os.path.abspath(f.file_path) == os.path.abspath(str(utils))
111+
for f in findings
112+
)
51113

52114

53-
@pytest.mark.skip(
54-
reason="requires full inter-procedural taint integration - ROADMAP P0"
55-
)
56115
def test_interprocedural_propagates_internal_source_return(tmp_path):
57-
pass
116+
"""Taint should propagate when the callee sources data internally."""
117+
utils = _write(
118+
tmp_path,
119+
"utils.py",
120+
"""
121+
def get_cmd():
122+
return input("cmd")
123+
""",
124+
)
125+
app = _write(
126+
tmp_path,
127+
"app.py",
128+
"""
129+
from utils import get_cmd
130+
131+
def handler():
132+
cmd = get_cmd()
133+
eval(cmd)
134+
""",
135+
)
136+
137+
gst = GlobalSymbolTable.build([str(utils), str(app)], root_dir=str(tmp_path))
138+
parser = PyASTParser(str(app))
139+
parser.parse()
140+
cfg = parser.extract_cfg()
141+
142+
tracker = TaintTracker(
143+
sources=["input", "request"],
144+
sinks=["eval", "os.system"],
145+
sanitizers=[],
146+
symbol_table=gst,
147+
)
148+
tracker.analyze_cfg(cfg, filepath=str(app))
149+
150+
findings = tracker.get_findings()
151+
assert any(
152+
f.sink_name == "eval"
153+
and os.path.abspath(f.file_path) == os.path.abspath(str(app))
154+
for f in findings
155+
)

0 commit comments

Comments
 (0)