@sema-agent/settings-schema 6.2.0 → 6.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +197 -0
  2. package/README.md +21 -9
  3. package/conformance/VERSION +1 -1
  4. package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +166 -0
  5. package/conformance/cases/c41-s792-encoded-pem-identity/input.json +62 -0
  6. package/conformance/cases/c41-s792-encoded-pem-identity/meta.json +12 -0
  7. package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +120 -0
  8. package/conformance/cases/c42-s792-unknown-key-secret-redacted/input.json +25 -0
  9. package/conformance/cases/c42-s792-unknown-key-secret-redacted/meta.json +11 -0
  10. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +137 -0
  11. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/input.json +44 -0
  12. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/meta.json +12 -0
  13. package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +115 -0
  14. package/conformance/cases/c44-s792-utf8-escape-glued-token/input.json +30 -0
  15. package/conformance/cases/c44-s792-utf8-escape-glued-token/meta.json +10 -0
  16. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +185 -0
  17. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/input.json +78 -0
  18. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/meta.json +11 -0
  19. package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/expected.json +88 -0
  20. package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/input.json +18 -0
  21. package/conformance/cases/c46-s811-host-name-url-userinfo-characterization/meta.json +6 -0
  22. package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/expected.json +98 -0
  23. package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/input.json +26 -0
  24. package/conformance/cases/c47-s811-roles-key-url-userinfo-characterization/meta.json +6 -0
  25. package/conformance/cases/c48-s935-model-headers-transported/expected.json +115 -0
  26. package/conformance/cases/c48-s935-model-headers-transported/input.json +37 -0
  27. package/conformance/cases/c48-s935-model-headers-transported/meta.json +6 -0
  28. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +98 -0
  29. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/input.json +28 -0
  30. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/meta.json +9 -0
  31. package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +0 -8
  32. package/conformance/cases/cli01-model-name-nfc-vs-nfd/meta.json +2 -1
  33. package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +0 -8
  34. package/conformance/cases/cli02-control-and-zero-width-chars/meta.json +2 -1
  35. package/dist/config-fns.d.ts +53 -22
  36. package/dist/config-fns.js +87 -33
  37. package/dist/cross-session.d.ts +8 -5
  38. package/dist/cross-session.js +8 -5
  39. package/dist/fleet.d.ts +3 -2
  40. package/dist/fleet.js +3 -2
  41. package/dist/hash.d.ts +3 -3
  42. package/dist/hash.js +3 -3
  43. package/dist/hooks.d.ts +8 -8
  44. package/dist/secret-scan.d.ts +39 -0
  45. package/dist/secret-scan.js +110 -0
  46. package/dist/types.d.ts +65 -7
  47. package/dist/types.js +57 -44
  48. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,203 @@
6
6
  > `@sema-agent/registry-core`(更早还曾是 `@sema-ai/registry-core`)。本档 `0.19.0` 以下各节里的
7
7
  > 包名是**历史记录**,如实保留不追改。
8
8
 
