@cejel/cejel 0.4.9 → 0.4.11
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 +10 -7
- package/dist/calibration/llm-detector.js +5 -5
- package/dist/{chunk-IHT7B6L7.js → chunk-63XQDYQ5.js} +1 -1
- package/dist/{chunk-GKYTLWQK.js → chunk-FSMZV3Y6.js} +1 -1
- package/dist/{chunk-IN2LPGGU.js → chunk-M6YYPJSN.js} +2 -2
- package/dist/{chunk-PTZU6G4E.js → chunk-O3MHSK25.js} +34 -6
- package/dist/{chunk-IQ42TE5H.js → chunk-RH6FKREX.js} +1 -1
- package/dist/{chunk-TUUNJP27.js → chunk-UBCIGOIP.js} +184 -35
- package/dist/index.js +808 -26
- package/dist/mcp/index.js +4 -4
- package/dist/packs/d-series/index.js +4 -4
- package/dist/packs/decision-contracts/index.js +3 -3
- package/docs/security/README.md +78 -0
- package/docs/security/issuer-revocations +18 -0
- package/docs/security/issuer-signers +34 -0
- package/package.json +18 -13
package/README.md
CHANGED
|
@@ -36,7 +36,10 @@ untouched holdout, every preregistered gate to pass, and a separately recorded p
|
|
|
36
36
|
|
|
37
37
|
### Measured recall (bounded)
|
|
38
38
|
|
|
39
|
-
> **Measured recall (bounded).** Cejel publishes
|
|
39
|
+
> **Measured recall (bounded).** Cejel publishes one cohort-level in-scope detection-recall
|
|
40
|
+
> figure, for the prospective v22 rubric, in `docs/calibration/v22-detection-recall/`; no general
|
|
41
|
+
> figure exists, and the three quantities called recall are set side by side on
|
|
42
|
+
> cejel.dev/methodology. On a frozen,
|
|
40
43
|
> preregistered, in-scope held-out fixture set of 30 named defects, the released public default
|
|
41
44
|
> (v17, tree `a857f0b3`) cited **16/30**, two-sided 95% Wilson interval **[36.1%, 69.8%]**; the
|
|
42
45
|
> prospective v22 detector (commit `8a289ea`, tree `10960a03`) cited **24/30**, two-sided 95%
|
|
@@ -89,7 +92,7 @@ if (-not $expected -or $actual -ne $expected.ToLowerInvariant()) {
|
|
|
89
92
|
```
|
|
90
93
|
|
|
91
94
|
> **Windows signing status:** `cejel-Windows-x86_64.exe` is intentionally unsigned in
|
|
92
|
-
> 0.4.
|
|
95
|
+
> 0.4.11 and may trigger Microsoft SmartScreen. The release build removes Node's inherited
|
|
93
96
|
> signature before SEA injection and fails unless Windows reports the result as `NotSigned`;
|
|
94
97
|
> it does not ship an invalid signature. Before running it, verify `SHA256SUMS` and the
|
|
95
98
|
> GitHub build-provenance attestation. Each binary also has an attached SPDX SBOM and an
|
|
@@ -112,7 +115,7 @@ docker run --rm --network=none -v "$PWD:/w" -w /w -v "$PWD/cejel:/cejel:ro" debi
|
|
|
112
115
|
npx @cejel/cejel@latest .
|
|
113
116
|
```
|
|
114
117
|
|
|
115
|
-
> **Distribution note:** Cejel `0.4.
|
|
118
|
+
> **Distribution note:** Cejel `0.4.11` is the coordinated release version for npm,
|
|
116
119
|
> standalone binaries, Docker/OCI, GitHub Action, Homebrew, and MCP Registry.
|
|
117
120
|
|
|
118
121
|
`npx` can reuse a stale cached package. Force the current npm release with the `@latest`
|
|
@@ -174,16 +177,16 @@ gh attestation verify ./cejel-Windows-x86_64.exe -R BargLabs/cejel
|
|
|
174
177
|
This is cryptographically signed provenance. It is distinct from Apple Developer ID or
|
|
175
178
|
Microsoft Authenticode code-signing.
|
|
176
179
|
|
|
177
|
-
**Docker / OCI.** The current container release is `0.4.
|
|
180
|
+
**Docker / OCI.** The current container release is `0.4.11`:
|
|
178
181
|
|
|
179
182
|
```bash
|
|
180
|
-
docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.4.
|
|
183
|
+
docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.4.11
|
|
181
184
|
```
|
|
182
185
|
|
|
183
186
|
The image defaults to `cejel-mcp` over stdio. To use the CLI instead:
|
|
184
187
|
|
|
185
188
|
```bash
|
|
186
|
-
docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.4.
|
|
189
|
+
docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.4.11 .
|
|
187
190
|
```
|
|
188
191
|
|
|
189
192
|
The OCI image carries an SBOM, maximum-mode build provenance, and a signed registry
|
|
@@ -513,7 +516,7 @@ The OCI image is an alternative when Docker is the preferred execution boundary.
|
|
|
513
516
|
the host path with the repository OpenClaw should allow Cejel to read:
|
|
514
517
|
|
|
515
518
|
```bash
|
|
516
|
-
npx -y openclaw@latest mcp set cejel-oci '{"command":"docker","args":["run","--rm","-i","-v","/absolute/path/to/repo:/workspace:ro","ghcr.io/barglabs/cejel:0.4.
|
|
519
|
+
npx -y openclaw@latest mcp set cejel-oci '{"command":"docker","args":["run","--rm","-i","-v","/absolute/path/to/repo:/workspace:ro","ghcr.io/barglabs/cejel:0.4.11"]}'
|
|
517
520
|
npx -y openclaw@latest mcp doctor cejel-oci --probe
|
|
518
521
|
```
|
|
519
522
|
|
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
deriveProductIdentity,
|
|
6
6
|
runCejelScan,
|
|
7
7
|
serializeWitanReport
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-UBCIGOIP.js";
|
|
9
9
|
import {
|
|
10
10
|
listCejelLlmPackFiles
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-M6YYPJSN.js";
|
|
12
|
+
import "../chunk-63XQDYQ5.js";
|
|
13
13
|
import {
|
|
14
14
|
external_exports
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-FSMZV3Y6.js";
|
|
16
16
|
import {
|
|
17
17
|
init_esm_shims,
|
|
18
18
|
readRepoText,
|
|
19
19
|
recordContentSkip,
|
|
20
20
|
recordFilesystemSkip
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-O3MHSK25.js";
|
|
22
22
|
|
|
23
23
|
// src/calibration/llm-detector.ts
|
|
24
24
|
init_esm_shims();
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
describeGitFailure,
|
|
6
6
|
execGit,
|
|
7
7
|
isExpectedGitAbsence
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-63XQDYQ5.js";
|
|
9
9
|
import {
|
|
10
10
|
init_esm_shims
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-O3MHSK25.js";
|
|
12
12
|
|
|
13
13
|
// src/packs/llm/files.ts
|
|
14
14
|
init_esm_shims();
|
|
@@ -171,7 +171,8 @@ function trackContentReads(_repoPath, collect) {
|
|
|
171
171
|
unattributedCriteria: /* @__PURE__ */ new Set(),
|
|
172
172
|
criterionSkipReasons: /* @__PURE__ */ new Map(),
|
|
173
173
|
signalSkipReasons: /* @__PURE__ */ new Map(),
|
|
174
|
-
withheldPaths: /* @__PURE__ */ new Map()
|
|
174
|
+
withheldPaths: /* @__PURE__ */ new Map(),
|
|
175
|
+
withheldPathIntersections: /* @__PURE__ */ new Map()
|
|
175
176
|
};
|
|
176
177
|
activeSession = session;
|
|
177
178
|
try {
|
|
@@ -225,16 +226,42 @@ function registerWithheldRepoPath(relativePath, reason) {
|
|
|
225
226
|
if (!session) return;
|
|
226
227
|
addSkipReason(session.withheldPaths, relativePath, reason);
|
|
227
228
|
}
|
|
228
|
-
function
|
|
229
|
+
function withheldRepoPaths() {
|
|
230
|
+
const session = activeSession;
|
|
231
|
+
if (!session) return [];
|
|
232
|
+
return [...session.withheldPaths].map(([path2, reasons]) => {
|
|
233
|
+
const intersection = session.withheldPathIntersections.get(path2);
|
|
234
|
+
return {
|
|
235
|
+
path: path2,
|
|
236
|
+
reasons,
|
|
237
|
+
signalsAdmitting: intersection ? [...intersection.signalsAdmitting].sort() : [],
|
|
238
|
+
actedOn: intersection?.actedOn ?? false
|
|
239
|
+
};
|
|
240
|
+
}).sort((left, right) => left.path.localeCompare(right.path));
|
|
241
|
+
}
|
|
242
|
+
function abstainSignalOnWithheldPaths(criterionId, signalId, wouldHaveRead, act) {
|
|
229
243
|
const session = activeSession;
|
|
230
244
|
if (!session) return false;
|
|
231
|
-
let
|
|
245
|
+
let admitted = false;
|
|
246
|
+
const signalKey = `${criterionId}.${signalId}`;
|
|
232
247
|
for (const [path2, reasons] of session.withheldPaths) {
|
|
233
248
|
if (!wouldHaveRead(path2)) continue;
|
|
234
|
-
|
|
235
|
-
|
|
249
|
+
admitted = true;
|
|
250
|
+
const existing = session.withheldPathIntersections.get(path2);
|
|
251
|
+
if (existing) {
|
|
252
|
+
existing.signalsAdmitting.add(signalKey);
|
|
253
|
+
if (act) existing.actedOn = true;
|
|
254
|
+
} else {
|
|
255
|
+
session.withheldPathIntersections.set(path2, {
|
|
256
|
+
signalsAdmitting: /* @__PURE__ */ new Set([signalKey]),
|
|
257
|
+
actedOn: act
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (act) {
|
|
261
|
+
for (const reason of reasons) attributeSkip(session, criterionId, [signalId], reason);
|
|
262
|
+
}
|
|
236
263
|
}
|
|
237
|
-
return
|
|
264
|
+
return admitted && act;
|
|
238
265
|
}
|
|
239
266
|
function recordFilesystemSkip(path2, error, deniedContext = false, deduplicate = true) {
|
|
240
267
|
const errno = errnoClass(error);
|
|
@@ -315,6 +342,7 @@ export {
|
|
|
315
342
|
withContentReadSignals,
|
|
316
343
|
recordContentSkip,
|
|
317
344
|
registerWithheldRepoPath,
|
|
345
|
+
withheldRepoPaths,
|
|
318
346
|
abstainSignalOnWithheldPaths,
|
|
319
347
|
recordFilesystemSkip,
|
|
320
348
|
readRepoText,
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
describeGitFailure,
|
|
6
6
|
execGit,
|
|
7
7
|
isExpectedGitAbsence
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-63XQDYQ5.js";
|
|
9
9
|
import {
|
|
10
10
|
external_exports
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-FSMZV3Y6.js";
|
|
12
12
|
import {
|
|
13
13
|
MAX_REPOSITORY_CONTENT_BYTES,
|
|
14
14
|
abstainSignalOnWithheldPaths,
|
|
@@ -23,8 +23,9 @@ import {
|
|
|
23
23
|
trackContentReads,
|
|
24
24
|
withContentReadCriterion,
|
|
25
25
|
withContentReadSignal,
|
|
26
|
-
withContentReadSignals
|
|
27
|
-
|
|
26
|
+
withContentReadSignals,
|
|
27
|
+
withheldRepoPaths
|
|
28
|
+
} from "./chunk-O3MHSK25.js";
|
|
28
29
|
|
|
29
30
|
// src/witan/schemas.ts
|
|
30
31
|
init_esm_shims();
|
|
@@ -226,6 +227,12 @@ var WitanContentReadSummarySchema = external_exports.object({
|
|
|
226
227
|
});
|
|
227
228
|
}
|
|
228
229
|
});
|
|
230
|
+
var WitanWithheldPathSchema = external_exports.object({
|
|
231
|
+
path: external_exports.string().min(1).max(700),
|
|
232
|
+
reason: WitanContentReadSkipReasonSchema,
|
|
233
|
+
signalsAdmitting: external_exports.array(external_exports.string().min(1).max(160)),
|
|
234
|
+
actedOn: external_exports.boolean()
|
|
235
|
+
}).strict();
|
|
229
236
|
var WitanRepoRefSchema = external_exports.object({
|
|
230
237
|
// Retained for backward-compatible verification of reports emitted through v0.4.0.
|
|
231
238
|
// New reports omit the checkout path so their bytes are independent of run location.
|
|
@@ -243,7 +250,11 @@ var WitanReportInputSchema = external_exports.object({
|
|
|
243
250
|
archetype: WitanRepoArchetypeSchema.optional(),
|
|
244
251
|
insufficientSourceReason: external_exports.string().min(1).max(2e3).optional(),
|
|
245
252
|
scanLimitations: external_exports.array(external_exports.string().min(1).max(1e3)).max(16).default([]),
|
|
246
|
-
contentReadSummary: WitanContentReadSummarySchema.optional()
|
|
253
|
+
contentReadSummary: WitanContentReadSummarySchema.optional(),
|
|
254
|
+
// Always populated by buildWitanInputFromRepo, including as [] when nothing was withheld —
|
|
255
|
+
// presence with an empty array is itself the "nothing withheld" statement, distinct from
|
|
256
|
+
// absence (a report predating this field, which does not claim one way or the other).
|
|
257
|
+
withheldPaths: external_exports.array(WitanWithheldPathSchema).default([])
|
|
247
258
|
}).strict();
|
|
248
259
|
var WitanCriterionScoreSchema = external_exports.object({
|
|
249
260
|
id: WitanCriterionIdSchema,
|
|
@@ -331,6 +342,10 @@ var WitanReportCommonSchema = external_exports.object({
|
|
|
331
342
|
archetype: WitanRepoArchetypeSchema.optional(),
|
|
332
343
|
scanLimitations: external_exports.array(external_exports.string().min(1).max(1e3)).max(16).optional(),
|
|
333
344
|
contentReadSummary: WitanContentReadSummarySchema.optional(),
|
|
345
|
+
// Additive-optional (report format 1.3): absent means a report produced before this field
|
|
346
|
+
// existed, never "nothing withheld" — createWitanReport always sets it (possibly to []) on any
|
|
347
|
+
// report it builds, so absence here is a legacy-report signal, not a claim about the scan.
|
|
348
|
+
withheldPaths: external_exports.array(WitanWithheldPathSchema).optional(),
|
|
334
349
|
// The @cejel/cejel version that produced this report. Absent on reports from before this
|
|
335
350
|
// field existed and on any caller that does not supply one (createWitanReport never defaults
|
|
336
351
|
// it). Constant within an installed version, so re-running the same version on the same
|
|
@@ -378,7 +393,7 @@ var WitanReportSchema = external_exports.preprocess((value) => {
|
|
|
378
393
|
}, StrictWitanReportSchema);
|
|
379
394
|
var WITAN_ATTESTATION_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
380
395
|
var WITAN_ATTESTATION_PREDICATE_TYPE = "https://cejel.dev/attestations/scan/v1";
|
|
381
|
-
var WITAN_REPORT_FORMAT_VERSION = "1.
|
|
396
|
+
var WITAN_REPORT_FORMAT_VERSION = "1.3";
|
|
382
397
|
var WitanAttestationOutcomeSchema = external_exports.discriminatedUnion("status", [
|
|
383
398
|
external_exports.object({
|
|
384
399
|
status: external_exports.literal("scored"),
|
|
@@ -1066,6 +1081,44 @@ function buildNextSteps(report, unmeasured, notApplicable, a1, coverageMetric) {
|
|
|
1066
1081
|
}
|
|
1067
1082
|
return steps;
|
|
1068
1083
|
}
|
|
1084
|
+
var WITHHELD_PATH_READ_FAILURE_REASONS = /* @__PURE__ */ new Set([
|
|
1085
|
+
"unreadable",
|
|
1086
|
+
"denied_path"
|
|
1087
|
+
]);
|
|
1088
|
+
var WITHHELD_PATH_REASON_LABELS = {
|
|
1089
|
+
too_large: "too large",
|
|
1090
|
+
non_regular_file: "not a regular file",
|
|
1091
|
+
unreadable: "unreadable",
|
|
1092
|
+
denied_path: "denied path",
|
|
1093
|
+
excluded_by_extension: "excluded by extension"
|
|
1094
|
+
};
|
|
1095
|
+
function describeWithheldReasons(entries) {
|
|
1096
|
+
return [...new Set(entries.map((entry) => WITHHELD_PATH_REASON_LABELS[entry.reason]))].sort().join(", ");
|
|
1097
|
+
}
|
|
1098
|
+
function buildWithheldPathsGapSentence(report) {
|
|
1099
|
+
const withheldPaths = report.withheldPaths;
|
|
1100
|
+
if (withheldPaths === void 0) return void 0;
|
|
1101
|
+
if (withheldPaths.length === 0) {
|
|
1102
|
+
return "No content was withheld from any signal.";
|
|
1103
|
+
}
|
|
1104
|
+
const count = withheldPaths.length;
|
|
1105
|
+
const entryWord = count === 1 ? "content entry was" : "content entries were";
|
|
1106
|
+
const pronoun = count === 1 ? "it" : "them";
|
|
1107
|
+
const reasonLabel = describeWithheldReasons(withheldPaths);
|
|
1108
|
+
const intersecting = withheldPaths.filter((entry) => entry.signalsAdmitting.length > 0);
|
|
1109
|
+
if (intersecting.length === 0) {
|
|
1110
|
+
return `${count} ${entryWord} withheld (${reasonLabel}); no signal that would have read ${pronoun} selected ${pronoun}, so no result depends on ${pronoun}.`;
|
|
1111
|
+
}
|
|
1112
|
+
const signals = [...new Set(intersecting.flatMap((entry) => entry.signalsAdmitting))].sort();
|
|
1113
|
+
const signalList = signals.join(", ");
|
|
1114
|
+
const actedOn = intersecting.some((entry) => entry.actedOn);
|
|
1115
|
+
if (actedOn) {
|
|
1116
|
+
const qualifier = intersecting.every((entry) => !WITHHELD_PATH_READ_FAILURE_REASONS.has(entry.reason)) ? "a disclosed coverage limit, not a read failure" : "content Cejel could not read";
|
|
1117
|
+
return `${count} ${entryWord} withheld (${reasonLabel}); ${signalList} abstained on ${pronoun} rather than assert an absence \u2014 ${qualifier}.`;
|
|
1118
|
+
}
|
|
1119
|
+
const reportsOnWhatItRead = signals.length === 1 ? "that signal reports on what it read" : "these signals report on what they read";
|
|
1120
|
+
return `${count} ${entryWord} withheld (${reasonLabel}); ${signalList} would have read ${pronoun}. Under this rubric ${reportsOnWhatItRead}; the prospective v23 rubric abstains instead.`;
|
|
1121
|
+
}
|
|
1069
1122
|
function buildRelyingPartySummary(report) {
|
|
1070
1123
|
const applicable = report.criteria.filter((criterion) => criterion.status !== "not_applicable");
|
|
1071
1124
|
const measured = applicable.filter((criterion) => criterion.status !== "insufficient_data");
|
|
@@ -1106,6 +1159,10 @@ function buildRelyingPartySummary(report) {
|
|
|
1106
1159
|
`${skipped} content entr${skipped === 1 ? "y was" : "ies were"} skipped for the reasons itemized in the certificate.`
|
|
1107
1160
|
);
|
|
1108
1161
|
}
|
|
1162
|
+
const withheldPathsSentence = buildWithheldPathsGapSentence(report);
|
|
1163
|
+
if (withheldPathsSentence !== void 0) {
|
|
1164
|
+
gaps.push(withheldPathsSentence);
|
|
1165
|
+
}
|
|
1109
1166
|
gaps.push(
|
|
1110
1167
|
"Anything absent from the measured evidence was not established: no measurement and no finding are not clearance."
|
|
1111
1168
|
);
|
|
@@ -2200,6 +2257,14 @@ function buildWitanInputFromRepo(options) {
|
|
|
2200
2257
|
contentReadSummary: tracked.summary
|
|
2201
2258
|
};
|
|
2202
2259
|
}
|
|
2260
|
+
function buildWithheldPathsReportField() {
|
|
2261
|
+
return withheldRepoPaths().map(({ path, reasons, signalsAdmitting, actedOn }) => ({
|
|
2262
|
+
path,
|
|
2263
|
+
reason: [...reasons].sort()[0],
|
|
2264
|
+
signalsAdmitting: [...signalsAdmitting],
|
|
2265
|
+
actedOn
|
|
2266
|
+
}));
|
|
2267
|
+
}
|
|
2203
2268
|
function buildWitanInputFromRepoUntracked(options) {
|
|
2204
2269
|
assertRepoPathExists(options.repoPath);
|
|
2205
2270
|
const generatedAt = options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2283,6 +2348,9 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
2283
2348
|
archetype: archetype.archetype,
|
|
2284
2349
|
...ignoredTargetReason ?? archetype.insufficientSourceReason ? { insufficientSourceReason: ignoredTargetReason ?? archetype.insufficientSourceReason } : {},
|
|
2285
2350
|
...scanLimitations.size > 0 ? { scanLimitations: [...scanLimitations] } : {},
|
|
2351
|
+
// Always present, even as [] — presence is the "nothing was withheld" statement itself, not
|
|
2352
|
+
// merely the absence of a limitation (goal_cejel_withheld_paths_always_disclosed_2026-09-22).
|
|
2353
|
+
withheldPaths: buildWithheldPathsReportField(),
|
|
2286
2354
|
signals: [
|
|
2287
2355
|
...coreSignals,
|
|
2288
2356
|
...(options.domainCollectors ?? []).map((collect) => collect(options.repoPath, repoFiles)),
|
|
@@ -3807,7 +3875,7 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
3807
3875
|
)
|
|
3808
3876
|
);
|
|
3809
3877
|
const healthCheck = healthChecks[0];
|
|
3810
|
-
const healthReadinessRouteReads = (file) => isAuthoredProductionPath(file) && isImplementationFile(file);
|
|
3878
|
+
const healthReadinessRouteReads = (file) => isAuthoredProductionPath(file) && (isImplementationFile(file) || HEALTH_ROUTE_FUNCTIONS_DIR_PATTERN.test(file));
|
|
3811
3879
|
const observabilityDepthReads = (file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isImplementationFile(file);
|
|
3812
3880
|
const { hasV20HealthOrReadinessRoute, hasV22PackageStartHealthOrReadinessRoute } = withContentReadSignal("A3", "health_readiness_route", () => ({
|
|
3813
3881
|
hasV20HealthOrReadinessRoute: useV20ExplicitGaps && repoFiles.some(
|
|
@@ -3819,22 +3887,33 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
3819
3887
|
V20_HEALTH_OR_READINESS_ROUTE_PATTERN
|
|
3820
3888
|
)
|
|
3821
3889
|
}));
|
|
3822
|
-
const healthReadinessRouteWithheld =
|
|
3890
|
+
const healthReadinessRouteWithheld = useV20ExplicitGaps && abstainSignalOnWithheldPaths(
|
|
3891
|
+
"A3",
|
|
3892
|
+
"health_readiness_route",
|
|
3893
|
+
healthReadinessRouteReads,
|
|
3894
|
+
useV23WithheldPathAbstention
|
|
3895
|
+
);
|
|
3823
3896
|
const serverEntrypoint = v22PackageStartHttpEntrypoint ?? findServerEntrypointFile(repoPath, repoFiles, useV27Detectors) ?? v20DirectHttpEntrypoint ?? null;
|
|
3824
3897
|
const runtimeContainer = useV27Detectors ? findRuntimeContainerEntrypointFile(repoPath, repoFiles, useV20ExplicitGaps) : null;
|
|
3825
3898
|
const errorBoundaries = repoFiles.filter(
|
|
3826
3899
|
(file) => (!useV27Detectors || isAuthoredProductionPath(file)) && /error-boundary|error\.(tsx|jsx|ts|js)$/.test(file)
|
|
3827
3900
|
);
|
|
3901
|
+
const errorBoundaryFileReads = (file) => observabilityDepthReads(file) || (!useV27Detectors || isAuthoredProductionPath(file)) && /(^|\/)server\//.test(file) && /\.(?:mjs|cjs)$/.test(file);
|
|
3828
3902
|
const expressErrorMiddlewareFile = errorBoundaries.length > 0 ? void 0 : withContentReadSignal(
|
|
3829
3903
|
"A3",
|
|
3830
3904
|
"prod_readiness_primitives",
|
|
3831
3905
|
() => repoFiles.find(
|
|
3832
|
-
(file) =>
|
|
3906
|
+
(file) => errorBoundaryFileReads(file) && fileContains(repoPath, file, EXPRESS_ERROR_MIDDLEWARE_PATTERN) && fileMatchesOutsideLineComments(repoPath, file, EXPRESS_MIDDLEWARE_REACHABLE_PATTERN)
|
|
3833
3907
|
)
|
|
3834
3908
|
);
|
|
3835
3909
|
const errorBoundary = errorBoundaries[0] ?? expressErrorMiddlewareFile;
|
|
3836
|
-
if (
|
|
3837
|
-
abstainSignalOnWithheldPaths(
|
|
3910
|
+
if (errorBoundaries.length === 0) {
|
|
3911
|
+
abstainSignalOnWithheldPaths(
|
|
3912
|
+
"A3",
|
|
3913
|
+
"prod_readiness_primitives",
|
|
3914
|
+
errorBoundaryFileReads,
|
|
3915
|
+
useV23WithheldPathAbstention
|
|
3916
|
+
);
|
|
3838
3917
|
}
|
|
3839
3918
|
const observabilityCount = withContentReadSignal(
|
|
3840
3919
|
"A3",
|
|
@@ -3844,9 +3923,12 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
3844
3923
|
/\b(?:pino|winston|bunyan|morgan)\b/
|
|
3845
3924
|
])
|
|
3846
3925
|
);
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3926
|
+
abstainSignalOnWithheldPaths(
|
|
3927
|
+
"A3",
|
|
3928
|
+
"observability_depth",
|
|
3929
|
+
observabilityDepthReads,
|
|
3930
|
+
useV23WithheldPathAbstention
|
|
3931
|
+
);
|
|
3850
3932
|
const rollbackSafetyCount = withContentReadSignal(
|
|
3851
3933
|
"A3",
|
|
3852
3934
|
"rollback_safety_depth",
|
|
@@ -4279,24 +4361,30 @@ function hasDeclaredVersionConstraint(dependency) {
|
|
|
4279
4361
|
}
|
|
4280
4362
|
function collectA5ClaimRealityEvidence(repoPath, repoFiles, useV27Detectors, useV23WithheldPathAbstention, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
|
|
4281
4363
|
const claimImplementationReads = claimImplementationFileReads(useV27Detectors);
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4364
|
+
abstainSignalOnWithheldPaths(
|
|
4365
|
+
"A5",
|
|
4366
|
+
"claim_match_rate",
|
|
4367
|
+
(file) => claimImplementationReads(file) || isClaimSourceFile(file),
|
|
4368
|
+
useV23WithheldPathAbstention
|
|
4369
|
+
);
|
|
4370
|
+
abstainSignalOnWithheldPaths(
|
|
4371
|
+
"A5",
|
|
4372
|
+
"claim_source_depth",
|
|
4373
|
+
isClaimSourceFile,
|
|
4374
|
+
useV23WithheldPathAbstention
|
|
4375
|
+
);
|
|
4376
|
+
abstainSignalOnWithheldPaths(
|
|
4377
|
+
"A5",
|
|
4378
|
+
"reconciliation_artifact_depth",
|
|
4379
|
+
isClaimRealityReconciliationPath,
|
|
4380
|
+
useV23WithheldPathAbstention
|
|
4381
|
+
);
|
|
4382
|
+
abstainSignalOnWithheldPaths(
|
|
4383
|
+
"A5",
|
|
4384
|
+
"negative_space_documentation",
|
|
4385
|
+
isNegativeSpaceDocCandidate,
|
|
4386
|
+
useV23WithheldPathAbstention
|
|
4387
|
+
);
|
|
4300
4388
|
const reconciliationArtifacts = useV27Detectors ? findClaimRealityReconciliationArtifacts(repoPath, repoFiles) : [];
|
|
4301
4389
|
const claimSourceFiles = useV27Detectors ? findClaimSourceFiles(repoFiles) : repoFiles.filter(
|
|
4302
4390
|
(file) => /^(?:README|readme)\.md$/.test(file) || /^docs\/[^/]+\.(?:md|mdx)$/.test(file)
|
|
@@ -7031,9 +7119,10 @@ var MAIN_ENTRYPOINT_FILE_PATTERN = /(^|\/)(main|server|app|wsgi|asgi|index|entry
|
|
|
7031
7119
|
var RACK_ENTRYPOINT_FILE_PATTERN = /(^|\/)(?:config\.ru|(?:main|server|app|start|run)\.rb)$/i;
|
|
7032
7120
|
var SERVER_ENTRYPOINT_PATTERN = /\bapp\.listen\s*\(|\bserver\.listen\s*\(\s*(?:PORT|port|\d+)|http\.ListenAndServe\s*\(|http\.ListenAndServeTLS\s*\(|axum::Server(?:::|\.)|actix_web::HttpServer(?:::|\.)|uvicorn\.run\s*\(/;
|
|
7033
7121
|
var V20_DIRECT_HTTP_SERVER_PATTERN = /\b(?:http|https)\.createServer\s*\([\s\S]{0,1500}?\)\.listen\s*\(\s*(?:PORT|port|\d+)/;
|
|
7034
|
-
var
|
|
7035
|
-
var
|
|
7036
|
-
var
|
|
7122
|
+
var HEALTH_ROUTE_FUNCTIONS_DIR_PATTERN = /(^|\/)functions\/.*\.(?:ts|tsx|js|jsx|py|go|rs|java|rb|cpp|cc|cxx|c|h|hpp|kt|swift|php)$/;
|
|
7123
|
+
var V20_HEALTH_OR_READINESS_ROUTE_PATTERN = /["'`](?:[\w${}./-]{0,60}\/[_-]?(?:(?:health|ready|live)z?|readiness|liveness|up)(?=["'`/])|(?:healthz|readiness|liveness)(?=["'`]))|@Get\s*\(\s*(["'`])health\1\s*\)/i;
|
|
7124
|
+
var EXPRESS_ERROR_MIDDLEWARE_PATTERN = /\(\s*(?:_?err|_?error)(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*,\s*(?:req|request)(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*,\s*(?:res|response)(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*,\s*_?next(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*\)\s*(?:=>|\{)/i;
|
|
7125
|
+
var EXPRESS_MIDDLEWARE_REACHABLE_PATTERN = /\.use\s*\(|\bmodule\.exports\b|\bexport\s+(?:default\b|function\b|const\b|class\b)/;
|
|
7037
7126
|
var RACK_SERVER_ENTRYPOINT_PATTERN = /Rack::(?:Server|Handler(?:::\w+)?)\.(?:start|run)\s*\(/;
|
|
7038
7127
|
var RACK_CONFIG_RUN_PATTERN = /^\s*run\s+(?:(?:[A-Z]\w*(?:::\w+)*(?:\.new)?|lambda)\b|->)/m;
|
|
7039
7128
|
var RUNTIME_CONTAINER_COMMAND_PATTERN = /^\s*(?:CMD|ENTRYPOINT)\s+.*(?:\b(?:start|serve|server|qgis|nginx|apache|gunicorn|uvicorn)\b|manage\.py\s+runserver).*$/im;
|
|
@@ -7225,6 +7314,12 @@ function fileContains(repoPath, file, pattern) {
|
|
|
7225
7314
|
if (!isRegularFile(fullPath)) return false;
|
|
7226
7315
|
return pattern.test(readRepoText(fullPath, "utf8"));
|
|
7227
7316
|
}
|
|
7317
|
+
function fileMatchesOutsideLineComments(repoPath, file, pattern) {
|
|
7318
|
+
const fullPath = join(repoPath, file);
|
|
7319
|
+
if (!isRegularFile(fullPath)) return false;
|
|
7320
|
+
const withoutLineComments = readRepoText(fullPath, "utf8").replace(/(?<!:)\/\/.*$/gm, "");
|
|
7321
|
+
return pattern.test(withoutLineComments);
|
|
7322
|
+
}
|
|
7228
7323
|
var PEM_PRIVATE_KEY_ASSIGNMENT_PATTERN = /(?:["'])?([A-Za-z_$][\w$.-]*)(?:["'])?\s*[:=]\s*(?:\|[-+0-9]*\s*\r?\n[ \t]*)?["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\2-----/g;
|
|
7229
7324
|
var BARE_PEM_PRIVATE_KEY_PATTERN = /["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\1-----/g;
|
|
7230
7325
|
var BARE_PEM_KEY_FILE_PATTERN = /(?:^|\/)id_(?:rsa|dsa|ecdsa|ed25519)$|\.(?:pem|key)$/i;
|
|
@@ -7308,6 +7403,8 @@ function findCommittedSecretInFile(repoPath, file, useV36Detectors = false, useV
|
|
|
7308
7403
|
if (pemSecret) return pemSecret;
|
|
7309
7404
|
}
|
|
7310
7405
|
if (useV39Detectors && isEnvHistoryPath(file, useV36Detectors)) {
|
|
7406
|
+
const databaseUrlCredential = findDatabaseUrlUserinfoCredential(secretScanContents);
|
|
7407
|
+
if (databaseUrlCredential) return databaseUrlCredential;
|
|
7311
7408
|
const weakCredential = findWeakExplicitEnvCredential(secretScanContents);
|
|
7312
7409
|
if (weakCredential) return weakCredential;
|
|
7313
7410
|
}
|
|
@@ -7421,6 +7518,8 @@ function findSecretFingerprintsInFile(repoPath, file, useV36Detectors = false, u
|
|
|
7421
7518
|
if (pemSecret) fingerprints.push(pemSecret.valueFingerprint);
|
|
7422
7519
|
}
|
|
7423
7520
|
if (useV39Detectors && isEnvHistoryPath(file, useV36Detectors)) {
|
|
7521
|
+
const databaseUrlCredential = findDatabaseUrlUserinfoCredential(scanContents);
|
|
7522
|
+
if (databaseUrlCredential) fingerprints.push(databaseUrlCredential.valueFingerprint);
|
|
7424
7523
|
const weakCredential = findWeakExplicitEnvCredential(scanContents);
|
|
7425
7524
|
if (weakCredential) fingerprints.push(weakCredential.valueFingerprint);
|
|
7426
7525
|
}
|
|
@@ -7531,6 +7630,46 @@ function findWeakExplicitEnvCredential(contents) {
|
|
|
7531
7630
|
}
|
|
7532
7631
|
return null;
|
|
7533
7632
|
}
|
|
7633
|
+
function findDatabaseUrlUserinfoCredential(contents) {
|
|
7634
|
+
const lines = contents.split(/\r?\n/);
|
|
7635
|
+
for (const [index, line] of lines.entries()) {
|
|
7636
|
+
if (/^\s*#/.test(line)) continue;
|
|
7637
|
+
const match = /^\s*(?:export\s+)?(DATABASE_URL)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s#]+))/i.exec(line);
|
|
7638
|
+
const identifier = match?.[1];
|
|
7639
|
+
const rawValue = match?.[2] ?? match?.[3] ?? match?.[4];
|
|
7640
|
+
if (!identifier || !rawValue) continue;
|
|
7641
|
+
let parsed;
|
|
7642
|
+
try {
|
|
7643
|
+
parsed = new URL(rawValue);
|
|
7644
|
+
} catch {
|
|
7645
|
+
continue;
|
|
7646
|
+
}
|
|
7647
|
+
if (parsed.protocol !== "postgres:" && parsed.protocol !== "postgresql:" || !parsed.username || !parsed.password) {
|
|
7648
|
+
continue;
|
|
7649
|
+
}
|
|
7650
|
+
let password;
|
|
7651
|
+
try {
|
|
7652
|
+
password = decodeURIComponent(parsed.password);
|
|
7653
|
+
} catch {
|
|
7654
|
+
continue;
|
|
7655
|
+
}
|
|
7656
|
+
if (isPlaceholderSecretValue(password)) continue;
|
|
7657
|
+
const characterClasses = [
|
|
7658
|
+
/[a-z]/.test(password) ? "lower" : "",
|
|
7659
|
+
/[A-Z]/.test(password) ? "upper" : "",
|
|
7660
|
+
/\d/.test(password) ? "digit" : "",
|
|
7661
|
+
/[^A-Za-z0-9]/.test(password) ? "symbol" : ""
|
|
7662
|
+
].filter(Boolean).join("+");
|
|
7663
|
+
return {
|
|
7664
|
+
identifier,
|
|
7665
|
+
line: index + 1,
|
|
7666
|
+
valueLength: password.length,
|
|
7667
|
+
characterClasses: characterClasses || "other",
|
|
7668
|
+
valueFingerprint: createHash2("sha256").update(password).digest("hex")
|
|
7669
|
+
};
|
|
7670
|
+
}
|
|
7671
|
+
return null;
|
|
7672
|
+
}
|
|
7534
7673
|
function findDefaultAdministrativeCredential(contents) {
|
|
7535
7674
|
const lines = contents.split(/\r?\n/);
|
|
7536
7675
|
for (const [index, line] of lines.entries()) {
|
|
@@ -7608,6 +7747,12 @@ function collectHistorySecretEvidence(repoPath, excludeHead, currentSecretFinger
|
|
|
7608
7747
|
secretMatch = pemMatch;
|
|
7609
7748
|
}
|
|
7610
7749
|
}
|
|
7750
|
+
if (!secretMatch && useV39Detectors && isEnvHistoryPath(entry.path, useV36Detectors, useV47Detectors)) {
|
|
7751
|
+
const databaseUrlCredential = findDatabaseUrlUserinfoCredential(scanContents);
|
|
7752
|
+
if (databaseUrlCredential && !currentFingerprints.has(databaseUrlCredential.valueFingerprint)) {
|
|
7753
|
+
secretMatch = databaseUrlCredential;
|
|
7754
|
+
}
|
|
7755
|
+
}
|
|
7611
7756
|
if (!secretMatch && useV39Detectors && isEnvHistoryPath(entry.path, useV36Detectors, useV47Detectors)) {
|
|
7612
7757
|
const weakCredential = findWeakExplicitEnvCredential(scanContents);
|
|
7613
7758
|
if (weakCredential && !currentFingerprints.has(weakCredential.valueFingerprint)) {
|
|
@@ -8684,6 +8829,10 @@ function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC, toolVersi
|
|
|
8684
8829
|
...consumedSignals.length > 0 ? { consumedSignals } : {},
|
|
8685
8830
|
...parsedInput.scanLimitations.length > 0 ? { scanLimitations: parsedInput.scanLimitations } : {},
|
|
8686
8831
|
...parsedInput.contentReadSummary ? { contentReadSummary: parsedInput.contentReadSummary } : {},
|
|
8832
|
+
// Always set alongside contentReadSummary: parsedInput.withheldPaths defaults to [] on the
|
|
8833
|
+
// input schema, so this is never omitted for a report this build produces — its presence
|
|
8834
|
+
// (even empty) is the disclosure itself (goal_cejel_withheld_paths_always_disclosed_2026-09-22).
|
|
8835
|
+
withheldPaths: parsedInput.withheldPaths,
|
|
8687
8836
|
// Only surface the full per-category map for rubrics with more than two buckets —
|
|
8688
8837
|
// the default two-category rubric is fully represented by codeTrustScore/processTrustScore.
|
|
8689
8838
|
...!abstained && categoryOrder.length > 2 ? { categoryScores: categoryScoreMap } : {},
|