progmune-runtime 2.1.6 → 3.2.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.
- package/README.md +108 -468
- package/dist/ablation-study.js +144 -0
- package/dist/ablation-study.test.js +18 -0
- package/dist/action-runtime.js +3 -1
- package/dist/active-learning.js +211 -0
- package/dist/analytics.js +139 -0
- package/dist/asset-factory.js +309 -0
- package/dist/asset-growth.js +244 -0
- package/dist/asset-promotion.js +382 -0
- package/dist/asset-quality.js +550 -0
- package/dist/audit/business-translator.js +285 -0
- package/dist/audit/cli.js +66 -0
- package/dist/audit/formatters/html.js +379 -0
- package/dist/audit/formatters/json.js +11 -0
- package/dist/audit/formatters/markdown.js +192 -0
- package/dist/audit/formatters/terminal.js +189 -0
- package/dist/audit/index.js +25 -0
- package/dist/audit/report-builder.js +318 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit.js +3 -3
- package/dist/auto-benchmark-generator.js +137 -0
- package/dist/auto-benchmark-generator.test.js +45 -0
- package/dist/auto-protocol-synthesizer.js +362 -0
- package/dist/auto-protocol-synthesizer.test.js +82 -0
- package/dist/autonomous-patch.js +175 -0
- package/dist/autonomous-patch.test.js +128 -0
- package/dist/badge/badge-server.js +98 -0
- package/dist/behavior-miner.js +442 -0
- package/dist/belief-layer.js +475 -0
- package/dist/benchmark-count.js +5 -0
- package/dist/benchmark-generator.js +211 -0
- package/dist/benchmark-harness.js +201 -0
- package/dist/benchmark-pass-rate.js +7 -0
- package/dist/benchmark-report.js +8 -3
- package/dist/benchmark-save.js +14 -1
- package/dist/bootstrap-validation.js +197 -0
- package/dist/bootstrap-validation.test.js +51 -0
- package/dist/branch-ledger.js +1 -1
- package/dist/capability-gap.js +130 -0
- package/dist/certify-html.js +351 -0
- package/dist/certify.js +326 -0
- package/dist/check.js +4 -4
- package/dist/compliance-miner.js +447 -0
- package/dist/continuous-benchmark.js +194 -0
- package/dist/continuous-benchmark.test.js +116 -0
- package/dist/corpus-stats.js +173 -0
- package/dist/counterfactual-engine.js +288 -0
- package/dist/coverage-dashboard.js +109 -0
- package/dist/coverage-system.test.js +205 -0
- package/dist/cross-repo-precision.js +352 -0
- package/dist/cve-benchmark.js +180 -0
- package/dist/cve-benchmark.test.js +28 -0
- package/dist/cve-collector.js +73 -0
- package/dist/data-quality.js +141 -0
- package/dist/decision-engine.js +388 -0
- package/dist/derive-metadata.js +250 -0
- package/dist/difficulty-active.test.js +198 -0
- package/dist/difficulty-map.js +244 -0
- package/dist/discovery-analytics.js +125 -0
- package/dist/discovery-model.js +149 -0
- package/dist/discovery-optimize.test.js +199 -0
- package/dist/discovery-trace.js +276 -0
- package/dist/discovery-trace.test.js +97 -0
- package/dist/emitter.js +83 -1
- package/dist/enterprise-dashboard.js +405 -0
- package/dist/eval-hardening.js +297 -0
- package/dist/eval-hardening.test.js +85 -0
- package/dist/evaluation-campaign.js +359 -0
- package/dist/evaluation-campaign.test.js +181 -0
- package/dist/evidence-growth.js +143 -0
- package/dist/evidence-repository.js +209 -0
- package/dist/evidence-system.js +441 -0
- package/dist/execute.js +15 -7
- package/dist/experimental/software-physics.js +291 -0
- package/dist/experimental/state-inference.js +516 -0
- package/dist/experimental/unsupervised-physics.js +230 -0
- package/dist/extract-ir-python.js +54 -7
- package/dist/extract-ir.js +376 -12
- package/dist/failure-collector.js +2 -2
- package/dist/failure-corpus.js +322 -9
- package/dist/feedback.js +16 -5
- package/dist/feedback.test.js +49 -0
- package/dist/file-lock.js +1 -1
- package/dist/flywheel-batch.js +292 -0
- package/dist/frameworks/express-cli.js +237 -0
- package/dist/frameworks/express-detector.js +445 -0
- package/dist/frameworks/express-detector.test.js +206 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/nestjs-detector.js +302 -0
- package/dist/frameworks/trpc-detector.js +161 -0
- package/dist/frameworks/version-awareness.js +179 -0
- package/dist/function-synonyms.js +164 -0
- package/dist/function-synonyms.test.js +68 -0
- package/dist/generalization.test.js +352 -0
- package/dist/goal-annotator.js +113 -0
- package/dist/goal-planner.js +563 -0
- package/dist/gold-cve.js +164 -0
- package/dist/gold-cve.test.js +104 -0
- package/dist/gold-quality.js +206 -0
- package/dist/gold-tiers.js +241 -0
- package/dist/governance-dashboard.js +327 -0
- package/dist/graph-viz.js +240 -0
- package/dist/guided-frontier.js +195 -0
- package/dist/hierarchical-planner.js +148 -0
- package/dist/identifier-parser.js +260 -0
- package/dist/immune-metrics.js +93 -0
- package/dist/immune-receiver.js +158 -0
- package/dist/immune-reporter.js +1 -1
- package/dist/improvement-orchestrator.js +206 -0
- package/dist/inject-p0-vocabulary.js +300 -0
- package/dist/intent-parser.js +218 -0
- package/dist/invariant-algebra.js +476 -0
- package/dist/invariant-calculus.js +533 -0
- package/dist/ir-utils.js +70 -0
- package/dist/ir-utils.test.js +50 -0
- package/dist/knowledge-api.js +312 -0
- package/dist/knowledge-evolution.js +452 -0
- package/dist/knowledge-explorer.js +506 -0
- package/dist/knowledge-flywheel.js +274 -0
- package/dist/knowledge-governance.js +338 -0
- package/dist/knowledge-governance.test.js +150 -0
- package/dist/knowledge-graph.js +181 -0
- package/dist/knowledge-guided-synth.js +246 -0
- package/dist/knowledge-loop.test.js +77 -0
- package/dist/knowledge-object.js +316 -0
- package/dist/knowledge-package.js +98 -0
- package/dist/kpi-dashboard.js +561 -0
- package/dist/l3-cross-function.js +280 -0
- package/dist/learning-ranker.js +148 -0
- package/dist/learning-ranker.test.js +291 -0
- package/dist/ledger/accountability.js +322 -0
- package/dist/ledger/chain-builder.js +185 -0
- package/dist/ledger/cli.js +222 -0
- package/dist/ledger/index.js +13 -0
- package/dist/ledger/signatures.js +193 -0
- package/dist/ledger/types.js +9 -0
- package/dist/llm.js +74 -3
- package/dist/load-benchmarks.js +8 -3
- package/dist/logger.js +66 -0
- package/dist/logger.test.js +37 -0
- package/dist/logistic-reward.js +339 -0
- package/dist/logistic-reward.test.js +180 -0
- package/dist/macro-graph.js +193 -0
- package/dist/macro-repair.js +183 -0
- package/dist/mcp-server.mjs +1202 -483
- package/dist/memory-layer.js +42 -5
- package/dist/multi-repo-precision.js +422 -0
- package/dist/name-free-protocol.js +425 -0
- package/dist/name-free-protocol.test.js +170 -0
- package/dist/name-scrambling.js +138 -0
- package/dist/name-scrambling.test.js +16 -0
- package/dist/p3-observability.test.js +281 -0
- package/dist/p5-orchestrator.test.js +225 -0
- package/dist/pairwise-preference.js +294 -0
- package/dist/pairwise-preference.test.js +140 -0
- package/dist/planner-constraints.js +104 -0
- package/dist/planner-prompts.js +155 -0
- package/dist/planner-telemetry.js +415 -0
- package/dist/planner-trace.js +214 -0
- package/dist/planner.js +162 -167
- package/dist/plsb/artifact.js +116 -0
- package/dist/plsb/cli.js +71 -0
- package/dist/plsb/index.js +19 -0
- package/dist/plsb/leaderboard.js +249 -0
- package/dist/plsb/report-md.js +156 -0
- package/dist/plsb/schema.js +179 -0
- package/dist/plsb-benchmark.js +284 -0
- package/dist/plsb-benchmark.test.js +119 -0
- package/dist/policy/cli.js +134 -0
- package/dist/policy/engine.js +333 -0
- package/dist/policy/index.js +12 -0
- package/dist/policy/types.js +59 -0
- package/dist/policy-miner.js +505 -0
- package/dist/precision-analyze.js +229 -0
- package/dist/precision-benchmark.js +147 -0
- package/dist/precision-label-c.js +134 -0
- package/dist/precision-label.js +193 -0
- package/dist/precision-report-c.js +149 -0
- package/dist/precision-report.js +246 -0
- package/dist/progmune-status.js +108 -0
- package/dist/proof-engine.js +479 -0
- package/dist/proof-provenance.js +315 -0
- package/dist/protocol-coverage.js +294 -0
- package/dist/protocol-detector.js +1189 -0
- package/dist/protocol-embedding-expanded.js +297 -0
- package/dist/protocol-embedding-expanded.test.js +97 -0
- package/dist/protocol-embedding.js +195 -0
- package/dist/protocol-embedding.test.js +82 -0
- package/dist/protocol-extractor-v2.js +354 -0
- package/dist/protocol-extractor-v2.test.js +140 -0
- package/dist/protocol-extractor.js +310 -0
- package/dist/protocol-extractor.test.js +113 -0
- package/dist/protocol-foundation.js +322 -0
- package/dist/protocol-foundation.test.js +163 -0
- package/dist/protocol-frontier.js +243 -0
- package/dist/protocol-frontier.test.js +92 -0
- package/dist/protocol-gap-analyzer.js +228 -0
- package/dist/protocol-gap-analyzer.test.js +49 -0
- package/dist/protocol-invariants.js +276 -0
- package/dist/protocol-invariants.test.js +111 -0
- package/dist/protocol-knowledge.js +464 -0
- package/dist/protocol-miner.js +343 -0
- package/dist/protocol-mining.js +207 -0
- package/dist/protocol-mining.test.js +37 -0
- package/dist/protocol-registry.js +1 -1
- package/dist/protocol-security-benchmark.js +222 -0
- package/dist/protocol-vulnerability.js +257 -0
- package/dist/protocol-vulnerability.test.js +60 -0
- package/dist/python-benchmark.js +120 -0
- package/dist/python-emitter.js +163 -45
- package/dist/python-protocol-extractor.js +187 -0
- package/dist/python-protocol-extractor.test.js +116 -0
- package/dist/realworld-benchmark.js +646 -0
- package/dist/realworld-benchmark.test.js +36 -0
- package/dist/repair-arch.test.js +411 -0
- package/dist/repair-evolution.test.js +454 -0
- package/dist/repair-executor.js +719 -0
- package/dist/repair-proposal.js +4 -4
- package/dist/repair-ranker.js +141 -0
- package/dist/repair-strategies.js +419 -0
- package/dist/repair-taxonomy.js +234 -0
- package/dist/repair-types.js +12 -0
- package/dist/repo-evaluator.js +250 -0
- package/dist/repo-evaluator.test.js +128 -0
- package/dist/resource-abstraction.js +242 -0
- package/dist/resource-detector.js +211 -0
- package/dist/result.test.js +43 -0
- package/dist/reward-system.js +411 -0
- package/dist/reward-system.test.js +175 -0
- package/dist/risk-model.js +215 -0
- package/dist/rule-miner.js +234 -7
- package/dist/rule-specificity.js +254 -0
- package/dist/runtime-types.js +27 -0
- package/dist/scaffold.js +208 -0
- package/dist/scale-collector.test.js +101 -0
- package/dist/scale-trajectory-collector.js +128 -0
- package/dist/sdk.js +250 -0
- package/dist/search-planner.js +4 -41
- package/dist/semantic-snapshot.js +1 -1
- package/dist/semantic-topology.js +121 -0
- package/dist/semantic-trace.js +310 -317
- package/dist/sequence-extractor.js +343 -0
- package/dist/skill-library.js +245 -0
- package/dist/skill-planner.test.js +189 -0
- package/dist/software-physics.js +291 -0
- package/dist/software-physics.test.js +81 -0
- package/dist/ssg-precision.js +478 -0
- package/dist/ssg-validator.js +71 -21
- package/dist/state-inference-doubleblind.test.js +160 -0
- package/dist/state-inference.js +516 -0
- package/dist/state-inference.test.js +115 -0
- package/dist/state-machine-fingerprint.js +345 -0
- package/dist/state-machine-fingerprint.test.js +120 -0
- package/dist/state-miner.js +386 -0
- package/dist/state-name-inference.js +213 -0
- package/dist/state-name-inference.test.js +69 -0
- package/dist/strategy-planner.js +262 -96
- package/dist/strategy-planner.test.js +135 -0
- package/dist/telemetry-analytics.test.js +402 -0
- package/dist/terminal-format.js +68 -0
- package/dist/terminal-format.test.js +83 -0
- package/dist/topology-factory.js +196 -0
- package/dist/topology-representation.js +242 -0
- package/dist/topology-representation.test.js +27 -0
- package/dist/trajectory-augmentation.js +254 -0
- package/dist/trajectory-augmentation.test.js +63 -0
- package/dist/trajectory-corpus.js +440 -0
- package/dist/trajectory-corpus.test.js +32 -0
- package/dist/trajectory-feedback.test.js +116 -0
- package/dist/transition-synthesizer.js +286 -0
- package/dist/transition-synthesizer.test.js +123 -0
- package/dist/trust/api-semantic-mapper.js +809 -0
- package/dist/trust/call-graph-propagator.js +225 -0
- package/dist/trust/cli.js +122 -0
- package/dist/trust/compliance-scorer.js +283 -0
- package/dist/trust/confidence-calculator.js +261 -0
- package/dist/trust/engine.js +1145 -0
- package/dist/trust/explainability.js +85 -0
- package/dist/trust/formatters/ci.js +42 -0
- package/dist/trust/formatters/json.js +11 -0
- package/dist/trust/formatters/terminal.js +152 -0
- package/dist/trust/index.js +39 -0
- package/dist/trust/phase1-verify.js +171 -0
- package/dist/trust/protocol-domain-validator.js +697 -0
- package/dist/trust/score-calculator.js +282 -0
- package/dist/trust/ssg-bridge.js +641 -0
- package/dist/trust/ssg-bridge.test.js +269 -0
- package/dist/trust/types.js +67 -0
- package/dist/trust/violation-trace.js +335 -0
- package/dist/trust-api.js +179 -0
- package/dist/trust-calibration.js +279 -0
- package/dist/unknown-protocol-discovery.js +339 -0
- package/dist/unknown-protocol-discovery.test.js +102 -0
- package/dist/unsupervised-physics.js +230 -0
- package/dist/unsupervised-physics.test.js +95 -0
- package/dist/utils.test.js +37 -0
- package/dist/validator.js +187 -10
- package/dist/verification-intelligence.js +475 -0
- package/dist/verify-api.js +432 -0
- package/dist/vi-impact-report.js +293 -0
- package/dist/wl-fingerprint.js +162 -0
- package/dist/wl-fingerprint.test.js +130 -0
- package/dist/zeroshot-strategy.js +139 -0
- package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
- package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +710 -0
- package/package.json +74 -7
- package/protocols.json +1956 -50
- package/.dockerignore +0 -14
- package/.mcp.json +0 -11
- package/.progmune_allowlist +0 -50
- package/.test_report/test_report.md +0 -87
- package/Dockerfile +0 -9
- package/FAQ.md +0 -167
- package/WHITEPAPER.md +0 -540
- package/demo-project/auth.ts +0 -55
- package/demo-project/tsconfig.json +0 -8
- package/dist/acl-breakdown.js +0 -13
- package/dist/all-sessions.js +0 -11
- package/dist/antibody-stats.js +0 -11
- package/dist/branch-tree-count.js +0 -14
- package/dist/common-fixpath.js +0 -12
- package/dist/constraint-types.js +0 -12
- package/dist/exec-metrics.js +0 -11
- package/dist/failure-report.js +0 -11
- package/dist/fast-path-hits.js +0 -13
- package/dist/fingerprint-list.js +0 -15
- package/dist/gen-history-log.js +0 -13
- package/dist/heatmap-data.js +0 -11
- package/dist/recent-session.js +0 -12
- package/dist/svl-distribution.js +0 -11
- package/dist/terminal-status.js +0 -11
- package/dist/token-savings.js +0 -11
- package/dist/total-repairs.js +0 -12
- package/dist/unresolved-count.js +0 -12
- package/dist/valid-fingerprints.js +0 -13
- package/dist/verify-ledgers.js +0 -11
- package/docs/whitepaper-style.css +0 -77
- package/docs/whitepaper-v2.1.md +0 -609
- package/docs/whitepaper-v2.2.md +0 -1064
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +0 -31
- package/public/dashboard.html +0 -119
- package/server/hub.js +0 -116
- package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
- package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
- package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
- package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
- package/test/replay-golden.ts +0 -84
- package/test_benchmark.js +0 -165
- package/test_comprehensive.mjs +0 -638
- package/test_concurrency.js +0 -129
- package/test_ir_robustness.js +0 -85
- package/test_semantic_contracts.js +0 -269
- package/test_ssg_stress.js +0 -156
- package/test_svl3.js +0 -58
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 5: Cross-Function Call Graph Propagator
|
|
4
|
+
*
|
|
5
|
+
* Uses the project's IR (Intermediate Representation) to trace protocol
|
|
6
|
+
* operations across function boundaries. When function A calls function B,
|
|
7
|
+
* B's semantic domains are propagated up to enrich A's analysis window.
|
|
8
|
+
*
|
|
9
|
+
* This addresses the single-function window limitation:
|
|
10
|
+
* Before: analyze only direct calls in function A
|
|
11
|
+
* After: analyze direct calls + propagated domains from callees
|
|
12
|
+
*
|
|
13
|
+
* Architecture:
|
|
14
|
+
* IR (call graph) → Build propagation index → Enrich semantic sequences
|
|
15
|
+
*
|
|
16
|
+
* For C projects without IR, heuristic-based inference is used instead
|
|
17
|
+
* (see TLS_INFERRED_NO_CERT_VERIFY check in protocol-domain-validator.ts).
|
|
18
|
+
*/
|
|
19
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
22
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
23
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
24
|
+
}
|
|
25
|
+
Object.defineProperty(o, k2, desc);
|
|
26
|
+
}) : (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
o[k2] = m[k];
|
|
29
|
+
}));
|
|
30
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
31
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
32
|
+
}) : function(o, v) {
|
|
33
|
+
o["default"] = v;
|
|
34
|
+
});
|
|
35
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
36
|
+
var ownKeys = function(o) {
|
|
37
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
38
|
+
var ar = [];
|
|
39
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
40
|
+
return ar;
|
|
41
|
+
};
|
|
42
|
+
return ownKeys(o);
|
|
43
|
+
};
|
|
44
|
+
return function (mod) {
|
|
45
|
+
if (mod && mod.__esModule) return mod;
|
|
46
|
+
var result = {};
|
|
47
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
48
|
+
__setModuleDefault(result, mod);
|
|
49
|
+
return result;
|
|
50
|
+
};
|
|
51
|
+
})();
|
|
52
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
|
+
exports.buildCallGraphFromIR = buildCallGraphFromIR;
|
|
54
|
+
exports.propagateDomains = propagateDomains;
|
|
55
|
+
exports.enrichWithPropagatedDomains = enrichWithPropagatedDomains;
|
|
56
|
+
exports.annotateGraphWithDomains = annotateGraphWithDomains;
|
|
57
|
+
exports.enrichSequence = enrichSequence;
|
|
58
|
+
exports.inferDomainsFromFunctionName = inferDomainsFromFunctionName;
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
// ═══════════════════════════════════════════════════════════════
|
|
61
|
+
// IR Loading & Call Graph Construction
|
|
62
|
+
// ═══════════════════════════════════════════════════════════════
|
|
63
|
+
/**
|
|
64
|
+
* Build a call graph index from the project IR.
|
|
65
|
+
* The IR must have function definitions with callee information.
|
|
66
|
+
*/
|
|
67
|
+
function buildCallGraphFromIR(irPath) {
|
|
68
|
+
const nodes = new Map();
|
|
69
|
+
let totalEdges = 0;
|
|
70
|
+
try {
|
|
71
|
+
const fs = require("fs");
|
|
72
|
+
const resolvedPath = irPath || path.resolve(process.cwd(), "ir.json");
|
|
73
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
74
|
+
return { nodes, totalFunctions: 0, totalEdges: 0 };
|
|
75
|
+
}
|
|
76
|
+
const raw = JSON.parse(fs.readFileSync(resolvedPath, "utf-8"));
|
|
77
|
+
const functions = Array.isArray(raw) ? raw : raw.functions || [];
|
|
78
|
+
for (const fn of functions) {
|
|
79
|
+
if (!fn.name)
|
|
80
|
+
continue;
|
|
81
|
+
const callees = [];
|
|
82
|
+
// Extract callees from various IR formats
|
|
83
|
+
if (Array.isArray(fn.calls)) {
|
|
84
|
+
callees.push(...fn.calls.map((c) => (typeof c === "string" ? c : c.name || c.function)));
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(fn.callees)) {
|
|
87
|
+
callees.push(...fn.callees);
|
|
88
|
+
}
|
|
89
|
+
// Infer callees from call graph edges
|
|
90
|
+
if (Array.isArray(fn.edges)) {
|
|
91
|
+
callees.push(...fn.edges.map((e) => (typeof e === "string" ? e : e.to || e.target)));
|
|
92
|
+
}
|
|
93
|
+
totalEdges += callees.length;
|
|
94
|
+
nodes.set(fn.name, {
|
|
95
|
+
name: fn.name,
|
|
96
|
+
file: fn.file || "",
|
|
97
|
+
callees: [...new Set(callees)], // deduplicate
|
|
98
|
+
domains: [], // populated later by semantic mapper
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// IR unavailable — return empty index
|
|
104
|
+
}
|
|
105
|
+
return { nodes, totalFunctions: nodes.size, totalEdges };
|
|
106
|
+
}
|
|
107
|
+
// ═══════════════════════════════════════════════════════════════
|
|
108
|
+
// Domain Propagation
|
|
109
|
+
// ═══════════════════════════════════════════════════════════════
|
|
110
|
+
/**
|
|
111
|
+
* Propagate semantic domains from callees to caller.
|
|
112
|
+
*
|
|
113
|
+
* Given a function name and a call graph index, returns the set of
|
|
114
|
+
* protocol domains from all reachable callees (up to maxDepth).
|
|
115
|
+
*
|
|
116
|
+
* This allows the Trust Engine to see protocol operations that happen
|
|
117
|
+
* inside called functions, not just in the immediate call sequence.
|
|
118
|
+
*/
|
|
119
|
+
function propagateDomains(functionName, graph, maxDepth = 3) {
|
|
120
|
+
if (graph.totalFunctions === 0)
|
|
121
|
+
return [];
|
|
122
|
+
const visited = new Set();
|
|
123
|
+
const domains = new Set();
|
|
124
|
+
function dfs(name, depth) {
|
|
125
|
+
if (depth > maxDepth || visited.has(name))
|
|
126
|
+
return;
|
|
127
|
+
visited.add(name);
|
|
128
|
+
const node = graph.nodes.get(name);
|
|
129
|
+
if (!node)
|
|
130
|
+
return;
|
|
131
|
+
// Add this node's domains
|
|
132
|
+
for (const d of node.domains) {
|
|
133
|
+
domains.add(d);
|
|
134
|
+
}
|
|
135
|
+
// Recurse into callees
|
|
136
|
+
for (const callee of node.callees) {
|
|
137
|
+
dfs(callee, depth + 1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
dfs(functionName, 0);
|
|
141
|
+
return [...domains];
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Enrich a semantic step with propagated domains from callees.
|
|
145
|
+
* Returns additional domains that should be considered present
|
|
146
|
+
* in the analysis window.
|
|
147
|
+
*/
|
|
148
|
+
function enrichWithPropagatedDomains(step, graph) {
|
|
149
|
+
if (graph.totalFunctions === 0)
|
|
150
|
+
return [];
|
|
151
|
+
return propagateDomains(step.api, graph, 3);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Annotate call graph nodes with semantic domains extracted from
|
|
155
|
+
* their direct call sequences.
|
|
156
|
+
*
|
|
157
|
+
* This should be called after the semantic mapper has processed
|
|
158
|
+
* each function's call sequence.
|
|
159
|
+
*/
|
|
160
|
+
function annotateGraphWithDomains(graph, functionDomains) {
|
|
161
|
+
for (const [funcName, domains] of functionDomains) {
|
|
162
|
+
const node = graph.nodes.get(funcName);
|
|
163
|
+
if (node) {
|
|
164
|
+
node.domains = domains;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Enrich a call sequence with cross-function context.
|
|
170
|
+
*
|
|
171
|
+
* For each function call in the sequence, looks up its callees in the
|
|
172
|
+
* call graph and propagates their protocol domains into the analysis window.
|
|
173
|
+
*/
|
|
174
|
+
function enrichSequence(steps, graph) {
|
|
175
|
+
const propagatedDomains = new Set();
|
|
176
|
+
for (const step of steps) {
|
|
177
|
+
const calleeDomains = enrichWithPropagatedDomains(step, graph);
|
|
178
|
+
for (const d of calleeDomains) {
|
|
179
|
+
propagatedDomains.add(d);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
directSteps: steps,
|
|
184
|
+
propagatedDomains: [...propagatedDomains],
|
|
185
|
+
graphAvailable: graph.totalFunctions > 0,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// ═══════════════════════════════════════════════════════════════
|
|
189
|
+
// Inference for C projects (no IR available)
|
|
190
|
+
// ═══════════════════════════════════════════════════════════════
|
|
191
|
+
/**
|
|
192
|
+
* Heuristic domain propagation for C projects without IR.
|
|
193
|
+
* Uses function name patterns to infer what domains a called function
|
|
194
|
+
* likely contains.
|
|
195
|
+
*/
|
|
196
|
+
function inferDomainsFromFunctionName(functionName) {
|
|
197
|
+
const name = functionName.toLowerCase();
|
|
198
|
+
const domains = [];
|
|
199
|
+
// SSL/TLS connection functions internally perform handshake + cert verify
|
|
200
|
+
if (name.includes("ssl_connect") ||
|
|
201
|
+
name.includes("tls_connect") ||
|
|
202
|
+
name.includes("do_connect") ||
|
|
203
|
+
name.includes("ssl_cfilter") ||
|
|
204
|
+
name.includes("ssl_setup")) {
|
|
205
|
+
domains.push("tls_handshake", "tls_cert");
|
|
206
|
+
}
|
|
207
|
+
// SSL context creation functions
|
|
208
|
+
if (name.includes("ssl_ctx_new") ||
|
|
209
|
+
name.includes("ssl_new") ||
|
|
210
|
+
name.includes("ssl_init")) {
|
|
211
|
+
domains.push("tls_handshake");
|
|
212
|
+
}
|
|
213
|
+
// Certificate loading/verification functions
|
|
214
|
+
if (name.includes("ssl_verify") ||
|
|
215
|
+
name.includes("cert_verify") ||
|
|
216
|
+
name.includes("check_cert") ||
|
|
217
|
+
name.includes("x509_verify")) {
|
|
218
|
+
domains.push("tls_cert");
|
|
219
|
+
}
|
|
220
|
+
// Connection functions that internally do TLS
|
|
221
|
+
if (name.includes("_connect") && !name.includes("disconnect")) {
|
|
222
|
+
domains.push("tls_handshake");
|
|
223
|
+
}
|
|
224
|
+
return domains;
|
|
225
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 1: Trust CLI — `progmune trust` command
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx ts-node src/trust/cli.ts <projectPath> [options]
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* --commit <sha> Git commit SHA
|
|
10
|
+
* --branch <name> Git branch name
|
|
11
|
+
* --policy <path> Policy config file (default: .progmune-policy.json)
|
|
12
|
+
* --language <lang> Project language
|
|
13
|
+
* --json Output as JSON
|
|
14
|
+
* --help, -h Show help
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const engine_1 = require("./engine");
|
|
52
|
+
const terminal_1 = require("./formatters/terminal");
|
|
53
|
+
const json_1 = require("./formatters/json");
|
|
54
|
+
const ci_1 = require("./formatters/ci");
|
|
55
|
+
const args = process.argv.slice(2);
|
|
56
|
+
async function main() {
|
|
57
|
+
// Help flag
|
|
58
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
59
|
+
console.log(`
|
|
60
|
+
Progmune Trust CLI — AI Trust Decision Engine
|
|
61
|
+
|
|
62
|
+
Usage:
|
|
63
|
+
npx ts-node src/trust/cli.ts <projectPath> [options]
|
|
64
|
+
|
|
65
|
+
Options:
|
|
66
|
+
--commit <sha> Git commit SHA for audit trail
|
|
67
|
+
--branch <name> Git branch name
|
|
68
|
+
--policy <path> Policy config file (default: .progmune-policy.json)
|
|
69
|
+
--language <lang> Project language (typescript, python, etc.)
|
|
70
|
+
--json Output machine-readable JSON
|
|
71
|
+
--help, -h Show this help
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
npx ts-node src/trust/cli.ts . --commit HEAD --json
|
|
75
|
+
`);
|
|
76
|
+
process.exit(0);
|
|
77
|
+
}
|
|
78
|
+
// Extract positional arg (project path)
|
|
79
|
+
const positional = args.filter((a) => !a.startsWith("--"));
|
|
80
|
+
const projectPath = positional[0] || process.cwd();
|
|
81
|
+
// Parse flags
|
|
82
|
+
const getFlag = (name) => {
|
|
83
|
+
const idx = args.indexOf(`--${name}`);
|
|
84
|
+
if (idx >= 0 && idx + 1 < args.length) {
|
|
85
|
+
return args[idx + 1];
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
};
|
|
89
|
+
const isJson = args.includes("--json");
|
|
90
|
+
const isCi = args.includes("--ci");
|
|
91
|
+
const commit = getFlag("commit") || "unknown";
|
|
92
|
+
const branch = getFlag("branch");
|
|
93
|
+
const policy = getFlag("policy");
|
|
94
|
+
const language = getFlag("language");
|
|
95
|
+
const ctx = {
|
|
96
|
+
projectPath: path.resolve(projectPath),
|
|
97
|
+
projectName: path.basename(path.resolve(projectPath)),
|
|
98
|
+
commit,
|
|
99
|
+
branch,
|
|
100
|
+
policyName: policy,
|
|
101
|
+
language,
|
|
102
|
+
};
|
|
103
|
+
try {
|
|
104
|
+
const decision = await (0, engine_1.evaluateTrust)(ctx);
|
|
105
|
+
if (isJson) {
|
|
106
|
+
console.log((0, json_1.formatTrustJSON)(decision));
|
|
107
|
+
}
|
|
108
|
+
else if (isCi) {
|
|
109
|
+
console.log((0, ci_1.formatTrustCI)(decision));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log((0, terminal_1.formatTrustTerminal)(decision));
|
|
113
|
+
}
|
|
114
|
+
// Exit code reflects decision
|
|
115
|
+
process.exit((0, ci_1.ciExitCode)(decision));
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
console.error(`❌ Trust evaluation failed: ${e.message}`);
|
|
119
|
+
process.exit(3);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
main();
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Compliance Scoring Engine
|
|
4
|
+
*
|
|
5
|
+
* Upgrades Progmune's verification from binary (violation/clean) to
|
|
6
|
+
* multi-dimensional continuous scores (0-1 per protocol dimension).
|
|
7
|
+
*
|
|
8
|
+
* The insight: SSG rules already encode what "perfect compliance" looks like.
|
|
9
|
+
* We just need to measure distance from that perfect state, dimension by
|
|
10
|
+
* dimension, instead of returning a single pass/fail.
|
|
11
|
+
*
|
|
12
|
+
* This is the first step toward a reward model — proving that continuous
|
|
13
|
+
* compliance scores are actionable for AI code generators.
|
|
14
|
+
*
|
|
15
|
+
* No ML. Pure rule-based scoring using existing PROTOCOLS + SAFEGUARD_RULES.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.scoreCompliance = scoreCompliance;
|
|
19
|
+
exports.scoreFunction = scoreFunction;
|
|
20
|
+
exports.formatComplianceReport = formatComplianceReport;
|
|
21
|
+
/**
|
|
22
|
+
* Protocol dimensions — mirrors the SSG PROTOCOLS but expressed as
|
|
23
|
+
* scorable dimensions rather than binary checks.
|
|
24
|
+
*/
|
|
25
|
+
const DIMENSIONS = [
|
|
26
|
+
{
|
|
27
|
+
id: "auth",
|
|
28
|
+
label: "认证与授权",
|
|
29
|
+
weight: 0.30,
|
|
30
|
+
triggers: [
|
|
31
|
+
/login|register|signin|signup|authenticate|auth|token|session|password|credential/i,
|
|
32
|
+
],
|
|
33
|
+
requiredSteps: [
|
|
34
|
+
{ pattern: /\b(login|signin|authenticate|getSessionUser|requirePermission|verifyToken|validateSession)\b/i, label: "auth_check", description: "身份验证" },
|
|
35
|
+
{ pattern: /\b(requirePermission|hasPermission|checkAccess|hasRole|adminCheck|isAuthorized)\b/i, label: "authz_check", description: "权限检查" },
|
|
36
|
+
{ pattern: /\b(logout|destroySession|revokeToken|endSession|invalidate)\b/i, label: "auth_cleanup", description: "会话清理" },
|
|
37
|
+
],
|
|
38
|
+
bonusPatterns: [
|
|
39
|
+
{ pattern: /\b(bcrypt|argon2|scrypt|pbkdf2|hashPassword|compare)\b/i, label: "secure_hash", description: "安全哈希" },
|
|
40
|
+
{ pattern: /\b(rateLimit|rate_limit|throttle|maxAttempts|loginLimiter)\b/i, label: "rate_limit", description: "速率限制" },
|
|
41
|
+
{ pattern: /\b(2fa|twoFactor|mfa|otp|verifyCode|totp)\b/i, label: "mfa", description: "多因素认证" },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "tls",
|
|
46
|
+
label: "TLS/加密",
|
|
47
|
+
weight: 0.15,
|
|
48
|
+
triggers: [
|
|
49
|
+
/ssl|tls|https|cert|certificate|encrypt|decrypt|createServer/i,
|
|
50
|
+
],
|
|
51
|
+
requiredSteps: [
|
|
52
|
+
{ pattern: /\b(ssl|tls|cert|https|createSecureContext|credentials)\b/i, label: "tls_config", description: "TLS 配置" },
|
|
53
|
+
{ pattern: /\b(ssl.*free|ssl.*cleanup|close.*ssl|disconnect)\b/i, label: "tls_cleanup", description: "TLS 清理" },
|
|
54
|
+
],
|
|
55
|
+
bonusPatterns: [
|
|
56
|
+
{ pattern: /\b(helmet|csp|hsts|secure.*header)\b/i, label: "security_headers", description: "安全响应头" },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: "payment",
|
|
61
|
+
label: "支付安全",
|
|
62
|
+
weight: 0.20,
|
|
63
|
+
triggers: [
|
|
64
|
+
/payment|pay|checkout|refund|charge|transaction|order.*create|order.*process/i,
|
|
65
|
+
],
|
|
66
|
+
requiredSteps: [
|
|
67
|
+
{ pattern: /\b(verifyOrder|getOrder|checkOrder|findOrder|orderExists)\b/i, label: "order_check", description: "订单验证" },
|
|
68
|
+
{ pattern: /\b(payment.*intent|pay.*create|create.*payment|initiate.*payment)\b/i, label: "pay_init", description: "支付发起" },
|
|
69
|
+
{ pattern: /\b(verify.*sign|check.*sign|webhook.*secret|hmac|signature)\b/i, label: "sig_check", description: "签名验证" },
|
|
70
|
+
{ pattern: /\b(capture.*payment|confirm.*order|payment.*done|payment.*success)\b/i, label: "pay_complete", description: "支付完成" },
|
|
71
|
+
],
|
|
72
|
+
bonusPatterns: [
|
|
73
|
+
{ pattern: /\b(idempotent|retry|rollback|compensat)\b/i, label: "idempotency", description: "幂等性" },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "data_integrity",
|
|
78
|
+
label: "数据完整性",
|
|
79
|
+
weight: 0.15,
|
|
80
|
+
triggers: [
|
|
81
|
+
/create|update|delete|insert|modify|save|write|mutate/i,
|
|
82
|
+
],
|
|
83
|
+
requiredSteps: [
|
|
84
|
+
{ pattern: /\b(validate|sanitize|check|verify|schema|zod|yup|joi)\b/i, label: "input_check", description: "输入校验" },
|
|
85
|
+
{ pattern: /\b(audit|log.*change|change.*log|track.*change|record.*mutation)\b/i, label: "audit_trail", description: "审计追踪" },
|
|
86
|
+
],
|
|
87
|
+
bonusPatterns: [
|
|
88
|
+
{ pattern: /\b(transaction|rollback|commit)\b/i, label: "transaction", description: "事务保护" },
|
|
89
|
+
{ pattern: /\b(parameterize|prepared.*statement|%s|\$1)\b/i, label: "sql_safe", description: "SQL 安全" },
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "resource",
|
|
94
|
+
label: "资源管理",
|
|
95
|
+
weight: 0.10,
|
|
96
|
+
triggers: [
|
|
97
|
+
/file|socket|connection|pool|stream|buffer|handle|upload|download/i,
|
|
98
|
+
],
|
|
99
|
+
requiredSteps: [
|
|
100
|
+
{ pattern: /\b(open|create|init|connect|start)\b/i, label: "resource_init", description: "资源获取" },
|
|
101
|
+
{ pattern: /\b(close|free|cleanup|destroy|disconnect|release|end)\b/i, label: "resource_free", description: "资源释放" },
|
|
102
|
+
],
|
|
103
|
+
bonusPatterns: [
|
|
104
|
+
{ pattern: /\b(try.*finally|defer|using|RAII)\b/i, label: "cleanup_guarantee", description: "清理保证" },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: "session",
|
|
109
|
+
label: "会话管理",
|
|
110
|
+
weight: 0.10,
|
|
111
|
+
triggers: [
|
|
112
|
+
/session|token|jwt|cookie|bearer/i,
|
|
113
|
+
],
|
|
114
|
+
requiredSteps: [
|
|
115
|
+
{ pattern: /\b(session.*create|create.*session|session.*init|token.*generate|jwt.*sign)\b/i, label: "sess_create", description: "会话创建" },
|
|
116
|
+
{ pattern: /\b(session.*check|session.*valid|validate.*session|verify.*token|token.*check)\b/i, label: "sess_validate", description: "会话验证" },
|
|
117
|
+
{ pattern: /\b(session.*destroy|session.*delete|token.*revoke|session.*expire|session.*timeout)\b/i, label: "sess_destroy", description: "会话销毁" },
|
|
118
|
+
],
|
|
119
|
+
bonusPatterns: [
|
|
120
|
+
{ pattern: /\b(httpOnly|secure.*cookie|sameSite|csrf)\b/i, label: "secure_cookie", description: "安全Cookie" },
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
];
|
|
124
|
+
// ── Scoring Logic ──
|
|
125
|
+
/**
|
|
126
|
+
* Score a function call sequence against all protocol dimensions.
|
|
127
|
+
*
|
|
128
|
+
* @param calls - Array of function call names in order
|
|
129
|
+
* @param functionPurpose - Optional function purpose for context (e.g., "退款处理", "用户注册")
|
|
130
|
+
* @returns ComplianceScore with per-dimension breakdown
|
|
131
|
+
*/
|
|
132
|
+
function scoreCompliance(calls, functionPurpose) {
|
|
133
|
+
const dimensions = [];
|
|
134
|
+
const allCallText = calls.join(" ");
|
|
135
|
+
const purposeText = functionPurpose || "";
|
|
136
|
+
for (const dim of DIMENSIONS) {
|
|
137
|
+
// Check if this dimension is relevant
|
|
138
|
+
const isRelevant = dim.triggers.some((t) => t.test(allCallText)) ||
|
|
139
|
+
dim.triggers.some((t) => t.test(purposeText));
|
|
140
|
+
if (!isRelevant && calls.length > 0) {
|
|
141
|
+
// Skip irrelevant dimensions — don't penalize what doesn't apply
|
|
142
|
+
dimensions.push({
|
|
143
|
+
dimension: dim.id,
|
|
144
|
+
score: 1.0,
|
|
145
|
+
maxScore: 1.0,
|
|
146
|
+
strengths: ["N/A — 不适用"],
|
|
147
|
+
gaps: [],
|
|
148
|
+
fixes: [],
|
|
149
|
+
});
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
// Score required steps: what % are present?
|
|
153
|
+
const requiredResults = dim.requiredSteps.map((step) => ({
|
|
154
|
+
...step,
|
|
155
|
+
present: step.pattern.test(allCallText),
|
|
156
|
+
}));
|
|
157
|
+
const requiredScore = dim.requiredSteps.length > 0
|
|
158
|
+
? requiredResults.filter((r) => r.present).length / dim.requiredSteps.length
|
|
159
|
+
: 1.0;
|
|
160
|
+
// Score bonus patterns: each present adds 0.1 to the ceiling
|
|
161
|
+
const bonusHits = dim.bonusPatterns.filter((b) => b.pattern.test(allCallText));
|
|
162
|
+
const bonusBoost = Math.min(bonusHits.length * 0.1, 0.2); // cap at +0.2
|
|
163
|
+
// Calculate strengths and gaps
|
|
164
|
+
const strengths = [
|
|
165
|
+
...requiredResults.filter((r) => r.present).map((r) => r.description),
|
|
166
|
+
...bonusHits.map((b) => b.description),
|
|
167
|
+
];
|
|
168
|
+
const gaps = [
|
|
169
|
+
...requiredResults.filter((r) => !r.present).map((r) => r.description),
|
|
170
|
+
];
|
|
171
|
+
// Generate fixes for missing steps
|
|
172
|
+
const fixes = requiredResults
|
|
173
|
+
.filter((r) => !r.present)
|
|
174
|
+
.map((r) => `添加 ${r.label}: ${r.description}`);
|
|
175
|
+
// Final score: required completion + bonus boost, clamped to [0, 1]
|
|
176
|
+
const rawScore = Math.min(requiredScore + bonusBoost, 1.0);
|
|
177
|
+
dimensions.push({
|
|
178
|
+
dimension: dim.id,
|
|
179
|
+
score: Math.round(rawScore * 100) / 100,
|
|
180
|
+
maxScore: 1.0,
|
|
181
|
+
strengths: strengths.length > 0 ? strengths : ["无"],
|
|
182
|
+
gaps,
|
|
183
|
+
fixes,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
// Calculate overall score (weighted average of relevant dimensions)
|
|
187
|
+
const relevantDims = dimensions.filter((d) => d.score < 1.0 || d.strengths[0] !== "N/A — 不适用");
|
|
188
|
+
const weights = DIMENSIONS.map((d) => d.weight);
|
|
189
|
+
const weightsMap = new Map(DIMENSIONS.map((d, i) => [d.id, weights[i] ?? 0]));
|
|
190
|
+
let overall = 0;
|
|
191
|
+
let totalWeight = 0;
|
|
192
|
+
for (const dim of dimensions) {
|
|
193
|
+
const w = weightsMap.get(dim.dimension) || 0.1;
|
|
194
|
+
// Only count relevant dimensions in the weighted average
|
|
195
|
+
if (dim.strengths[0] !== "N/A — 不适用") {
|
|
196
|
+
overall += dim.score * w;
|
|
197
|
+
totalWeight += w;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
overall = totalWeight > 0 ? overall / totalWeight : 1.0;
|
|
201
|
+
overall = Math.round(overall * 100) / 100;
|
|
202
|
+
// Find weakest dimension
|
|
203
|
+
const scoredDims = dimensions.filter((d) => d.strengths[0] !== "N/A — 不适用");
|
|
204
|
+
const weakest = scoredDims.length > 0
|
|
205
|
+
? scoredDims.reduce((min, d) => (d.score < min.score ? d : min), scoredDims[0])
|
|
206
|
+
: null;
|
|
207
|
+
// Find top fix (from the weakest dimension)
|
|
208
|
+
const topFix = weakest?.fixes[0] || "无需修复";
|
|
209
|
+
// Build suggested sequence: existing present calls + missing calls
|
|
210
|
+
const presentCalls = calls.filter((c) => {
|
|
211
|
+
for (const dim of DIMENSIONS) {
|
|
212
|
+
for (const step of dim.requiredSteps) {
|
|
213
|
+
if (step.pattern.test(c))
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
});
|
|
219
|
+
const missingCalls = weakest?.fixes.map((f) => f.replace("添加 ", "")) || [];
|
|
220
|
+
return {
|
|
221
|
+
overall,
|
|
222
|
+
dimensions,
|
|
223
|
+
weakestDimension: weakest?.dimension || "all_clear",
|
|
224
|
+
topFix,
|
|
225
|
+
suggestedSequence: [
|
|
226
|
+
...presentCalls,
|
|
227
|
+
...missingCalls.map((m) => `[需添加] ${m}`),
|
|
228
|
+
],
|
|
229
|
+
presentCalls,
|
|
230
|
+
missingCalls,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Score a specific function for compliance given its purpose.
|
|
235
|
+
* Convenience wrapper for scoring a single function context.
|
|
236
|
+
*/
|
|
237
|
+
function scoreFunction(functionName, calls, purpose) {
|
|
238
|
+
const allCalls = [functionName, ...calls];
|
|
239
|
+
return scoreCompliance(allCalls, purpose);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Format a compliance score as a human-readable report.
|
|
243
|
+
*/
|
|
244
|
+
function formatComplianceReport(score) {
|
|
245
|
+
const lines = [
|
|
246
|
+
"═══════════════════════════════════════",
|
|
247
|
+
" Progmune Compliance Score",
|
|
248
|
+
"═══════════════════════════════════════",
|
|
249
|
+
"",
|
|
250
|
+
` Overall: ${renderScoreBar(score.overall)} ${(score.overall * 100).toFixed(0)}%`,
|
|
251
|
+
` Weakest: ${score.weakestDimension}`,
|
|
252
|
+
` Top Fix: ${score.topFix}`,
|
|
253
|
+
"",
|
|
254
|
+
" Dimensions:",
|
|
255
|
+
];
|
|
256
|
+
for (const dim of score.dimensions) {
|
|
257
|
+
const bar = renderScoreBar(dim.score);
|
|
258
|
+
lines.push(` ${bar} ${dim.dimension.padEnd(16)} ${(dim.score * 100).toFixed(0)}%`);
|
|
259
|
+
if (dim.gaps.length > 0) {
|
|
260
|
+
lines.push(` ⚠️ 缺失: ${dim.gaps.join(", ")}`);
|
|
261
|
+
}
|
|
262
|
+
if (dim.fixes.length > 0) {
|
|
263
|
+
lines.push(` 🔧 ${dim.fixes.join("; ")}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (score.missingCalls.length > 0) {
|
|
267
|
+
lines.push("");
|
|
268
|
+
lines.push(" 建议添加的调用:");
|
|
269
|
+
for (const c of score.missingCalls) {
|
|
270
|
+
lines.push(` → ${c}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return lines.join("\n");
|
|
274
|
+
}
|
|
275
|
+
function renderScoreBar(score) {
|
|
276
|
+
if (score >= 0.9)
|
|
277
|
+
return "🟢";
|
|
278
|
+
if (score >= 0.7)
|
|
279
|
+
return "🟡";
|
|
280
|
+
if (score >= 0.5)
|
|
281
|
+
return "🟠";
|
|
282
|
+
return "🔴";
|
|
283
|
+
}
|