progmune-runtime 3.7.11 → 3.7.13
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 +24 -0
- package/README.md +4 -3
- package/README.zh-CN.md +4 -3
- package/dist/extract-ir-go.js +383 -0
- package/dist/extract-ir-go.test.js +179 -0
- package/dist/extract-project-ir.js +6 -0
- package/dist/frameworks/fiber-detector.js +125 -0
- package/dist/frameworks/fiber-detector.test.js +57 -0
- package/dist/frameworks/gin-detector.js +133 -0
- package/dist/frameworks/gin-detector.test.js +64 -0
- package/dist/frameworks/hapi-detector.js +132 -0
- package/dist/frameworks/hapi-detector.test.js +60 -0
- package/dist/frameworks/index.js +13 -1
- package/dist/frameworks/koa-detector.js +128 -0
- package/dist/frameworks/koa-detector.test.js +57 -0
- package/dist/sdk.js +1 -1
- package/dist/trust/engine.js +212 -0
- package/package.json +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Koa Framework Adapter — Protocol Detection for Koa
|
|
4
|
+
*
|
|
5
|
+
* 第 9 个框架适配(TS/JS 第 5 个专用检测器,代码串级镜像 express-detector):
|
|
6
|
+
*
|
|
7
|
+
* app.use(authMiddleware) 全局认证中间件
|
|
8
|
+
* router.post('/x', authMW, handler) 路由级认证中间件链
|
|
9
|
+
*
|
|
10
|
+
* 规则:
|
|
11
|
+
* KOA_ROUTE_NO_AUTH mutation 路由注册(post/put/patch/delete/del)
|
|
12
|
+
* 中间件链里没有认证名中间件,且文件内无认证
|
|
13
|
+
* 全局 app.use——路由级 missing-auth
|
|
14
|
+
*
|
|
15
|
+
* 口径(如实):
|
|
16
|
+
* - get 读操作不检查;认证入口路径词汇豁免(login/regist/auth/token)
|
|
17
|
+
* - 认证中间件按名字词表识别(auth/login/permission/token/session/
|
|
18
|
+
* jwt/verify/guard);自定义认证名不含词表漏判(保守方向=漏报)
|
|
19
|
+
* - 文件级窗口(与 Express 检测器同款):跨文件注册的全局中间件不可见
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.analyzeKoaApp = analyzeKoaApp;
|
|
56
|
+
exports.analyzeKoaFile = analyzeKoaFile;
|
|
57
|
+
const fs = __importStar(require("fs"));
|
|
58
|
+
const MUTATION_METHODS = new Set(["post", "put", "patch", "delete", "del"]);
|
|
59
|
+
const AUTH_ENTRY_WORDS = [
|
|
60
|
+
"login", "signin", "sign_in", "regist", "signup", "sign_up",
|
|
61
|
+
"token", "auth", "health",
|
|
62
|
+
];
|
|
63
|
+
const AUTH_FN_WORDS = [
|
|
64
|
+
"auth", "login", "permission", "token", "credential", "session",
|
|
65
|
+
"jwt", "verify", "guard", "protect", "passport",
|
|
66
|
+
];
|
|
67
|
+
function isAuthEntryPath(pathName) {
|
|
68
|
+
const lower = pathName.toLowerCase();
|
|
69
|
+
return AUTH_ENTRY_WORDS.some((w) => lower.includes(w));
|
|
70
|
+
}
|
|
71
|
+
function isAuthFnName(name) {
|
|
72
|
+
const lower = name.toLowerCase();
|
|
73
|
+
return AUTH_FN_WORDS.some((w) => lower.includes(w));
|
|
74
|
+
}
|
|
75
|
+
// ── Analysis(代码串级) ──
|
|
76
|
+
function analyzeKoaApp(code) {
|
|
77
|
+
const issues = [];
|
|
78
|
+
const routes = [];
|
|
79
|
+
const authGlobalMiddleware = [];
|
|
80
|
+
const hasKoa = /\bKoa\b|\bkoa\b/.test(code);
|
|
81
|
+
if (!hasKoa) {
|
|
82
|
+
return { hasKoa: false, routes, authGlobalMiddleware, issues };
|
|
83
|
+
}
|
|
84
|
+
// 全局认证中间件:app.use(authFn)
|
|
85
|
+
const useRe = /\.use\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/g;
|
|
86
|
+
let m;
|
|
87
|
+
while ((m = useRe.exec(code)) !== null) {
|
|
88
|
+
if (isAuthFnName(m[1]))
|
|
89
|
+
authGlobalMiddleware.push(m[1]);
|
|
90
|
+
}
|
|
91
|
+
// 路由注册:router.post('/x', mw1, mw2, handler) / .del()
|
|
92
|
+
const routeRe = /\.(get|post|put|patch|delete|del)\s*\(\s*['"]([^'"]+)['"]/g;
|
|
93
|
+
while ((m = routeRe.exec(code)) !== null) {
|
|
94
|
+
const method = m[1].toLowerCase() === "del" ? "delete" : m[1].toLowerCase();
|
|
95
|
+
const pathName = m[2];
|
|
96
|
+
// 从路径串向后截 300 字符窗口,收集中间件名(认证名判定)
|
|
97
|
+
const windowStart = m.index + m[0].length;
|
|
98
|
+
const window = code.slice(windowStart, windowStart + 300);
|
|
99
|
+
const mwNames = window.match(/[A-Za-z_$][\w$]*/g) || [];
|
|
100
|
+
const hasAuthMw = mwNames.some((name) => isAuthFnName(name));
|
|
101
|
+
routes.push({
|
|
102
|
+
method,
|
|
103
|
+
path: pathName,
|
|
104
|
+
protected: hasAuthMw,
|
|
105
|
+
line: code.slice(0, m.index).split("\n").length,
|
|
106
|
+
});
|
|
107
|
+
if (MUTATION_METHODS.has(method) && !hasAuthMw
|
|
108
|
+
&& authGlobalMiddleware.length === 0 && !isAuthEntryPath(pathName)) {
|
|
109
|
+
issues.push({
|
|
110
|
+
severity: "medium",
|
|
111
|
+
rule: "KOA_ROUTE_NO_AUTH",
|
|
112
|
+
message: `Route ${method.toUpperCase()} ${pathName} has no auth middleware ` +
|
|
113
|
+
`and the app registers no auth middleware — any caller can reach it.`,
|
|
114
|
+
route: `${method.toUpperCase()} ${pathName}`,
|
|
115
|
+
line: code.slice(0, m.index).split("\n").length,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return { hasKoa: true, routes, authGlobalMiddleware, issues };
|
|
120
|
+
}
|
|
121
|
+
function analyzeKoaFile(filePath) {
|
|
122
|
+
if (!fs.existsSync(filePath))
|
|
123
|
+
return null;
|
|
124
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
125
|
+
if (!/from\s+['"]koa|require\(['"]koa/.test(code))
|
|
126
|
+
return null;
|
|
127
|
+
return analyzeKoaApp(code);
|
|
128
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* koa-detector.test.ts — Koa 框架适配器规则回归(纯函数,无文件 I/O)
|
|
5
|
+
*/
|
|
6
|
+
const vitest_1 = require("vitest");
|
|
7
|
+
const koa_detector_1 = require("./koa-detector");
|
|
8
|
+
const app = (routes, extra = "") => `
|
|
9
|
+
import Koa from "koa";
|
|
10
|
+
import Router from "@koa/router";
|
|
11
|
+
const app = new Koa();
|
|
12
|
+
const router = new Router();
|
|
13
|
+
${extra}
|
|
14
|
+
${routes}
|
|
15
|
+
app.use(router.routes());
|
|
16
|
+
`;
|
|
17
|
+
(0, vitest_1.describe)("koa-detector", () => {
|
|
18
|
+
(0, vitest_1.it)("R1:无认证中间件的 mutation 路由 → KOA_ROUTE_NO_AUTH", () => {
|
|
19
|
+
const { issues } = (0, koa_detector_1.analyzeKoaApp)(app(`
|
|
20
|
+
router.post("/transfer", async (ctx) => { ctx.body = "ok"; });
|
|
21
|
+
`));
|
|
22
|
+
(0, vitest_1.expect)(issues.map((i) => i.rule)).toContain("KOA_ROUTE_NO_AUTH");
|
|
23
|
+
});
|
|
24
|
+
(0, vitest_1.it)("R1:路由级认证中间件保护不报", () => {
|
|
25
|
+
const { issues } = (0, koa_detector_1.analyzeKoaApp)(app(`
|
|
26
|
+
router.post("/transfer", authenticate, async (ctx) => { ctx.body = "ok"; });
|
|
27
|
+
`));
|
|
28
|
+
(0, vitest_1.expect)(issues).toHaveLength(0);
|
|
29
|
+
});
|
|
30
|
+
(0, vitest_1.it)("R1:全局 app.use 认证中间件保护不报", () => {
|
|
31
|
+
const { issues } = (0, koa_detector_1.analyzeKoaApp)(app(`router.post("/transfer", async (ctx) => { ctx.body = "ok"; });`, `app.use(authenticate);`));
|
|
32
|
+
(0, vitest_1.expect)(issues).toHaveLength(0);
|
|
33
|
+
});
|
|
34
|
+
(0, vitest_1.it)("R1:非认证中间件(日志)不视为保护", () => {
|
|
35
|
+
const { issues } = (0, koa_detector_1.analyzeKoaApp)(app(`
|
|
36
|
+
router.post("/transfer", logger, async (ctx) => { ctx.body = "ok"; });
|
|
37
|
+
`));
|
|
38
|
+
(0, vitest_1.expect)(issues.map((i) => i.rule)).toContain("KOA_ROUTE_NO_AUTH");
|
|
39
|
+
});
|
|
40
|
+
(0, vitest_1.it)("R1:GET 读操作不报", () => {
|
|
41
|
+
const { issues } = (0, koa_detector_1.analyzeKoaApp)(app(`
|
|
42
|
+
router.get("/articles", async (ctx) => { ctx.body = []; });
|
|
43
|
+
`));
|
|
44
|
+
(0, vitest_1.expect)(issues).toHaveLength(0);
|
|
45
|
+
});
|
|
46
|
+
(0, vitest_1.it)("R1 豁免:login 认证入口路径不报", () => {
|
|
47
|
+
const { issues } = (0, koa_detector_1.analyzeKoaApp)(app(`
|
|
48
|
+
router.post("/login", async (ctx) => { ctx.body = "token"; });
|
|
49
|
+
`));
|
|
50
|
+
(0, vitest_1.expect)(issues).toHaveLength(0);
|
|
51
|
+
});
|
|
52
|
+
(0, vitest_1.it)("非 Koa 代码不产生任何问题", () => {
|
|
53
|
+
const { hasKoa, issues } = (0, koa_detector_1.analyzeKoaApp)(`import express from "express"; const app = express(); app.post("/x", h);`);
|
|
54
|
+
(0, vitest_1.expect)(hasKoa).toBe(false);
|
|
55
|
+
(0, vitest_1.expect)(issues).toHaveLength(0);
|
|
56
|
+
});
|
|
57
|
+
});
|
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.13";
|
|
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
|
@@ -67,11 +67,16 @@ const django_detector_1 = require("../frameworks/django-detector");
|
|
|
67
67
|
const flask_detector_1 = require("../frameworks/flask-detector");
|
|
68
68
|
const fastify_detector_1 = require("../frameworks/fastify-detector");
|
|
69
69
|
const nextjs_detector_1 = require("../frameworks/nextjs-detector");
|
|
70
|
+
const koa_detector_1 = require("../frameworks/koa-detector");
|
|
71
|
+
const hapi_detector_1 = require("../frameworks/hapi-detector");
|
|
72
|
+
const gin_detector_1 = require("../frameworks/gin-detector");
|
|
73
|
+
const fiber_detector_1 = require("../frameworks/fiber-detector");
|
|
70
74
|
const ssg_bridge_1 = require("./ssg-bridge");
|
|
71
75
|
const call_sequence_1 = require("../call-sequence");
|
|
72
76
|
const extract_ir_1 = require("../extract-ir");
|
|
73
77
|
const extract_ir_python_1 = require("../extract-ir-python");
|
|
74
78
|
const extract_ir_c_1 = require("../extract-ir-c");
|
|
79
|
+
const extract_ir_go_1 = require("../extract-ir-go");
|
|
75
80
|
// ── Main Entry Point ──
|
|
76
81
|
/**
|
|
77
82
|
* Trust 决策主入口:收集 → 归一化 → 评分 → 决策 → 组装。
|
|
@@ -102,6 +107,10 @@ async function evaluateTrust(ctx) {
|
|
|
102
107
|
const flaskResult = collectFlaskViolations(ctx);
|
|
103
108
|
const fastifyResult = collectFastifyViolations(ctx);
|
|
104
109
|
const nextjsResult = collectNextjsViolations(ctx);
|
|
110
|
+
const koaResult = collectKoaViolations(ctx);
|
|
111
|
+
const hapiResult = collectHapiViolations(ctx);
|
|
112
|
+
const ginResult = collectGinViolations(ctx);
|
|
113
|
+
const fiberResult = collectFiberViolations(ctx);
|
|
105
114
|
const coverageData = collectVerificationCoverage(ctx);
|
|
106
115
|
const governanceDefects = collectGovernanceDefects(ctx);
|
|
107
116
|
// ═══════════════════════════════════════
|
|
@@ -124,6 +133,10 @@ async function evaluateTrust(ctx) {
|
|
|
124
133
|
...flaskResult.violations,
|
|
125
134
|
...fastifyResult.violations,
|
|
126
135
|
...nextjsResult.violations,
|
|
136
|
+
...koaResult.violations,
|
|
137
|
+
...hapiResult.violations,
|
|
138
|
+
...ginResult.violations,
|
|
139
|
+
...fiberResult.violations,
|
|
127
140
|
];
|
|
128
141
|
// ═══════════════════════════════════════
|
|
129
142
|
// PHASE 3: SCORE
|
|
@@ -288,6 +301,42 @@ async function evaluateTrust(ctx) {
|
|
|
288
301
|
issuesFound: nextjsResult.violations.length,
|
|
289
302
|
}
|
|
290
303
|
: undefined,
|
|
304
|
+
/** Koa framework adapter coverage — route/auth-middleware analysis */
|
|
305
|
+
koaCoverage: koaResult.coverage.routes > 0
|
|
306
|
+
? {
|
|
307
|
+
appsDetected: koaResult.coverage.apps,
|
|
308
|
+
totalRoutes: koaResult.coverage.routes,
|
|
309
|
+
filesScanned: koaResult.coverage.filesScanned,
|
|
310
|
+
issuesFound: koaResult.violations.length,
|
|
311
|
+
}
|
|
312
|
+
: undefined,
|
|
313
|
+
/** Hapi framework adapter coverage — route-config auth analysis */
|
|
314
|
+
hapiCoverage: hapiResult.coverage.routes > 0
|
|
315
|
+
? {
|
|
316
|
+
appsDetected: hapiResult.coverage.apps,
|
|
317
|
+
totalRoutes: hapiResult.coverage.routes,
|
|
318
|
+
filesScanned: hapiResult.coverage.filesScanned,
|
|
319
|
+
issuesFound: hapiResult.violations.length,
|
|
320
|
+
}
|
|
321
|
+
: undefined,
|
|
322
|
+
/** Gin framework adapter coverage — Go route/auth-middleware analysis */
|
|
323
|
+
ginCoverage: ginResult.coverage.routes > 0
|
|
324
|
+
? {
|
|
325
|
+
appsDetected: ginResult.coverage.apps,
|
|
326
|
+
totalRoutes: ginResult.coverage.routes,
|
|
327
|
+
filesScanned: ginResult.coverage.filesScanned,
|
|
328
|
+
issuesFound: ginResult.violations.length,
|
|
329
|
+
}
|
|
330
|
+
: undefined,
|
|
331
|
+
/** Fiber framework adapter coverage — Go route/auth-middleware analysis */
|
|
332
|
+
fiberCoverage: fiberResult.coverage.routes > 0
|
|
333
|
+
? {
|
|
334
|
+
appsDetected: fiberResult.coverage.apps,
|
|
335
|
+
totalRoutes: fiberResult.coverage.routes,
|
|
336
|
+
filesScanned: fiberResult.coverage.filesScanned,
|
|
337
|
+
issuesFound: fiberResult.violations.length,
|
|
338
|
+
}
|
|
339
|
+
: undefined,
|
|
291
340
|
},
|
|
292
341
|
dimensions: {
|
|
293
342
|
policyCompliance: {
|
|
@@ -426,6 +475,168 @@ function mapPolicyViolation(rv, filePath, _enterprisePolicy) {
|
|
|
426
475
|
* Collect Express-specific security violations from the framework detector.
|
|
427
476
|
* Maps ExpressSecurityIssue[] → TrustViolation[].
|
|
428
477
|
*/
|
|
478
|
+
function collectGoFrameworkViolations(ctx, analyzer, rulePrefix, fixText, policyRef) {
|
|
479
|
+
const violations = [];
|
|
480
|
+
const coverage = { apps: 0, routes: 0, filesScanned: 0 };
|
|
481
|
+
try {
|
|
482
|
+
const fs = require("fs");
|
|
483
|
+
// Go 项目结构特殊:cmd/internal/pkg + 根目录 main.go 都要扫
|
|
484
|
+
const candidateDirs = ["cmd", "internal", "pkg", "src", "server", "app", "api", "routes", "lib", "handlers", "middleware"];
|
|
485
|
+
const extensions = languageToExtensions(ctx.language);
|
|
486
|
+
const files = [];
|
|
487
|
+
for (const dir of candidateDirs) {
|
|
488
|
+
const dirPath = path.join(ctx.projectPath, dir);
|
|
489
|
+
if (!fs.existsSync(dirPath))
|
|
490
|
+
continue;
|
|
491
|
+
try {
|
|
492
|
+
files.push(...walkDir(dirPath, extensions, 100));
|
|
493
|
+
}
|
|
494
|
+
catch { /* skip */ }
|
|
495
|
+
}
|
|
496
|
+
try {
|
|
497
|
+
for (const entry of fs.readdirSync(ctx.projectPath)) {
|
|
498
|
+
const full = path.join(ctx.projectPath, entry);
|
|
499
|
+
if (!fs.statSync(full).isFile())
|
|
500
|
+
continue;
|
|
501
|
+
if (extensions.some((ext) => entry.endsWith(ext)))
|
|
502
|
+
files.push(full);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
catch { /* best-effort */ }
|
|
506
|
+
for (const file of files) {
|
|
507
|
+
if (file.includes("_test."))
|
|
508
|
+
continue;
|
|
509
|
+
coverage.filesScanned++;
|
|
510
|
+
try {
|
|
511
|
+
const analysis = analyzer(file);
|
|
512
|
+
if (!analysis || !analysis[`has${rulePrefix}`])
|
|
513
|
+
continue;
|
|
514
|
+
coverage.apps++;
|
|
515
|
+
coverage.routes += analysis.routes.length;
|
|
516
|
+
for (const issue of analysis.issues) {
|
|
517
|
+
violations.push({
|
|
518
|
+
severity: issue.severity === "low" ? "low" : issue.severity,
|
|
519
|
+
rule_id: issue.rule,
|
|
520
|
+
file: path.relative(ctx.projectPath, file),
|
|
521
|
+
function: "unknown",
|
|
522
|
+
message: issue.message,
|
|
523
|
+
evidence: issue.route || "",
|
|
524
|
+
why: `Framework structural analysis: ${issue.message}`,
|
|
525
|
+
fix: fixText,
|
|
526
|
+
policy_ref: policyRef,
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
catch { /* skip unreadable files */ }
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
catch { /* best-effort */ }
|
|
534
|
+
return { violations, coverage };
|
|
535
|
+
}
|
|
536
|
+
function collectGinViolations(ctx) {
|
|
537
|
+
return collectGoFrameworkViolations(ctx, gin_detector_1.analyzeGinFile, "Gin", "Add an auth middleware to the route registration, or register auth middleware with r.Use/r.Group.", "framework-safety.gin");
|
|
538
|
+
}
|
|
539
|
+
function collectFiberViolations(ctx) {
|
|
540
|
+
return collectGoFrameworkViolations(ctx, fiber_detector_1.analyzeFiberFile, "Fiber", "Add an auth middleware to the route registration, or register auth middleware with app.Use.", "framework-safety.fiber");
|
|
541
|
+
}
|
|
542
|
+
function collectKoaViolations(ctx) {
|
|
543
|
+
const violations = [];
|
|
544
|
+
const coverage = { apps: 0, routes: 0, filesScanned: 0 };
|
|
545
|
+
try {
|
|
546
|
+
const fs = require("fs");
|
|
547
|
+
const candidateDirs = ["src", "server", "app", "api", "routes", "lib"];
|
|
548
|
+
const extensions = languageToExtensions(ctx.language);
|
|
549
|
+
for (const dir of candidateDirs) {
|
|
550
|
+
const dirPath = path.join(ctx.projectPath, dir);
|
|
551
|
+
if (!fs.existsSync(dirPath))
|
|
552
|
+
continue;
|
|
553
|
+
let files;
|
|
554
|
+
try {
|
|
555
|
+
files = walkDir(dirPath, extensions, 100);
|
|
556
|
+
}
|
|
557
|
+
catch {
|
|
558
|
+
continue;
|
|
559
|
+
}
|
|
560
|
+
for (const file of files) {
|
|
561
|
+
if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(file))
|
|
562
|
+
continue;
|
|
563
|
+
coverage.filesScanned++;
|
|
564
|
+
try {
|
|
565
|
+
const analysis = (0, koa_detector_1.analyzeKoaFile)(file);
|
|
566
|
+
if (!analysis || !analysis.hasKoa)
|
|
567
|
+
continue;
|
|
568
|
+
coverage.apps++;
|
|
569
|
+
coverage.routes += analysis.routes.length;
|
|
570
|
+
for (const issue of analysis.issues) {
|
|
571
|
+
violations.push({
|
|
572
|
+
severity: issue.severity === "low" ? "low" : issue.severity,
|
|
573
|
+
rule_id: issue.rule,
|
|
574
|
+
file: path.relative(ctx.projectPath, file),
|
|
575
|
+
function: "unknown",
|
|
576
|
+
message: issue.message,
|
|
577
|
+
evidence: issue.route || "",
|
|
578
|
+
why: `Framework structural analysis: ${issue.message}`,
|
|
579
|
+
fix: `Add an auth middleware to the route registration, or register an auth middleware globally with app.use.`,
|
|
580
|
+
policy_ref: "framework-safety.koa",
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
catch { /* skip unreadable files */ }
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
catch { /* best-effort */ }
|
|
589
|
+
return { violations, coverage };
|
|
590
|
+
}
|
|
591
|
+
function collectHapiViolations(ctx) {
|
|
592
|
+
const violations = [];
|
|
593
|
+
const coverage = { apps: 0, routes: 0, filesScanned: 0 };
|
|
594
|
+
try {
|
|
595
|
+
const fs = require("fs");
|
|
596
|
+
const candidateDirs = ["src", "server", "app", "api", "routes", "lib"];
|
|
597
|
+
const extensions = languageToExtensions(ctx.language);
|
|
598
|
+
for (const dir of candidateDirs) {
|
|
599
|
+
const dirPath = path.join(ctx.projectPath, dir);
|
|
600
|
+
if (!fs.existsSync(dirPath))
|
|
601
|
+
continue;
|
|
602
|
+
let files;
|
|
603
|
+
try {
|
|
604
|
+
files = walkDir(dirPath, extensions, 100);
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
for (const file of files) {
|
|
610
|
+
if (/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(file))
|
|
611
|
+
continue;
|
|
612
|
+
coverage.filesScanned++;
|
|
613
|
+
try {
|
|
614
|
+
const analysis = (0, hapi_detector_1.analyzeHapiFile)(file);
|
|
615
|
+
if (!analysis || !analysis.hasHapi)
|
|
616
|
+
continue;
|
|
617
|
+
coverage.apps++;
|
|
618
|
+
coverage.routes += analysis.routes.length;
|
|
619
|
+
for (const issue of analysis.issues) {
|
|
620
|
+
violations.push({
|
|
621
|
+
severity: issue.severity === "low" ? "low" : issue.severity,
|
|
622
|
+
rule_id: issue.rule,
|
|
623
|
+
file: path.relative(ctx.projectPath, file),
|
|
624
|
+
function: "unknown",
|
|
625
|
+
message: issue.message,
|
|
626
|
+
evidence: issue.route || "",
|
|
627
|
+
why: `Framework structural analysis: ${issue.message}`,
|
|
628
|
+
fix: `Add an auth strategy reference to the route options (auth: "<strategy>"), or remove the explicit auth: false.`,
|
|
629
|
+
policy_ref: "framework-safety.hapi",
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
catch { /* skip unreadable files */ }
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
catch { /* best-effort */ }
|
|
638
|
+
return { violations, coverage };
|
|
639
|
+
}
|
|
429
640
|
function collectNextjsViolations(ctx) {
|
|
430
641
|
const violations = [];
|
|
431
642
|
const coverage = { apps: 0, routes: 0, filesScanned: 0 };
|
|
@@ -999,6 +1210,7 @@ async function collectProtocolViolations(ctx, callGraph) {
|
|
|
999
1210
|
javascript: () => (0, extract_ir_1.extractIR)(ctx.projectPath),
|
|
1000
1211
|
python: () => (0, extract_ir_python_1.extractIRPython)(ctx.projectPath),
|
|
1001
1212
|
c: () => (0, extract_ir_c_1.extractIRC)(ctx.projectPath),
|
|
1213
|
+
go: () => (0, extract_ir_go_1.extractIRGo)(ctx.projectPath),
|
|
1002
1214
|
};
|
|
1003
1215
|
const extractFn = autoExtractor[lang];
|
|
1004
1216
|
if (extractFn) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "progmune-runtime",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.13",
|
|
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/",
|