70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
|
|
"""安全探针统一的「判据 + 退出码」小工具(供 test/security-hardening/*.py 使用)。
|
|||
|
|
|
|||
|
|
为什么需要它
|
|||
|
|
------------
|
|||
|
|
这些探针早期只 `print` 观察结果、结尾写死 `return 0` ⇒ **无论安全控制是否生效都 exit 0**:
|
|||
|
|
人眼看到的是 ❌/异常现象,自动化(CI、部署前置检查、agent 验收)看到的却是绿灯。
|
|||
|
|
这与 `tsc --noEmit` 空跑、`tcp_e2e_smoke.py` 永远 exit 0 属**同一类假绿**。
|
|||
|
|
|
|||
|
|
统一口径(与 AGENTS.md「验收流程」一致)
|
|||
|
|
--------------------------------------
|
|||
|
|
0 = 全部判据通过
|
|||
|
|
1 = 有判据失败(失败项额外打到 stderr,便于只看 stderr 定位)
|
|||
|
|
2 = 环境/连接问题(调用方自行 `raise SystemExit(2)`,例如读不到 .env、连不上 Redis/后端)
|
|||
|
|
|
|||
|
|
用法
|
|||
|
|
----
|
|||
|
|
import sys
|
|||
|
|
from verdict import Verdict
|
|||
|
|
|
|||
|
|
v = Verdict("probe_upload(测试环境 uploads 安全)")
|
|||
|
|
v.check("A1 头像上传 svg 应被拒", st != 200, f"status={st}")
|
|||
|
|
...
|
|||
|
|
sys.exit(v.exit_code())
|
|||
|
|
|
|||
|
|
约定
|
|||
|
|
----
|
|||
|
|
- 判据只写**明确、可复现**的期望("应被拒" = status != 200;"应通过" = status == 200);
|
|||
|
|
拿不准阈值/口径的现象(如限流具体第几次触发)**不要**写成判据,保持为观察输出,
|
|||
|
|
否则会制造误报 —— 误报与假绿同样有害。
|
|||
|
|
- 判据数可能为 0(纯取证脚本):此时 exit_code() 返回 0,但会打印"未设置判据"提醒。
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import sys
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Verdict:
|
|||
|
|
def __init__(self, title):
|
|||
|
|
self.title = title
|
|||
|
|
self.checks = [] # [(label, passed, detail)]
|
|||
|
|
|
|||
|
|
def check(self, label, passed, detail=""):
|
|||
|
|
"""记录一条判据;返回 bool 便于就地分支"""
|
|||
|
|
passed = bool(passed)
|
|||
|
|
self.checks.append((label, passed, detail))
|
|||
|
|
return passed
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def failed(self):
|
|||
|
|
return [(l, d) for l, p, d in self.checks if not p]
|
|||
|
|
|
|||
|
|
def exit_code(self):
|
|||
|
|
"""打印结论并返回退出码(0=全通过;1=有失败)"""
|
|||
|
|
n_pass = len(self.checks) - len(self.failed)
|
|||
|
|
print(f"\n===== 判据结论:{self.title} =====")
|
|||
|
|
for label, passed, detail in self.checks:
|
|||
|
|
suffix = f" ({detail})" if detail else ""
|
|||
|
|
print(f" {'✅' if passed else '❌'} {label}{suffix}")
|
|||
|
|
print(f" 合计 {n_pass}/{len(self.checks)} 判据通过")
|
|||
|
|
if not self.checks:
|
|||
|
|
print(" ⚠️ 本脚本未设置任何判据(纯取证脚本)—— 不要拿它的退出码当验收判据")
|
|||
|
|
return 0
|
|||
|
|
if self.failed:
|
|||
|
|
print("\n未通过判据(stderr):", file=sys.stderr)
|
|||
|
|
for label, detail in self.failed:
|
|||
|
|
suffix = f" ({detail})" if detail else ""
|
|||
|
|
print(f" ❌ {label}{suffix}", file=sys.stderr)
|
|||
|
|
print(f"合计 {n_pass}/{len(self.checks)} 判据通过,{len(self.failed)} 项失败", file=sys.stderr)
|
|||
|
|
return 1
|
|||
|
|
return 0
|