9
+ ## Unreleased
10
+
11
+ ## 6.4.0 — 2026-10-01 — `models.models[].headers` 解析透明键(S-935;与 `compat` 同一只变换)
12
+
13
+ > **判档 = minor**(新键 = additive,README「发版纪律」)。缺席 = 键不铸 = 6.3.0 行为,零迁移;不带 `headers` 的文档读出来逐字节不变
14
+ > (conformance 全语料重加冕:既有 61 条 case、`baseline.json`、`VERSION` 零 diff)。
15
+
16
+ ### 病(6.3.0 及更早)
17
+
18
+ sema-server 7.108.0(S-931)给模型目录条目加了 `headers`(部署 ↔ 供应商的 per-model 附加请求头,例 `{"anthropic-beta":"context-1m-2025-08-07"}`
19
+ 开 1M 上下文)。本包 `ModelEntry` 没有这个键 ⇒ 每一条按域 schema 解析的读口都把它剥掉并报 `unknown-keys-dropped`
20
+ (实测:`[{"domain":"models","kind":"unknown-keys-dropped","keys":[["models",0,"headers"]]}]`)—— 远端 `readEffectiveWire`、`buildEffective`、
21
+ 文件腿 `FileConfigStore.getEffective()`(严格 / `{tolerant:true}` 两形,`loadLocalConfig` 走严格形)、`FileConfigStore.getDomain`
22
+ (`exportBundle` 的读法);写腿 `writeDomainFile` / `validateDomain` 同样剥,且连告警都没有。经生产配置通道写下的 `headers`
23
+ 到不了 server 目录,只有绕过读腿的输入(盘上 LKG / 手写 effective)到得了。
24
+
25
+ ### 修
26
+
27
+ - `ModelEntry.headers?`(`src/types.ts`):`z.unknown().optional().transform(createFreeFormTransform("headers"))` —— 与 `compat` **同一只**
28
+ 变换(本版把 `compat` 那段内联闭包提成包内私有的 `createFreeFormTransform(field)`,两键共用,不再各写一份):缺席不铸键;其余只过
29
+ `vetFreeFormJson` 自由形安全判(密钥形键 / 值、访问器、环、非 JSON、深于 `MAX_EXTRA_BODY_DEPTH`),拒句与 `compat` 逐字同形、只换字段名。
30
+ - **不做形校验**:字符串 / 数组 / 数字 / `null` / 非规范大小写的名(`Anthropic-Beta`)/ 白名单外的名 / 非字符串值 / 写在不读它的线路上,
31
+ 本包一律逐字过。头名闭集白名单 × 读它的线路 × 值形的判据**唯一属主是 sema-server**(坏声明 ⇒ 该条目整条不可用),本包复述一份必漂。
32
+ - 登记进 `PARSE_TRANSPARENT_KEYS`(`models[].headers`,`since: "6.4.0"`,`validatedBy: "consumer"`)。`ParseTransparentValidator` 的
33
+ `consumer` 词注去掉了「丢掉坏的那一个键、保留宿主条目」这句处置描述 —— 坏值怎么处置是执法点自己的契约(`headers` 的执法点整条
34
+ 条目不可用,`mcp.servers[].source` 的只丢标签),写在各登记行的 `why` 里,本包不替它定(规则少一条,不加新词)。
35
+ - 同深度上限:`src/hash.ts` `MAX_EXTRA_BODY_DEPTH` 注文补 `headers`(与 `extraBody` / `compat` 同一个包裹深度,同一个上限)。
36
+ - 文档:README「Declared-but-unvalidated keys」、`llms.txt` 解析透明键登记表段(4 员 → 5 员)、`llms-full.txt` 补 `headers`。README 同句
37
+ 顺手订正:它把 `models[].extraBody` 列成解析透明键、把「the key's own guard」列成第三种校验方 —— 两处都与实现不符(`extraBody`
38
+ 有自己的形门,不在登记表里;校验方闭集只有 `engine-ingest` / `consumer` 两词),改成与 `src/types.ts` 一致。
39
+
40
+ ### 红先 / 门
41
+
42
+ - `test/model-headers-6.4.0.test.ts` 16 格,修前 15 红 1 绿(绿的是 ③ 负控):① 五条读腿逐字交出、零告警 ①b 写腿逐字落盘
43
+ ② 密钥形值 / 键(`sk-ant-…` / 私钥 PEM / `Bearer …` / 密钥形键 / 数组里的嵌套值)拒,拒句 = `compat` 拒句换字段名 ②b 五条读腿上与
44
+ `compat` 同一个处置(经 `buildEffective` 的四条腿逐条丢 `entry-dropped`、兄弟幸存;`getDomain` 整域净化后抛)、零回显
45
+ ③ 负控:不带 `headers` 的文档 effective / wire 的 `stableHash` 与 6.3.0 实测金样逐位相同 ④ 不做形校验 ⑤ `stableHash` 键序无关、
46
+ 声明 / 换值改变哈希、恰在 `MAX_EXTRA_BODY_DEPTH` 收且包进 effective 可哈希、超一层拒(与 `compat` 同层)⑥ 登记行。
47
+ - 解析透明键登记表门(`test/parse-transparent-keys.test.ts`,从 `DOMAIN_SCHEMAS` 行为派生、双向对拍)随新键自动覆盖。
48
+ - conformance 两条新语料(`npm run conformance:bless` 加冕):`c48-s935-model-headers-transported`(三腿逐字交出、零告警;openai 线路上的
49
+ 数组形 `headers` 照样过;无 `headers` 的条目不铸键)、`c49-s935-model-headers-secret-entry-dropped`(密钥形值 ⇒ 条目逐条丢、零回显,
50
+ `secrets` 已登记)。对 6.3.0 源码实测三腿同红(c48:2 处 effective 差 + 1 条多出的 `unknown-keys-dropped`;c49:6 处 effective 差、
51
+ 缺 1 条 `entry-dropped`、多 1 条 `unknown-keys-dropped`);`firstRedOn` 留空(historical 阶梯证据真跑于入库之前,不手填,c41 同口径)。
52
+
53
+ ### 消费方
54
+
55
+ - **sema-server 7.109.0 提货**:`config-provider.ts` `mapToServiceEffective` 加一行过境 `headers`,并把 `test/catalog-entry-headers.test.ts`
56
+ 末段 🚧 S-931-schema 两格(「两条读腿今天剥掉 `headers`」)翻成「读腿逐字交出 `headers`」的正向钉、删该段 🚧。
57
+ - center / TOC:`ModelEntry` 的推断类型多一个 `headers?: unknown`;不读它的消费方零改动。
58
+
59
+ ## 6.3.0 — 2026-09-27 — 安全修(codex gpt-6-astra 对 v6.1.0 复审 [high],回填 D-4):可披露身份改白名单闭形 `DISCLOSABLE_IDENTITY_RE` · `redactSecrets` 整串判套在一处归一化上 · 告警坐标出门一道
60
+
61
+ > **判档 = minor**(发版方裁,2026-09-27;原排 6.2.1 当日补丁):按 README「发版纪律」,新导出 `DISCLOSABLE_IDENTITY_RE` = minor
62
+ > (归一化 `secretScanForms` 在合并复审 opus 腿 L7 / M1 之后收进包内私有模块 `src/secret-scan.ts`,不是导出)。另:`isDisclosableIdentity` / `isReportableRefusalName` 对「不是标识符」的串由答 true 收成答 false,
63
+ > `entry-dropped.identity` 与引擎 `modelRefusals.entries` 随之少带这类名(告警面 / 诊断面收紧,effective 与校验结果不动)。
64
+ > 不改任何 wire 形、类型面、默认值与校验结果。
65
+
66
+ ### 病(6.1.0 起;5153d2a 之前没有 `entry-dropped.identity` 这个面)
67
+
68
+ 一条被丢条目的 `id = "key=" + encodeURIComponent(<PEM 私钥>)` ⇒ 四个出门面原样带出整段编码私钥,解码即可用:
69
+ `entry-dropped.identity.id`(与 `projectWarning` 投影)、同串作 Record 坏键时的坐标段、zod error 的 `received` / `message`、
70
+ `isReportableRefusalName` 答 true(引擎会把它当 `modelRefusals.entries` 的键送上心跳)。机制:`PRIVATE_KEY_PEM_HEADER` 只测
71
+ **原文**(`-----BEGIN%20PRIVATE%20KEY` 的 `%20` 不在 `[A-Z0-9 ]`);令牌那一遍把整串当一个 run 交 `containsSecretToken`,它解码后
72
+ 按**行首锚**判,`key=` 前缀让锚失败。
73
+
74
+ ### 修(以点改面,不加 PEM 检测臂)
75
+
76
+ - **身份面 = 标识符白名单**(`src/config-fns.ts`):新导出 `DISCLOSABLE_IDENTITY_RE = /^[A-Za-z0-9._:/@+-]{1,256}$/`(无旗标;上限 256 与 web-admin 读侧名长上限对齐,发版方裁)。
77
+ `isDisclosableIdentity(v)` = 字符串 ∧ 落在该闭形里 ∧ `redactSecrets(v) === v`。「不像密钥就可披露」不再是**充分**条件
78
+ (6.1.0 / 6.2.0 的判据,放行了上面的形);密钥否决留作**合取项**:集内照样有 `sk-…` / `ghp_…` / `AKIA…` / 三段 JWT(实测:
79
+ 只留白名单 ⇒ S-712「秘密形字面」、S-712 对抗轮 #2、S-768 B3 逐格判、S-768 N1 四格钉红),且否决必须是坐标段 / error 文案
80
+ 用的**同一只**脱敏器(对抗轮 #2 律;集内残例 = `@` 之后的单点 `eyJ…` 串,裸探测器判阴、脱敏器会改,语料 c41 压着)。
81
+ `isReportableRefusalName` 经它,同收。不可披露 ⇒ 按既有的「无名」处置(键缺席;没有新造占位词)。「无密钥形」这一项里另有一只
82
+ URL userinfo 判定(合并复审 opus 腿 L1):`https://admin:hunter2@db…` / `postgres://svc:pw@h` 的字符全在集内,6.2.0 起两只身份谓词都答
83
+ 可披露;复用校验侧 `NON_SECRET_URL` 拒写的**同一只**判定(抽成包内私有 `carriesUserinfo`,`NON_SECRET_URL` 与 https 插件源的 refine
84
+ 改调它,校验结果逐字不变),不加新检测臂。路径里的 `@`(`https://host/@scope/pkg`)照旧可披露。
85
+ - **自由文本面 = 一处归一化**(包内私有模块 `src/secret-scan.ts`,不进主 barrel、不进 `exports`:展示侧读法不做公开契约 —— opus 腿 L7):
86
+ `percentLayers(text)` 给校验侧探测器,`secretScanForms(text)` 回纯数据记录给展示侧脱敏器 ——
87
+ `percentLayers` = 原文 + 至多三层宽容百分号解码(到不动点即停),正是 `containsSecretToken` 一直在读的那几层,抽成一处定义;
88
+ `forms` = 这些层 + 每层的**边界读法**(连续的 `%HH` 按 UTF-8 整段解,解不开的转义读作空格 —— 转义的十六进制位永远不粘到后面那个词上;
89
+ codex r2 F-r2-1:`key=%C2%A0sk-live-…` 逐个解码解不开 NBSP,`A0sk-…` 让令牌边界消失)+ 以上每个形把 `+` 读作空格(一层
90
+ application/x-www-form-urlencoded 读法),去重、先见先排;`unresolved` = 三层百分号层走完仍能再解(归一化没走完)。`containsSecretToken` 只读 `percentLayers`,**判定逐格不变**(冻结 6.2.0 实现作 oracle:4000 条确定性伪随机串,
91
+ 字母表含 `+` / `Bearer+` / `basic+`,+ 既有样例零分歧;头注的 major 理由不变,不放宽也不收紧)。`redactSecrets` 的整串判是一条规则:
92
+ `unresolved` ⇒ 整串扣(**归一化不完的东西不出门**,codex r1 F1 四层编码),PEM header 在原文的每一个 `forms` 上测(codex r1 F1:
93
+ `-----BEGIN+PRIVATE+KEY` 形式编码头),`containsSecretToken` 在替换结果的每一个 `forms` 上测。没有长度截断臂:每层一遍线性替换,
94
+ 不存在「超长就只看原文」的 fail-open 口。「解码失败」在宽容解码下不存在:单个解不开的转义(`%E4` 这类不完整 UTF-8)是不动点,
95
+ 不算「没走完」—— 所以编码的非 ASCII 文本(`%E4%B8%AD`)照旧只做令牌级替换(任务书字面是「仍有 `%xx` 即扣」,这里取「还能再解一层」,
96
+ 差别只在这一类合法文本上,候发版方复核)。
97
+ - **读路径不放大**(合并复审 opus 腿 M1 [medium,对 6.2.0 回归],同轮修):① 两处解码都**零异常、单遍**:单个转义只有 ASCII 的
98
+ `%00`–`%7F` 单独解得开,写成字符集替换;转义串的 UTF-8 判定写成良构判定(Unicode Table 3-7),不再对每个非 ASCII `%HH` 抛一次
99
+ 异常 —— 结果与原 try/catch 逐字相同(冻结 a33e7fd 实现作 oracle:6000 条伪随机 + 边界样例零分歧);② `containsSecretToken` 只算
100
+ `percentLayers`,展示形只有 `redactSecrets` 算;未替换过的串复用同一组形;③ 出门坐标的段有长度上界(与身份白名单同一只 256,
101
+ 私有常量 `DISCLOSABLE_IDENTITY_MAX` 两处复用):超长段不扫描、整段 `[redacted-secret]`(O(1),fail-closed)。读数(opus `perf2/3/4.mjs`
102
+ 同输入,本机;冷 = 原脚本直跑,暖 = 先热身再三次取中位数):
103
+
104
+ | 输入 | 6.2.0 | 修前(a33e7fd) | 修后 |
105
+ |---|---|---|---|
106
+ | perf2 64 × 16 KB `%C2` 未知键名 | 冷 4–9 ms / 暖 0.5 ms | 冷 17.8–19.3 s | 冷 7–21 ms / 暖 0.3–0.4 ms |
107
+ | perf2 16 × 64 KB `%C2` 未知键名 | 冷 0–1 ms / 暖 0.3 ms | 冷 24.0 s | 冷 0 ms / 暖 0.2 ms |
108
+ | perf3 64 × 16 KB plain / `%20` / `%C2` / 编码中文 | 暖 0.2–0.3 ms | 23 ms / 308 ms / 17.1 s / 10.6 s | 冷 4–13 / 1 / 0 / 0 ms,暖 0.1–0.3 ms |
109
+ | perf4 16 × 16 KB 编码中文 roles 坏键(6.2.0 已脱敏的路径)| 6.1–14.4 s | 41.8 s | 146–526 ms |
110
+
111
+ 冷读数里修后比 6.2.0 多出的十几 ms 是本模块首调的编译热身(同一脚本先跑 6.2.0 那份);暖读数与 6.2.0 同量级,perf2 / perf3 / perf4 三组满足「≤ 6.2.0 的 2×」(发版前 opus 验修腿在负载 40–123 下复跑同结论)。
112
+ `redactSecrets` 对**单个大块良性文本**(1 MB,`perf.mjs`)仍比 6.2.0 慢 1.9–2.9 ×(ASCII 转义密集又夹 `+` 的良性文本 2.5–4.4 ×,20,000 个 250 字坏键 18 s → 54 s;它要多判边界读法与 `+` 读法那几个形;最坏形反向:1 MB `%C2` 从 6.2.0 的 ≈190 s 降到 1–3 s,编码中文 / 非法 UTF-8 快 20–100 倍);读路径上
113
+ 能进它的长串只剩 zod 回显的坏值(坐标段已被 256 上界截住),对编码中文 / `%C2` 这类转义密集文本修后比 6.2.0 快 20–60 ×。
114
+ - **铸点不动**:`buildEffective` 的 `entry-dropped.identity` 经 `readEntryIdentity` → `isDisclosableIdentity`,投影随之。
115
+ - **坐标出门一道**(codex r1 F2,发版方裁并入;opus 腿 M2 同轮补臂):告警里用户写的串 —— 被丢条目的 `path`、`unknown-keys-dropped` /
116
+ `unknown-keys-carried` 的 `keys`、`hosts-grandfathered` 的 `droppedNames` —— 出门前每段过同一道私有 `outgoingSegment`:长过 256
117
+ (与身份白名单同一只上界)整段 `[redacted-secret]`,否则过 `redactSecrets`(同一只脱敏器,不另设判据;数字段原样)。`droppedNames`
118
+ 判在夹紧成 63 字**之前**(截断会把秘密形截成认不出的半截);此前它以 63 字原文前缀出门,`sk-live-…` 与编码 PEM 的 header 加私钥体
119
+ 前段都在里面(6.2.0 同形)。`legacy-governance-lifted.keys` 不经它:键只来自闭集 `GOVERNANCE_MIRROR_KEYS`,按构造没有用户写的串。
120
+ **先钳后扫**(codex 验修腿 S-813 [medium],对 a33e7fd / 6.2.0 回归,同轮修):`droppedNames` 只给前 64 个(与未知键同一只 `DROP_SCAN_MAX_KEYS`)
121
+ 铸标签,其余只计数 —— `grandfatherHostsRead` 的结果加 `droppedTotal`(真实条数,与 `UnknownKeyScan` 的 `{ keys, total }` 同形),
122
+ `hosts-grandfathered` 告警加可选 `droppedTotal?`(在场 ⇔ 标签被钳,缺席 = 列表是穷举;additive,投影随带)。修前 M2 让每个超长主机名
123
+ 都先归一化、条数无上限:1 个合法 host + 5000 个 `'%41'.repeat(85)` 名(≈ 1.3 MB)每次读 620–786 ms(本机;codex 读 1.5–1.8 s,
124
+ vitest 负载下 2.2 s),无 `onWarning` 也付;修后暖读 6.8–17 ms(6.2.0 0.8–2.7 ms,a33e7fd 0.5–1.3 ms)。
125
+ 此前只有被丢 Record 条目的键段过脱敏器,未知键告警的键名 0.19.0 起从来不过:`sk-…` / `ghp_…` / 编码 PEM 形的未知键名原样进告警与投影。
126
+ 导出的 `droppedKeys` / `carriedUnknownKeys` 给的 `keys` 同为出门形(`total` 照数真实条数);schema 已知字段名脱敏器原样放行。
127
+
128
+ ### 谁受影响(行为面)
129
+
130
+ - `entry-dropped.identity.name` / `.id` 与引擎的 refusal 名:含 `%` / `=` / 空白 / 引号 / 反斜杠 / 控制与零宽字符 / 非 ASCII /
131
+ 超 256 字的串,6.2.0 带,6.3.0 起不带。**普查**(6.2.0 基线树全部 fixture / 测试 / 语料 / 样例 / 文档里的 `name` / `id` 字面
132
+ 1261 个 + 6.2.0 refusal 名样例):新拒 19 个,**真实形的模型 id / 名字 0 个** —— 对抗语料 10 个(cli01 NFC/NFD `café`、cli02 BEL /
133
+ 零宽、cli05 300 / 303 字 —— cli05 两条是**合法**条目,在 effective 里,不上身份面;作 refusal 名时不再上报)、按自身 schema 就
134
+ 非法的负样例 5 个(`BAD NAME` 类)、空串 1 个、自由展示名 3 个(`Team A` / `Global again` = scopes wire、BudgetGroup 在 gate 域,
135
+ 不上身份面;`评审团` = CollabTemplate.name —— collab 域被丢的模板身份里 6.3.0 起只剩 `id`(一条规则,展示名不另设谓词 —— 发版方裁))。新放行 0 个。
136
+ - **刻意的代价(合并复审 opus 腿 L3 / L4,一条规则的一致推广,丢的是诊断文本不是泄露)**:良性英文里的 `basic+…` / `bearer+…`
137
+ (`https://x.com/search?q=basic+authentication`、身份 `gpt-4o+basic+reasoning-v2`)在 `+` 读法上判阳,自由文本整串扣、身份不可披露
138
+ (6.2.0 对原文空格形 `q=basic authentication` 本就整串扣);名字 / id 含空格、中文、括号的被丢条目(`{name:"GPT 4o", id:"gpt-4o (2024-08-06)"}`)
139
+ 身份整体缺席,运维 UI 上只剩坐标。
140
+ - 语料:`cli01` / `cli02` 的 `expected.json` 重 bless,diff 只少两处 identity(meta 加 note);**消费方自己入库的语料**若金样里
141
+ identity 带了集外字符,提货时要重新加冕(`docs/CONFORMANCE.md` 同口径)。
142
+ - 自由文本面(过 `redactSecrets` 的那几处 = 告警坐标的键段、zod error 文案):含编码私钥 header(百分号编码、`+` 代空格)的串、
143
+ 以及三层解码走完仍能再解的串,整串变 `[redacted-secret]`(6.2.0 原样出门);替换结果的 `+` 读法 / 边界读法上判阳的
144
+ (`auth=Bearer+<令牌>`、`key=%C2%A0sk-…`、`%E4sk-…` 这类转义粘令牌)同样整串扣。编码的非 ASCII、一层就解完的 `%` 文本照旧只做令牌级替换。
145
+ - 未知键告警(`unknown-keys-dropped` / `-carried`)与 `droppedKeys` / `carriedUnknownKeys` 的 `keys`、`hosts-grandfathered.droppedNames`:
146
+ 秘密形段与超过 256 字的段读 `[redacted-secret]`(6.2.0 原样;超长良性键名 / 主机名因此也不再逐字点名 —— fail-closed 的代价);两条不同的秘密键会读成同一段,告警比对本就是多重集(c10 同律)。良性拼错键照旧逐字点名;承运进 effective 的键与值不动。
147
+
148
+ ### 残留(如实登记;6.2.0 同形,非本版回归)
149
+
150
+ - 裸 base64 私钥体(无 header):任何一面的检测器都认不出 —— 粗粒度启发式的固有边界(真边界仍是结构化字段上的 env-NAME);身份面只在它
151
+ 带了集外字符(`=` 填充、`key=` 前缀、编码)时由白名单挡下,纯 `[A-Za-z0-9+/]` 的一段体照样可披露。
152
+ - 形式编码与多层百分号编码之外的变形(base64 包一层、HTML 实体、`\u002D` 转义等):不在归一化里,见上一条同一边界;非 `%HH` 的坏转义粘令牌
153
+ (`%ZZsk-…`)照旧是既有登记的粗粒度缺口。
154
+ - **codex r2 F-r2-2 [medium](未修,6.2.0 同形)**:短的 Basic / Bearer 凭据在 `+` 读法上 —— `note:Basic+dTpw`(`dTpw` 解出 `u:p`)——
155
+ 脱敏器的定点 Bearer 一遍只跑原文,整串判用的探测器那支要求 ≥ 8 个令牌字符,所以它原样出门,且落在身份白名单字符集内,身份 / refusal 名
156
+ 两谓词都答可披露。同族的 `%20` 形(`note:Basic%20dTpw` / `x-Bearer%20abc123`,合并复审 opus 腿 L2)在自由文本面同样原样出门
157
+ (身份面由白名单挡住 `%`)。发版方令本车第二轮只收敛 high,登 DEBTS。
158
+ - URL userinfo 否决只判得了能解析成 URL 的串:无 scheme 的 `svc:pw@host` 解析成不透明路径,不算带 userinfo。
159
+ - **S-811 [high,既有非回归,本版不修]**:自由文本面(`hosts-grandfathered.droppedNames`、坏 roles 键的坐标段与 zod 错误句)不查 URL userinfo
160
+ —— `https://admin:hunter2@<host>` 原样出门(本版只在身份面加这只判定;设计随 S-809 三表归一)。现状由语料 🚧 特征格钉住:
161
+ `c46-s811-host-name-url-userinfo-characterization` / `c47-s811-roles-key-url-userinfo-characterization`,修后两条金样翻成 `[redacted-secret]`、
162
+ meta.secrets 补上 `hunter2`。
163
+ - AWS secret key 形(40 位 base64 类)、只有 footer 没有 header 的扁平 PEM(`MC4C…+-----END+PRIVATE+KEY-----`)等粗粒度启发式认不出的形:
164
+ 与上面「裸 base64 私钥体」同一边界(opus 腿 L1 附注)。
165
+ - codex r1 三条(F1 `+` 形式编码 / 扁平 PEM、URLSearchParams 形、四层编码;F2 未知键坐标;F3 长名上限)本版均已处置,见「修」。
166
+ - codex r2(`--base 1965aa9 --scope branch`,本车第二轮也是最后一轮):F-r2-1 [high](转义的 UTF-8 字节粘令牌)红先成立并修(边界读法,见「修」);
167
+ F-r2-2 [medium] 未修(见上)。
168
+ - 以上 codex gpt-6-astra r1 复核(车PE 收车档逐条复现:修后树与 6.2.0 源两侧同跑)。另:codex r1 F3 —— 首稿上限 200 让
169
+ schema 合法的 201–256 字名不再上报 / 不进身份;发版方裁上限改 256(与 web-admin 读侧对齐),超 256 字的合法名(`ModelEntry.name`
170
+ 无长度上限)仍按无名处置 —— 对 6.2.0 是诊断面收窄,见「谁受影响」。
171
+
172
+ ### 测试
173
+
174
+ - 新 `test/disclosable-identity-6.3.0.test.ts`:R1 codex 复现形(现场生成 Ed25519,身份 / 投影 / 坐标 / error / refusal 名四面 +
175
+ 四重编码)· R2 白名单正负矩阵(常量源串逐字、无旗标;集内密钥形否决;经 `buildEffective` 同律扫)· R3 `redactSecrets` 归一化
176
+ (单 / 双重编码整串扣、出门不变量、6.2.0 读数逐字的控制格、`secretScanForms` 形 —— 测试直接 import 私有模块)· `containsSecretToken` 6.2.0 oracle。
177
+ 红先提交(ef13bef)上 6.2.0 读数:11 红 / 5 绿(绿 = 正控 / 同律扫 / 控制 / 前提 / oracle);修提交加的 R1 四重编码格在 6.2.0
178
+ 源上同红(终版文件对 6.2.0:12 红 / 5 绿)。
179
+ - 语料 `c41-s792-encoded-pem-identity`(contract):编码 PEM 作 id 与 roles 坏键、四重编码 id、集内 `sk-` 形与 `ops@eyJ…` 形 name、
180
+ 真实形 id 正控;6.2.0 上三腿告警差 + 秘密扫描命中 2 条。
181
+ - B 组(codex r1 F1 部分修):`+` 扁平形四面、URLSearchParams 形作 Record 坏键、四层编码作 Record 坏键 / 未知键名、出门不变量、
182
+ 正控(含 `%` 路径 / 编码非 ASCII / 三层恰解完 / `+` 在真实 id 里原样)、边界(恰四层的非秘密串按没走完整串扣);`secretScanForms` 形钉
183
+ 改纯数据记录;原 🚧 四层前提格翻钉;语料 `c43-s792-form-and-deep-encoded-pem`。变异门加两靶:`+` 读法去掉、`unresolved` 去掉
184
+ (都由 c43 杀);三只 PEM 靶 needle 随改。
185
+ - M1 组(opus 腿 M1):坐标段 256 上界(256 逐字 / 257 整段扣)、宽容解码零异常(`decodeURIComponent` 抛出计数 = 0,修前 4001)、
186
+ opus perf2 同输入宽上界验收格(< 5 s,修前 19.3 s)、a33e7fd 归一化冻结 oracle(6000 条)。
187
+ - B2 组(codex r2 F-r2-1):三形前提(探测器判阴、标准解码后判阳)、自由文本整串扣、Record 坏键 / 未知键名两面;五层编码只剩 `unresolved`
188
+ 扣得住的格;语料 `c44-s792-utf8-escape-glued-token`(`path%2Fto%E4%B8%AD` 正控逐字留)、c43 追加五层一条。变异门加「边界读法去掉」一靶(c44 杀),
189
+ `+` 读法靶 needle 随改。
190
+ - A 组(codex r1 F2):strip 域顶层 / 嵌套、open-world 承运、编码 PEM 键名、导出扫描函数四格;语料 `c42-s792-unknown-key-secret-redacted`
191
+ (6.3.0 修前三腿告警差 + 秘密扫描命中 3 条)。变异门加一靶坐标出门去脱敏,两只嵌套坐标靶 needle 随改。
192
+ - S-813 组:语义格(70 个超长名 ⇒ 标签 64 个、`droppedTotal` 70、告警同带)、控制格(不超界时告警不铸 `droppedTotal`)、
193
+ 性能格(codex 同输入,暖读中位数 ≤ 50 ms;修前 vitest 内 2.2 s)。
194
+ - 🚧 S-811 特征语料 c46 / c47(见「残留」;不是红先复现,是现状钉)。
195
+ - 合并复审 opus 腿组:M2(`droppedNames` 两格)、L1(负正各一格)、M1(见上);语料 `c45-s792-identity-bounds-and-decode-depth`
196
+ (256 / 257 字名与未知键名、三重编码 mcp 参数、`postgres://svc:…@db…` id、`sk-live-…` 超长主机名)—— 杀手语料,不是红先复现。
197
+ - 变异门(`test/conformance-mutation.test.ts`)身份 / 脱敏 / 出门各靶的**实测**杀手(车PE 逐靶探针,十六靶无活口):
198
+ 白名单闭形 → cli01 + cli02 + c45(**都不是秘密形**:编码 PEM 那一形在这个变异体下先被脱敏器否决扣下,c41 不再红 —— opus 腿 L5;
199
+ 秘密轴上白名单独自承重的形没有语料钉);集内否决 → `true` / 退回裸探测器 → c41 + c43;整串兜底 → c40 + c41 + c43 + c44;
200
+ 任意位置 PEM → c40 + c41 + c43;PEM 判在替换前 → c40;PEM 判在归一化形 → c41 + c43;`+` 读法 → c43;边界读法 → c44;
201
+ `unresolved` → c43;`outgoingSegment` 去脱敏 → c10 + c40–c45;`DISCLOSABLE_IDENTITY_MAX` 放大 / `outgoingSegment` 去上界 /
202
+ `SECRET_SCAN_DECODE_LAYERS` 3 → 2 / 去 userinfo 否决 / `droppedNames` 不过出门 → c45。
203
+
204
+ - **test-only(S-595,零运行期变化;随 6.3.0 折入)**:上游漂移门加 ④ —— `CROSS_SESSION_INBOUND_VALUES` / `DIALOG_EXPIRY_VALUES` / `DIALOG_EXPIRY_DEFAULT` 与引擎运行期导出 `CROSS_SESSION_INBOUND_SETTINGS` / `CROSS_SESSION_DIALOG_EXPIRY_VALUES` / `CROSS_SESSION_DIALOG_EXPIRY_DEFAULT` 逐字同序相等(登记镜像:登记表恒空、零豁免,顺序另钉,两只 zod 受理面 = 引擎词表 + `"default"`;负控一格 + 五发变异各红一格;对拍的是 devDependency 钉住的那版引擎,不证明与消费方自装的任意引擎版本相容),等值门从消费方挪回镜像持有者;`src/cross-session.ts` 头注「与引擎词表的锁步」段与 `llms-full.txt` 同句改口径,头注同一处 6.0.0 起陈旧的计数(`"default"` 是第四拼写 / 值域三个)订正为第五 / 四个(注释 / 文档)。
205
+
9
206
  ## 6.2.0 — 2026-09-26 — additive:`ModelRefusalsWire` / `ModelRefusalsSignal` 一处定义 + `readModelRefusals`(S-768;条目两码 / 组拒四码闭集是读方返回型,wire 开放串、读方收窄,心跳字段形坏降缺席 —— 诊断键不打路由)· `isDisclosableIdentity` / `isReportableRefusalName` 导出 · devDep core 2.1.0 → 7.33.1 精确(漂移门 ② 改读运行期 `MODEL_ROLES`,删陈旧豁免)· README / llms-full zod 两实例真话(server / CLI 已在 zod 4;归一 = 7.0.0,S-777)
10
207
 
11
208
  > 本节全 additive(新导出 / 新可选键),零删零改名。判档口径见 README「发版纪律」:新键 / 新导出 = minor。心跳上的新键
package/README.md CHANGED
@@ -140,19 +140,27 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
140
140
  limits/workers) stay fail-loud even in tolerant mode. 0.19.0 adds a FAIL-LOUD sweep on the same read:
141
141
  a `config.d/*.json` the local set will never read (a name that is not a domain, or a non-portable one)
142
142
  and any top-level key a domain schema strips are both NAMED through `onWarning`
143
- (`unread-config-file` / `unknown-keys-dropped`) instead of vanishing silently. **2.0.0** adds per-ENTRY
143
+ (`unread-config-file` / `unknown-keys-dropped`) instead of vanishing silently (**6.3.0**: every warning coordinate —
144
+ unknown keys included — leaves through `redactSecrets`, so a secret-shaped key segment reads `[redacted-secret]`;
145
+ `droppedKeys` / `carriedUnknownKeys` return the same outgoing form). **2.0.0** adds per-ENTRY
144
146
  tolerance on the same read path: one malformed catalog entry is dropped on its own (named through
145
147
  `onWarning` as `entry-dropped`) and its siblings survive, instead of the whole domain falling to
146
148
  its schema default. It is the SAME `DOMAIN_READ_FALLBACK` table that decides where this applies — gate
147
149
  domains keep failing loud, entry by entry included. **S-712** (additive) lets each `entry-dropped` carry an
148
150
  optional `identity?: { name?, id?, enabled? }` read from the dropped entry's OWN text (`DroppedEntryIdentity`):
149
151
  a string `name` / `id` and a boolean `enabled`, own data properties only, each key present only when it
150
- reads with that exact type and `redactSecrets` leaves it unchanged — the SAME redactor the coordinates and the
151
- error text go through (not guessed, not coerced, not masked — a masked string identifies nothing); nothing
152
- readable ⇒ no `identity` key (absent = "not safely readable", whichever the reason). A consumer learns WHICH entry was
152
+ reads with that exact type and `isDisclosableIdentity` says it may ride out (not guessed, not coerced, not
153
+ masked — a masked string identifies nothing). **6.3.0**: an identity is an IDENTIFIER, not free text — it must
154
+ match the closed allowlist `DISCLOSABLE_IDENTITY_RE` (`^[A-Za-z0-9._:/@+-]{1,256}$`; `%`, `=`, whitespace, quotes,
155
+ backslash, control / zero-width / non-ASCII characters and anything over 256 chars are out) AND be left unchanged
156
+ by `redactSecrets`, the SAME redactor the coordinates and the error text go through (the allowlist still admits
157
+ `sk-…` / `ghp_…` shapes) AND not be a URL carrying userinfo (`postgres://svc:pw@h` — the same judgement `NON_SECRET_URL`
158
+ refuses on). 6.1.0–6.2.0 judged by the redactor alone and let a `key=`-prefixed, percent-encoded
159
+ private-key PEM ride out whole (codex review [high]). Nothing readable ⇒ no `identity` key (absent = "not safely
160
+ readable", whichever the reason). A consumer learns WHICH entry was
153
161
  dropped without re-reading the source document by coordinate (the document it holds may not be the one this
154
162
  read saw). `identity` is part of the warning projection, so the conformance gate compares it (corpus
155
- case `c40` pins the two laws JSON can express; the historical normalizer strips it, since older versions cannot
163
+ cases `c40` / `c41` pin the laws JSON can express; the historical normalizer strips it, since older versions cannot
156
164
  mint it — `ConformanceNormalizer.warning?`). **3.0.0** re-casts every coordinate these warnings
157
165
  carry (`entry-dropped.path`, `unknown-keys-*.keys`) from a dot-joined STRING to a structured
158
166
  `ConfigPath = readonly (string | number)[]` — a string segment is an object key (verbatim, `.` included),
@@ -198,11 +206,15 @@ therefore shows only `zod`. Node built-ins only (`node:crypto`, `node:fs`).
198
206
  least one reportable refusal; absence is read by the announced `version` — an engine that predates the key never
199
207
  sends it (UNKNOWN, not "none"). A name that cannot ride out is left out by the engine, judged by ONE predicate,
200
208
  `isReportableRefusalName` (the wire can carry the key — `__proto__` it cannot — and `isDisclosableIdentity`, the
201
- secret-shape rule the dropped-entry identity on read warnings already uses).
209
+ rule the dropped-entry identity on read warnings already uses: the `DISCLOSABLE_IDENTITY_RE` allowlist ∧ not
210
+ secret-shaped, 6.3.0).
202
211
  - **Declared-but-unvalidated keys** (`PARSE_TRANSPARENT_KEYS`, 2.0.0): the ONE registry of keys this package
203
- carries verbatim WITHOUT checking their shape, each row naming who does check it (the engine's ingest side,
204
- a consumer's projection, or the key's own guard). Today: `models[].extraBody`, `models[].compat` (1.10.0),
205
- `mcp.servers[].source` (1.7.2), `mcp.servers[].toolFaces` (1.9.0), and an entitlement override's `value`.
212
+ carries verbatim WITHOUT checking their shape, each row naming who does check it (the engine's ingest side or
213
+ a consumer's enforcement point). Today: `models[].compat` (1.10.0), `models[].headers` (6.4.0; the header-name
214
+ allowlist × lane is sema-server's, not restated here), `mcp.servers[].source` (1.7.2), `mcp.servers[].toolFaces`
215
+ (1.9.0), and an entitlement override's `value`. `models[].extraBody` is NOT one of them — it has its own shape
216
+ gate (a plain JSON object); what it leaves open is the key vocabulary inside. `compat` / `headers` / `extraBody`
217
+ still clear the one free-form safety judgment (secret-shaped key/value, accessor, cycle, non-JSON, too deep ⇒ rejected).
206
218
  - **Extension namespaces** (`EXTENSION_NAMESPACES`, 1.5.0): keys another writer puts in a domain document by
207
219
  contract and this package deliberately does not model (today: the shell's entry-level `sema*` fields on
208
220
  `models`). They are still stripped, but they are NOT reported as unknown keys — intent is declared at the
@@ -1 +1 @@
1
- 6.2.0
1
+ 6.4.0
@@ -0,0 +1,166 @@
1
+ {
2
+ "domains": {
3
+ "outcome": "effective",
4
+ "effective": {
5
+ "collab": {
6
+ "templates": []
7
+ },
8
+ "entitlement": {
9
+ "budgetGroups": [],
10
+ "groupBindings": [],
11
+ "subjects": [],
12
+ "tiers": []
13
+ },
14
+ "execution": {
15
+ "allowedLanes": [
16
+ "host"
17
+ ],
18
+ "required": false
19
+ },
20
+ "governance": {},
21
+ "hosts": {
22
+ "hosts": []
23
+ },
24
+ "limits": {},
25
+ "mcp": {
26
+ "servers": []
27
+ },
28
+ "models": {
29
+ "models": [
30
+ {
31
+ "api": "openai-completions",
32
+ "apiKeyEnv": "OPENAI_API_KEY",
33
+ "enabled": true,
34
+ "id": "keep-id",
35
+ "name": "keep",
36
+ "provider": "openai",
37
+ "quotaWeight": 1,
38
+ "reasoning": false,
39
+ "tier": "standard",
40
+ "vision": false
41
+ }
42
+ ],
43
+ "roles": {
44
+ "summarize": {
45
+ "model": "keep"
46
+ }
47
+ },
48
+ "tierGroups": []
49
+ },
50
+ "plugins": {
51
+ "plugins": []
52
+ },
53
+ "projects": {
54
+ "projects": {}
55
+ },
56
+ "prompts": {
57
+ "bindings": []
58
+ },
59
+ "rosters": {
60
+ "rosters": []
61
+ },
62
+ "runtime": {},
63
+ "scenarios": {
64
+ "scenarios": []
65
+ },
66
+ "skills": {
67
+ "skills": []
68
+ },
69
+ "systems": {
70
+ "systems": []
71
+ },
72
+ "workers": {
73
+ "workers": []
74
+ }
75
+ },
76
+ "warnings": [
77
+ {
78
+ "domain": "models",
79
+ "identity": {
80
+ "name": "encoded-pem"
81
+ },
82
+ "kind": "entry-dropped",
83
+ "path": [
84
+ "models",
85
+ 1
86
+ ]
87
+ },
88
+ {
89
+ "domain": "models",
90
+ "identity": {
91
+ "id": "in-charset-secret-name-id"
92
+ },
93
+ "kind": "entry-dropped",
94
+ "path": [
95
+ "models",
96
+ 2
97
+ ]
98
+ },
99
+ {
100
+ "domain": "models",
101
+ "identity": {
102
+ "id": "one-dot-jwt-name-id"
103
+ },
104
+ "kind": "entry-dropped",
105
+ "path": [
106
+ "models",
107
+ 3
108
+ ]
109
+ },
110
+ {
111
+ "domain": "models",
112
+ "identity": {
113
+ "id": "accounts/fireworks/models/llama-v3p1-8b-instruct",
114
+ "name": "gpt-4o-2024-08-06"
115
+ },
116
+ "kind": "entry-dropped",
117
+ "path": [
118
+ "models",
119
+ 4
120
+ ]
121
+ },
122
+ {
123
+ "domain": "models",
124
+ "identity": {
125
+ "id": "claude-3-5-sonnet@20240620:free+high",
126
+ "name": "o3-mini"
127
+ },
128
+ "kind": "entry-dropped",
129
+ "path": [
130
+ "models",
131
+ 5
132
+ ]
133
+ },
134
+ {
135
+ "domain": "models",
136
+ "identity": {
137
+ "name": "deep-encoded-pem"
138
+ },
139
+ "kind": "entry-dropped",
140
+ "path": [
141
+ "models",
142
+ 6
143
+ ]
144
+ },
145
+ {
146
+ "domain": "models",
147
+ "kind": "entry-dropped",
148
+ "path": [
149
+ "roles",
150
+ "[redacted-secret]"
151
+ ]
152
+ }
153
+ ],
154
+ "metadata": {
155
+ "version": 12,
156
+ "updatedAt": "2026-09-27T00:00:00.000Z"
157
+ }
158
+ },
159
+ "wire": {
160
+ "derive": true
161
+ },
162
+ "localRoot": {
163
+ "derive": true,
164
+ "localVersion": 1537928059550602
165
+ }
166
+ }
@@ -0,0 +1,62 @@
1
+ {
2
+ "meta": {
3
+ "version": 12,
4
+ "updatedAt": "2026-09-27T00:00:00.000Z"
5
+ },
6
+ "domains": {
7
+ "models": {
8
+ "models": [
9
+ {
10
+ "name": "keep",
11
+ "provider": "openai",
12
+ "id": "keep-id",
13
+ "apiKeyEnv": "OPENAI_API_KEY"
14
+ },
15
+ {
16
+ "name": "encoded-pem",
17
+ "provider": "openai",
18
+ "id": "key=-----BEGIN%20PRIVATE%20KEY-----%0AMC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000%0A-----END%20PRIVATE%20KEY-----%0A",
19
+ "apiKeyEnv": "not an env name"
20
+ },
21
+ {
22
+ "name": "sk-FAKE-S621-CORPUS-0000000000",
23
+ "provider": "openai",
24
+ "id": "in-charset-secret-name-id",
25
+ "apiKeyEnv": "not an env name"
26
+ },
27
+ {
28
+ "name": "ops@eyJFAKES621CORPUSHDR0.tail",
29
+ "provider": "openai",
30
+ "id": "one-dot-jwt-name-id",
31
+ "apiKeyEnv": "not an env name"
32
+ },
33
+ {
34
+ "name": "gpt-4o-2024-08-06",
35
+ "provider": "openai",
36
+ "id": "accounts/fireworks/models/llama-v3p1-8b-instruct",
37
+ "apiKeyEnv": "not an env name"
38
+ },
39
+ {
40
+ "name": "o3-mini",
41
+ "provider": "openai",
42
+ "id": "claude-3-5-sonnet@20240620:free+high",
43
+ "apiKeyEnv": "not an env name"
44
+ },
45
+ {
46
+ "name": "deep-encoded-pem",
47
+ "provider": "openai",
48
+ "id": "key=-----BEGIN%25252520PRIVATE%25252520KEY-----%2525250AMC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000%2525250A-----END%25252520PRIVATE%25252520KEY-----%2525250A",
49
+ "apiKeyEnv": "not an env name"
50
+ }
51
+ ],
52
+ "roles": {
53
+ "summarize": {
54
+ "model": "keep"
55
+ },
56
+ "key=-----BEGIN%20PRIVATE%20KEY-----%0AMC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000%0A-----END%20PRIVATE%20KEY-----%0A": {
57
+ "model": "keep"
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "contributor": "contract",
3
+ "claim": "6.3.0(codex gpt-6-astra 对 v6.1.0 复审 [high] 回填 D-4):`key=` + 百分号编码的 PEM 私钥 —— 原文上 PEM header 带 `%20` 看不见、`containsSecretToken` 的行首锚被 `key=` 前缀打败 —— 同时作被丢条目的 `id`(身份面)与 `roles` 的坏键(坐标面 + zod error 的 received / message 面)。身份面走白名单闭形 `DISCLOSABLE_IDENTITY_RE`(`%` / `=` 不在集内)⇒ 只剩 name(四重编码的同一把私钥同样只剩 name —— 身份面靠白名单闭形,与编码深度无关;6.3.0 起自由文本面也在解码界上 fail-closed);坐标 / error 走 `redactSecrets`,整串判套在归一化形(原文 + 探测器同界的解码层)上 ⇒ 坐标段整段扣留、error 不带私钥。另压白名单的两道边:字符集**之内**的密钥形 name(`sk-` 形;`ops@` 后接单点 `eyJ…` 形 —— 探测器判阴、坐标脱敏器会改)⇒ 不带 name,身份的否决判据与坐标脱敏同一只;正控:带 `/` `@` `:` `+` 的真实形上游 id 与合法 name 逐字带出。",
4
+ "firstRedOn": [],
5
+ "secrets": [
6
+ "MC4CAQAwBQYDK2VwBCIEIFAKES621ENCODEDPEMCORPUSBODY00000000000000",
7
+ "BEGIN%20PRIVATE%20KEY",
8
+ "sk-FAKE-S621-CORPUS-0000000000",
9
+ "eyJFAKES621CORPUSHDR0"
10
+ ],
11
+ "note": "车PE(2026-09-27)红先:加本条时 6.2.0 读腿在 domains 腿上报告警差(identity.id 与 roles 坐标段原样带出编码私钥),秘密扫描同时命中私钥体行。变异门 `test/conformance-mutation.test.ts` 为 isDisclosableIdentity / redactSecrets 登记的靶(逐靶实测,车PE 收车档):去掉白名单(退回「脱敏器原样放行即可披露」)⇒ cli01 / cli02 红(本条四重编码那条原先也红,借的是「自由文本面只读三层解码」那个缺口;6.3.0 B 在解码界上 fail-closed 后,一重 / 四重编码两条都由修后的 redactSecrets 否决,身份面两道闸各自扣下,本条对该靶不再红 —— 已按原 🚧 注翻过);白名单之内的否决去掉(`redactSecrets(v) === v` → `true`)⇒ `sk-` 形 name 带出红;否决退回裸探测器(→ `!containsSecretToken(v)`)⇒ `ops@eyJ…` 形 name 带出红;归一化形退回只有原文(`secretScanForms(text)` → `[text]`)⇒ roles 坐标段与 error 带出编码私钥红。firstRedOn 为空:`conformance/historical/` 的阶梯证据(2.1.0 及更早)真跑于本条入库之前,没有本条的读数,那一栏只收 historical 真跑;本条对 6.2.0 的红由红先提交实测(见车PE 收车档),不手填进 firstRedOn。"
12
+ }
@@ -0,0 +1,120 @@
1
+ {
2
+ "domains": {
3
+ "outcome": "effective",
4
+ "effective": {
5
+ "collab": {
6
+ "templates": []
7
+ },
8
+ "entitlement": {
9
+ "budgetGroups": [],
10
+ "groupBindings": [],
11
+ "subjects": [],
12
+ "tiers": []
13
+ },
14
+ "execution": {
15
+ "allowedLanes": [
16
+ "host"
17
+ ],
18
+ "required": false
19
+ },
20
+ "governance": {},
21
+ "hosts": {
22
+ "hosts": []
23
+ },
24
+ "limits": {
25
+ "maxToolCallz": 3,
26
+ "sk-live-FAKEC42CARRIEDKEY000000000": 5
27
+ },
28
+ "mcp": {
29
+ "servers": []
30
+ },
31
+ "models": {
32
+ "models": [
33
+ {
34
+ "api": "openai-completions",
35
+ "apiKeyEnv": "OPENAI_API_KEY",
36
+ "enabled": true,
37
+ "id": "keep-id",
38
+ "name": "keep",
39
+ "provider": "openai",
40
+ "quotaWeight": 1,
41
+ "reasoning": false,
42
+ "tier": "standard",
43
+ "vision": false
44
+ }
45
+ ],
46
+ "roles": {},
47
+ "tierGroups": []
48
+ },
49
+ "plugins": {
50
+ "plugins": []
51
+ },
52
+ "projects": {
53
+ "projects": {}
54
+ },
55
+ "prompts": {
56
+ "bindings": []
57
+ },
58
+ "rosters": {
59
+ "rosters": []
60
+ },
61
+ "runtime": {},
62
+ "scenarios": {
63
+ "scenarios": []
64
+ },
65
+ "skills": {
66
+ "skills": []
67
+ },
68
+ "systems": {
69
+ "systems": []
70
+ },
71
+ "workers": {
72
+ "workers": []
73
+ }
74
+ },
75
+ "warnings": [
76
+ {
77
+ "domain": "models",
78
+ "keys": [
79
+ [
80
+ "models",
81
+ 0,
82
+ "[redacted-secret]"
83
+ ],
84
+ [
85
+ "models",
86
+ 0,
87
+ "typoKey"
88
+ ],
89
+ [
90
+ "[redacted-secret]"
91
+ ]
92
+ ],
93
+ "kind": "unknown-keys-dropped"
94
+ },
95
+ {
96
+ "domain": "limits",
97
+ "keys": [
98
+ [
99
+ "[redacted-secret]"
100
+ ],
101
+ [
102
+ "maxToolCallz"
103
+ ]
104
+ ],
105
+ "kind": "unknown-keys-carried"
106
+ }
107
+ ],
108
+ "metadata": {
109
+ "version": 13,
110
+ "updatedAt": "2026-09-27T00:00:00.000Z"
111
+ }
112
+ },
113
+ "wire": {
114
+ "derive": true
115
+ },
116
+ "localRoot": {
117
+ "derive": true,
118
+ "localVersion": 547314222926085
119
+ }
120
+ }