按周工交付的 SEC-Z-01 清理后源码建立 M-Z 主干,并按 Git 仓库惯例重排目录。 - aomin-gateway/ 后端工程 87 文件,原样保留,路径与构建命令未变 - demo/ 演示前端与设备模拟器 9 文件(原 02_前端与设备端),非正式三端 - docs/handover/ 交付与安全清理材料 6 份,内容未改 - docs/contract/ J-00 规格、OpenAPI、内部五接口、状态机、Mock 样例 - docs/reference/ 指纹与校验清单,含 84 个源码文件的 sha256 清单 目录重排只动位置不动内容:源码树 84 文件哈希与交付一致,逐文件自校验通过; 12 条 Flyway 迁移与交付包内镜像逐字节一致。 未纳入:迁移镜像目录(与工程内一套重复,改留指纹)、target/、证据 raw、 应急恢复脚本、评审用虚构台账数据。 安全:SEC-Z-01 15 处明文已清零,导入前重新扫描无新增命中,本仓库无历史污染。 行尾按交付字节原样保存(.gitattributes 设 * -text),不做归一化以免整树摘要失效。
5.6 KiB
5.6 KiB
SEC-Z-01 最小安全清理差异清单(Z-01)
| 项 | 内容 |
|---|---|
| 依据 | AM-S1-GOV-020 §4.2「第一优先级:完成 SEC-Z-01 最小安全清理」 |
| 执行人 | 周工 |
| 执行时间 | 2026-11-13 11:20–11:45 |
| 范围 | 15 处明文 + 1 处启动守卫(为满足"缺参明确失败") |
| 是否升版 | 否,V1.9 内做最小安全清理,未升 V1.10,无无关重构 |
| 口令复述 | 本件不含原口令任何字符,差异表中一律写作「(原值已脱敏)」 |
一、清理前后总览
| 项 | 清理前 | 清理后 |
|---|---|---|
| 含明文口令的文件 | 15 个 | 0 |
| 明文出现次数 | 15 处(每文件 1 处) | 0 |
| 配置层(yml) | 带非空默认值 | 无默认值,缺参启动中止 |
| 脚本层(py) | 带非空默认值 | 无默认值,缺参直接退出 |
| 文档层(md) | 写出明文示例 | 改为占位符 |
| 历史包 | 6 个 zip 内含旧凭据 | 已移入受控归档目录,标注禁止外发 |
二、逐处差异(15 处)
A 类:后端配置(1 处)
| # | 文件 | 位置 | 改前 | 改后 |
|---|---|---|---|---|
| 1 | 01_后端源码/src/main/resources/application.yml |
spring.datasource.password |
${DB_PASS:<原值已脱敏>} |
${DB_PASS} |
同时把上方两行自相矛盾的注释("包里不再有第二处明文"——实际上还有 14 处)改成实际口径。
B 类:Python 证据脚本(12 处)
12 个脚本写法相同,改法相同。以一处示意:
| 改前 | 改后 |
|---|---|
DB_PASS = os.environ.get("AOMIN_DB_PASS", "<原值已脱敏>") |
DB_PASS = os.environ.get("AOMIN_DB_PASS")加 4 行守卫:为空则 raise SystemExit(...),打印设置方法 |
| # | 文件 |
|---|---|
| 2 | 06_统一闭环题自测证据/alarm_retry_test.py |
| 3 | 06_统一闭环题自测证据/closed_loop_test.py |
| 4 | 06_统一闭环题自测证据/cmd_chain_test.py |
| 5 | 06_统一闭环题自测证据/run_evidence.py |
| 6 | 06_统一闭环题自测证据/scope_test.py |
| 7 | 06_统一闭环题自测证据/v15_fix_test.py |
| 8 | 06_统一闭环题自测证据/v16_fix_test.py |
| 9 | 06_统一闭环题自测证据/v17_merge_test.py |
| 10 | 06_统一闭环题自测证据/v18_fix_test.py |
| 11 | 06_统一闭环题自测证据/v19_gov008_test.py |
| 12 | 06_统一闭环题自测证据/v20_node_test.py |
| 13 | 06_统一闭环题自测证据/stamp_evidence.py(特殊:原为内联调用 "-p" + os.environ.get(...),改为在模块顶部定义 DB_PASS 并加同样的守卫,调用处改引用常量) |
C 类:说明文档(2 处)
| # | 文件 | 位置 | 改前 | 改后 |
|---|---|---|---|---|
| 14 | 05_启动构建与测试说明/启动构建与测试说明.md |
§5 启动 | export DB_PASS=<原值已脱敏> |
export DB_PASS=<口令>,注释改为"必需:配置里已无默认值,未设置时启动直接失败" |
| 15 | 09_代码与版本/README.md |
环境变量表 | 默认值列写出明文 | root / 必需,无默认值;说明未设置时脚本直接退出 |
三、新增 1 处启动守卫(为什么必须有)
这是本次唯一的代码改动,不是重构,是为满足 §4.2「缺少必需口令时明确失败」。
问题:只删默认值,并没有"明确失败"
先按最省事的方式改(password: ${DB_PASS} 不设默认值)实测了一次,结果不是报"缺少 DB_PASS",而是:
Access denied for user 'root'@'localhost' (using password: YES)
原因:Spring Boot 的占位符解析在“解析不到”时是忽略而不是报错,于是拿空口令去连库。表现出来像"口令错了",实际是"根本没配"——这正是不允许的静默回退。
处理:在 main 里提前拦住
| 文件 | 改动 |
|---|---|
src/main/java/cn/aomin/gateway/GatewayApplication.java |
main() 首行调用 requireEnv("DB_PASS", "数据库口令");新增私有方法 requireEnv,为空则打印提示并 System.exit(1) |
改后实测输出(退出码 1):
[启动中止] 缺少必需的环境变量 DB_PASS(数据库口令)。
SEC-Z-01:程序不再内置任何默认口令,也不会回退到包内明文。
设置方法:Windows set DB_PASS=<口令> Linux/macOS export DB_PASS=<口令>
只校验"有没有",不校验"对不对",也不打印任何取值。
四、构建产物与缓存清理
| 对象 | 处理 |
|---|---|
target/classes/application.yml |
旧副本含明文;mvn clean package 后已由新文件覆盖,实测 jar 内配置 0 命中 |
__pycache__ / *.pyc |
已删除(编译产物里会残留字符串常量) |
应用日志 G:/aomin-dev/logs |
实测 0 命中,保留(不含凭据,且是运行证据) |
target/ 其余内容 |
43 MB 构建产物,已随 clean 重建;不入代码仓库(见 03 的 .gitignore) |
五、历史包处置
_历史包/ 下 6 个 zip 都含旧凭据(解开后逐成员扫描,命中 13~15 处)。直接按字节搜 zip 查不出来,因为内容被 deflate 压缩过。
| 处理 | 说明 |
|---|---|
| 目录改名 | _历史包/ → _历史包_含旧凭据_禁止外发/ |
| 登记 | 同目录 _清单_含旧凭据.md 记录每个 zip 的字节数、SHA-256、含旧口令文件数 |
| 约束 | 禁止外发、禁止入代码库、禁止上传公共 AI;仅作内部留档 |
这是"轮换结论"要覆盖的一部分:口令一旦进过包,就不能假设它只存在于一个地方。
后续:清理后哈希、构建说明、155 条证据边界见同目录 03_… 与 04_…。