progmune-runtime 3.7.61 → 3.7.63

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,72 @@
1
1
  # Changelog
2
2
 
3
- ## [3.7.61] — 2026-10-06(未发布)
3
+ ## [3.7.63] — 2026-10-10
4
+
5
+ ### No Input Sanitization 触发词收窄(修冤枉第四刀:删 insert/append/write 通用词)
6
+
7
+ **根因**:trigger 里三个「输出系」通用词系统性词撞,从没在真实语料命中过
8
+ 一条 XSS——`insert` 撞 Kysely `insertInto` 数据入库(docmost 补标注
9
+ 30/30 条 FP)、`append` 撞 Python `list.append` 内存数组(Python 盲测
10
+ 45 项目 270 条 FP)、`write` 撞 `writeFileSync` 文件写入(TS 盲测 8 项目
11
+ FP + docmost 告警流 6 条)。
12
+
13
+ - **修复**:trigger 收窄为 `render|display|output|innerHTML|
14
+ dangerouslySetInnerHTML|echo|printf|sprintf|insertAdjacentHTML`——
15
+ DOM sink 用原始调用名显式保留(`el.insertAdjacentHTML(...)` 的提取名
16
+ 就是 `insertAdjacentHTML`),XSS 本意完整保留
17
+ - **逐条归类(R30)**:TS 盲测 LOST 10(8 项目 writeFileSync 族 +
18
+ webshape_F insertInto/insert)、Python 盲测 LOST 45 项目(全部
19
+ list.append)、ADDED 0/0——**无一例外是存储 I/O 词撞,零 XSS sink**
20
+ - **头条数字零影响(如实)**:TS 795 gold 与 Python 729 gold 均不含
21
+ No Input Sanitization 条目(金标全为 protocol/auth/resource 类),
22
+ recall/precision 不变;docmost 告警流 942→865(No Input Sanitization
23
+ 83→6,残留 6 条为 render/output 词族:react-email 服务端渲染×2、
24
+ 函数名 generatePdfRenderToken 撞 render×1、存储驱动 outputFile×3)
25
+ - **A/B 隔离注记**:主路径 violations 扫描间 7↔10 波动为 LLM 语义映射
26
+ 抖动(已知类,见 skyvern CROSS_DOMAIN 记录),与本次改动无因果——
27
+ safeguard 规则不进 violations(§51 证据流与判定分离),同条件 A/B
28
+ 对照主路径一致
29
+ - **回归锁定**:`src/protocol-detector-nis-trigger.test.ts` 5 例
30
+ (insertInto/writeFileSync/list.append 不触发、insertAdjacentHTML 仍
31
+ 触发、render+sanitize 守卫仍抑制);taintpath 160/160;fr-007
32
+ pre5/post0、fr-016 pre7/post0 维持
33
+ - **已知代价(如实记录)**:`document.write` 的提取名是裸 `write`,
34
+ 与文件写入词法不可区分,本刀后不再命中——三语料里 0 条由
35
+ document.write 驱动;浏览器侧 DOM sink 证据通道(marker 化)留待
36
+ R27 方向
37
+
38
+ ## [3.7.62] — 2026-10-09
39
+
40
+ ### 迁移脚本排除出应用面(修冤枉第三刀:§49.19 补标注后首个数据驱动修复)
41
+
42
+ **背景**:§49.19 补标注队列全部完成——docmost 174/174 条逐条人工核实
43
+ (gold_confidence=verified,verified_by=ai-read-source),**全部为 FP**。
44
+ 这补齐了修冤枉第二刀撞到的数据边界(fp-pool 重扫 381→189 后「可干净
45
+ 动刀的根因挖完」的剩余瓶颈就是标注不足)。
46
+
47
+ - **证据**:`blind-benchmark/reports/advisories/annotation-queue.jsonl`
48
+ (174 条全量 gold 字段落档)+ `blind-benchmark/fp-gold.jsonl`(+174 条
49
+ schema v2)+ `annotate-batches/`(16 批原始标注记录)
50
+ - **首个根因**:Kysely 迁移脚本 `up()/down()`(src/database/migrations/)
51
+ 被多族规则误报——addColumn/createTable 词撞 Authorization
52
+ (Unauthenticated Mutation),dropTable 撞 Data Mutation 族。
53
+ 迁移只在部署时跑一次、不暴露请求面,与 node_modules/benchmarks 同属
54
+ 「无应用面」判定。
55
+ - **修复**:`src/extract-ir.ts` 提取循环跳过 `/migrations/` 路径
56
+ (与 node_modules 跳过并列;Python 侧 alembic 未在本轮测量,暂不动——
57
+ 按三要件,没有数据不扩面)
58
+ - **A/B 实测**(docmost apps/server 同引擎双扫描):告警流 992 → 942
59
+ (−50,恰好等于 fullscan 里 47 条 Unauthenticated Mutation + 3 条
60
+ Ownership Check 的迁移脚本 FP);**判定/分数/主路径违规零变化**
61
+ (NEEDS_REVIEW 86 / 7 violations / auth 44,两侧完全一致)
62
+ - **回归锁定**:新测试 `src/extract-ir-migrations.test.ts`(2 例:迁移
63
+ 不提取 + migrations 字样非目录不误伤);TS 盲测 119 项目零漂移
64
+ (仅时间戳变化);taintpath 160/160;fr-007 pre5/post0、fr-016
65
+ pre7/post0 维持
66
+ - 已知代价(如实记录):迁移文件里的真实问题(如数据回填 SQLi)不再
67
+ 检出——与 node_modules 跳过同一取舍,提取面=应用面
68
+
69
+ ## [3.7.61] — 2026-10-06
4
70
 
