@vibecheckai/cli 3.2.2 → 3.2.4
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/bin/.generated +25 -25
- package/bin/dev/run-v2-torture.js +30 -30
- package/bin/runners/ENHANCEMENT_GUIDE.md +121 -121
- package/bin/runners/lib/__tests__/entitlements-v2.test.js +295 -295
- package/bin/runners/lib/agent-firewall/ai/false-positive-analyzer.js +474 -0
- package/bin/runners/lib/agent-firewall/claims/extractor.js +117 -28
- package/bin/runners/lib/agent-firewall/evidence/env-evidence.js +23 -14
- package/bin/runners/lib/agent-firewall/evidence/route-evidence.js +72 -1
- package/bin/runners/lib/agent-firewall/interceptor/base.js +2 -2
- package/bin/runners/lib/agent-firewall/policy/default-policy.json +6 -0
- package/bin/runners/lib/agent-firewall/policy/engine.js +34 -3
- package/bin/runners/lib/agent-firewall/policy/rules/fake-success.js +29 -4
- package/bin/runners/lib/agent-firewall/policy/rules/ghost-route.js +12 -0
- package/bin/runners/lib/agent-firewall/truthpack/loader.js +21 -0
- package/bin/runners/lib/agent-firewall/utils/ignore-checker.js +118 -0
- package/bin/runners/lib/analyzers.js +606 -325
- package/bin/runners/lib/auth-truth.js +193 -193
- package/bin/runners/lib/backup.js +62 -62
- package/bin/runners/lib/billing.js +107 -107
- package/bin/runners/lib/claims.js +118 -118
- package/bin/runners/lib/cli-ui.js +540 -540
- package/bin/runners/lib/contracts/auth-contract.js +202 -202
- package/bin/runners/lib/contracts/env-contract.js +181 -181
- package/bin/runners/lib/contracts/external-contract.js +206 -206
- package/bin/runners/lib/contracts/guard.js +168 -168
- package/bin/runners/lib/contracts/index.js +89 -89
- package/bin/runners/lib/contracts/plan-validator.js +311 -311
- package/bin/runners/lib/contracts/route-contract.js +199 -199
- package/bin/runners/lib/contracts.js +804 -804
- package/bin/runners/lib/detect.js +89 -89
- package/bin/runners/lib/doctor/autofix.js +254 -254
- package/bin/runners/lib/doctor/index.js +37 -37
- package/bin/runners/lib/doctor/modules/dependencies.js +325 -325
- package/bin/runners/lib/doctor/modules/index.js +46 -46
- package/bin/runners/lib/doctor/modules/network.js +250 -250
- package/bin/runners/lib/doctor/modules/project.js +312 -312
- package/bin/runners/lib/doctor/modules/runtime.js +224 -224
- package/bin/runners/lib/doctor/modules/security.js +348 -348
- package/bin/runners/lib/doctor/modules/system.js +213 -213
- package/bin/runners/lib/doctor/modules/vibecheck.js +394 -394
- package/bin/runners/lib/doctor/reporter.js +262 -262
- package/bin/runners/lib/doctor/service.js +262 -262
- package/bin/runners/lib/doctor/types.js +113 -113
- package/bin/runners/lib/doctor/ui.js +263 -263
- package/bin/runners/lib/doctor-v2.js +608 -608
- package/bin/runners/lib/drift.js +425 -425
- package/bin/runners/lib/enforcement.js +72 -72
- package/bin/runners/lib/engines/accessibility-engine.js +190 -0
- package/bin/runners/lib/engines/api-consistency-engine.js +162 -0
- package/bin/runners/lib/engines/ast-cache.js +99 -0
- package/bin/runners/lib/engines/code-quality-engine.js +255 -0
- package/bin/runners/lib/engines/console-logs-engine.js +115 -0
- package/bin/runners/lib/engines/cross-file-analysis-engine.js +268 -0
- package/bin/runners/lib/engines/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/deprecated-api-engine.js +226 -0
- package/bin/runners/lib/engines/empty-catch-engine.js +150 -0
- package/bin/runners/lib/engines/file-filter.js +131 -0
- package/bin/runners/lib/engines/hardcoded-secrets-engine.js +251 -0
- package/bin/runners/lib/engines/mock-data-engine.js +272 -0
- package/bin/runners/lib/engines/parallel-processor.js +71 -0
- package/bin/runners/lib/engines/performance-issues-engine.js +265 -0
- package/bin/runners/lib/engines/security-vulnerabilities-engine.js +243 -0
- package/bin/runners/lib/engines/todo-fixme-engine.js +115 -0
- package/bin/runners/lib/engines/type-aware-engine.js +152 -0
- package/bin/runners/lib/engines/unsafe-regex-engine.js +225 -0
- package/bin/runners/lib/engines/vibecheck-engines/README.md +53 -0
- package/bin/runners/lib/engines/vibecheck-engines/index.js +15 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/ast-cache.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/code-quality-engine.js +291 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/console-logs-engine.js +83 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/dead-code-engine.js +198 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/deprecated-api-engine.js +275 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/empty-catch-engine.js +167 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/file-filter.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/hardcoded-secrets-engine.js +139 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/mock-data-engine.js +140 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/parallel-processor.js +164 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/performance-issues-engine.js +234 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/type-aware-engine.js +217 -0
- package/bin/runners/lib/engines/vibecheck-engines/lib/unsafe-regex-engine.js +78 -0
- package/bin/runners/lib/engines/vibecheck-engines/package.json +13 -0
- package/bin/runners/lib/enterprise-detect.js +603 -603
- package/bin/runners/lib/enterprise-init.js +942 -942
- package/bin/runners/lib/env-resolver.js +417 -417
- package/bin/runners/lib/env-template.js +66 -66
- package/bin/runners/lib/env.js +189 -189
- package/bin/runners/lib/extractors/client-calls.js +990 -990
- package/bin/runners/lib/extractors/fastify-route-dump.js +573 -573
- package/bin/runners/lib/extractors/fastify-routes.js +426 -426
- package/bin/runners/lib/extractors/index.js +363 -363
- package/bin/runners/lib/extractors/next-routes.js +524 -524
- package/bin/runners/lib/extractors/proof-graph.js +431 -431
- package/bin/runners/lib/extractors/route-matcher.js +451 -451
- package/bin/runners/lib/extractors/truthpack-v2.js +377 -377
- package/bin/runners/lib/extractors/ui-bindings.js +547 -547
- package/bin/runners/lib/findings-schema.js +281 -281
- package/bin/runners/lib/firewall-prompt.js +50 -50
- package/bin/runners/lib/global-flags.js +213 -213
- package/bin/runners/lib/graph/graph-builder.js +265 -265
- package/bin/runners/lib/graph/html-renderer.js +413 -413
- package/bin/runners/lib/graph/index.js +32 -32
- package/bin/runners/lib/graph/runtime-collector.js +215 -215
- package/bin/runners/lib/graph/static-extractor.js +518 -518
- package/bin/runners/lib/html-report.js +650 -650
- package/bin/runners/lib/interactive-menu.js +1496 -1496
- package/bin/runners/lib/llm.js +75 -75
- package/bin/runners/lib/meter.js +61 -61
- package/bin/runners/lib/missions/evidence.js +126 -126
- package/bin/runners/lib/patch.js +40 -40
- package/bin/runners/lib/permissions/auth-model.js +213 -213
- package/bin/runners/lib/permissions/idor-prover.js +205 -205
- package/bin/runners/lib/permissions/index.js +45 -45
- package/bin/runners/lib/permissions/matrix-builder.js +198 -198
- package/bin/runners/lib/pkgjson.js +28 -28
- package/bin/runners/lib/policy.js +295 -295
- package/bin/runners/lib/preflight.js +142 -142
- package/bin/runners/lib/reality/correlation-detectors.js +359 -359
- package/bin/runners/lib/reality/index.js +318 -318
- package/bin/runners/lib/reality/request-hashing.js +416 -416
- package/bin/runners/lib/reality/request-mapper.js +453 -453
- package/bin/runners/lib/reality/safety-rails.js +463 -463
- package/bin/runners/lib/reality/semantic-snapshot.js +408 -408
- package/bin/runners/lib/reality/toast-detector.js +393 -393
- package/bin/runners/lib/reality-findings.js +84 -84
- package/bin/runners/lib/receipts.js +179 -179
- package/bin/runners/lib/redact.js +29 -29
- package/bin/runners/lib/replay/capsule-manager.js +154 -154
- package/bin/runners/lib/replay/index.js +263 -263
- package/bin/runners/lib/replay/player.js +348 -348
- package/bin/runners/lib/replay/recorder.js +331 -331
- package/bin/runners/lib/report-output.js +187 -187
- package/bin/runners/lib/report.js +135 -135
- package/bin/runners/lib/route-detection.js +1140 -1140
- package/bin/runners/lib/sandbox/index.js +59 -59
- package/bin/runners/lib/sandbox/proof-chain.js +399 -399
- package/bin/runners/lib/sandbox/sandbox-runner.js +205 -205
- package/bin/runners/lib/sandbox/worktree.js +174 -174
- package/bin/runners/lib/scan-output.js +525 -190
- package/bin/runners/lib/schema-validator.js +350 -350
- package/bin/runners/lib/schemas/contracts.schema.json +160 -160
- package/bin/runners/lib/schemas/finding.schema.json +100 -100
- package/bin/runners/lib/schemas/mission-pack.schema.json +206 -206
- package/bin/runners/lib/schemas/proof-graph.schema.json +176 -176
- package/bin/runners/lib/schemas/reality-report.schema.json +162 -162
- package/bin/runners/lib/schemas/share-pack.schema.json +180 -180
- package/bin/runners/lib/schemas/ship-report.schema.json +117 -117
- package/bin/runners/lib/schemas/truthpack-v2.schema.json +303 -303
- package/bin/runners/lib/schemas/validator.js +438 -438
- package/bin/runners/lib/score-history.js +282 -282
- package/bin/runners/lib/share-pack.js +239 -239
- package/bin/runners/lib/snippets.js +67 -67
- package/bin/runners/lib/status-output.js +253 -253
- package/bin/runners/lib/terminal-ui.js +351 -271
- package/bin/runners/lib/upsell.js +510 -510
- package/bin/runners/lib/usage.js +153 -153
- package/bin/runners/lib/validate-patch.js +156 -156
- package/bin/runners/lib/verdict-engine.js +628 -628
- package/bin/runners/reality/engine.js +917 -917
- package/bin/runners/reality/flows.js +122 -122
- package/bin/runners/reality/report.js +378 -378
- package/bin/runners/reality/session.js +193 -193
- package/bin/runners/runGuard.js +168 -168
- package/bin/runners/runProof.zip +0 -0
- package/bin/runners/runProve.js +8 -0
- package/bin/runners/runReality.js +14 -0
- package/bin/runners/runScan.js +17 -1
- package/bin/runners/runTruth.js +15 -3
- package/mcp-server/tier-auth.js +4 -4
- package/mcp-server/tools/index.js +72 -72
- package/package.json +1 -1
|
@@ -1,438 +1,438 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schema Validator v2
|
|
3
|
-
*
|
|
4
|
-
* Validates artifacts against JSON schemas and provides
|
|
5
|
-
* consistent finding/evidence generation.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
"use strict";
|
|
9
|
-
|
|
10
|
-
const fs = require("fs");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
const crypto = require("crypto");
|
|
13
|
-
|
|
14
|
-
// Schema cache
|
|
15
|
-
const schemaCache = new Map();
|
|
16
|
-
|
|
17
|
-
// Schema IDs
|
|
18
|
-
const SCHEMAS = {
|
|
19
|
-
FINDING: "finding.schema.json",
|
|
20
|
-
SHIP_REPORT: "ship-report.schema.json",
|
|
21
|
-
REALITY_REPORT: "reality-report.schema.json",
|
|
22
|
-
TRUTHPACK_V2: "truthpack-v2.schema.json",
|
|
23
|
-
CONTRACTS: "contracts.schema.json",
|
|
24
|
-
PROOF_GRAPH: "proof-graph.schema.json",
|
|
25
|
-
MISSION_PACK: "mission-pack.schema.json",
|
|
26
|
-
SHARE_PACK: "share-pack.schema.json",
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// =============================================================================
|
|
30
|
-
// SCHEMA LOADING
|
|
31
|
-
// =============================================================================
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Load a schema by ID
|
|
35
|
-
*/
|
|
36
|
-
function loadSchema(schemaId) {
|
|
37
|
-
if (schemaCache.has(schemaId)) {
|
|
38
|
-
return schemaCache.get(schemaId);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const schemaPath = path.join(__dirname, schemaId);
|
|
42
|
-
if (!fs.existsSync(schemaPath)) {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
|
48
|
-
schemaCache.set(schemaId, schema);
|
|
49
|
-
return schema;
|
|
50
|
-
} catch (e) {
|
|
51
|
-
console.error(`Failed to load schema ${schemaId}:`, e.message);
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Simple JSON Schema validator (subset of draft 2020-12)
|
|
58
|
-
* For production, use Ajv - this is a minimal implementation
|
|
59
|
-
*/
|
|
60
|
-
function validateAgainstSchema(data, schema, path = "") {
|
|
61
|
-
const errors = [];
|
|
62
|
-
|
|
63
|
-
if (!schema || !data) {
|
|
64
|
-
return errors;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Type check
|
|
68
|
-
if (schema.type) {
|
|
69
|
-
let actualType = Array.isArray(data) ? "array" : typeof data;
|
|
70
|
-
|
|
71
|
-
// Handle integer type - JavaScript has no integer type, only number
|
|
72
|
-
// Treat whole numbers as integers for schema validation
|
|
73
|
-
if (actualType === "number" && Number.isInteger(data)) {
|
|
74
|
-
actualType = "integer";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const allowedTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
78
|
-
|
|
79
|
-
// Allow integer where number is expected, and vice versa for whole numbers
|
|
80
|
-
const typeMatches = allowedTypes.some(t => {
|
|
81
|
-
if (t === actualType) return true;
|
|
82
|
-
if (t === "integer" && actualType === "number" && Number.isInteger(data)) return true;
|
|
83
|
-
if (t === "number" && actualType === "integer") return true;
|
|
84
|
-
if (t === "null" && data === null) return true;
|
|
85
|
-
return false;
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
if (!typeMatches) {
|
|
89
|
-
errors.push({
|
|
90
|
-
path,
|
|
91
|
-
message: `Expected ${allowedTypes.join("|")}, got ${actualType}`,
|
|
92
|
-
keyword: "type",
|
|
93
|
-
});
|
|
94
|
-
return errors; // Stop on type mismatch
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Const check
|
|
99
|
-
if (schema.const !== undefined && data !== schema.const) {
|
|
100
|
-
errors.push({
|
|
101
|
-
path,
|
|
102
|
-
message: `Expected constant "${schema.const}", got "${data}"`,
|
|
103
|
-
keyword: "const",
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Enum check
|
|
108
|
-
if (schema.enum && !schema.enum.includes(data)) {
|
|
109
|
-
errors.push({
|
|
110
|
-
path,
|
|
111
|
-
message: `Value must be one of: ${schema.enum.join(", ")}`,
|
|
112
|
-
keyword: "enum",
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Pattern check
|
|
117
|
-
if (schema.pattern && typeof data === "string") {
|
|
118
|
-
const regex = new RegExp(schema.pattern);
|
|
119
|
-
if (!regex.test(data)) {
|
|
120
|
-
errors.push({
|
|
121
|
-
path,
|
|
122
|
-
message: `String must match pattern: ${schema.pattern}`,
|
|
123
|
-
keyword: "pattern",
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Required properties
|
|
129
|
-
if (schema.required && typeof data === "object" && !Array.isArray(data)) {
|
|
130
|
-
for (const prop of schema.required) {
|
|
131
|
-
if (!(prop in data)) {
|
|
132
|
-
errors.push({
|
|
133
|
-
path: path ? `${path}.${prop}` : prop,
|
|
134
|
-
message: `Required property "${prop}" is missing`,
|
|
135
|
-
keyword: "required",
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Object properties
|
|
142
|
-
if (schema.properties && typeof data === "object" && !Array.isArray(data)) {
|
|
143
|
-
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
144
|
-
if (key in data) {
|
|
145
|
-
const propPath = path ? `${path}.${key}` : key;
|
|
146
|
-
errors.push(...validateAgainstSchema(data[key], propSchema, propPath));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Array items
|
|
152
|
-
if (schema.items && Array.isArray(data)) {
|
|
153
|
-
for (let i = 0; i < data.length; i++) {
|
|
154
|
-
const itemPath = `${path}[${i}]`;
|
|
155
|
-
errors.push(...validateAgainstSchema(data[i], schema.items, itemPath));
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Min/max length
|
|
160
|
-
if (schema.maxLength && typeof data === "string" && data.length > schema.maxLength) {
|
|
161
|
-
errors.push({
|
|
162
|
-
path,
|
|
163
|
-
message: `String length must be <= ${schema.maxLength}`,
|
|
164
|
-
keyword: "maxLength",
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Min/max for numbers
|
|
169
|
-
if (schema.minimum !== undefined && typeof data === "number" && data < schema.minimum) {
|
|
170
|
-
errors.push({
|
|
171
|
-
path,
|
|
172
|
-
message: `Value must be >= ${schema.minimum}`,
|
|
173
|
-
keyword: "minimum",
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
if (schema.maximum !== undefined && typeof data === "number" && data > schema.maximum) {
|
|
177
|
-
errors.push({
|
|
178
|
-
path,
|
|
179
|
-
message: `Value must be <= ${schema.maximum}`,
|
|
180
|
-
keyword: "maximum",
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return errors;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// =============================================================================
|
|
188
|
-
// ARTIFACT VALIDATION
|
|
189
|
-
// =============================================================================
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Validate an artifact file against its schema
|
|
193
|
-
*/
|
|
194
|
-
function validateArtifact(filePath, schemaId) {
|
|
195
|
-
const schema = loadSchema(schemaId);
|
|
196
|
-
if (!schema) {
|
|
197
|
-
return {
|
|
198
|
-
valid: false,
|
|
199
|
-
errors: [{ path: "", message: `Schema ${schemaId} not found`, keyword: "schema" }],
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
let data;
|
|
204
|
-
try {
|
|
205
|
-
data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
206
|
-
} catch (e) {
|
|
207
|
-
return {
|
|
208
|
-
valid: false,
|
|
209
|
-
errors: [{ path: "", message: `Failed to read/parse file: ${e.message}`, keyword: "parse" }],
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const errors = validateAgainstSchema(data, schema);
|
|
214
|
-
return {
|
|
215
|
-
valid: errors.length === 0,
|
|
216
|
-
errors,
|
|
217
|
-
data,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Validate and write artifact with schema check
|
|
223
|
-
*/
|
|
224
|
-
function writeValidatedArtifact(filePath, data, schemaId) {
|
|
225
|
-
const schema = loadSchema(schemaId);
|
|
226
|
-
|
|
227
|
-
if (schema) {
|
|
228
|
-
const errors = validateAgainstSchema(data, schema);
|
|
229
|
-
if (errors.length > 0) {
|
|
230
|
-
console.warn(`Warning: Artifact ${filePath} has ${errors.length} schema violations:`);
|
|
231
|
-
errors.slice(0, 5).forEach(e => console.warn(` - ${e.path}: ${e.message}`));
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
236
|
-
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
237
|
-
|
|
238
|
-
return { written: true, path: filePath };
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// =============================================================================
|
|
242
|
-
// FINDING GENERATION
|
|
243
|
-
// =============================================================================
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Generate stable fingerprint for deduplication
|
|
247
|
-
*/
|
|
248
|
-
function generateFingerprint(parts) {
|
|
249
|
-
const content = parts.filter(Boolean).join("|");
|
|
250
|
-
return crypto.createHash("sha256").update(content).digest("hex");
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Generate finding ID
|
|
255
|
-
*/
|
|
256
|
-
function generateFindingId(detectorId, fingerprint) {
|
|
257
|
-
return `F_${detectorId}_${fingerprint.slice(0, 8).toUpperCase()}`;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Generate evidence ID
|
|
262
|
-
*/
|
|
263
|
-
function generateEvidenceId(fingerprint) {
|
|
264
|
-
return `E_${fingerprint.slice(0, 12).toUpperCase()}`;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Create a spec-compliant finding
|
|
269
|
-
*/
|
|
270
|
-
function createFinding(options = {}) {
|
|
271
|
-
const {
|
|
272
|
-
detectorId,
|
|
273
|
-
severity = "WARN",
|
|
274
|
-
category = "Routes",
|
|
275
|
-
scope = "client",
|
|
276
|
-
title,
|
|
277
|
-
why,
|
|
278
|
-
confidence = "medium",
|
|
279
|
-
evidence = [],
|
|
280
|
-
repro = null,
|
|
281
|
-
related = [],
|
|
282
|
-
proofNode = null,
|
|
283
|
-
missionType = null,
|
|
284
|
-
// For fingerprint generation
|
|
285
|
-
file = null,
|
|
286
|
-
path: routePath = null,
|
|
287
|
-
method = null,
|
|
288
|
-
} = options;
|
|
289
|
-
|
|
290
|
-
const fingerprint = generateFingerprint([
|
|
291
|
-
detectorId,
|
|
292
|
-
file,
|
|
293
|
-
routePath,
|
|
294
|
-
method,
|
|
295
|
-
title,
|
|
296
|
-
]);
|
|
297
|
-
|
|
298
|
-
return {
|
|
299
|
-
id: generateFindingId(detectorId, fingerprint),
|
|
300
|
-
detectorId,
|
|
301
|
-
fingerprint: `sha256:${fingerprint}`,
|
|
302
|
-
severity,
|
|
303
|
-
category,
|
|
304
|
-
scope,
|
|
305
|
-
title,
|
|
306
|
-
why,
|
|
307
|
-
confidence,
|
|
308
|
-
evidence: evidence.map((e, i) => ({
|
|
309
|
-
id: e.id || generateEvidenceId(fingerprint + i),
|
|
310
|
-
kind: e.kind || "file",
|
|
311
|
-
...e,
|
|
312
|
-
})),
|
|
313
|
-
repro,
|
|
314
|
-
related,
|
|
315
|
-
proofNode,
|
|
316
|
-
missionType,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Create file evidence
|
|
322
|
-
*/
|
|
323
|
-
function createFileEvidence(options = {}) {
|
|
324
|
-
const { file, lines, snippetHash, reason } = options;
|
|
325
|
-
const fp = generateFingerprint([file, lines, reason]);
|
|
326
|
-
|
|
327
|
-
return {
|
|
328
|
-
id: generateEvidenceId(fp),
|
|
329
|
-
kind: "file",
|
|
330
|
-
file,
|
|
331
|
-
lines,
|
|
332
|
-
snippetHash,
|
|
333
|
-
reason,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Create runtime evidence
|
|
339
|
-
*/
|
|
340
|
-
function createRuntimeEvidence(options = {}) {
|
|
341
|
-
const { url, httpStatus, reason, data } = options;
|
|
342
|
-
const fp = generateFingerprint([url, httpStatus, reason]);
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
id: generateEvidenceId(fp),
|
|
346
|
-
kind: "request",
|
|
347
|
-
url,
|
|
348
|
-
httpStatus,
|
|
349
|
-
reason,
|
|
350
|
-
data,
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
// =============================================================================
|
|
355
|
-
// SEVERITY POLICY
|
|
356
|
-
// =============================================================================
|
|
357
|
-
|
|
358
|
-
const SEVERITY_POLICY = {
|
|
359
|
-
// Route issues
|
|
360
|
-
ROUTE_MISSING: { default: "BLOCK", withRuntimeProof: "BLOCK" },
|
|
361
|
-
ROUTE_404: { default: "BLOCK" },
|
|
362
|
-
ROUTE_405: { default: "WARN" },
|
|
363
|
-
|
|
364
|
-
// Auth issues
|
|
365
|
-
AUTH_BYPASS: { default: "BLOCK" },
|
|
366
|
-
AUTH_MISSING: { default: "WARN", criticalPath: "BLOCK" },
|
|
367
|
-
|
|
368
|
-
// Fake success
|
|
369
|
-
FAKE_SUCCESS: { default: "BLOCK" },
|
|
370
|
-
SUCCESS_TOAST_NO_CHANGE: { default: "BLOCK" },
|
|
371
|
-
SUCCESS_BEFORE_REQUEST: { default: "BLOCK" },
|
|
372
|
-
|
|
373
|
-
// Dead UI
|
|
374
|
-
DEAD_CLICK: { default: "BLOCK" },
|
|
375
|
-
NO_FEEDBACK: { default: "WARN" },
|
|
376
|
-
|
|
377
|
-
// Contract drift
|
|
378
|
-
CONTRACT_DRIFT_ROUTE: { default: "WARN", blocking: "BLOCK" },
|
|
379
|
-
CONTRACT_DRIFT_ENV: { default: "WARN", required: "BLOCK" },
|
|
380
|
-
CONTRACT_DRIFT_AUTH: { default: "BLOCK" },
|
|
381
|
-
|
|
382
|
-
// Billing
|
|
383
|
-
STRIPE_WEBHOOK_UNVERIFIED: { default: "BLOCK" },
|
|
384
|
-
PAID_SURFACE_UNENFORCED: { default: "BLOCK" },
|
|
385
|
-
|
|
386
|
-
// Security
|
|
387
|
-
OWNER_MODE_BYPASS: { default: "BLOCK" },
|
|
388
|
-
HARDCODED_SECRET: { default: "BLOCK" },
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Get severity based on policy
|
|
393
|
-
*/
|
|
394
|
-
function getSeverity(issueType, context = {}) {
|
|
395
|
-
const policy = SEVERITY_POLICY[issueType];
|
|
396
|
-
if (!policy) return "WARN";
|
|
397
|
-
|
|
398
|
-
// Check context-specific overrides
|
|
399
|
-
if (context.hasRuntimeProof && policy.withRuntimeProof) {
|
|
400
|
-
return policy.withRuntimeProof;
|
|
401
|
-
}
|
|
402
|
-
if (context.isCriticalPath && policy.criticalPath) {
|
|
403
|
-
return policy.criticalPath;
|
|
404
|
-
}
|
|
405
|
-
if (context.isBlocking && policy.blocking) {
|
|
406
|
-
return policy.blocking;
|
|
407
|
-
}
|
|
408
|
-
if (context.isRequired && policy.required) {
|
|
409
|
-
return policy.required;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return policy.default;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// =============================================================================
|
|
416
|
-
// EXPORTS
|
|
417
|
-
// =============================================================================
|
|
418
|
-
|
|
419
|
-
module.exports = {
|
|
420
|
-
// Schema operations
|
|
421
|
-
SCHEMAS,
|
|
422
|
-
loadSchema,
|
|
423
|
-
validateAgainstSchema,
|
|
424
|
-
validateArtifact,
|
|
425
|
-
writeValidatedArtifact,
|
|
426
|
-
|
|
427
|
-
// Finding generation
|
|
428
|
-
generateFingerprint,
|
|
429
|
-
generateFindingId,
|
|
430
|
-
generateEvidenceId,
|
|
431
|
-
createFinding,
|
|
432
|
-
createFileEvidence,
|
|
433
|
-
createRuntimeEvidence,
|
|
434
|
-
|
|
435
|
-
// Severity policy
|
|
436
|
-
SEVERITY_POLICY,
|
|
437
|
-
getSeverity,
|
|
438
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Schema Validator v2
|
|
3
|
+
*
|
|
4
|
+
* Validates artifacts against JSON schemas and provides
|
|
5
|
+
* consistent finding/evidence generation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const path = require("path");
|
|
12
|
+
const crypto = require("crypto");
|
|
13
|
+
|
|
14
|
+
// Schema cache
|
|
15
|
+
const schemaCache = new Map();
|
|
16
|
+
|
|
17
|
+
// Schema IDs
|
|
18
|
+
const SCHEMAS = {
|
|
19
|
+
FINDING: "finding.schema.json",
|
|
20
|
+
SHIP_REPORT: "ship-report.schema.json",
|
|
21
|
+
REALITY_REPORT: "reality-report.schema.json",
|
|
22
|
+
TRUTHPACK_V2: "truthpack-v2.schema.json",
|
|
23
|
+
CONTRACTS: "contracts.schema.json",
|
|
24
|
+
PROOF_GRAPH: "proof-graph.schema.json",
|
|
25
|
+
MISSION_PACK: "mission-pack.schema.json",
|
|
26
|
+
SHARE_PACK: "share-pack.schema.json",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// =============================================================================
|
|
30
|
+
// SCHEMA LOADING
|
|
31
|
+
// =============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load a schema by ID
|
|
35
|
+
*/
|
|
36
|
+
function loadSchema(schemaId) {
|
|
37
|
+
if (schemaCache.has(schemaId)) {
|
|
38
|
+
return schemaCache.get(schemaId);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const schemaPath = path.join(__dirname, schemaId);
|
|
42
|
+
if (!fs.existsSync(schemaPath)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
|
|
48
|
+
schemaCache.set(schemaId, schema);
|
|
49
|
+
return schema;
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error(`Failed to load schema ${schemaId}:`, e.message);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Simple JSON Schema validator (subset of draft 2020-12)
|
|
58
|
+
* For production, use Ajv - this is a minimal implementation
|
|
59
|
+
*/
|
|
60
|
+
function validateAgainstSchema(data, schema, path = "") {
|
|
61
|
+
const errors = [];
|
|
62
|
+
|
|
63
|
+
if (!schema || !data) {
|
|
64
|
+
return errors;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Type check
|
|
68
|
+
if (schema.type) {
|
|
69
|
+
let actualType = Array.isArray(data) ? "array" : typeof data;
|
|
70
|
+
|
|
71
|
+
// Handle integer type - JavaScript has no integer type, only number
|
|
72
|
+
// Treat whole numbers as integers for schema validation
|
|
73
|
+
if (actualType === "number" && Number.isInteger(data)) {
|
|
74
|
+
actualType = "integer";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const allowedTypes = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
78
|
+
|
|
79
|
+
// Allow integer where number is expected, and vice versa for whole numbers
|
|
80
|
+
const typeMatches = allowedTypes.some(t => {
|
|
81
|
+
if (t === actualType) return true;
|
|
82
|
+
if (t === "integer" && actualType === "number" && Number.isInteger(data)) return true;
|
|
83
|
+
if (t === "number" && actualType === "integer") return true;
|
|
84
|
+
if (t === "null" && data === null) return true;
|
|
85
|
+
return false;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!typeMatches) {
|
|
89
|
+
errors.push({
|
|
90
|
+
path,
|
|
91
|
+
message: `Expected ${allowedTypes.join("|")}, got ${actualType}`,
|
|
92
|
+
keyword: "type",
|
|
93
|
+
});
|
|
94
|
+
return errors; // Stop on type mismatch
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Const check
|
|
99
|
+
if (schema.const !== undefined && data !== schema.const) {
|
|
100
|
+
errors.push({
|
|
101
|
+
path,
|
|
102
|
+
message: `Expected constant "${schema.const}", got "${data}"`,
|
|
103
|
+
keyword: "const",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Enum check
|
|
108
|
+
if (schema.enum && !schema.enum.includes(data)) {
|
|
109
|
+
errors.push({
|
|
110
|
+
path,
|
|
111
|
+
message: `Value must be one of: ${schema.enum.join(", ")}`,
|
|
112
|
+
keyword: "enum",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Pattern check
|
|
117
|
+
if (schema.pattern && typeof data === "string") {
|
|
118
|
+
const regex = new RegExp(schema.pattern);
|
|
119
|
+
if (!regex.test(data)) {
|
|
120
|
+
errors.push({
|
|
121
|
+
path,
|
|
122
|
+
message: `String must match pattern: ${schema.pattern}`,
|
|
123
|
+
keyword: "pattern",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Required properties
|
|
129
|
+
if (schema.required && typeof data === "object" && !Array.isArray(data)) {
|
|
130
|
+
for (const prop of schema.required) {
|
|
131
|
+
if (!(prop in data)) {
|
|
132
|
+
errors.push({
|
|
133
|
+
path: path ? `${path}.${prop}` : prop,
|
|
134
|
+
message: `Required property "${prop}" is missing`,
|
|
135
|
+
keyword: "required",
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Object properties
|
|
142
|
+
if (schema.properties && typeof data === "object" && !Array.isArray(data)) {
|
|
143
|
+
for (const [key, propSchema] of Object.entries(schema.properties)) {
|
|
144
|
+
if (key in data) {
|
|
145
|
+
const propPath = path ? `${path}.${key}` : key;
|
|
146
|
+
errors.push(...validateAgainstSchema(data[key], propSchema, propPath));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Array items
|
|
152
|
+
if (schema.items && Array.isArray(data)) {
|
|
153
|
+
for (let i = 0; i < data.length; i++) {
|
|
154
|
+
const itemPath = `${path}[${i}]`;
|
|
155
|
+
errors.push(...validateAgainstSchema(data[i], schema.items, itemPath));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Min/max length
|
|
160
|
+
if (schema.maxLength && typeof data === "string" && data.length > schema.maxLength) {
|
|
161
|
+
errors.push({
|
|
162
|
+
path,
|
|
163
|
+
message: `String length must be <= ${schema.maxLength}`,
|
|
164
|
+
keyword: "maxLength",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Min/max for numbers
|
|
169
|
+
if (schema.minimum !== undefined && typeof data === "number" && data < schema.minimum) {
|
|
170
|
+
errors.push({
|
|
171
|
+
path,
|
|
172
|
+
message: `Value must be >= ${schema.minimum}`,
|
|
173
|
+
keyword: "minimum",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (schema.maximum !== undefined && typeof data === "number" && data > schema.maximum) {
|
|
177
|
+
errors.push({
|
|
178
|
+
path,
|
|
179
|
+
message: `Value must be <= ${schema.maximum}`,
|
|
180
|
+
keyword: "maximum",
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return errors;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// =============================================================================
|
|
188
|
+
// ARTIFACT VALIDATION
|
|
189
|
+
// =============================================================================
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Validate an artifact file against its schema
|
|
193
|
+
*/
|
|
194
|
+
function validateArtifact(filePath, schemaId) {
|
|
195
|
+
const schema = loadSchema(schemaId);
|
|
196
|
+
if (!schema) {
|
|
197
|
+
return {
|
|
198
|
+
valid: false,
|
|
199
|
+
errors: [{ path: "", message: `Schema ${schemaId} not found`, keyword: "schema" }],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let data;
|
|
204
|
+
try {
|
|
205
|
+
data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
206
|
+
} catch (e) {
|
|
207
|
+
return {
|
|
208
|
+
valid: false,
|
|
209
|
+
errors: [{ path: "", message: `Failed to read/parse file: ${e.message}`, keyword: "parse" }],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const errors = validateAgainstSchema(data, schema);
|
|
214
|
+
return {
|
|
215
|
+
valid: errors.length === 0,
|
|
216
|
+
errors,
|
|
217
|
+
data,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Validate and write artifact with schema check
|
|
223
|
+
*/
|
|
224
|
+
function writeValidatedArtifact(filePath, data, schemaId) {
|
|
225
|
+
const schema = loadSchema(schemaId);
|
|
226
|
+
|
|
227
|
+
if (schema) {
|
|
228
|
+
const errors = validateAgainstSchema(data, schema);
|
|
229
|
+
if (errors.length > 0) {
|
|
230
|
+
console.warn(`Warning: Artifact ${filePath} has ${errors.length} schema violations:`);
|
|
231
|
+
errors.slice(0, 5).forEach(e => console.warn(` - ${e.path}: ${e.message}`));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
236
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
|
|
237
|
+
|
|
238
|
+
return { written: true, path: filePath };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// =============================================================================
|
|
242
|
+
// FINDING GENERATION
|
|
243
|
+
// =============================================================================
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Generate stable fingerprint for deduplication
|
|
247
|
+
*/
|
|
248
|
+
function generateFingerprint(parts) {
|
|
249
|
+
const content = parts.filter(Boolean).join("|");
|
|
250
|
+
return crypto.createHash("sha256").update(content).digest("hex");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Generate finding ID
|
|
255
|
+
*/
|
|
256
|
+
function generateFindingId(detectorId, fingerprint) {
|
|
257
|
+
return `F_${detectorId}_${fingerprint.slice(0, 8).toUpperCase()}`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Generate evidence ID
|
|
262
|
+
*/
|
|
263
|
+
function generateEvidenceId(fingerprint) {
|
|
264
|
+
return `E_${fingerprint.slice(0, 12).toUpperCase()}`;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Create a spec-compliant finding
|
|
269
|
+
*/
|
|
270
|
+
function createFinding(options = {}) {
|
|
271
|
+
const {
|
|
272
|
+
detectorId,
|
|
273
|
+
severity = "WARN",
|
|
274
|
+
category = "Routes",
|
|
275
|
+
scope = "client",
|
|
276
|
+
title,
|
|
277
|
+
why,
|
|
278
|
+
confidence = "medium",
|
|
279
|
+
evidence = [],
|
|
280
|
+
repro = null,
|
|
281
|
+
related = [],
|
|
282
|
+
proofNode = null,
|
|
283
|
+
missionType = null,
|
|
284
|
+
// For fingerprint generation
|
|
285
|
+
file = null,
|
|
286
|
+
path: routePath = null,
|
|
287
|
+
method = null,
|
|
288
|
+
} = options;
|
|
289
|
+
|
|
290
|
+
const fingerprint = generateFingerprint([
|
|
291
|
+
detectorId,
|
|
292
|
+
file,
|
|
293
|
+
routePath,
|
|
294
|
+
method,
|
|
295
|
+
title,
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
id: generateFindingId(detectorId, fingerprint),
|
|
300
|
+
detectorId,
|
|
301
|
+
fingerprint: `sha256:${fingerprint}`,
|
|
302
|
+
severity,
|
|
303
|
+
category,
|
|
304
|
+
scope,
|
|
305
|
+
title,
|
|
306
|
+
why,
|
|
307
|
+
confidence,
|
|
308
|
+
evidence: evidence.map((e, i) => ({
|
|
309
|
+
id: e.id || generateEvidenceId(fingerprint + i),
|
|
310
|
+
kind: e.kind || "file",
|
|
311
|
+
...e,
|
|
312
|
+
})),
|
|
313
|
+
repro,
|
|
314
|
+
related,
|
|
315
|
+
proofNode,
|
|
316
|
+
missionType,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Create file evidence
|
|
322
|
+
*/
|
|
323
|
+
function createFileEvidence(options = {}) {
|
|
324
|
+
const { file, lines, snippetHash, reason } = options;
|
|
325
|
+
const fp = generateFingerprint([file, lines, reason]);
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
id: generateEvidenceId(fp),
|
|
329
|
+
kind: "file",
|
|
330
|
+
file,
|
|
331
|
+
lines,
|
|
332
|
+
snippetHash,
|
|
333
|
+
reason,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Create runtime evidence
|
|
339
|
+
*/
|
|
340
|
+
function createRuntimeEvidence(options = {}) {
|
|
341
|
+
const { url, httpStatus, reason, data } = options;
|
|
342
|
+
const fp = generateFingerprint([url, httpStatus, reason]);
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
id: generateEvidenceId(fp),
|
|
346
|
+
kind: "request",
|
|
347
|
+
url,
|
|
348
|
+
httpStatus,
|
|
349
|
+
reason,
|
|
350
|
+
data,
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// =============================================================================
|
|
355
|
+
// SEVERITY POLICY
|
|
356
|
+
// =============================================================================
|
|
357
|
+
|
|
358
|
+
const SEVERITY_POLICY = {
|
|
359
|
+
// Route issues
|
|
360
|
+
ROUTE_MISSING: { default: "BLOCK", withRuntimeProof: "BLOCK" },
|
|
361
|
+
ROUTE_404: { default: "BLOCK" },
|
|
362
|
+
ROUTE_405: { default: "WARN" },
|
|
363
|
+
|
|
364
|
+
// Auth issues
|
|
365
|
+
AUTH_BYPASS: { default: "BLOCK" },
|
|
366
|
+
AUTH_MISSING: { default: "WARN", criticalPath: "BLOCK" },
|
|
367
|
+
|
|
368
|
+
// Fake success
|
|
369
|
+
FAKE_SUCCESS: { default: "BLOCK" },
|
|
370
|
+
SUCCESS_TOAST_NO_CHANGE: { default: "BLOCK" },
|
|
371
|
+
SUCCESS_BEFORE_REQUEST: { default: "BLOCK" },
|
|
372
|
+
|
|
373
|
+
// Dead UI
|
|
374
|
+
DEAD_CLICK: { default: "BLOCK" },
|
|
375
|
+
NO_FEEDBACK: { default: "WARN" },
|
|
376
|
+
|
|
377
|
+
// Contract drift
|
|
378
|
+
CONTRACT_DRIFT_ROUTE: { default: "WARN", blocking: "BLOCK" },
|
|
379
|
+
CONTRACT_DRIFT_ENV: { default: "WARN", required: "BLOCK" },
|
|
380
|
+
CONTRACT_DRIFT_AUTH: { default: "BLOCK" },
|
|
381
|
+
|
|
382
|
+
// Billing
|
|
383
|
+
STRIPE_WEBHOOK_UNVERIFIED: { default: "BLOCK" },
|
|
384
|
+
PAID_SURFACE_UNENFORCED: { default: "BLOCK" },
|
|
385
|
+
|
|
386
|
+
// Security
|
|
387
|
+
OWNER_MODE_BYPASS: { default: "BLOCK" },
|
|
388
|
+
HARDCODED_SECRET: { default: "BLOCK" },
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Get severity based on policy
|
|
393
|
+
*/
|
|
394
|
+
function getSeverity(issueType, context = {}) {
|
|
395
|
+
const policy = SEVERITY_POLICY[issueType];
|
|
396
|
+
if (!policy) return "WARN";
|
|
397
|
+
|
|
398
|
+
// Check context-specific overrides
|
|
399
|
+
if (context.hasRuntimeProof && policy.withRuntimeProof) {
|
|
400
|
+
return policy.withRuntimeProof;
|
|
401
|
+
}
|
|
402
|
+
if (context.isCriticalPath && policy.criticalPath) {
|
|
403
|
+
return policy.criticalPath;
|
|
404
|
+
}
|
|
405
|
+
if (context.isBlocking && policy.blocking) {
|
|
406
|
+
return policy.blocking;
|
|
407
|
+
}
|
|
408
|
+
if (context.isRequired && policy.required) {
|
|
409
|
+
return policy.required;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return policy.default;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// =============================================================================
|
|
416
|
+
// EXPORTS
|
|
417
|
+
// =============================================================================
|
|
418
|
+
|
|
419
|
+
module.exports = {
|
|
420
|
+
// Schema operations
|
|
421
|
+
SCHEMAS,
|
|
422
|
+
loadSchema,
|
|
423
|
+
validateAgainstSchema,
|
|
424
|
+
validateArtifact,
|
|
425
|
+
writeValidatedArtifact,
|
|
426
|
+
|
|
427
|
+
// Finding generation
|
|
428
|
+
generateFingerprint,
|
|
429
|
+
generateFindingId,
|
|
430
|
+
generateEvidenceId,
|
|
431
|
+
createFinding,
|
|
432
|
+
createFileEvidence,
|
|
433
|
+
createRuntimeEvidence,
|
|
434
|
+
|
|
435
|
+
// Severity policy
|
|
436
|
+
SEVERITY_POLICY,
|
|
437
|
+
getSeverity,
|
|
438
|
+
};
|