test: 加强 #201/#224 回归测试至可复现级别

- #201 warmup 测试改为与全历史计算基准对比数值 (ewm 指标从首值播种,
  仅断言非 NaN 无法区分新旧代码)
- #224 注入测试改用合法同构 parquet 做目标 (读 /etc/passwd 在未加固
  连接下同样报错, 区分不了攻击面是否真实闭合)

两个测试均已在修复前代码 (41205b1/e89ea9b) 上验证会失败。
This commit is contained in:
shy3130
2026-09-03 13:26:42 +08:00
parent ef59df4a67
commit 91fef6d793
2 changed files with 30 additions and 3 deletions
+10 -2
View File
@@ -49,9 +49,17 @@ def test_load_panel_warms_up_indicators(monkeypatch) -> None:
# warmup 行不进入结果面板 (pandas datetime64 与 date 直接比较会类型不符)
assert str(panel["date"].min())[:10] == start.isoformat()
assert str(panel["date"].max())[:10] == end.isoformat()
# 区间首日的指标已有历史窗口可用, 不再是 NaN
# 数值复现: 区间头部的指标必须与「全历史计算后裁剪」的基准一致。
# 只断言非 NaN 区分不了新旧代码 —— ewm 类指标 (RSI/MACD) 从首个值
# 播种, 无 warmup 时首行也有值, 只是数值失真 (#201 的实际危害)
from app.indicators.pipeline import compute_all
reference = compute_all(df).filter(pl.col("date") >= start)
ref_rsi = float(reference["rsi_14"][0])
first = panel.iloc[0]
assert first["rsi_14"] == first["rsi_14"] # NaN != NaN
assert abs(first["rsi_14"] - ref_rsi) < 1.0, (
f"rsi_14 without warmup: {first['rsi_14']} vs reference {ref_rsi}"
)
def test_load_panel_insufficient_history_degrades_gracefully(monkeypatch) -> None:
+20 -1
View File
@@ -37,7 +37,7 @@ def test_normal_condition_still_works() -> None:
def test_injected_read_parquet_is_rejected() -> None:
# 注入试图读任意文件: external_access 关闭后 DuckDB 直接报错,
# run_custom 的 except 分支吞错返回空结果, 而非泄漏文件内容
# run() 的 except 分支吞错返回空结果, 而非泄漏文件内容
svc = _service_with_panel(_panel())
result = svc.run(
date(2026, 9, 2),
@@ -47,6 +47,25 @@ def test_injected_read_parquet_is_rejected() -> None:
assert result.rows == []
def test_injected_read_of_valid_parquet_leaks_nothing(tmp_path) -> None:
# 用合法 parquet 做注入目标才能区分新旧代码: /etc/passwd 不是 parquet,
# 未加固的连接读它也会报错; 合法同构文件在未加固连接下会真的混入结果
victim = tmp_path / "victim.parquet"
_panel().write_parquet(victim)
svc = _service_with_panel(pl.DataFrame(
{"symbol": ["999999.SZ"], "close": [99.0], "turnover_rate": [9.0]}
))
result = svc.run(
date(2026, 9, 2),
[f"1=1) UNION SELECT * FROM read_parquet('{victim}') --"],
limit=10,
)
# 加固后注入被拒 → 整条查询 fail-closed 返回空 (或至多剩原面板行);
# 未加固时 victim 的 2 行会混入结果
assert all(r["symbol"] == "999999.SZ" for r in result.rows)
assert len(result.rows) <= 1
def test_injected_copy_write_is_rejected(tmp_path) -> None:
target = tmp_path / "pwned.csv"
svc = _service_with_panel(_panel())