5
71
  ### fr-017 点亮:授权身份锚点取自请求输入(修复回归 11/17)
6
72
 
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ const fs = __importStar(require("fs"));
38
+ const os = __importStar(require("os"));
39
+ const path = __importStar(require("path"));
40
+ const extract_ir_1 = require("./extract-ir");
41
+ /**
42
+ * /migrations/ 目录跳过(2026-10-08 §49.19 补标注的产物)。
43
+ *
44
+ * 数据:docmost 补标注队列 174 条里,迁移脚本 up()/down() 被多族规则报
45
+ * (Authorization 族 + Data Mutation 族——addColumn/createTable 词撞),
46
+ * 全部人工核实为 FP:迁移只在部署时跑一次,不暴露请求面,与
47
+ * node_modules/benchmarks 同属「无应用面」判定。
48
+ *
49
+ * 锁定行为:migrations 目录下的源文件整体不参与提取;
50
+ * 同项目其他目录正常提取(防误伤)。
51
+ */
52
+ const TSCONFIG = JSON.stringify({
53
+ compilerOptions: {
54
+ target: "ES2020",
55
+ module: "commonjs",
56
+ moduleResolution: "node",
57
+ strict: false,
58
+ skipLibCheck: true,
59
+ noEmit: true,
60
+ },
61
+ include: ["**/*.ts"],
62
+ });
63
+ function makeProject(files) {
64
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pm-migrations-"));
65
+ fs.writeFileSync(path.join(dir, "tsconfig.json"), TSCONFIG);
66
+ fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify({ name: "fixture", version: "0.0.0", private: true }));
67
+ for (const [rel, body] of Object.entries(files)) {
68
+ const abs = path.join(dir, rel);
69
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
70
+ fs.writeFileSync(abs, body);
71
+ }
72
+ return dir;
73
+ }
74
+ (0, vitest_1.describe)("extractIR:/migrations/ 目录跳过(无应用面判定)", () => {
75
+ (0, vitest_1.it)("迁移脚本不提取,同项目其他目录正常提取", () => {
76
+ const dir = makeProject({
77
+ "src/app.ts": `export function handleCreatePage(): void {
78
+ console.log("app surface");
79
+ }`,
80
+ "src/database/migrations/20260101T000000-init.ts": `import { Kysely } from "kysely";
81
+ export async function up(db: Kysely<any>): Promise<void> {
82
+ await db.schema.createTable("page").addColumn("id", "text").execute();
83
+ }
84
+ export async function down(db: Kysely<any>): Promise<void> {
85
+ await db.schema.dropTable("page").execute();
86
+ }`,
87
+ });
88
+ const fns = (0, extract_ir_1.extractIR)(dir).map((f) => f.name).sort();
89
+ // 应用面函数保留
90
+ (0, vitest_1.expect)(fns).toContain("handleCreatePage");
91
+ // 迁移脚本 up/down 不参与提取
92
+ (0, vitest_1.expect)(fns).not.toContain("up");
93
+ (0, vitest_1.expect)(fns).not.toContain("down");
94
+ });
95
+ (0, vitest_1.it)("路径含 migrations 字样但不属于 /migrations/ 目录的文件不误伤", () => {
96
+ const dir = makeProject({
97
+ "src/migrations-service.ts": `export function runMigrations(): void {
98
+ console.log("service, not a migration dir");
99
+ }`,
100
+ });
101
+ const fns = (0, extract_ir_1.extractIR)(dir).map((f) => f.name);
102
+ (0, vitest_1.expect)(fns).toContain("runMigrations");
103
+ });
104
+ });
@@ -3136,6 +3136,14 @@ function _extractSingleProject(absRoot, tsconfigPath) {
3136
3136
  for (const sf of project.getSourceFiles()) {
3137
3137
  if (sf.getFilePath().includes("node_modules"))
3138
3138
  continue; // lib.d.ts 等声明文件只贡献噪声(2026-09-17 性能)
3139
+ // 2026-10-08(§49.19 补标注 174/174 的证据):迁移脚本不是应用表面——
3140
+ // Kysely 的 addColumn/createTable/insertInto 词撞变更/审计规则族
3141
+ // (docmost 全量告警 ~5% 全 FP:up()/down() 迁移函数被多族规则报)。
3142
+ // 与 node_modules 同款「无应用面」判定:迁移只在部署时跑一次,
3143
+ // 不暴露请求面。Python 侧 alembic(/migrations/ 或 /alembic/versions/)
3144
+ // 未在本轮测量,暂不动。
3145
+ if (sf.getFilePath().split(path.sep).join("/").includes("/migrations/"))
3146
+ continue;
3139
3147
  const relPath = path.relative(absRoot, sf.getFilePath());
3140
3148
  const _ft0 = Date.now();
3141
3149
  for (const f of sf.getFunctions()) {
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const protocol_detector_1 = require("./protocol-detector");
5
+ /**
6
+ * No Input Sanitization 触发词收窄(修冤枉第四刀,2026-10-09)锁定。
7
+ *
8
+ * 数据:docmost 补标注 30/30 FP(Kysely insertInto)、Python 盲测
9
+ * 270/270 FP(list.append)、TS 盲测 33/33 FP(writeFileSync 族)——
10
+ * 「输出系」通用词系统性词撞,本意是 XSS sink 却从没在真实语料
11
+ * 命中过一条 XSS。收窄后 trigger 只留真实 sink 语义:
12
+ * render/display/output/innerHTML/dangerouslySetInnerHTML/echo/
13
+ * printf/sprintf + 显式 DOM sink 原始名 insertAdjacentHTML。
14
+ *
15
+ * 已知代价:document.write 的提取名是裸 "write",与文件写入不可分,
16
+ * 不再命中(盲测/语料里 0 条由 document.write 驱动,如实记录)。
17
+ */
18
+ const RULE = "No Input Sanitization";
19
+ const has = (vs) => vs.some((v) => v.rule === RULE);
20
+ (0, vitest_1.describe)("No Input Sanitization:触发词收窄(修冤枉第四刀)", () => {
21
+ (0, vitest_1.it)("数据库写入 insertInto 不再触发(docmost 30/30 FP 根因)", () => {
22
+ const vs = (0, protocol_detector_1.detectSafeguardViolations)(["dbOrTx", "executeTakeFirst", "returningAll", "values", "insertInto", "__progmune_input_effect__"], "PageRepo.insertPage", "typescript");
23
+ (0, vitest_1.expect)(has(vs)).toBe(false);
24
+ });
25
+ (0, vitest_1.it)("文件写入 writeFileSync 不再触发(TS 盲测 8 项目 FP 根因)", () => {
26
+ const vs = (0, protocol_detector_1.detectSafeguardViolations)(["writeFileSync", "mkdirSync"], "uploadFile", "typescript");
27
+ (0, vitest_1.expect)(has(vs)).toBe(false);
28
+ });
29
+ (0, vitest_1.it)("Python list.append 不再触发(Python 盲测 45 项目 270 条 FP 根因)", () => {
30
+ const vs = (0, protocol_detector_1.detectSafeguardViolations)(["users", "append", "sessions", "append"], "register", "python");
31
+ (0, vitest_1.expect)(has(vs)).toBe(false);
32
+ });
33
+ (0, vitest_1.it)("真实 DOM sink insertAdjacentHTML 仍触发(XSS 本意保留)", () => {
34
+ const vs = (0, protocol_detector_1.detectSafeguardViolations)(["insertAdjacentHTML"], "renderComment", "typescript");
35
+ (0, vitest_1.expect)(has(vs)).toBe(true);
36
+ });
37
+ (0, vitest_1.it)("真实渲染 sink render 仍触发,带 sanitize 守卫仍抑制", () => {
38
+ const bare = (0, protocol_detector_1.detectSafeguardViolations)(["render"], "renderTemplate", "typescript");
39
+ (0, vitest_1.expect)(has(bare)).toBe(true);
40
+ const guarded = (0, protocol_detector_1.detectSafeguardViolations)(["render", "sanitizeHtml"], "renderTemplate", "typescript");
41
+ (0, vitest_1.expect)(has(guarded)).toBe(false);
42
+ });
43
+ });
@@ -1090,10 +1090,25 @@ const SAFEGUARD_RULES = [
1090
1090
  ],
1091
1091
  },
1092
1092
  // ── Resource: Input sanitization missing ──
1093
+ //
1094
+ // 修冤枉第四刀(2026-10-09,§49.19 补标注数据):trigger 里三个「输出系」
1095
+ // 通用词在真实与合成语料上系统性词撞,全量 FP——
1096
+ // insert:Kysely insertInto 数据入库(docmost 标注 30/30 FP;
1097
+ // 盲测 webshape_F 2/2 FP)
1098
+ // append:Python list.append 内存数组(Python 盲测 270/270 FP)
1099
+ // write: 文件写入 writeFileSync(TS 盲测 33/33 FP)
1100
+ // 本意是 XSS sink(DOM 插入/文档写入),逐条核实无一条真阳性。
1101
+ // 收窄方案:删通用词,DOM sink 用**原始调用名**显式保留
1102
+ // (extractDirectCalls 对属性访问发射 getName() 原样名:
1103
+ // el.insertAdjacentHTML(...) → "insertAdjacentHTML")。
1104
+ // 已知代价(如实记录):document.write 的原始发射名是 "write",
1105
+ // 与文件写入词法不可区分,本刀后不再命中——盲测/语料里 0 条由
1106
+ // document.write 驱动,风险=浏览器侧代码的 XSS sink 词,留待
1107
+ // marker 化证据通道(R27 方向)。
1093
1108
  {
1094
1109
  name: "No Input Sanitization",
1095
1110
  category: "resource",
1096
- trigger: /\b(render|display|write|output|append|insert|innerHTML|dangerouslySetInnerHTML|document\.write|echo|printf|sprintf)\b/i,
1111
+ trigger: /\b(render|display|output|innerHTML|dangerouslySetInnerHTML|echo|printf|sprintf|insertAdjacentHTML)\b/i,
1097
1112
  safeguards: [
1098
1113
  { pattern: /\b(sanitize|escape|encode|html\w*entities|htmlspecialchars|encodeURI|stripTags|purify|DOMPurify|clean|filter\w*html|escape\w*html)\b/i, label: "sanitize" },
1099
1114
  ],
package/dist/sdk.js CHANGED
@@ -20,7 +20,7 @@ const risk_model_1 = require("./risk-model");
20
20
  const protocol_knowledge_1 = require("./protocol-knowledge");
21
21
  const evidence_repository_1 = require("./evidence-repository");
22
22
  /** Runtime version — stable public identifier. Internal layers evolve underneath. */
23
- exports.RUNTIME_VERSION = "3.7.61";
23
+ exports.RUNTIME_VERSION = "3.7.63";
24
24
  function verify(filePath) {
25
25
  const cert = (0, certify_1.certify)(filePath);
26
26
  const kb = (0, protocol_knowledge_1.buildKnowledgeBase)();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "progmune-runtime",
3
- "version": "3.7.61",
3
+ "version": "3.7.63",
4
4
  "description": "Progmune — AI Trust Decision Engine. Verify AI-generated code before it reaches production. Outputs APPROVED / NEEDS_REVIEW / BLOCKED with evidence.",
5
5
  "files": [
6
6
  "dist/",