openpitstop 1.0.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/LICENSE +21 -0
- package/README.md +512 -0
- package/demo-repo/README.md +24 -0
- package/demo-repo/package-lock.json +4483 -0
- package/demo-repo/package.json +17 -0
- package/demo-repo/src/config.js +5 -0
- package/demo-repo/src/db.js +31 -0
- package/demo-repo/src/server.js +13 -0
- package/demo-repo/src/userController.js +22 -0
- package/demo-repo/src/userRepo.js +18 -0
- package/demo-repo/src/userService.js +59 -0
- package/demo-repo/test/user.test.js +12 -0
- package/demo-repo-fintech/README.md +27 -0
- package/demo-repo-fintech/package-lock.json +1023 -0
- package/demo-repo-fintech/package.json +14 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/src/gateway.js +26 -0
- package/demo-repo-fintech/src/server.js +61 -0
- package/demo-repo-generators/package-lock.json +1508 -0
- package/demo-repo-generators/package.json +17 -0
- package/demo-repo-generators/public/index.html +10 -0
- package/demo-repo-generators/scripts/build.js +17 -0
- package/demo-repo-generators/src/counter.js +16 -0
- package/demo-repo-integrity/README.md +49 -0
- package/demo-repo-integrity/package.json +12 -0
- package/demo-repo-integrity/src/money.js +19 -0
- package/demo-repo-integrity/test/money.test.js +17 -0
- package/dist/analyzers/accessibility.d.ts +18 -0
- package/dist/analyzers/accessibility.js +248 -0
- package/dist/analyzers/accessibility.js.map +1 -0
- package/dist/analyzers/dependencyGraph.d.ts +14 -0
- package/dist/analyzers/dependencyGraph.js +206 -0
- package/dist/analyzers/dependencyGraph.js.map +1 -0
- package/dist/analyzers/devex.d.ts +19 -0
- package/dist/analyzers/devex.js +301 -0
- package/dist/analyzers/devex.js.map +1 -0
- package/dist/analyzers/duplication.d.ts +2 -0
- package/dist/analyzers/duplication.js +47 -0
- package/dist/analyzers/duplication.js.map +1 -0
- package/dist/analyzers/index.d.ts +12 -0
- package/dist/analyzers/index.js +38 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
- package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
- package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
- package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
- package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
- package/dist/analyzers/integrity/exitCheat.js +31 -0
- package/dist/analyzers/integrity/exitCheat.js.map +1 -0
- package/dist/analyzers/integrity/git.d.ts +10 -0
- package/dist/analyzers/integrity/git.js +181 -0
- package/dist/analyzers/integrity/git.js.map +1 -0
- package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
- package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
- package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
- package/dist/analyzers/integrity/helpers.d.ts +21 -0
- package/dist/analyzers/integrity/helpers.js +66 -0
- package/dist/analyzers/integrity/helpers.js.map +1 -0
- package/dist/analyzers/integrity/index.d.ts +10 -0
- package/dist/analyzers/integrity/index.js +27 -0
- package/dist/analyzers/integrity/index.js.map +1 -0
- package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
- package/dist/analyzers/integrity/mockOverreach.js +72 -0
- package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
- package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
- package/dist/analyzers/integrity/suppressionCreep.js +27 -0
- package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
- package/dist/analyzers/integrity/testTamper.d.ts +2 -0
- package/dist/analyzers/integrity/testTamper.js +153 -0
- package/dist/analyzers/integrity/testTamper.js.map +1 -0
- package/dist/analyzers/integrity/types.d.ts +63 -0
- package/dist/analyzers/integrity/types.js +13 -0
- package/dist/analyzers/integrity/types.js.map +1 -0
- package/dist/analyzers/ledger/attacks.d.ts +17 -0
- package/dist/analyzers/ledger/attacks.js +135 -0
- package/dist/analyzers/ledger/attacks.js.map +1 -0
- package/dist/analyzers/ledger/discover.d.ts +15 -0
- package/dist/analyzers/ledger/discover.js +122 -0
- package/dist/analyzers/ledger/discover.js.map +1 -0
- package/dist/analyzers/ledger/evidence.d.ts +22 -0
- package/dist/analyzers/ledger/evidence.js +127 -0
- package/dist/analyzers/ledger/evidence.js.map +1 -0
- package/dist/analyzers/ledger/harness.d.ts +4 -0
- package/dist/analyzers/ledger/harness.js +270 -0
- package/dist/analyzers/ledger/harness.js.map +1 -0
- package/dist/analyzers/ledger/index.d.ts +14 -0
- package/dist/analyzers/ledger/index.js +116 -0
- package/dist/analyzers/ledger/index.js.map +1 -0
- package/dist/analyzers/ledger/types.d.ts +31 -0
- package/dist/analyzers/ledger/types.js +2 -0
- package/dist/analyzers/ledger/types.js.map +1 -0
- package/dist/analyzers/perf.d.ts +2 -0
- package/dist/analyzers/perf.js +106 -0
- package/dist/analyzers/perf.js.map +1 -0
- package/dist/analyzers/reliability.d.ts +22 -0
- package/dist/analyzers/reliability.js +339 -0
- package/dist/analyzers/reliability.js.map +1 -0
- package/dist/analyzers/routes.d.ts +17 -0
- package/dist/analyzers/routes.js +115 -0
- package/dist/analyzers/routes.js.map +1 -0
- package/dist/analyzers/security.d.ts +2 -0
- package/dist/analyzers/security.js +302 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/suiteRunner.d.ts +64 -0
- package/dist/analyzers/suiteRunner.js +381 -0
- package/dist/analyzers/suiteRunner.js.map +1 -0
- package/dist/analyzers/tests.d.ts +2 -0
- package/dist/analyzers/tests.js +215 -0
- package/dist/analyzers/tests.js.map +1 -0
- package/dist/analyzers/types.d.ts +203 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/analyzers/util.d.ts +35 -0
- package/dist/analyzers/util.js +209 -0
- package/dist/analyzers/util.js.map +1 -0
- package/dist/ciCheckA.d.ts +2 -0
- package/dist/ciCheckA.js +5 -0
- package/dist/ciCheckA.js.map +1 -0
- package/dist/ciCheckB.d.ts +2 -0
- package/dist/ciCheckB.js +6 -0
- package/dist/ciCheckB.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/budget.d.ts +33 -0
- package/dist/commands/budget.js +108 -0
- package/dist/commands/budget.js.map +1 -0
- package/dist/commands/ci.d.ts +14 -0
- package/dist/commands/ci.js +157 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.js +163 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/digest.d.ts +8 -0
- package/dist/commands/digest.js +189 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +98 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/drive.d.ts +3 -0
- package/dist/commands/drive.js +170 -0
- package/dist/commands/drive.js.map +1 -0
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +136 -0
- package/dist/commands/gate.js.map +1 -0
- package/dist/commands/honesty.d.ts +2 -0
- package/dist/commands/honesty.js +182 -0
- package/dist/commands/honesty.js.map +1 -0
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +244 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/integrity.d.ts +2 -0
- package/dist/commands/integrity.js +74 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/memory.d.ts +6 -0
- package/dist/commands/memory.js +90 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/pen.d.ts +17 -0
- package/dist/commands/pen.js +158 -0
- package/dist/commands/pen.js.map +1 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +37 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/readyCheck.d.ts +21 -0
- package/dist/commands/readyCheck.js +68 -0
- package/dist/commands/readyCheck.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +48 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/repro.d.ts +3 -0
- package/dist/commands/repro.js +61 -0
- package/dist/commands/repro.js.map +1 -0
- package/dist/commands/scan.d.ts +27 -0
- package/dist/commands/scan.js +348 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +163 -0
- package/dist/commands/share.js.map +1 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +103 -0
- package/dist/commands/trends.js.map +1 -0
- package/dist/commands/try.d.ts +12 -0
- package/dist/commands/try.js +208 -0
- package/dist/commands/try.js.map +1 -0
- package/dist/commands/verify.d.ts +44 -0
- package/dist/commands/verify.js +381 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.js +70 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +61 -0
- package/dist/evidence.js.map +1 -0
- package/dist/graph/correlate.d.ts +7 -0
- package/dist/graph/correlate.js +246 -0
- package/dist/graph/correlate.js.map +1 -0
- package/dist/graph/integrity.d.ts +22 -0
- package/dist/graph/integrity.js +39 -0
- package/dist/graph/integrity.js.map +1 -0
- package/dist/installer/targets.d.ts +23 -0
- package/dist/installer/targets.js +162 -0
- package/dist/installer/targets.js.map +1 -0
- package/dist/memory/store.d.ts +33 -0
- package/dist/memory/store.js +131 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/pen/dynamic.d.ts +36 -0
- package/dist/pen/dynamic.js +622 -0
- package/dist/pen/dynamic.js.map +1 -0
- package/dist/pen/fix.d.ts +35 -0
- package/dist/pen/fix.js +239 -0
- package/dist/pen/fix.js.map +1 -0
- package/dist/pen/proof.d.ts +46 -0
- package/dist/pen/proof.js +112 -0
- package/dist/pen/proof.js.map +1 -0
- package/dist/pen/report.d.ts +11 -0
- package/dist/pen/report.js +258 -0
- package/dist/pen/report.js.map +1 -0
- package/dist/pen/static.d.ts +17 -0
- package/dist/pen/static.js +565 -0
- package/dist/pen/static.js.map +1 -0
- package/dist/pen/store.d.ts +11 -0
- package/dist/pen/store.js +36 -0
- package/dist/pen/store.js.map +1 -0
- package/dist/pen/types.d.ts +112 -0
- package/dist/pen/types.js +53 -0
- package/dist/pen/types.js.map +1 -0
- package/dist/report/format.d.ts +126 -0
- package/dist/report/format.js +706 -0
- package/dist/report/format.js.map +1 -0
- package/dist/report/score.d.ts +47 -0
- package/dist/report/score.js +164 -0
- package/dist/report/score.js.map +1 -0
- package/dist/repro/framework.d.ts +26 -0
- package/dist/repro/framework.js +224 -0
- package/dist/repro/framework.js.map +1 -0
- package/dist/repro/generate.d.ts +14 -0
- package/dist/repro/generate.js +669 -0
- package/dist/repro/generate.js.map +1 -0
- package/dist/repro/ids.d.ts +32 -0
- package/dist/repro/ids.js +175 -0
- package/dist/repro/ids.js.map +1 -0
- package/dist/repro/index.d.ts +24 -0
- package/dist/repro/index.js +69 -0
- package/dist/repro/index.js.map +1 -0
- package/dist/repro/pen.d.ts +26 -0
- package/dist/repro/pen.js +321 -0
- package/dist/repro/pen.js.map +1 -0
- package/dist/repro/run.d.ts +18 -0
- package/dist/repro/run.js +36 -0
- package/dist/repro/run.js.map +1 -0
- package/dist/sandbox/proxy.d.ts +41 -0
- package/dist/sandbox/proxy.js +231 -0
- package/dist/sandbox/proxy.js.map +1 -0
- package/dist/sandbox/startCmd.d.ts +24 -0
- package/dist/sandbox/startCmd.js +71 -0
- package/dist/sandbox/startCmd.js.map +1 -0
- package/dist/ui/spinner.d.ts +18 -0
- package/dist/ui/spinner.js +26 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/verify/metrics.d.ts +13 -0
- package/dist/verify/metrics.js +72 -0
- package/dist/verify/metrics.js.map +1 -0
- package/package.json +68 -0
- package/scripts/cheat-demo.cjs +102 -0
- package/templates/ledger/preload.cjs +331 -0
- package/templates/pen/preload.cjs +196 -0
- package/templates/pitstop.prompt.md +371 -0
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import boxen from "boxen";
|
|
4
|
+
import Table from "cli-table3";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import { analyzeTests } from "../analyzers/tests.js";
|
|
8
|
+
import { analyzePerf } from "../analyzers/perf.js";
|
|
9
|
+
import { analyzeSecurity } from "../analyzers/security.js";
|
|
10
|
+
import { analyzeDuplication } from "../analyzers/duplication.js";
|
|
11
|
+
import { addEntry } from "../memory/store.js";
|
|
12
|
+
import { newestModifiedFile } from "../analyzers/util.js";
|
|
13
|
+
import { classifyRisk, deltasOf, metricsOf } from "../verify/metrics.js";
|
|
14
|
+
import { computeScore } from "../report/score.js";
|
|
15
|
+
import { getDiff } from "../analyzers/integrity/git.js";
|
|
16
|
+
import { buildIntegrityReport } from "../graph/integrity.js";
|
|
17
|
+
import { seal, checkEvidence } from "../evidence.js";
|
|
18
|
+
/**
|
|
19
|
+
* Every verify run doubles as an integrity gate: it diffs the working tree
|
|
20
|
+
* against the last commit (HEAD) and runs the AI-agent-cheat detectors. A
|
|
21
|
+
* SUSPICIOUS or CONFIRMED_CHEAT verdict BLOCKS the change regardless of test
|
|
22
|
+
* or perf numbers.
|
|
23
|
+
*/
|
|
24
|
+
function integrityGate(repo) {
|
|
25
|
+
const changes = getDiff(repo, "HEAD");
|
|
26
|
+
const report = buildIntegrityReport(repo, "HEAD", "working tree", changes);
|
|
27
|
+
return { verdict: report.verdict, findings: report.findings, summary: report.summary };
|
|
28
|
+
}
|
|
29
|
+
function readBaseline(repo) {
|
|
30
|
+
const p = path.join(repo, ".pitstop", "scan-latest.json");
|
|
31
|
+
if (!fs.existsSync(p))
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
// PowerShell (and some editors) write UTF-8 JSON with a BOM; JSON.parse
|
|
35
|
+
// rejects it, so strip a leading U+FEFF before parsing.
|
|
36
|
+
const raw = fs.readFileSync(p, "utf8");
|
|
37
|
+
const clean = raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw;
|
|
38
|
+
return JSON.parse(clean);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function currentMetrics(repo) {
|
|
45
|
+
const [tests, perf, security, duplication] = await Promise.all([
|
|
46
|
+
analyzeTests(repo),
|
|
47
|
+
analyzePerf(repo),
|
|
48
|
+
analyzeSecurity(repo),
|
|
49
|
+
analyzeDuplication(repo),
|
|
50
|
+
]);
|
|
51
|
+
return {
|
|
52
|
+
tests: {
|
|
53
|
+
total: tests.total,
|
|
54
|
+
passed: tests.passed,
|
|
55
|
+
failed: tests.failed,
|
|
56
|
+
durationMs: tests.durationMs,
|
|
57
|
+
coverage: tests.coverage,
|
|
58
|
+
},
|
|
59
|
+
perf: {
|
|
60
|
+
buildTimeMs: perf.buildTimeMs,
|
|
61
|
+
bundleSizeBytes: perf.bundleSizeBytes,
|
|
62
|
+
},
|
|
63
|
+
securityCount: security.issues.length,
|
|
64
|
+
duplicationCount: duplication.cloneCount,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Verify re-measures only the fast metrics (tests/perf/security/dup), so the
|
|
69
|
+
* "current" OpenPitStop Score is computed from the last scan with exactly those
|
|
70
|
+
* categories patched in — the other categories keep their scan snapshot.
|
|
71
|
+
*/
|
|
72
|
+
function currentScoreOf(baselineResult, current, integrityPenalty) {
|
|
73
|
+
// Only patch categories that actually ran in the baseline scan; a category
|
|
74
|
+
// that printed "skipped" must stay skipped on both sides of the delta,
|
|
75
|
+
// otherwise the score could move for purely toolchain reasons.
|
|
76
|
+
const hybrid = { ...baselineResult };
|
|
77
|
+
if (hybrid.tests.status === "ok") {
|
|
78
|
+
hybrid.tests = {
|
|
79
|
+
status: "ok",
|
|
80
|
+
total: current.tests.total,
|
|
81
|
+
passed: current.tests.passed,
|
|
82
|
+
failed: current.tests.failed,
|
|
83
|
+
durationMs: current.tests.durationMs,
|
|
84
|
+
coverage: current.tests.coverage,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (hybrid.perf.status === "ok") {
|
|
88
|
+
hybrid.perf = {
|
|
89
|
+
status: "ok",
|
|
90
|
+
buildTimeMs: current.perf.buildTimeMs,
|
|
91
|
+
bundleSizeBytes: current.perf.bundleSizeBytes,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (hybrid.security.status === "ok") {
|
|
95
|
+
const synthetic = Array.from({ length: current.securityCount }, () => ({
|
|
96
|
+
type: "security",
|
|
97
|
+
severity: "high",
|
|
98
|
+
description: "current security findings (from verify)",
|
|
99
|
+
}));
|
|
100
|
+
hybrid.security = { status: "ok", issues: synthetic };
|
|
101
|
+
}
|
|
102
|
+
if (hybrid.duplication.status === "ok") {
|
|
103
|
+
hybrid.duplication = { status: "ok", cloneCount: current.duplicationCount, clones: [] };
|
|
104
|
+
}
|
|
105
|
+
return computeScore(hybrid, { integrityPenalty });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The shared verify pipeline (also used by `pitstop gate`): re-measure the
|
|
109
|
+
* fast metrics, diff against the scan baseline, run the integrity gate, check
|
|
110
|
+
* the baseline's evidence signature, and write a sealed verify report. Returns
|
|
111
|
+
* everything a renderer needs plus the exit code to propagate.
|
|
112
|
+
*/
|
|
113
|
+
export async function runVerify(repo) {
|
|
114
|
+
const baselineResult = readBaseline(repo);
|
|
115
|
+
if (!baselineResult) {
|
|
116
|
+
return {
|
|
117
|
+
repo,
|
|
118
|
+
missingBaseline: true,
|
|
119
|
+
stale: false,
|
|
120
|
+
risk: "High",
|
|
121
|
+
integrity: {
|
|
122
|
+
verdict: "CLEAN",
|
|
123
|
+
findings: [],
|
|
124
|
+
summary: { confirmed: 0, suspicious: 0, total: 0 },
|
|
125
|
+
},
|
|
126
|
+
blocked: false,
|
|
127
|
+
current: {
|
|
128
|
+
tests: { total: 0, passed: 0, failed: 0, durationMs: 0 },
|
|
129
|
+
perf: {},
|
|
130
|
+
securityCount: 0,
|
|
131
|
+
duplicationCount: 0,
|
|
132
|
+
},
|
|
133
|
+
baseline: {
|
|
134
|
+
tests: { total: 0, passed: 0, failed: 0, durationMs: 0 },
|
|
135
|
+
perf: {},
|
|
136
|
+
securityCount: 0,
|
|
137
|
+
duplicationCount: 0,
|
|
138
|
+
},
|
|
139
|
+
deltas: {
|
|
140
|
+
passed: 0,
|
|
141
|
+
failed: 0,
|
|
142
|
+
durationMs: 0,
|
|
143
|
+
coverage: 0,
|
|
144
|
+
buildTimeMs: 0,
|
|
145
|
+
bundleSizeBytes: 0,
|
|
146
|
+
security: 0,
|
|
147
|
+
duplication: 0,
|
|
148
|
+
},
|
|
149
|
+
baselineScore: { score: 0, grade: "F", categories: [], analyzed: 0, total: 0 },
|
|
150
|
+
currentScore: { score: 0, grade: "F", categories: [], analyzed: 0, total: 0 },
|
|
151
|
+
scoreDelta: 0,
|
|
152
|
+
exitCode: 1,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Evidence signature: recompute the digest of the stored scan and compare.
|
|
156
|
+
// A baseline edited after OpenPitStop wrote it breaks the chain — the score
|
|
157
|
+
// delta is then computed against a document OpenPitStop cannot vouch for.
|
|
158
|
+
const evidence = checkEvidence(baselineResult);
|
|
159
|
+
const baseline = metricsOf(baselineResult);
|
|
160
|
+
const current = await currentMetrics(repo);
|
|
161
|
+
const d = deltasOf(baseline, current);
|
|
162
|
+
// Baseline staleness: if files changed after the baseline scan, the delta
|
|
163
|
+
// and score are computed against a snapshot that no longer matches the
|
|
164
|
+
// working tree — warn loudly instead of silently presenting stale math.
|
|
165
|
+
let staleNote = "";
|
|
166
|
+
const newest = newestModifiedFile(repo);
|
|
167
|
+
const baselineAt = Date.parse(baselineResult.timestamp);
|
|
168
|
+
if (newest && baselineAt && newest.mtimeMs > baselineAt + 1000) {
|
|
169
|
+
const relFile = path.relative(repo, newest.file) || newest.file;
|
|
170
|
+
staleNote =
|
|
171
|
+
`baseline is STALE — ${relFile} changed ${new Date(newest.mtimeMs).toISOString()}, ` +
|
|
172
|
+
`after the baseline scan (${baselineResult.timestamp}). Score delta vs baseline is ` +
|
|
173
|
+
"approximate; re-run `pitstop scan` for a fresh baseline.";
|
|
174
|
+
}
|
|
175
|
+
const stale = staleNote !== "";
|
|
176
|
+
const risk = classifyRisk(baseline, current, d);
|
|
177
|
+
// Integrity gate — runs automatically on every verify, gating the commit.
|
|
178
|
+
const integrity = integrityGate(repo);
|
|
179
|
+
const blocked = integrity.verdict === "SUSPICIOUS" || integrity.verdict === "CONFIRMED_CHEAT";
|
|
180
|
+
const integrityPenalty = blocked
|
|
181
|
+
? integrity.verdict === "CONFIRMED_CHEAT"
|
|
182
|
+
? 25
|
|
183
|
+
: 10
|
|
184
|
+
: 0;
|
|
185
|
+
const baselineScore = computeScore(baselineResult);
|
|
186
|
+
const currentScore = currentScoreOf(baselineResult, current, integrityPenalty);
|
|
187
|
+
const scoreDelta = currentScore.score - baselineScore.score;
|
|
188
|
+
const outDir = path.join(repo, ".pitstop");
|
|
189
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
190
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
191
|
+
const file = path.join(outDir, `verify-${ts}.json`);
|
|
192
|
+
const report = {
|
|
193
|
+
timestamp: new Date().toISOString(),
|
|
194
|
+
repo,
|
|
195
|
+
baselineTimestamp: baselineResult.timestamp,
|
|
196
|
+
risk,
|
|
197
|
+
blocked,
|
|
198
|
+
stale,
|
|
199
|
+
staleNote: stale ? staleNote : undefined,
|
|
200
|
+
evidence,
|
|
201
|
+
status: blocked ? "BLOCKED" : evidence.status === "tampered" ? "SUSPICIOUS_EVIDENCE" : "OK",
|
|
202
|
+
exitCode: blocked
|
|
203
|
+
? integrity.verdict === "CONFIRMED_CHEAT"
|
|
204
|
+
? 2
|
|
205
|
+
: 1
|
|
206
|
+
: risk === "High"
|
|
207
|
+
? 1
|
|
208
|
+
: 0,
|
|
209
|
+
integrity,
|
|
210
|
+
deltas: d,
|
|
211
|
+
baseline,
|
|
212
|
+
current,
|
|
213
|
+
score: {
|
|
214
|
+
baseline: baselineScore.score,
|
|
215
|
+
current: currentScore.score,
|
|
216
|
+
delta: scoreDelta,
|
|
217
|
+
grade: currentScore.grade,
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
fs.writeFileSync(file, JSON.stringify(seal(report, `pitstop verify result for ${repo}`), null, 2));
|
|
221
|
+
return {
|
|
222
|
+
repo,
|
|
223
|
+
missingBaseline: false,
|
|
224
|
+
baselineTimestamp: baselineResult.timestamp,
|
|
225
|
+
evidence,
|
|
226
|
+
stale,
|
|
227
|
+
staleNote: stale ? staleNote : undefined,
|
|
228
|
+
risk,
|
|
229
|
+
integrity,
|
|
230
|
+
blocked,
|
|
231
|
+
current,
|
|
232
|
+
baseline,
|
|
233
|
+
deltas: d,
|
|
234
|
+
baselineScore,
|
|
235
|
+
currentScore,
|
|
236
|
+
scoreDelta,
|
|
237
|
+
exitCode: blocked
|
|
238
|
+
? integrity.verdict === "CONFIRMED_CHEAT"
|
|
239
|
+
? 2
|
|
240
|
+
: 1
|
|
241
|
+
: risk === "High"
|
|
242
|
+
? 1
|
|
243
|
+
: 0,
|
|
244
|
+
file,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function fmtBundle(b) {
|
|
248
|
+
return b == null ? "—" : `${(b / 1024).toFixed(1)} KB`;
|
|
249
|
+
}
|
|
250
|
+
function fmtMs(n) {
|
|
251
|
+
return n == null ? "—" : `${n} ms`;
|
|
252
|
+
}
|
|
253
|
+
function fmtPct(n) {
|
|
254
|
+
return n == null ? "—" : `${n.toFixed(1)}%`;
|
|
255
|
+
}
|
|
256
|
+
function signed(n) {
|
|
257
|
+
return n > 0 ? `+${n}` : `${n}`;
|
|
258
|
+
}
|
|
259
|
+
function deltaBytes(n) {
|
|
260
|
+
if (n === 0)
|
|
261
|
+
return "0";
|
|
262
|
+
return `${n > 0 ? "+" : ""}${(n / 1024).toFixed(1)} KB`;
|
|
263
|
+
}
|
|
264
|
+
function deltaMs(n) {
|
|
265
|
+
if (n === 0)
|
|
266
|
+
return "0";
|
|
267
|
+
return `${n > 0 ? "+" : ""}${n} ms`;
|
|
268
|
+
}
|
|
269
|
+
function deltaPct(n) {
|
|
270
|
+
if (n === 0)
|
|
271
|
+
return "0";
|
|
272
|
+
return `${n > 0 ? "+" : ""}${n.toFixed(1)}%`;
|
|
273
|
+
}
|
|
274
|
+
function evidenceLine(ev) {
|
|
275
|
+
if (!ev || ev.status === "missing") {
|
|
276
|
+
return chalk.dim(`evidence: untracked (baseline carries no OpenPitStop signature)`);
|
|
277
|
+
}
|
|
278
|
+
if (ev.status === "verified") {
|
|
279
|
+
return chalk.green(`evidence: ✓ signed ${ev.digest.slice(0, 12)}…`);
|
|
280
|
+
}
|
|
281
|
+
return chalk.red(`evidence: ✗ TAMPERED — ${ev.reason ?? "digest mismatch"} ` +
|
|
282
|
+
`(expected ${ev.expected?.slice(0, 12)}…, got ${ev.digest.slice(0, 12)}…)`);
|
|
283
|
+
}
|
|
284
|
+
function renderVerifyBox(o) {
|
|
285
|
+
const d = o.deltas;
|
|
286
|
+
const baseline = o.baseline;
|
|
287
|
+
const current = o.current;
|
|
288
|
+
const baselineScore = o.baselineScore;
|
|
289
|
+
const currentScore = o.currentScore;
|
|
290
|
+
const table = new Table({
|
|
291
|
+
head: ["Metric", "Baseline", "Current", "Δ"],
|
|
292
|
+
style: { head: ["cyan"], border: [] },
|
|
293
|
+
});
|
|
294
|
+
const row = (name, baseStr, curStr, deltaNum, deltaStr, higherIsBetter) => {
|
|
295
|
+
const col = deltaNum === 0
|
|
296
|
+
? chalk.dim
|
|
297
|
+
: (higherIsBetter ? deltaNum > 0 : deltaNum < 0)
|
|
298
|
+
? chalk.green
|
|
299
|
+
: chalk.red;
|
|
300
|
+
table.push([chalk.bold(name), baseStr, curStr, col(deltaStr)]);
|
|
301
|
+
};
|
|
302
|
+
row("Tests passed", String(baseline.tests.passed), String(current.tests.passed), d.passed, signed(d.passed), true);
|
|
303
|
+
row("Tests failed", String(baseline.tests.failed), String(current.tests.failed), d.failed, signed(d.failed), false);
|
|
304
|
+
row("Duration", fmtMs(baseline.tests.durationMs), fmtMs(current.tests.durationMs), d.durationMs, deltaMs(d.durationMs), false);
|
|
305
|
+
row("Coverage", fmtPct(baseline.tests.coverage), fmtPct(current.tests.coverage), d.coverage, deltaPct(d.coverage), true);
|
|
306
|
+
row("Build time", fmtMs(baseline.perf.buildTimeMs), fmtMs(current.perf.buildTimeMs), d.buildTimeMs, deltaMs(d.buildTimeMs), false);
|
|
307
|
+
row("Bundle size", fmtBundle(baseline.perf.bundleSizeBytes), fmtBundle(current.perf.bundleSizeBytes), d.bundleSizeBytes, deltaBytes(d.bundleSizeBytes), false);
|
|
308
|
+
row("Security findings", String(baseline.securityCount), String(current.securityCount), d.security, signed(d.security), false);
|
|
309
|
+
row("Duplication clones", String(baseline.duplicationCount), String(current.duplicationCount), d.duplication, signed(d.duplication), false);
|
|
310
|
+
row("OpenPitStop score", `${baselineScore.score}/100 (${baselineScore.grade})`, `${currentScore.score}/100 (${currentScore.grade})`, o.scoreDelta, o.scoreDelta > 0 ? `+${o.scoreDelta} pts` : `${o.scoreDelta} pts`, true);
|
|
311
|
+
const riskColor = o.risk === "High" ? chalk.red : o.risk === "Medium" ? chalk.yellow : chalk.green;
|
|
312
|
+
let riskLine;
|
|
313
|
+
if (o.blocked) {
|
|
314
|
+
riskLine = `${chalk.bold("Regression risk:")} ${chalk.red("BLOCKED — integrity violation")} (${o.integrity.verdict})`;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
riskLine = `${chalk.bold("Regression risk:")} ${riskColor(o.risk.toUpperCase())}`;
|
|
318
|
+
}
|
|
319
|
+
const integrityParts = [];
|
|
320
|
+
if (o.blocked) {
|
|
321
|
+
integrityParts.push(chalk.red(`${chalk.bold("Integrity gate:")} ${o.integrity.verdict} — ` +
|
|
322
|
+
`${o.integrity.summary.confirmed} confirmed · ${o.integrity.summary.suspicious} suspicious`));
|
|
323
|
+
for (const f of o.integrity.findings) {
|
|
324
|
+
const tag = f.confidence === "confirmed" ? chalk.red("CONFIRMED") : chalk.yellow("SUSPICIOUS");
|
|
325
|
+
const loc = f.line ? `${f.file}:${f.line}` : f.file;
|
|
326
|
+
integrityParts.push(` ${tag} ${chalk.bold(f.detector)} / ${f.pattern} @ ${chalk.cyan(loc)}`, ` ${f.evidence}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const contentParts = [];
|
|
330
|
+
if (o.stale) {
|
|
331
|
+
contentParts.push(chalk.yellow(`⚠ ${o.staleNote}`));
|
|
332
|
+
}
|
|
333
|
+
contentParts.push(evidenceLine(o.evidence));
|
|
334
|
+
if (o.evidence && o.evidence.status === "tampered") {
|
|
335
|
+
contentParts.push(chalk.red(`${chalk.bold("Evidence gate:")} the baseline was edited after OpenPitStop signed it — ` +
|
|
336
|
+
"score delta is against an untrustworthy snapshot. Re-run `pitstop scan`."));
|
|
337
|
+
}
|
|
338
|
+
if (o.stale)
|
|
339
|
+
contentParts.push("");
|
|
340
|
+
contentParts.push(...integrityParts, riskLine);
|
|
341
|
+
contentParts.push("", table.toString());
|
|
342
|
+
const content = contentParts.join("\n");
|
|
343
|
+
const color = o.blocked ? "red" : o.risk === "High" ? "red" : o.risk === "Medium" ? "yellow" : "green";
|
|
344
|
+
return {
|
|
345
|
+
text: content,
|
|
346
|
+
color,
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
export const verify = new Command("verify")
|
|
350
|
+
.description("Re-run tests/perf, diff against the last scan baseline, AND gate on the integrity diff since HEAD — " +
|
|
351
|
+
"SUSPICIOUS/CONFIRMED_CHEAT verifies exit 1/2 with 'BLOCKED — integrity violation'.")
|
|
352
|
+
.argument("[repo]", "path to the repo to verify", ".")
|
|
353
|
+
.action(async (repoArg) => {
|
|
354
|
+
const repo = path.resolve(repoArg);
|
|
355
|
+
console.log(chalk.cyan(`\nVerifying ${repo} ...\n`));
|
|
356
|
+
const outcome = await runVerify(repo);
|
|
357
|
+
if (outcome.missingBaseline) {
|
|
358
|
+
console.log(chalk.red("no baseline found — run `pitstop scan` first to create .pitstop/scan-latest.json"));
|
|
359
|
+
process.exitCode = 1;
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const { text, color } = renderVerifyBox(outcome);
|
|
363
|
+
console.log(boxen(text, {
|
|
364
|
+
title: " PITSTOP — Verify ",
|
|
365
|
+
titleAlignment: "center",
|
|
366
|
+
borderStyle: "double",
|
|
367
|
+
padding: 1,
|
|
368
|
+
borderColor: color,
|
|
369
|
+
}));
|
|
370
|
+
console.log(chalk.dim(`\nReport written to ${outcome.file}\n`));
|
|
371
|
+
const summary = outcome.blocked
|
|
372
|
+
? `verify BLOCKED (integrity ${outcome.integrity.verdict})`
|
|
373
|
+
: `verify ${outcome.risk}: score ${outcome.currentScore.score}/100 (${outcome.currentScore.grade}) · tests ${signed(outcome.deltas.failed)} fail, bundle ${deltaBytes(outcome.deltas.bundleSizeBytes)}, security ${signed(outcome.deltas.security)}`;
|
|
374
|
+
addEntry(repo, {
|
|
375
|
+
type: "fix",
|
|
376
|
+
summary,
|
|
377
|
+
context: `verify against baseline ${outcome.baselineTimestamp} — integrity ${outcome.integrity.verdict}`,
|
|
378
|
+
});
|
|
379
|
+
process.exitCode = outcome.exitCode;
|
|
380
|
+
});
|
|
381
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/commands/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* `pitstop watch` — the live shield.
|
|
4
|
+
*
|
|
5
|
+
* Polls the working tree; the moment a file changes it runs the fast static
|
|
6
|
+
* pass and prints the score delta + any new security issues. A dev server's
|
|
7
|
+
* worth of feedback with a `try`-scan's worth of cost. Ctrl+C stops it.
|
|
8
|
+
*/
|
|
9
|
+
export declare const watch: Command;
|
|
10
|
+
export default watch;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { performance } from "node:perf_hooks";
|
|
5
|
+
import { newestModifiedFile } from "../analyzers/util.js";
|
|
6
|
+
import { tryScan } from "./try.js";
|
|
7
|
+
import { persistScan } from "./scan.js";
|
|
8
|
+
import { stampFindings } from "../repro/ids.js";
|
|
9
|
+
import { correlate } from "../graph/correlate.js";
|
|
10
|
+
import { computeScore } from "../report/score.js";
|
|
11
|
+
/**
|
|
12
|
+
* `pitstop watch` — the live shield.
|
|
13
|
+
*
|
|
14
|
+
* Polls the working tree; the moment a file changes it runs the fast static
|
|
15
|
+
* pass and prints the score delta + any new security issues. A dev server's
|
|
16
|
+
* worth of feedback with a `try`-scan's worth of cost. Ctrl+C stops it.
|
|
17
|
+
*/
|
|
18
|
+
export const watch = new Command("watch")
|
|
19
|
+
.description("Live shield: poll the repo and re-run the fast static pass whenever a file changes, " +
|
|
20
|
+
"printing the score delta and any new security issues. Ctrl+C stops.")
|
|
21
|
+
.argument("[repo]", "path to the repo to watch (default: current dir)", ".")
|
|
22
|
+
.option("--interval <ms>", "poll interval in ms (default 10000)", "10000")
|
|
23
|
+
.action(async (repoArg, options) => {
|
|
24
|
+
const repo = path.resolve(repoArg);
|
|
25
|
+
const interval = Math.max(2000, Math.floor(Number(options.interval) || 10000));
|
|
26
|
+
let lastMtime = newestModifiedFile(repo)?.mtimeMs ?? 0;
|
|
27
|
+
let lastScore = null;
|
|
28
|
+
let lastSecrets = 0;
|
|
29
|
+
console.log(chalk.cyan(`\nWatching ${repo} (every ${interval}ms — Ctrl+C to stop)\n`));
|
|
30
|
+
const tick = async () => {
|
|
31
|
+
const now = newestModifiedFile(repo);
|
|
32
|
+
if (!now || now.mtimeMs === lastMtime)
|
|
33
|
+
return;
|
|
34
|
+
lastMtime = now.mtimeMs;
|
|
35
|
+
const changed = path.relative(repo, now.file).replace(/\\/g, "/");
|
|
36
|
+
const t0 = performance.now();
|
|
37
|
+
let line;
|
|
38
|
+
try {
|
|
39
|
+
const result = await tryScan(repo);
|
|
40
|
+
stampFindings(result);
|
|
41
|
+
result.clusters = correlate(repo, result).clusters;
|
|
42
|
+
persistScan(repo, result);
|
|
43
|
+
const sc = computeScore(result);
|
|
44
|
+
const secrets = result.security.status === "ok" ? result.security.issues.length : 0;
|
|
45
|
+
const newSecrets = secrets > lastSecrets ? ` · ${chalk.red(`+${secrets - lastSecrets} security`)}` : "";
|
|
46
|
+
const delta = lastScore != null
|
|
47
|
+
? sc.score - lastScore >= 0
|
|
48
|
+
? chalk.green(`(+${sc.score - lastScore})`)
|
|
49
|
+
: chalk.red(`(${sc.score - lastScore})`)
|
|
50
|
+
: "";
|
|
51
|
+
line = `${new Date().toISOString().slice(11, 19)} score ${sc.score}/100 (${sc.grade})${delta} · security ${secrets}${newSecrets} · changed ${changed} · ${Math.round(performance.now() - t0)}ms`;
|
|
52
|
+
lastScore = sc.score;
|
|
53
|
+
lastSecrets = secrets;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
line = `${new Date().toISOString().slice(11, 19)} scan failed on ${changed}: ${err.message}`;
|
|
57
|
+
}
|
|
58
|
+
console.log(line);
|
|
59
|
+
};
|
|
60
|
+
await new Promise((resolve) => {
|
|
61
|
+
const timer = setInterval(() => void tick(), interval);
|
|
62
|
+
process.on("SIGINT", () => {
|
|
63
|
+
clearInterval(timer);
|
|
64
|
+
console.log(chalk.dim("\nwatch stopped — baseline refreshed with every change."));
|
|
65
|
+
resolve();
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
export default watch;
|
|
70
|
+
//# sourceMappingURL=watch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/commands/watch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KACtC,WAAW,CACV,sFAAsF;IACpF,qEAAqE,CACxE;KACA,QAAQ,CAAC,QAAQ,EAAE,kDAAkD,EAAE,GAAG,CAAC;KAC3E,MAAM,CAAC,iBAAiB,EAAE,qCAAqC,EAAE,OAAO,CAAC;KACzE,MAAM,CAAC,KAAK,EAAE,OAAe,EAAE,OAA8B,EAAE,EAAE;IAChE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC;IAE/E,IAAI,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC;IACvD,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,WAAW,QAAQ,wBAAwB,CAAC,CAC1E,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;QACtB,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO;QAC9C,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAElE,MAAM,EAAE,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAC7B,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;YACnC,aAAa,CAAC,MAAM,CAAC,CAAC;YACtB,MAAM,CAAC,QAAQ,GAAG,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC;YACnD,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAE1B,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACpF,MAAM,UAAU,GAAG,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,WAAW,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxG,MAAM,KAAK,GACT,SAAS,IAAI,IAAI;gBACf,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,SAAS,IAAI,CAAC;oBACzB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,SAAS,GAAG,CAAC;oBAC3C,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,GAAG,SAAS,GAAG,CAAC;gBAC1C,CAAC,CAAC,EAAE,CAAC;YACT,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,KAAK,SAAS,EAAE,CAAC,KAAK,IAAI,KAAK,eAAe,OAAO,GAAG,UAAU,cAAc,OAAO,MAAM,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC;YACjM,SAAS,GAAG,EAAE,CAAC,KAAK,CAAC;YACrB,WAAW,GAAG,OAAO,CAAC;QACxB,CAAC;QAAC,OAAO,GAAQ,EAAE,CAAC;YAClB,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,mBAAmB,OAAO,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC;QAC1G,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC;IAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClC,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,KAAK,IAAI,EAAE,EAAE,QAAQ,CAAC,CAAC;QACvD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACxB,aAAa,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC,CAAC;YAClF,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEL,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* evidence.ts — OpenPitStop's tamper-evident evidence chain.
|
|
3
|
+
*
|
|
4
|
+
* Every scan/cluster/verify document OpenPitStop writes carries a deterministic
|
|
5
|
+
* cryptographic digest of its own content. If anyone edits the JSON after the
|
|
6
|
+
* fact — a score inflated, a finding deleted — the digest stops matching and
|
|
7
|
+
* OpenPitStop reports the chain as broken. No key material is involved: this is
|
|
8
|
+
* not "who signed this" but "not a byte changed since OpenPitStop wrote it",
|
|
9
|
+
* which is the property that makes agent report honesty checkable.
|
|
10
|
+
*/
|
|
11
|
+
export interface OpenPitStopEvidence {
|
|
12
|
+
scheme: "pitstop-canonical-sha256-v1";
|
|
13
|
+
/** sha256 hex over the canonical JSON of the document (evidence excluded). */
|
|
14
|
+
digest: string;
|
|
15
|
+
/** Human description of what the digest covers. */
|
|
16
|
+
of: string;
|
|
17
|
+
signedAt: string;
|
|
18
|
+
}
|
|
19
|
+
/** Canonical JSON: keys sorted recursively, no whitespace, floats verbatim. */
|
|
20
|
+
export declare function canonicalize(value: unknown): string;
|
|
21
|
+
export declare function digestOf(value: unknown): string;
|
|
22
|
+
/** Attach a signed evidence block to a document (returns the document itself). */
|
|
23
|
+
export declare function seal<T extends object>(doc: T, of: string): T & {
|
|
24
|
+
evidence: OpenPitStopEvidence;
|
|
25
|
+
};
|
|
26
|
+
export type EvidenceStatus = "verified" | "tampered" | "missing";
|
|
27
|
+
export interface EvidenceCheck {
|
|
28
|
+
status: EvidenceStatus;
|
|
29
|
+
digest: string;
|
|
30
|
+
expected?: string;
|
|
31
|
+
reason?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Recompute the digest over a sealed document (excluding its own evidence
|
|
35
|
+
* field) and compare against the claimed signature.
|
|
36
|
+
*/
|
|
37
|
+
export declare function checkEvidence(value: unknown): EvidenceCheck;
|
package/dist/evidence.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/** The evidence field itself is never part of the digest it protects. */
|
|
3
|
+
const EXCLUDED_KEYS = new Set(["evidence"]);
|
|
4
|
+
/** Canonical JSON: keys sorted recursively, no whitespace, floats verbatim. */
|
|
5
|
+
export function canonicalize(value) {
|
|
6
|
+
if (value === null || typeof value !== "object")
|
|
7
|
+
return JSON.stringify(value);
|
|
8
|
+
if (Array.isArray(value))
|
|
9
|
+
return "[" + value.map((v) => canonicalize(v)).join(",") + "]";
|
|
10
|
+
const obj = value;
|
|
11
|
+
const parts = [];
|
|
12
|
+
for (const key of Object.keys(obj).sort()) {
|
|
13
|
+
if (EXCLUDED_KEYS.has(key))
|
|
14
|
+
continue;
|
|
15
|
+
if (obj[key] === undefined)
|
|
16
|
+
continue; // JSON.stringify drops these on write
|
|
17
|
+
parts.push(JSON.stringify(key) + ":" + canonicalize(obj[key]));
|
|
18
|
+
}
|
|
19
|
+
return "{" + parts.join(",") + "}";
|
|
20
|
+
}
|
|
21
|
+
export function digestOf(value) {
|
|
22
|
+
return createHash("sha256").update(canonicalize(value), "utf8").digest("hex");
|
|
23
|
+
}
|
|
24
|
+
/** Attach a signed evidence block to a document (returns the document itself). */
|
|
25
|
+
export function seal(doc, of) {
|
|
26
|
+
const evidence = {
|
|
27
|
+
scheme: "pitstop-canonical-sha256-v1",
|
|
28
|
+
digest: digestOf(doc),
|
|
29
|
+
of,
|
|
30
|
+
signedAt: new Date().toISOString(),
|
|
31
|
+
};
|
|
32
|
+
return { ...doc, evidence };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Recompute the digest over a sealed document (excluding its own evidence
|
|
36
|
+
* field) and compare against the claimed signature.
|
|
37
|
+
*/
|
|
38
|
+
export function checkEvidence(value) {
|
|
39
|
+
const ev = value?.evidence;
|
|
40
|
+
const digest = digestOf(value);
|
|
41
|
+
if (!ev || !ev.digest) {
|
|
42
|
+
return {
|
|
43
|
+
status: "missing",
|
|
44
|
+
digest,
|
|
45
|
+
reason: "document carries no OpenPitStop evidence block — written by a non-OpenPitStop tool or pre-0.6.0",
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (ev.scheme !== "pitstop-canonical-sha256-v1") {
|
|
49
|
+
return { status: "tampered", digest, expected: ev.digest, reason: `unrecognized scheme "${ev.scheme}"` };
|
|
50
|
+
}
|
|
51
|
+
if (digest !== ev.digest) {
|
|
52
|
+
return {
|
|
53
|
+
status: "tampered",
|
|
54
|
+
digest,
|
|
55
|
+
expected: ev.digest,
|
|
56
|
+
reason: "digest does not match document content — evidence was edited after OpenPitStop wrote it",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { status: "verified", digest };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../src/evidence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAsBzC,yEAAyE;AACzE,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;AAE5C,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACzF,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QACrC,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,SAAS,CAAC,sCAAsC;QAC5E,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,IAAI,CAAmB,GAAM,EAAE,EAAU;IACvD,MAAM,QAAQ,GAAwB;QACpC,MAAM,EAAE,6BAA6B;QACrC,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC;QACrB,EAAE;QACF,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC;IACF,OAAO,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,EAAE,GAAI,KAA4C,EAAE,QAAQ,CAAC;IACnE,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC;QACtB,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,MAAM;YACN,MAAM,EAAE,iGAAiG;SAC1G,CAAC;IACJ,CAAC;IACD,IAAI,EAAE,CAAC,MAAM,KAAK,6BAA6B,EAAE,CAAC;QAChD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC;IAC3G,CAAC;IACD,IAAI,MAAM,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO;YACL,MAAM,EAAE,UAAU;YAClB,MAAM;YACN,QAAQ,EAAE,EAAE,CAAC,MAAM;YACnB,MAAM,EAAE,yFAAyF;SAClG,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ScanResult, Cluster, ClusterFinding } from "../analyzers/types.js";
|
|
2
|
+
/** Flatten Phase-1 results into a list of file-based findings. */
|
|
3
|
+
export declare function collectFindings(repo: string, r: ScanResult): ClusterFinding[];
|
|
4
|
+
export declare function correlate(repo: string, r: ScanResult): {
|
|
5
|
+
clusters: Cluster[];
|
|
6
|
+
findings: ClusterFinding[];
|
|
7
|
+
};
|