progmune-runtime 3.7.54 → 3.7.55
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 +35 -0
- package/dist/extract-ir.js +3 -0
- package/dist/frameworks/nestjs-detector.js +94 -1
- package/dist/frameworks/nestjs-detector.test.js +177 -0
- package/dist/sdk.js +1 -1
- package/dist/trust/engine.js +9 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.7.55] — 2026-09-29
|
|
4
|
+
|
|
5
|
+
### 主路径(trust 判定)真实项目首测 + NestJS 失明误报修复(§50)
|
|
6
|
+
|
|
7
|
+
**首次对产品判定路径做真实项目全量测量**(`blind-benchmark/REALWORLD_MAIN_PATH_V1.md`):
|
|
8
|
+
docmost(NestJS 5k★)修复前被主路径判 **BLOCKED(score 48,auth 0 分)**——132 条违规里
|
|
9
|
+
126 条逐条核实为失明/语义误报,且同一批违规在 policyCompliance 与 protocolSafety
|
|
10
|
+
**双维度重复扣分**。主路径与旁路(safeguard,§49 加权精确率 5.8%)同病:
|
|
11
|
+
真实公告位置命中 0/18(fr-corpus 17 条交叉证实协议状态机 0 检出),
|
|
12
|
+
但主路径把噪声直接变成产品级判定——**虚假 BLOCKED 比虚假告警伤害大**。
|
|
13
|
+
|
|
14
|
+
**四刀修复(全部 src 接线层,判定核心不动)**:
|
|
15
|
+
|
|
16
|
+
1. **E3 DTO 通道接入 NestJS 适配器**:`app.useGlobalPipes(...)` 全局管道识别 +
|
|
17
|
+
路由入参类型 ∈ 已校验 DTO 集合(复用 `validatedDtoClassNames`,加继承闭包与
|
|
18
|
+
`createZodDto` Zod 形态)⇒ 不报 NO_VALIDATION。`extract-ir.ts` 导出 E3 集合函数。
|
|
19
|
+
2. **无结构化输入豁免**:入参无 @Body/@Query/@Param(仅 AuthUser/Req/Res/
|
|
20
|
+
UploadedFile)的路由不报 NO_VALIDATION——没有可校验的输入。
|
|
21
|
+
3. **auth 端点豁免扩展**:+ password-reset / reset-password / verify-token /
|
|
22
|
+
verify / setup / init(docmost 实测 3 条 NO_AUTH 误报)。
|
|
23
|
+
4. **双维度去重**:policyCompliance 只吃策略类违规(policy_ref 以
|
|
24
|
+
framework/protocol-safety 开头的已在 protocolSafety 计分)。
|
|
25
|
+
|
|
26
|
+
**效果**:docmost **BLOCKED(48) → APPROVED(87)**,违规 132 → 6(NESTJS 误报
|
|
27
|
+
126 → 0;残留 6 条含 1 条撞 CVE-2026-33193 攻击面的真实语义告警);
|
|
28
|
+
hedgedoc(Zod 校验体系 + public/private 双 API 面)47 → 32,剩余 19 条逐条定性
|
|
29
|
+
为规则粒度边界(@Param 裸类型 / @Body('field') 单字段,真无校验,如实保留)。
|
|
30
|
+
|
|
31
|
+
**踩坑**:`param.getType().getText()` 返回 `import("...").DtoName` 形态——取
|
|
32
|
+
首个标识符会取到 `import` 关键字,改取全部标识符任一命中;测试断言 route
|
|
33
|
+
格式无前导斜杠(`POST pages/create`),3 个测试曾因此"假通过"。
|
|
34
|
+
|
|
35
|
+
**测试**:nestjs-detector 17/17(新增全局管道+DTO / Zod / 继承 / 无输入 7 条
|
|
36
|
+
正反向);trust 全家 95/95。
|
|
37
|
+
|
|
3
38
|
## [3.7.54] — 2026-09-27
|
|
4
39
|
|
|
5
40
|
### §47 签名级会话证据 + §48 输入校验族摸底(含两条判据被数据否决)
|
package/dist/extract-ir.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.validatedDtoClassNames = validatedDtoClassNames;
|
|
36
37
|
exports.extractIR = extractIR;
|
|
37
38
|
exports.extractIRWithTypes = extractIRWithTypes;
|
|
38
39
|
const ts_morph_1 = require("ts-morph");
|
|
@@ -493,6 +494,8 @@ function decoratorIsValidator(text) {
|
|
|
493
494
|
/**
|
|
494
495
|
* 全项目扫描:类自身或其属性上带校验装饰器的类名集合。
|
|
495
496
|
* 只看类名(不做类型解析)—— 与污点管线口径一致,跨文件引用靠名字对上。
|
|
497
|
+
* 导出供框架适配器复用(2026-09-29:NestJS 检测器的 NO_VALIDATION
|
|
498
|
+
* 失明误报——路由 DTO 在别的文件带 class-validator 装饰器,检测器看不到)。
|
|
496
499
|
*/
|
|
497
500
|
function validatedDtoClassNames(project) {
|
|
498
501
|
const out = new Set();
|
|
@@ -21,6 +21,7 @@ exports.analyzeNestJSFile = analyzeNestJSFile;
|
|
|
21
21
|
exports.formatNestJSReport = formatNestJSReport;
|
|
22
22
|
const ts_morph_1 = require("ts-morph");
|
|
23
23
|
const route_window_1 = require("./route-window");
|
|
24
|
+
const extract_ir_1 = require("../extract-ir");
|
|
24
25
|
// ── Core Analysis ──
|
|
25
26
|
function analyzeNestJSProject(projectRoot) {
|
|
26
27
|
let project;
|
|
@@ -65,6 +66,66 @@ function analyzeNestJSProject(projectRoot) {
|
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
const hasGlobalAuthGuard = analysis.globalAuthGuards.length > 0;
|
|
69
|
+
// ── 全局校验管道(2026-09-29:docmost 123 条 NO_VALIDATION 失明误报的根因)──
|
|
70
|
+
// NestJS 标准做法是 bootstrap 里 app.useGlobalPipes(new ValidationPipe(...)),
|
|
71
|
+
// 路由本身不带 @UsePipes 装饰器 ⇒ 此前全部报 NO_VALIDATION。
|
|
72
|
+
// 检测方式:任意非 node_modules 源文件出现 .useGlobalPipes( 调用(参数不限——
|
|
73
|
+
// hedgedoc 形态是 useGlobalPipes(setupValidationPipe(logger)),管道经辅助函数
|
|
74
|
+
// 创建,字面 ValidationPipe 不在调用处)。
|
|
75
|
+
// 保守判定:全局管道只豁免「入参是已校验 DTO」的路由(见下),裸类型入参仍报。
|
|
76
|
+
let hasGlobalValidationPipe = false;
|
|
77
|
+
for (const file of project.getSourceFiles()) {
|
|
78
|
+
if (file.getFilePath().includes("node_modules"))
|
|
79
|
+
continue;
|
|
80
|
+
if (/\.(test|spec)\.ts$/.test(file.getFilePath()))
|
|
81
|
+
continue;
|
|
82
|
+
if (/\.useGlobalPipes\s*\(/.test(file.getText())) {
|
|
83
|
+
hasGlobalValidationPipe = true;
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// ── 已校验 DTO 类名集合(E3 通道同款实现,import 落地代码而非重写)──
|
|
88
|
+
const validatedDtos = hasGlobalValidationPipe
|
|
89
|
+
? (0, extract_ir_1.validatedDtoClassNames)(project)
|
|
90
|
+
: new Set();
|
|
91
|
+
// ── 继承闭包(2026-09-29 docmost 实测:RemoveFavoriteDto extends AddFavoriteDto)──
|
|
92
|
+
// E3 集合只收「类自身带校验装饰器」的类,继承来的装饰器看不到。
|
|
93
|
+
// 只扩 detector 侧集合,不动 E3 函数(后者影响 safeguard 盲测基线)。
|
|
94
|
+
for (const file of project.getSourceFiles()) {
|
|
95
|
+
if (file.getFilePath().includes("node_modules"))
|
|
96
|
+
continue;
|
|
97
|
+
for (const cls of file.getClasses()) {
|
|
98
|
+
let base = cls.getBaseClass();
|
|
99
|
+
while (base) {
|
|
100
|
+
const baseName = base.getName();
|
|
101
|
+
if (baseName && validatedDtos.has(baseName)) {
|
|
102
|
+
const clsName = cls.getName();
|
|
103
|
+
if (clsName)
|
|
104
|
+
validatedDtos.add(clsName);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
base = base.getBaseClass();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ── Zod 形态扩展(2026-09-29 hedgedoc 实测)──
|
|
112
|
+
// class LoginDto extends createZodDto(LoginSchema) {} —— nestjs-zod 的校验
|
|
113
|
+
// 体系不产 class-validator 装饰器,E3 通道看不见。extends 表达式文本
|
|
114
|
+
// 含 createZodDto( 即视为已校验 DTO。同样只扩 detector 侧集合。
|
|
115
|
+
for (const file of project.getSourceFiles()) {
|
|
116
|
+
if (file.getFilePath().includes("node_modules"))
|
|
117
|
+
continue;
|
|
118
|
+
for (const cls of file.getClasses()) {
|
|
119
|
+
const ext = cls.getExtends();
|
|
120
|
+
if (!ext)
|
|
121
|
+
continue;
|
|
122
|
+
if (/createZodDto\s*\(/.test(ext.getText())) {
|
|
123
|
+
const clsName = cls.getName();
|
|
124
|
+
if (clsName)
|
|
125
|
+
validatedDtos.add(clsName);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
68
129
|
// ── 第二遍:模块级中间件保护(Nest 5 时代惯用法)──
|
|
69
130
|
// class XxxModule implements NestModule { configure(consumer) {
|
|
70
131
|
// consumer.apply(AuthMiddleware).forRoutes({path, method}, ...) } }
|
|
@@ -154,6 +215,25 @@ function analyzeNestJSProject(projectRoot) {
|
|
|
154
215
|
|| method.getDecorator("AllowAnon") !== undefined;
|
|
155
216
|
// 认证守卫 = 认证名分类后的守卫(ThrottlerGuard 等限流守卫不算认证)
|
|
156
217
|
const authGuards = guards.filter(isAuthGuardName);
|
|
218
|
+
// 入参是否指向已校验 DTO:@Body()/@Query()/@Param() 参数的
|
|
219
|
+
// 类型名 ∈ class-validator 装饰类集合(E3 通道语义)。
|
|
220
|
+
// ts-morph 的类型文本会带 import 前缀:import(".../page.dto").PageInfoDto
|
|
221
|
+
// 或泛型 Selectable<import("...").Users> —— 取全部标识符,
|
|
222
|
+
// 任一命中已校验 DTO 集合即豁免(联合类型/裸类型/数组皆可对上;
|
|
223
|
+
// db 类型 Users 等不在集合,不会误豁免)。
|
|
224
|
+
const hasValidatedDto = method.getParameters().some((param) => {
|
|
225
|
+
const decNames = param.getDecorators().map((d) => d.getName());
|
|
226
|
+
if (!decNames.some((n) => ["Body", "Query", "Param"].includes(n)))
|
|
227
|
+
return false;
|
|
228
|
+
const typeText = param.getType().getText();
|
|
229
|
+
const ids = typeText.match(/[A-Za-z_$][\w$]*/g) || [];
|
|
230
|
+
return ids.some((id) => validatedDtos.has(id));
|
|
231
|
+
});
|
|
232
|
+
// 无结构化输入:入参没有 @Body/@Query/@Param——只有 AuthUser/Req/Res/
|
|
233
|
+
// UploadedFile 等上下文注入或文件对象(2026-09-29 docmost 实测:
|
|
234
|
+
// auth/collab-token、auth/logout 等无 body 路由被误报 NO_VALIDATION)。
|
|
235
|
+
// 没有可校验的 DTO 输入,规则判定面不覆盖,不报。
|
|
236
|
+
const hasStructuredInput = method.getParameters().some((param) => param.getDecorators().some((d) => ["Body", "Query", "Param"].includes(d.getName())));
|
|
157
237
|
const route = {
|
|
158
238
|
method: httpMethod,
|
|
159
239
|
path: fullPath,
|
|
@@ -161,6 +241,7 @@ function analyzeNestJSProject(projectRoot) {
|
|
|
161
241
|
handler: method.getName() || "unknown",
|
|
162
242
|
hasAuthGuard: authGuards.length > 0,
|
|
163
243
|
hasValidationPipe: pipes.length > 0,
|
|
244
|
+
hasValidatedDto,
|
|
164
245
|
guards,
|
|
165
246
|
pipes,
|
|
166
247
|
isPublicDecorated,
|
|
@@ -188,7 +269,12 @@ function analyzeNestJSProject(projectRoot) {
|
|
|
188
269
|
fix: `Add @UseGuards(AuthGuard) to the method or controller class, or remove the @Public marker.`,
|
|
189
270
|
});
|
|
190
271
|
}
|
|
191
|
-
|
|
272
|
+
// 豁免:@UsePipes 装饰器,或「全局 ValidationPipe + 入参是已校验 DTO」
|
|
273
|
+
// (docmost 实测 123 条失明误报的修复——路由 DTO 在别的文件带
|
|
274
|
+
// class-validator 装饰器,只看 @UsePipes 永远看不到),
|
|
275
|
+
// 或路由无结构化输入(没有可校验的东西)
|
|
276
|
+
const validatedByGlobalPipe = hasGlobalValidationPipe && route.hasValidatedDto;
|
|
277
|
+
if (!route.hasValidationPipe && !validatedByGlobalPipe && hasStructuredInput) {
|
|
192
278
|
analysis.issues.push({
|
|
193
279
|
type: "NESTJS_NO_VALIDATION",
|
|
194
280
|
severity: "medium",
|
|
@@ -367,6 +453,12 @@ function isPublicRoute(path) {
|
|
|
367
453
|
/\/auth\/register$/i, /\/register$/i, /\/signup$/i,
|
|
368
454
|
/\/auth\/signup$/i, /\/auth\/signin$/i, /\/signin$/i,
|
|
369
455
|
/\/auth\/refresh$/i, /\/auth\/forgot/i, /\/auth\/reset/i,
|
|
456
|
+
// auth 流程自身端点(2026-09-29 docmost 实测:auth/setup、
|
|
457
|
+
// auth/password-reset、auth/verify-token 被误报 NO_AUTH——
|
|
458
|
+
// 用户尚未登录,这些端点天生无认证;原 /auth/reset 匹配不了 password-reset)
|
|
459
|
+
/\/auth\/password-reset$/i, /\/auth\/reset-password$/i,
|
|
460
|
+
/\/auth\/verify-token$/i, /\/auth\/verify$/i,
|
|
461
|
+
/\/auth\/setup$/i, /\/auth\/init$/i,
|
|
370
462
|
/\/health$/i, /\/healthcheck$/i, /\/ping$/i, /\/status$/i,
|
|
371
463
|
/\/public\//i, /\/static\//i,
|
|
372
464
|
];
|
|
@@ -462,6 +554,7 @@ function analyzeNestJSFile(filePath) {
|
|
|
462
554
|
handler: method.getName() || "unknown",
|
|
463
555
|
hasAuthGuard: guards.filter(isAuthGuardName).length > 0,
|
|
464
556
|
hasValidationPipe: pipes.length > 0,
|
|
557
|
+
hasValidatedDto: false, // 单文件分析无跨文件 DTO 上下文(项目级用 analyzeNestJSProject)
|
|
465
558
|
guards,
|
|
466
559
|
pipes,
|
|
467
560
|
isPublicDecorated: false, // 单文件分析不解析 @Public(项目级用 analyzeNestJSProject)
|
|
@@ -241,3 +241,180 @@ export class AdminController {
|
|
|
241
241
|
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_AUTH" && i.route === "POST /users")).toBe(true);
|
|
242
242
|
});
|
|
243
243
|
});
|
|
244
|
+
// ═══════════════════════════════════════════════════════════
|
|
245
|
+
// 全局 ValidationPipe + DTO 校验识别(2026-09-29 docmost 实测:
|
|
246
|
+
// 123 条 NO_VALIDATION 失明误报的修复——路由 DTO 在别的文件带
|
|
247
|
+
// class-validator 装饰器,只看 @UsePipes 永远看不到)
|
|
248
|
+
// ═══════════════════════════════════════════════════════════
|
|
249
|
+
(0, vitest_1.describe)("nestjs-detector 全局管道 + DTO 校验通道", () => {
|
|
250
|
+
function writeValidatedApp() {
|
|
251
|
+
write("tsconfig.json", TSCONFIG);
|
|
252
|
+
write("src/main.ts", `
|
|
253
|
+
import { ValidationPipe } from "@nestjs/common";
|
|
254
|
+
import { NestFactory } from "@nestjs/core";
|
|
255
|
+
import { AppModule } from "./app.module";
|
|
256
|
+
async function bootstrap() {
|
|
257
|
+
const app = await NestFactory.create(AppModule);
|
|
258
|
+
app.useGlobalPipes(
|
|
259
|
+
new ValidationPipe({ whitelist: true, transform: true }),
|
|
260
|
+
);
|
|
261
|
+
await app.listen(3000);
|
|
262
|
+
}
|
|
263
|
+
`);
|
|
264
|
+
write("src/app.module.ts", `
|
|
265
|
+
import { Module } from "@nestjs/common";
|
|
266
|
+
import { PageController } from "./page.controller";
|
|
267
|
+
@Module({ controllers: [PageController] })
|
|
268
|
+
export class AppModule {}
|
|
269
|
+
`);
|
|
270
|
+
write("src/create-page.dto.ts", `
|
|
271
|
+
import { IsString, IsUUID, IsOptional } from "class-validator";
|
|
272
|
+
export class CreatePageDto {
|
|
273
|
+
@IsOptional() @IsString() title?: string;
|
|
274
|
+
@IsUUID() spaceId: string;
|
|
275
|
+
}
|
|
276
|
+
`);
|
|
277
|
+
write("src/page.controller.ts", `
|
|
278
|
+
import { Controller, Post, Body } from "@nestjs/common";
|
|
279
|
+
import { CreatePageDto } from "./create-page.dto";
|
|
280
|
+
@Controller("pages")
|
|
281
|
+
export class PageController {
|
|
282
|
+
@Post("create")
|
|
283
|
+
create(@Body() dto: CreatePageDto) { return {}; }
|
|
284
|
+
}
|
|
285
|
+
`);
|
|
286
|
+
}
|
|
287
|
+
(0, vitest_1.it)("全局 ValidationPipe + 入参是已校验 DTO → 不报 NO_VALIDATION", () => {
|
|
288
|
+
writeValidatedApp();
|
|
289
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
290
|
+
const nv = a.issues.filter((i) => i.type === "NESTJS_NO_VALIDATION");
|
|
291
|
+
(0, vitest_1.expect)(nv).toEqual([]);
|
|
292
|
+
});
|
|
293
|
+
(0, vitest_1.it)("反向:DTO 摘掉 class-validator 装饰器 → 精确转红", () => {
|
|
294
|
+
writeValidatedApp();
|
|
295
|
+
write("src/create-page.dto.ts", `
|
|
296
|
+
export class CreatePageDto {
|
|
297
|
+
title?: string;
|
|
298
|
+
spaceId: string;
|
|
299
|
+
}
|
|
300
|
+
`);
|
|
301
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
302
|
+
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_VALIDATION" && i.route === "POST pages/create")).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
(0, vitest_1.it)("反向:摘掉全局管道 → 精确转红(无 @UsePipes 的路由失去豁免源)", () => {
|
|
305
|
+
writeValidatedApp();
|
|
306
|
+
write("src/main.ts", `
|
|
307
|
+
async function bootstrap() { await (await import("@nestjs/core").then(m => m.NestFactory.create(Object))).listen(3000); }
|
|
308
|
+
`);
|
|
309
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
310
|
+
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_VALIDATION" && i.route === "POST pages/create")).toBe(true);
|
|
311
|
+
});
|
|
312
|
+
(0, vitest_1.it)("继承 DTO:子类 extends 已校验父类 → 不报(RemoveFavoriteDto 形态)", () => {
|
|
313
|
+
writeValidatedApp();
|
|
314
|
+
write("src/create-page.dto.ts", `
|
|
315
|
+
import { IsString, IsUUID, IsOptional } from "class-validator";
|
|
316
|
+
export class CreatePageDto {
|
|
317
|
+
@IsOptional() @IsString() title?: string;
|
|
318
|
+
@IsUUID() spaceId: string;
|
|
319
|
+
}
|
|
320
|
+
export class UpdatePageDto extends CreatePageDto {}
|
|
321
|
+
`);
|
|
322
|
+
write("src/page.controller.ts", `
|
|
323
|
+
import { Controller, Post, Body } from "@nestjs/common";
|
|
324
|
+
import { UpdatePageDto } from "./create-page.dto";
|
|
325
|
+
@Controller("pages")
|
|
326
|
+
export class PageController {
|
|
327
|
+
@Post("update")
|
|
328
|
+
update(@Body() dto: UpdatePageDto) { return {}; }
|
|
329
|
+
}
|
|
330
|
+
`);
|
|
331
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
332
|
+
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_VALIDATION" && i.route === "POST pages/update")).toBe(false);
|
|
333
|
+
});
|
|
334
|
+
(0, vitest_1.it)("无结构化输入(仅 AuthUser/Req)→ 不报 NO_VALIDATION(collab-token/logout 形态)", () => {
|
|
335
|
+
write("tsconfig.json", TSCONFIG);
|
|
336
|
+
write("src/main.ts", `
|
|
337
|
+
import { ValidationPipe } from "@nestjs/common";
|
|
338
|
+
async function bootstrap() { (globalThis as any).app.useGlobalPipes(new ValidationPipe()); }
|
|
339
|
+
`);
|
|
340
|
+
write("src/app.module.ts", `
|
|
341
|
+
import { Module } from "@nestjs/common";
|
|
342
|
+
import { AuthController } from "./auth.controller";
|
|
343
|
+
@Module({ controllers: [AuthController] })
|
|
344
|
+
export class AppModule {}
|
|
345
|
+
`);
|
|
346
|
+
write("src/auth.controller.ts", `
|
|
347
|
+
import { Controller, Post, Req } from "@nestjs/common";
|
|
348
|
+
@Controller("auth")
|
|
349
|
+
export class AuthController {
|
|
350
|
+
@Post("logout")
|
|
351
|
+
logout(@Req() req: any) { return {}; }
|
|
352
|
+
}
|
|
353
|
+
`);
|
|
354
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
355
|
+
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_VALIDATION" && i.route === "POST auth/logout")).toBe(false);
|
|
356
|
+
});
|
|
357
|
+
(0, vitest_1.it)("Zod 形态:class XDto extends createZodDto(schema) + 辅助函数全局管道 → 不报(hedgedoc 形态)", () => {
|
|
358
|
+
write("tsconfig.json", TSCONFIG);
|
|
359
|
+
write("src/main.ts", `
|
|
360
|
+
import { setupValidationPipe } from "./setup-pipes";
|
|
361
|
+
async function bootstrap() { (globalThis as any).app.useGlobalPipes(setupValidationPipe()); }
|
|
362
|
+
`);
|
|
363
|
+
write("src/setup-pipes.ts", `
|
|
364
|
+
import { createZodValidationPipe } from "nestjs-zod";
|
|
365
|
+
export function setupValidationPipe(): any { return new (createZodValidationPipe({}))(); }
|
|
366
|
+
`);
|
|
367
|
+
write("src/app.module.ts", `
|
|
368
|
+
import { Module } from "@nestjs/common";
|
|
369
|
+
import { NotesController } from "./notes.controller";
|
|
370
|
+
@Module({ controllers: [NotesController] })
|
|
371
|
+
export class AppModule {}
|
|
372
|
+
`);
|
|
373
|
+
write("src/login.dto.ts", `
|
|
374
|
+
import { createZodDto } from "nestjs-zod";
|
|
375
|
+
export class LoginDto extends createZodDto({}) {}
|
|
376
|
+
`);
|
|
377
|
+
write("src/notes.controller.ts", `
|
|
378
|
+
import { Controller, Post, Body } from "@nestjs/common";
|
|
379
|
+
import { LoginDto } from "./login.dto";
|
|
380
|
+
@Controller("notes")
|
|
381
|
+
export class NotesController {
|
|
382
|
+
@Post("create")
|
|
383
|
+
create(@Body() dto: LoginDto) { return {}; }
|
|
384
|
+
}
|
|
385
|
+
`);
|
|
386
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
387
|
+
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_VALIDATION" && i.route === "POST notes/create")).toBe(false);
|
|
388
|
+
});
|
|
389
|
+
(0, vitest_1.it)("反向:Zod DTO 摘掉 createZodDto 继承 → 精确转红", () => {
|
|
390
|
+
write("tsconfig.json", TSCONFIG);
|
|
391
|
+
write("src/main.ts", `
|
|
392
|
+
import { setupValidationPipe } from "./setup-pipes";
|
|
393
|
+
async function bootstrap() { (globalThis as any).app.useGlobalPipes(setupValidationPipe()); }
|
|
394
|
+
`);
|
|
395
|
+
write("src/setup-pipes.ts", `
|
|
396
|
+
import { createZodValidationPipe } from "nestjs-zod";
|
|
397
|
+
export function setupValidationPipe(): any { return new (createZodValidationPipe({}))(); }
|
|
398
|
+
`);
|
|
399
|
+
write("src/app.module.ts", `
|
|
400
|
+
import { Module } from "@nestjs/common";
|
|
401
|
+
import { NotesController } from "./notes.controller";
|
|
402
|
+
@Module({ controllers: [NotesController] })
|
|
403
|
+
export class AppModule {}
|
|
404
|
+
`);
|
|
405
|
+
write("src/login.dto.ts", `
|
|
406
|
+
export class LoginDto {}
|
|
407
|
+
`);
|
|
408
|
+
write("src/notes.controller.ts", `
|
|
409
|
+
import { Controller, Post, Body } from "@nestjs/common";
|
|
410
|
+
import { LoginDto } from "./login.dto";
|
|
411
|
+
@Controller("notes")
|
|
412
|
+
export class NotesController {
|
|
413
|
+
@Post("create")
|
|
414
|
+
create(@Body() dto: LoginDto) { return {}; }
|
|
415
|
+
}
|
|
416
|
+
`);
|
|
417
|
+
const a = (0, nestjs_detector_1.analyzeNestJSProject)(dir);
|
|
418
|
+
(0, vitest_1.expect)(a.issues.some((i) => i.type === "NESTJS_NO_VALIDATION" && i.route === "POST notes/create")).toBe(true);
|
|
419
|
+
});
|
|
420
|
+
});
|
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.
|
|
23
|
+
exports.RUNTIME_VERSION = "3.7.55";
|
|
24
24
|
function verify(filePath) {
|
|
25
25
|
const cert = (0, certify_1.certify)(filePath);
|
|
26
26
|
const kb = (0, protocol_knowledge_1.buildKnowledgeBase)();
|
package/dist/trust/engine.js
CHANGED
|
@@ -151,7 +151,14 @@ async function evaluateTrust(ctx) {
|
|
|
151
151
|
writeTrustFailuresToCorpus(ctx, allViolations);
|
|
152
152
|
// PHASE 3: SCORE
|
|
153
153
|
// ═══════════════════════════════════════
|
|
154
|
-
|
|
154
|
+
// 企业策略维度只吃策略类违规——框架适配器(framework.*)与协议检测
|
|
155
|
+
// (protocol-safety.*)的违规在 protocolSafety 维度已计分,同池双计
|
|
156
|
+
// 会把失明误报放大成双倍扣分(2026-09-29 docmost 实测:132 条
|
|
157
|
+
// NestJS 违规双计,auth 0 分 + score 48 + BLOCKED,逐条核实 126 条为 FP)。
|
|
158
|
+
const policyViolations = allViolations.filter((v) => !v.policy_ref.startsWith("framework") &&
|
|
159
|
+
!v.policy_ref.startsWith("protocol-safety") &&
|
|
160
|
+
v.policy_ref !== "protocol");
|
|
161
|
+
const policyResult = (0, score_calculator_1.scorePolicyCompliance)(policyViolations);
|
|
155
162
|
const protocolResult = (0, score_calculator_1.scoreProtocolSafety)(allViolations);
|
|
156
163
|
const coverageResult = (0, score_calculator_1.scoreVerificationCoverage)(coverageData);
|
|
157
164
|
const governanceResult = (0, score_calculator_1.scoreGovernanceIntegrity)(governanceDefects);
|
|
@@ -361,7 +368,7 @@ async function evaluateTrust(ctx) {
|
|
|
361
368
|
score: policyResult.score,
|
|
362
369
|
weight: types_1.DEFAULT_DIMENSION_WEIGHTS.policyCompliance,
|
|
363
370
|
confidence: policyResult.hasCritical ? "LOW" : "HIGH",
|
|
364
|
-
violations:
|
|
371
|
+
violations: policyViolations,
|
|
365
372
|
},
|
|
366
373
|
protocolSafety: protocolResult,
|
|
367
374
|
verificationCoverage: coverageResult,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "progmune-runtime",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.55",
|
|
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/",
|