4e68d0e5da
既有 37 个脚本是按模块切片的(门禁/草稿/提交/审核各一个),有三处固定盲区: 跨步骤的状态轨迹没人断言、切片之间那条缝(驳回后改了字段再交)、以及脚本 docstring 自述与实际断言不符(e2e-m3-draft.py:11 自述的那条断言全文没有)。 docs/11 给出 A~J 十组覆盖矩阵与判据口径(含用 grep 核对实际断言、不读自述的 规程),scripts/e2e-m3-flow.py 只补这三处、其余交给既有脚本,并钉住 F-125 / F-128 的现状(XFAIL)。verify-all 的脚本数口径随之变成 38。 配套 scripts/dev-reveal-roster-idcard.py:L3-B 走查要在门禁界面手输身份证, 而明文只在建夹具那一刻存在过(库里是 AES 密文 + 脱敏 + HMAC 索引), 此脚本只读解密打印,不入库;用完连同夹具任务一起 --clean。
72 lines
2.8 KiB
Python
72 lines
2.8 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
"""临时脚本(只读):解出本轮走查夹具(e2e-m3-flow.py 建的 taskMain)名册行身份证明文。
|
||
|
||
用途:L3-B 浏览器走查要在门禁界面里**手动输入姓名 + 身份证号**,
|
||
而明文只在建夹具的那一刻存在过(库里是 AES 密文 / 脱敏 / HMAC 索引)。
|
||
dev 的 AES 密钥在 application-dev.yml 里(提交进仓库的开发专用密钥),格式见 AesGcmCipher:
|
||
Base64( IV(12) || ciphertext || tag(16) )。
|
||
|
||
**只读,不写库。** 走查完请连同夹具任务一起清掉(`python scripts/e2e-m3-flow.py --clean <taskId>`)。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import base64
|
||
import re
|
||
import subprocess
|
||
import sys
|
||
|
||
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
||
|
||
if hasattr(sys.stdout, "reconfigure"):
|
||
sys.stdout.reconfigure(encoding="utf-8")
|
||
|
||
TASK_ID = int(sys.argv[1]) if len(sys.argv) > 1 else 2311
|
||
|
||
DEV_YML = r"D:\IDEA_Project\xiaotutongxue\backend\xt-server\src\main\resources\application-dev.yml"
|
||
TEXT = open(DEV_YML, encoding="utf-8").read()
|
||
AES_KEY = base64.b64decode(re.search(r"aes-key:\s*(\S+)", TEXT).group(1))
|
||
CIPHER = AESGCM(AES_KEY)
|
||
|
||
|
||
def psql(sql: str) -> list[list[str]]:
|
||
p = subprocess.run(
|
||
["docker", "exec", "xt-pg", "psql", "-U", "fill", "-d", "filldb",
|
||
"-A", "-F", "\t", "-P", "pager=off", "-t", "-c", sql],
|
||
capture_output=True,
|
||
)
|
||
if p.returncode != 0:
|
||
raise SystemExit(p.stderr.decode("utf-8", "replace"))
|
||
# ⚠️ 不能对整段输出 .strip() —— psql 的 -A 模式在**末尾字段为空**时会留下结尾制表符,
|
||
# 整段 strip 会把它吃掉,于是"未绑定"的那一行少一列、解包直接报
|
||
# `ValueError: not enough values to unpack`(而这个报错看起来像解密失败)。
|
||
out = p.stdout.decode("utf-8", "replace")
|
||
return [ln.split("\t") for ln in out.splitlines() if ln.strip()]
|
||
|
||
|
||
def decrypt(cipher_b64: str) -> str:
|
||
raw = base64.b64decode(cipher_b64.strip())
|
||
return CIPHER.decrypt(raw[:12], raw[12:], None).decode("utf-8")
|
||
|
||
|
||
rows = psql(f"""
|
||
SELECT id, real_name, COALESCE(id_card_no,''), COALESCE(id_card_mask,''),
|
||
COALESCE(bind_user_id::text,'')
|
||
FROM collect_roster
|
||
WHERE deleted = 0 AND task_id = {TASK_ID}
|
||
ORDER BY id
|
||
""")
|
||
|
||
print(f"任务 {TASK_ID} 的名册(走查用;只打印,不入库)")
|
||
print("名册行 | 姓名 | 身份证(明文) | 脱敏 | 绑定用户")
|
||
for r in rows:
|
||
rid, name, enc = r[0], r[1], r[2]
|
||
mask = r[3] if len(r) > 3 else ""
|
||
bind = r[4] if len(r) > 4 else ""
|
||
try:
|
||
plain = decrypt(enc)
|
||
except Exception as e: # noqa: BLE001
|
||
print(f"{rid} | {name} | [解不开:{type(e).__name__}] | {mask} | {bind or '未绑定'}")
|
||
continue
|
||
print(f"{rid} | {name} | {plain} | {mask} | {bind or '未绑定'}")
|