9.9 KiB
W1-T3 最终独立复审报告(第二轮修复)
状态
完成。本次复审只读取实现、迁移、来源清单、前两轮报告和本地/官方 CSV,并独立执行指定测试、来源审计及 ESCO URI 只读核验;未修改任何实现文件,仅写入本报告。
Verdict
FAIL
第二轮唯一 Important finding 尚未完全关闭:
- 47 条 O*NET
software_skills来源当前数据全部真实,但测试只验证“每条 DB 三元组存在于快照集合中”,没有验证“每个技能代码对应唯一正确的三元组”;交换两个未被单点断言的技能来源后,测试仍会通过。 - 137 条非空 Importance 的值均已逐条核对,但测试对
importance_is_proxy仍主要是0/1枚举检查,仅 10 条做proxy=1精确断言;将其余 127 条中的任一1改为0后,测试仍会通过。
以上属于回归保护缺口,不阻断当前种子数据的正确性,但按 TO-12 和本轮“篡改必须失败”的要求,仍阻断 T4。
独立测试结果
环境:
JAVA_HOME=D:\java\jdk- Maven:
D:\maven\apache-maven-3.9.7\bin\mvn.cmd - 工作目录:
D:\1\zhi\zhilai\mydreamjob-server
执行:
$env:JAVA_HOME='D:\java\jdk'
& 'D:\maven\apache-maven-3.9.7\bin\mvn.cmd' '-Dtest=SkillTaxonomyMigrationTest,SkillCloudSeedDataIntegrationTest' test
结果:
SkillCloudSeedDataIntegrationTest: Tests run: 7, Failures: 0, Errors: 0, Skipped: 0
SkillTaxonomyMigrationTest: Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
Total: Tests run: 9, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS
Total time: 39.800 s
来源审计方法与结果
1. O*NET 31.0 官方来源核验
独立下载官方发布包:
- URL:
https://www.onetcenter.org/dl_files/database/db_31_0_csv.zip - HTTP:
200 - 文件大小:
16,237,378bytes - HTTP
Last-Modified:Tue, 25 Aug 2026 03:46:00 GMT - 官方 ZIP SHA-256:
55033FC68B4C13EC23E7F74DC6378660F6E854E75D55D6E333AE0A761D3987CD
从官方 ZIP 解压后与 mydreamjob-server/target/onet-31 逐文件比较,SHA-256 完全一致:
| 文件 | SHA-256 |
|---|---|
software_skills.csv |
6AABB96B464288DB849580E6510530EFFF3F80E25DDE2BB23F1FC36BB526B016 |
work_activities.csv |
6C266205B2926C0C854A7A577AAE29FE492C71BB0DF7E197ADCA830B58FE4EA1 |
knowledge.csv |
88092E17D16096CDEBE292FEE4389DEB460EDDA24AE8B4637212C69C71562F55 |
essential_skills.csv |
68C4D31D8C55077238395DCA40CC4E6D32E6A608B5474BB8264B1F98AE415BD7 |
transferable_skills.csv |
F17D0419E818A3BF2F55076510C76C4AEF01CC8F5AEE2670D2F44F6F44AC60F7 |
occupation_data.csv |
A09EAE1D6609686E44E05B7290993A1C8B523D8CA224BC0EEDC194C855C3EE02 |
因此,target/onet-31 中的本轮 CSV 来源与官方 O*NET 31.0 发布包一致,不是本地改写版本。
2. 证据快照与官方 CSV 对照
mydreamjob-server/src/test/resources/skill-cloud/onet-31-reference.csv:
- 总行数:92 条数据行。
SOFTWARE:47 条,47 个唯一键,无重复。IMPORTANCE:45 条唯一SOC + Element ID组合,无重复。- 47/47 条
SOFTWARE三元组均精确命中software_skills.csv。 - 45/45 条
IMPORTANCE均命中声明的官方 CSV,Data Value精确一致。 - 未发现无依据行、伪造三元组或伪造数值。
SOFTWARE的data_value均为空,符合software_skills没有 Importance 值的结构。
结论:当前证据快照确实来自 O*NET 31.0 官方 CSV,数据与官方 CSV 完全一致。
3. V36 数据全量审计
独立解析 V36 后确认:
| 项目 | 结果 |
|---|---|
| O*NET 技能 | 47 |
| DB 使用的 SOFTWARE 三元组集合与快照集合相等 | 是 |
| 职位技能关系 | 139 |
| 非空 Importance | 137 |
| 空 Importance | 2 |
| 非空关系正确命中快照键和值 | 137/137 |
非空关系当前 importance_is_proxy=1 |
137/137 |
| 空关系的 SOC/Element ID 为 NULL、proxy=0 | 2/2 |
| 137 条关系使用的唯一 Importance 键 | 45 |
| 快照 IMPORTANCE 键集合与实际使用集合相等 | 是 |
结论:当前数据层没有发现伪造来源、错误数值或错误代理标记。
4. Manifest 分类覆盖
从 source-manifest.md 独立解析 10 个职位及声明分类,并与 V36 对比:
- 职位:10/10 一致。
- 分类:68/68 存在。
- 每个声明分类至少关联 1 条技能关系;本轮独立统计最小值为 2、最大值为 3。
- 未发现分类遗漏或职位—分类清单不一致。
5. ESCO 职位 URI
V36 与测试中的 10 个职位映射一致,其中 8 个唯一 ESCO occupation URI。对 8 个 URI 调用 ESCO 官方只读 API,全部返回 HTTP 200:
- 前端开发工程师 URI
- 后端开发工程师 URI
- 测试工程师 URI
- 金融数据分析师 URI
- 风控建模工程师 URI
- 教育产品经理 URI
- 教学设计师 URI
- 教育数据分析师 URI
DevOps、量化开发复用软件开发者 URI 的项目代理关系已在 V36 注释中明确,和当前批准清单一致。
上一轮 Finding 关闭状态
| 第一轮/第二轮 Finding | 状态 | 证据 |
|---|---|---|
| C-1:混合来源未经批准 | 关闭 | 契约、design、source manifest 均已明确 ESCO 主来源 + O*NET software_skills 补充;V35/V36 字段语义与修订契约一致。 |
| I-1:代理 SOC/ESCO URI 不可追溯 | 关闭 | 10 个职位均有 ESCO source version 和 URI;8 个唯一 URI 独立返回 200;代理 SOC、Element ID、proxy 均落表。 |
| I-2:manifest 分类遗漏 | 关闭 | 68/68 分类存在,每个分类至少 1 条关系,清单与 V36 一致。 |
| I-3a:Importance 逐条可追溯数据 | 关闭 | V35 增加来源字段;V36 的 137/137 非空关系均匹配官方快照,当前数据真实。 |
| I-3b:测试全量覆盖 47/47 软件来源 | 部分关闭 | 47 条都做集合成员验证,但只有 5 条做 skillCode -> sourceCode 精确映射断言;未断言的两个技能交换来源仍可通过。 |
| I-3c:测试全量覆盖 137/137 非空 Importance | 部分关闭 | 137 条都验证来源键和数值;但 proxy 只对 10 条精确断言,其余 127 条可在 1 和 0 之间翻转而不失败。 |
Findings
Important-1:47 条 O*NET 软件来源未做完整的逐技能精确映射断言
位置:
mydreamjob-server/src/test/java/com/mydreamjob/skill/SkillCloudSeedDataIntegrationTest.java:160-183- 精确映射单点断言:
185-189 - 辅助断言:
474-478
问题:
当前循环对每条 DB source_code 只检查其三元组是否存在于快照中,再要求 DB 行数与快照行数相等。它验证了“集合完整”,但没有验证每个技能代码必须绑定哪个三元组。
可证伪反例:
- 任意交换两个未被
assertOnetSkillSource覆盖的 O*NET 技能来源,例如SPRING_BOOT与DJANGO。 - DB 仍然有 47 个唯一且合法的三元组,集合仍与快照相等。
- 现有 5 条单点断言不涉及这两个技能,因此测试仍通过,但技能溯源已经被错误交换。
影响:
skills.source_code 是来源追踪字段。来源交换会静默破坏“技能到上游来源 ID”的准确性,当前回归测试无法阻止。
修复要求:
- 在快照中增加
skill_code(仅 SOFTWARE 行使用),或新增完整的skillCode -> SOC|Workplace Example|Element ID映射资源。 - 对 47/47 条 O*NET 技能逐条断言精确来源,而不是仅做集合成员检查。
Important-2:Importance Proxy 标记未做全量语义断言
位置:
- 枚举检查:
SkillCloudSeedDataIntegrationTest.java:201-203 - 循环内代理检查:
229-230 - 仅 10 条精确断言:
271-294 - 精确断言辅助方法:
481-494
问题:
assertTrue(proxy == 0 || proxy == 1) 只证明值处于合法范围,不能证明它符合种子数据的来源语义。V36 当前明确要求 137 条非空 Importance 全部是跨来源代理,必须为 1;测试只对 10 条做 proxy=1 断言。
可证伪反例:
- 将任意一个未被
assertImportance覆盖的非空关系从importance_is_proxy=1改为0。 - 该行仍满足数据库 CHECK 和测试的
0/1判断,SQL 计数也不检查非空代理必须为 1,因此测试仍通过。 importance、SOC、Element ID 和 Data Value 均未发生变化,但来源语义已经被篡改。
影响:
代理标记是来源审计字段。当前测试不能保证 137 条全部保持已批准的 proxy 语义,无法满足“篡改 Proxy 标记必须失败”的要求。
修复要求:
- 明确断言所有非空 Importance 的
importance_is_proxy=1,以及所有空 Importance 的importance_is_proxy=0;或为 137 条关系提供逐关系期望映射。 - 若业务允许直接 O*NET Importance 与代理 Importance 并存,需要按关系保存期望值并逐条断言。
Minor-1:快照的官方来源属性依赖评审时外部核验,未被自动化锁定
loadEvidence 只验证快照头部、列数、允许的 source_file 名称和键唯一性,不会读取官方 CSV 或校验官方发布包哈希。
当前快照已由本次复审用官方 ZIP 哈希确认,因此不阻断;但未来若快照的 source_file 被改成同一允许集合内的另一个文件,只要测试资源本身同步修改,现有测试无法识别来源文件标签变化。建议在来源文档或测试资源旁记录官方 ZIP/CSV 哈希,作为后续审计基线。
未决问题
- T3 当前数据层已通过,不能因为数据正确就忽略 Important-1 和 Important-2 的回归缺口。
- 真实 MySQL 8.4 尚未执行;本轮仅按要求执行 H2
MODE=MySQL。该限制沿用前轮,不构成本次新的阻断项。 - V35/V36 当前满足修订后的混合来源契约;未发现需要再次 Rewind 的契约冲突。
阻断项
T3 仍为 FAIL。在补齐 47/47 逐技能精确来源映射和 137/137 Importance 代理语义断言前,不应进入 T4。当前测试 9/9 通过只能证明现有数据与当前快照一致,不能证明上述两类篡改必然失败。