@cejel/cejel 0.4.7 → 0.4.8
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 +6 -6
- package/dist/calibration/llm-detector.js +5 -5
- package/dist/{chunk-QTLLASOO.js → chunk-6R3VJGJ2.js} +270 -109
- package/dist/{chunk-EP7YYWEP.js → chunk-ERNYMFH4.js} +2 -2
- package/dist/{chunk-UYJBSQCV.js → chunk-J6R5OHN7.js} +1 -1
- package/dist/{chunk-OIGCYULH.js → chunk-KNQBI3IX.js} +1 -1
- package/dist/{chunk-ZNVILRHK.js → chunk-V3B2V4WA.js} +39 -15
- package/dist/{chunk-XHAETB7N.js → chunk-ZPQOZLB7.js} +1 -1
- package/dist/index.js +6 -5
- package/dist/mcp/index.js +5 -5
- package/dist/packs/d-series/index.js +4 -4
- package/dist/packs/decision-contracts/index.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ if (-not $expected -or $actual -ne $expected.ToLowerInvariant()) {
|
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
> **Windows signing status:** `cejel-Windows-x86_64.exe` is intentionally unsigned in
|
|
96
|
-
> 0.4.
|
|
96
|
+
> 0.4.8 and may trigger Microsoft SmartScreen. The release build removes Node's inherited
|
|
97
97
|
> signature before SEA injection and fails unless Windows reports the result as `NotSigned`;
|
|
98
98
|
> it does not ship an invalid signature. Before running it, verify `SHA256SUMS` and the
|
|
99
99
|
> GitHub build-provenance attestation. Each binary also has an attached SPDX SBOM and an
|
|
@@ -116,7 +116,7 @@ docker run --rm --network=none -v "$PWD:/w" -w /w -v "$PWD/cejel:/cejel:ro" debi
|
|
|
116
116
|
npx @cejel/cejel@latest .
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
> **Distribution note:** Cejel `0.4.
|
|
119
|
+
> **Distribution note:** Cejel `0.4.8` is the coordinated release version for npm,
|
|
120
120
|
> standalone binaries, Docker/OCI, GitHub Action, Homebrew, and MCP Registry.
|
|
121
121
|
|
|
122
122
|
`npx` can reuse a stale cached package. Force the current npm release with the `@latest`
|
|
@@ -178,16 +178,16 @@ gh attestation verify ./cejel-Windows-x86_64.exe -R BargLabs/cejel
|
|
|
178
178
|
This is cryptographically signed provenance. It is distinct from Apple Developer ID or
|
|
179
179
|
Microsoft Authenticode code-signing.
|
|
180
180
|
|
|
181
|
-
**Docker / OCI.** The current container release is `0.4.
|
|
181
|
+
**Docker / OCI.** The current container release is `0.4.8`:
|
|
182
182
|
|
|
183
183
|
```bash
|
|
184
|
-
docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.4.
|
|
184
|
+
docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.4.8
|
|
185
185
|
```
|
|
186
186
|
|
|
187
187
|
The image defaults to `cejel-mcp` over stdio. To use the CLI instead:
|
|
188
188
|
|
|
189
189
|
```bash
|
|
190
|
-
docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.4.
|
|
190
|
+
docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.4.8 .
|
|
191
191
|
```
|
|
192
192
|
|
|
193
193
|
The OCI image carries an SBOM, maximum-mode build provenance, and a signed registry
|
|
@@ -519,7 +519,7 @@ The OCI image is an alternative when Docker is the preferred execution boundary.
|
|
|
519
519
|
the host path with the repository OpenClaw should allow Cejel to read:
|
|
520
520
|
|
|
521
521
|
```bash
|
|
522
|
-
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.
|
|
522
|
+
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.8"]}'
|
|
523
523
|
npx -y openclaw@latest mcp doctor cejel-oci --probe
|
|
524
524
|
```
|
|
525
525
|
|
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
deriveProductIdentity,
|
|
6
6
|
runCejelScan,
|
|
7
7
|
serializeWitanReport
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-6R3VJGJ2.js";
|
|
9
9
|
import {
|
|
10
10
|
listCejelLlmPackFiles
|
|
11
|
-
} from "../chunk-
|
|
12
|
-
import "../chunk-
|
|
11
|
+
} from "../chunk-ERNYMFH4.js";
|
|
12
|
+
import "../chunk-KNQBI3IX.js";
|
|
13
13
|
import {
|
|
14
14
|
external_exports
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-J6R5OHN7.js";
|
|
16
16
|
import {
|
|
17
17
|
init_esm_shims,
|
|
18
18
|
readRepoText,
|
|
19
19
|
recordContentSkip,
|
|
20
20
|
recordFilesystemSkip
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-V3B2V4WA.js";
|
|
22
22
|
|
|
23
23
|
// src/calibration/llm-detector.ts
|
|
24
24
|
init_esm_shims();
|
|
@@ -5,24 +5,24 @@ import {
|
|
|
5
5
|
describeGitFailure,
|
|
6
6
|
execGit,
|
|
7
7
|
isExpectedGitAbsence
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KNQBI3IX.js";
|
|
9
9
|
import {
|
|
10
10
|
external_exports
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-J6R5OHN7.js";
|
|
12
12
|
import {
|
|
13
13
|
MAX_REPOSITORY_CONTENT_BYTES,
|
|
14
|
+
classifyAbstentionReason,
|
|
14
15
|
contentReadSignalKey,
|
|
15
16
|
init_esm_shims,
|
|
16
17
|
readRepoText,
|
|
17
18
|
readRepoTextPrefix,
|
|
18
19
|
recordContentSkip,
|
|
19
|
-
recordContentSkipForCriteria,
|
|
20
20
|
recordFilesystemSkip,
|
|
21
21
|
trackContentReads,
|
|
22
22
|
withContentReadCriterion,
|
|
23
23
|
withContentReadSignal,
|
|
24
24
|
withContentReadSignals
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-V3B2V4WA.js";
|
|
26
26
|
|
|
27
27
|
// src/witan/schemas.ts
|
|
28
28
|
init_esm_shims();
|
|
@@ -162,6 +162,13 @@ var WitanCriterionMetricSchema = external_exports.object({
|
|
|
162
162
|
condition: external_exports.string().min(1).max(240).optional()
|
|
163
163
|
}).strict().optional()
|
|
164
164
|
}).strict();
|
|
165
|
+
var WitanContentReadSkipReasonSchema = external_exports.enum([
|
|
166
|
+
"unreadable",
|
|
167
|
+
"too_large",
|
|
168
|
+
"excluded_by_extension",
|
|
169
|
+
"denied_path",
|
|
170
|
+
"non_regular_file"
|
|
171
|
+
]);
|
|
165
172
|
var WitanCriterionSignalSchema = external_exports.object({
|
|
166
173
|
criterionId: WitanCriterionIdSchema,
|
|
167
174
|
positiveEvidence: external_exports.array(WitanEvidencePointerSchema).default([]),
|
|
@@ -171,9 +178,19 @@ var WitanCriterionSignalSchema = external_exports.object({
|
|
|
171
178
|
notApplicable: external_exports.literal(true).optional(),
|
|
172
179
|
// A collector reached relevant files but could not read their contents. This is a
|
|
173
180
|
// measurement gap, never negative evidence: scoreCriterion maps it to insufficient_data.
|
|
174
|
-
insufficientData: external_exports.literal(true).optional()
|
|
181
|
+
insufficientData: external_exports.literal(true).optional(),
|
|
182
|
+
// Why insufficientData is true. Omitted (or 'unreadable'/'denied_path') means an
|
|
183
|
+
// environmental read failure — evidence Cejel expected to see and could not; that stays in
|
|
184
|
+
// the composite denominator at score 0 so losing evidence never flatters the certificate
|
|
185
|
+
// (ADR-0001 does not cover this case — it is genuine evidence loss). 'too_large',
|
|
186
|
+
// 'excluded_by_extension', and 'non_regular_file' are self-imposed coverage limits Cejel
|
|
187
|
+
// chose itself: those are excluded from the composite exactly like ordinary
|
|
188
|
+
// insufficient_data (ADR-0001: coverage is disclosed, never discounts a score).
|
|
189
|
+
insufficientDataReason: WitanContentReadSkipReasonSchema.optional()
|
|
175
190
|
}).strict().refine((signal) => !(signal.notApplicable && signal.insufficientData), {
|
|
176
191
|
message: "A criterion cannot be both not applicable and insufficient data."
|
|
192
|
+
}).refine((signal) => signal.insufficientDataReason === void 0 || signal.insufficientData === true, {
|
|
193
|
+
message: "insufficientDataReason may only be set alongside insufficientData: true."
|
|
177
194
|
});
|
|
178
195
|
var WitanContentReadSummarySchema = external_exports.object({
|
|
179
196
|
skipped: external_exports.number().int().nonnegative(),
|
|
@@ -301,7 +318,14 @@ var WitanReportCommonSchema = external_exports.object({
|
|
|
301
318
|
consumedSignals: external_exports.array(WitanConsumedSignalSummarySchema).optional(),
|
|
302
319
|
archetype: WitanRepoArchetypeSchema.optional(),
|
|
303
320
|
scanLimitations: external_exports.array(external_exports.string().min(1).max(1e3)).max(16).optional(),
|
|
304
|
-
contentReadSummary: WitanContentReadSummarySchema.optional()
|
|
321
|
+
contentReadSummary: WitanContentReadSummarySchema.optional(),
|
|
322
|
+
// The @cejel/cejel version that produced this report. Absent on reports from before this
|
|
323
|
+
// field existed and on any caller that does not supply one (createWitanReport never defaults
|
|
324
|
+
// it). Constant within an installed version, so re-running the same version on the same
|
|
325
|
+
// commit stays byte-stable (the exact property v0.3.1 removed generatedAt to protect); it
|
|
326
|
+
// differs across versions, which lets a consumer key incremental reuse on it
|
|
327
|
+
// (goal_cejel_0_4_8_abstention_scoring_fix_2026-09-08).
|
|
328
|
+
toolVersion: external_exports.string().min(1).max(80).optional()
|
|
305
329
|
});
|
|
306
330
|
var WitanScoredReportSchema = WitanReportCommonSchema.extend({
|
|
307
331
|
verdict: WitanScoredVerdictSchema,
|
|
@@ -342,7 +366,7 @@ var WitanReportSchema = external_exports.preprocess((value) => {
|
|
|
342
366
|
}, StrictWitanReportSchema);
|
|
343
367
|
var WITAN_ATTESTATION_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
344
368
|
var WITAN_ATTESTATION_PREDICATE_TYPE = "https://cejel.dev/attestations/scan/v1";
|
|
345
|
-
var WITAN_REPORT_FORMAT_VERSION = "1.
|
|
369
|
+
var WITAN_REPORT_FORMAT_VERSION = "1.1";
|
|
346
370
|
var WitanAttestationOutcomeSchema = external_exports.discriminatedUnion("status", [
|
|
347
371
|
external_exports.object({
|
|
348
372
|
status: external_exports.literal("scored"),
|
|
@@ -2007,29 +2031,56 @@ function inventoryAbsenceFinding(options) {
|
|
|
2007
2031
|
} : {}
|
|
2008
2032
|
};
|
|
2009
2033
|
}
|
|
2010
|
-
function wholesaleCriterionAbstention(signal) {
|
|
2034
|
+
function wholesaleCriterionAbstention(signal, reasons) {
|
|
2035
|
+
const { insufficientDataReason } = classifyAbstentionReason(reasons);
|
|
2011
2036
|
return {
|
|
2012
2037
|
criterionId: signal.criterionId,
|
|
2013
2038
|
positiveEvidence: [],
|
|
2014
2039
|
findings: [],
|
|
2015
2040
|
metrics: [],
|
|
2016
2041
|
insufficientData: true,
|
|
2017
|
-
|
|
2042
|
+
...insufficientDataReason !== void 0 ? { insufficientDataReason } : {},
|
|
2043
|
+
notes: insufficientDataReason !== void 0 ? "Cejel abstained on this criterion because relevant repository content was declined under the repository content size limit or excluded from scanning by policy. This is a disclosed coverage limit, not a read failure, and does not affect the composite score." : "Cejel abstained on this criterion because one or more relevant repository files could not be read. Resolve the content-read limitations and re-scan."
|
|
2018
2044
|
};
|
|
2019
2045
|
}
|
|
2046
|
+
function skipReasonsForCriterion(criterionId, tracked) {
|
|
2047
|
+
const combined = new Set(tracked.criterionSkipReasons.get(criterionId) ?? []);
|
|
2048
|
+
const prefix = contentReadSignalKey(criterionId, "");
|
|
2049
|
+
for (const [key, reasons] of tracked.signalSkipReasons) {
|
|
2050
|
+
if (!key.startsWith(prefix)) continue;
|
|
2051
|
+
for (const reason of reasons) combined.add(reason);
|
|
2052
|
+
}
|
|
2053
|
+
return combined;
|
|
2054
|
+
}
|
|
2055
|
+
function skipReasonsForSignal(criterionId, signalName, tracked) {
|
|
2056
|
+
const reasons = tracked.signalSkipReasons.get(contentReadSignalKey(criterionId, signalName));
|
|
2057
|
+
return reasons ? new Set(reasons) : void 0;
|
|
2058
|
+
}
|
|
2020
2059
|
function buildWitanInputFromRepo(options) {
|
|
2021
2060
|
const tracked = trackContentReads(options.repoPath, () => buildWitanInputFromRepoUntracked(options));
|
|
2022
2061
|
const affectedCriteria = tracked.affectedCriteria;
|
|
2023
2062
|
const rubricVersion = options.rubricVersion ?? WITAN_RUBRIC_VERSION;
|
|
2024
2063
|
if (rubricVersion !== WITAN_RUBRIC_VERSION_V23) {
|
|
2025
|
-
const
|
|
2026
|
-
|
|
2027
|
-
)
|
|
2028
|
-
|
|
2064
|
+
const readFailureCriteria = [];
|
|
2065
|
+
const coverageLimitedCriteria = [];
|
|
2066
|
+
const signals2 = (tracked.value.signals ?? []).map((signal) => {
|
|
2067
|
+
if (!affectedCriteria.has(signal.criterionId)) return signal;
|
|
2068
|
+
const reasons = skipReasonsForCriterion(signal.criterionId, tracked);
|
|
2069
|
+
const { insufficientDataReason } = classifyAbstentionReason(reasons);
|
|
2070
|
+
(insufficientDataReason !== void 0 ? coverageLimitedCriteria : readFailureCriteria).push(
|
|
2071
|
+
signal.criterionId
|
|
2072
|
+
);
|
|
2073
|
+
return wholesaleCriterionAbstention(signal, reasons);
|
|
2074
|
+
});
|
|
2029
2075
|
const scanLimitations2 = [...tracked.value.scanLimitations ?? []];
|
|
2030
|
-
if (
|
|
2076
|
+
if (readFailureCriteria.length > 0 && scanLimitations2.length < 16) {
|
|
2077
|
+
scanLimitations2.push(
|
|
2078
|
+
`${readFailureCriteria.length} ${readFailureCriteria.length === 1 ? "criterion" : "criteria"} abstained because relevant repository content could not be read. Counts and errno classes are recorded in contentReadSummary; file paths are intentionally omitted.`
|
|
2079
|
+
);
|
|
2080
|
+
}
|
|
2081
|
+
if (coverageLimitedCriteria.length > 0 && scanLimitations2.length < 16) {
|
|
2031
2082
|
scanLimitations2.push(
|
|
2032
|
-
`${
|
|
2083
|
+
`${coverageLimitedCriteria.length} ${coverageLimitedCriteria.length === 1 ? "criterion" : "criteria"} abstained because relevant repository content was declined under the repository content size limit or excluded from scanning by policy \u2014 a disclosed coverage limit, not a read failure. This does not affect the composite score.`
|
|
2033
2084
|
);
|
|
2034
2085
|
}
|
|
2035
2086
|
return {
|
|
@@ -2039,45 +2090,68 @@ function buildWitanInputFromRepo(options) {
|
|
|
2039
2090
|
contentReadSummary: tracked.summary
|
|
2040
2091
|
};
|
|
2041
2092
|
}
|
|
2042
|
-
const
|
|
2043
|
-
const
|
|
2093
|
+
const wholesaleAbstainedReadFailureCriteria = [];
|
|
2094
|
+
const wholesaleAbstainedCoverageLimitedCriteria = [];
|
|
2095
|
+
const partiallyAbstainedReadFailureSignals = [];
|
|
2096
|
+
const partiallyAbstainedCoverageLimitedSignals = [];
|
|
2044
2097
|
const signals = (tracked.value.signals ?? []).map((signal) => {
|
|
2045
2098
|
if (!affectedCriteria.has(signal.criterionId)) return signal;
|
|
2046
2099
|
if (tracked.unattributedCriteria.has(signal.criterionId)) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2100
|
+
const reasons = tracked.criterionSkipReasons.get(signal.criterionId);
|
|
2101
|
+
const { insufficientDataReason: insufficientDataReason2 } = classifyAbstentionReason(reasons);
|
|
2102
|
+
(insufficientDataReason2 !== void 0 ? wholesaleAbstainedCoverageLimitedCriteria : wholesaleAbstainedReadFailureCriteria).push(signal.criterionId);
|
|
2103
|
+
return wholesaleCriterionAbstention(signal, reasons);
|
|
2049
2104
|
}
|
|
2050
2105
|
const prefix = contentReadSignalKey(signal.criterionId, "");
|
|
2051
2106
|
const affectedNames = [...tracked.affectedSignals].filter((key) => key.startsWith(prefix)).map((key) => key.slice(prefix.length));
|
|
2052
2107
|
if (affectedNames.length === 0) {
|
|
2053
|
-
|
|
2108
|
+
wholesaleAbstainedReadFailureCriteria.push(signal.criterionId);
|
|
2054
2109
|
return wholesaleCriterionAbstention(signal);
|
|
2055
2110
|
}
|
|
2056
2111
|
const affectedNameSet = new Set(affectedNames);
|
|
2057
2112
|
const metrics = (signal.metrics ?? []).filter((metric2) => !affectedNameSet.has(metric2.name));
|
|
2058
2113
|
const anythingSurvives = metrics.length > 0 || (signal.positiveEvidence?.length ?? 0) > 0 || (signal.findings?.length ?? 0) > 0;
|
|
2114
|
+
const droppedReasons = /* @__PURE__ */ new Set();
|
|
2115
|
+
for (const name of affectedNames) {
|
|
2116
|
+
for (const reason of skipReasonsForSignal(signal.criterionId, name, tracked) ?? []) {
|
|
2117
|
+
droppedReasons.add(reason);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2059
2120
|
if (!anythingSurvives) {
|
|
2060
|
-
|
|
2061
|
-
|
|
2121
|
+
const { insufficientDataReason: insufficientDataReason2 } = classifyAbstentionReason(droppedReasons);
|
|
2122
|
+
(insufficientDataReason2 !== void 0 ? wholesaleAbstainedCoverageLimitedCriteria : wholesaleAbstainedReadFailureCriteria).push(signal.criterionId);
|
|
2123
|
+
return wholesaleCriterionAbstention(signal, droppedReasons);
|
|
2062
2124
|
}
|
|
2125
|
+
const { insufficientDataReason } = classifyAbstentionReason(droppedReasons);
|
|
2126
|
+
const isReadFailure = insufficientDataReason === void 0;
|
|
2063
2127
|
for (const name of affectedNames) {
|
|
2064
|
-
|
|
2128
|
+
(isReadFailure ? partiallyAbstainedReadFailureSignals : partiallyAbstainedCoverageLimitedSignals).push(`${signal.criterionId}.${name}`);
|
|
2065
2129
|
}
|
|
2066
2130
|
return {
|
|
2067
2131
|
...signal,
|
|
2068
2132
|
metrics,
|
|
2069
|
-
notes: `Cejel abstained on ${[...affectedNames].sort().join(", ")} in this criterion because that content could not be read; other signals in this criterion were computed from readable files and are unaffected.`
|
|
2133
|
+
notes: isReadFailure ? `Cejel abstained on ${[...affectedNames].sort().join(", ")} in this criterion because that content could not be read; other signals in this criterion were computed from readable files and are unaffected.` : `Cejel declined ${[...affectedNames].sort().join(", ")} in this criterion under the repository content size limit or an extension exclusion; other signals in this criterion were computed from readable files and are unaffected. This is a disclosed coverage limit, not a read failure.`
|
|
2070
2134
|
};
|
|
2071
2135
|
});
|
|
2072
2136
|
const scanLimitations = [...tracked.value.scanLimitations ?? []];
|
|
2073
|
-
if (
|
|
2137
|
+
if (wholesaleAbstainedReadFailureCriteria.length > 0 && scanLimitations.length < 16) {
|
|
2138
|
+
scanLimitations.push(
|
|
2139
|
+
`${wholesaleAbstainedReadFailureCriteria.length} ${wholesaleAbstainedReadFailureCriteria.length === 1 ? "criterion" : "criteria"} abstained because relevant repository content could not be read and the affected signal could not be isolated. Counts and errno classes are recorded in contentReadSummary; file paths are intentionally omitted.`
|
|
2140
|
+
);
|
|
2141
|
+
}
|
|
2142
|
+
if (wholesaleAbstainedCoverageLimitedCriteria.length > 0 && scanLimitations.length < 16) {
|
|
2143
|
+
scanLimitations.push(
|
|
2144
|
+
`${wholesaleAbstainedCoverageLimitedCriteria.length} ${wholesaleAbstainedCoverageLimitedCriteria.length === 1 ? "criterion" : "criteria"} abstained because relevant repository content was declined under the repository content size limit or excluded from scanning by policy, and the affected signal could not be isolated. This is a disclosed coverage limit, not a read failure, and does not affect the composite score.`
|
|
2145
|
+
);
|
|
2146
|
+
}
|
|
2147
|
+
if (partiallyAbstainedReadFailureSignals.length > 0 && scanLimitations.length < 16) {
|
|
2074
2148
|
scanLimitations.push(
|
|
2075
|
-
`${
|
|
2149
|
+
`${partiallyAbstainedReadFailureSignals.length} ${partiallyAbstainedReadFailureSignals.length === 1 ? "signal" : "signals"} abstained because its own repository content could not be read, while other signals in the same criterion were unaffected: ${[...partiallyAbstainedReadFailureSignals].sort().join(", ")}.`
|
|
2076
2150
|
);
|
|
2077
2151
|
}
|
|
2078
|
-
if (
|
|
2152
|
+
if (partiallyAbstainedCoverageLimitedSignals.length > 0 && scanLimitations.length < 16) {
|
|
2079
2153
|
scanLimitations.push(
|
|
2080
|
-
`${
|
|
2154
|
+
`${partiallyAbstainedCoverageLimitedSignals.length} ${partiallyAbstainedCoverageLimitedSignals.length === 1 ? "signal" : "signals"} declined under the repository content size limit or an extension exclusion, while other signals in the same criterion were unaffected: ${[...partiallyAbstainedCoverageLimitedSignals].sort().join(", ")}. This is a disclosed coverage limit, not a read failure.`
|
|
2081
2155
|
);
|
|
2082
2156
|
}
|
|
2083
2157
|
return {
|
|
@@ -2107,6 +2181,7 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
2107
2181
|
const usesV21ExecutedEscalations = rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
|
|
2108
2182
|
const usesV22PackageStartEntrypoint = rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
|
|
2109
2183
|
const usesV23CommandCoverage = rubricVersion === WITAN_RUBRIC_VERSION_V23;
|
|
2184
|
+
const usesV23PemPrivateKeyGrammar = rubricVersion === WITAN_RUBRIC_VERSION_V23;
|
|
2110
2185
|
const structuralArchetype = classifyRepoArchetype(inventoryFiles, rubricVersion);
|
|
2111
2186
|
const readableArchetype = rubricVersion === WITAN_RUBRIC_VERSION_V13 || rubricVersion === WITAN_RUBRIC_VERSION_V14 || rubricVersion === WITAN_RUBRIC_VERSION_V15 || rubricVersion === WITAN_RUBRIC_VERSION_V16 || usesV17DetectorClosure(rubricVersion) ? applyReadableSourceRepresentationGate(
|
|
2112
2187
|
options.repoPath,
|
|
@@ -2143,6 +2218,7 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
2143
2218
|
usesV21ExecutedEscalations,
|
|
2144
2219
|
usesV22PackageStartEntrypoint,
|
|
2145
2220
|
usesV23CommandCoverage,
|
|
2221
|
+
usesV23PemPrivateKeyGrammar,
|
|
2146
2222
|
reviewableSourceProof,
|
|
2147
2223
|
inventoryAbsenceContext,
|
|
2148
2224
|
scanLimitations
|
|
@@ -2754,7 +2830,7 @@ function buildNotApplicableSignal(criterionId, reason) {
|
|
|
2754
2830
|
notApplicable: true
|
|
2755
2831
|
};
|
|
2756
2832
|
}
|
|
2757
|
-
function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, useV23CommandCoverage, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set()) {
|
|
2833
|
+
function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, useV23CommandCoverage, useV23PemPrivateKeyGrammar, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set()) {
|
|
2758
2834
|
const signals = [];
|
|
2759
2835
|
const mono = resolveMonorepoContext(repoPath, scanLimitations);
|
|
2760
2836
|
const collectCriterion = (criterionId, collect) => withContentReadCriterion(criterionId, collect);
|
|
@@ -2781,6 +2857,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2781
2857
|
useV36Detectors,
|
|
2782
2858
|
useV39Detectors,
|
|
2783
2859
|
useV47Detectors,
|
|
2860
|
+
useV23PemPrivateKeyGrammar,
|
|
2784
2861
|
useV18NativeRls,
|
|
2785
2862
|
inventoryAbsenceContext,
|
|
2786
2863
|
scanLimitations
|
|
@@ -3196,7 +3273,7 @@ function buildA1AuthenticatedAbsenceSignal(repoPath, proof, inventoryCount, inve
|
|
|
3196
3273
|
notes: `V17 authenticated absence: ${proof.eligibleSourceFileCount} criterion-ratable of ${proof.sourceShapedFileCount} source-shaped files; sampled ${proof.sampledFileCount}; ${proof.readableSampledFileCount} representative-text files.`
|
|
3197
3274
|
};
|
|
3198
3275
|
}
|
|
3199
|
-
function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, inventoryAbsenceContext, scanLimitations) {
|
|
3276
|
+
function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV23PemPrivateKeyGrammar, useV18NativeRls, inventoryAbsenceContext, scanLimitations) {
|
|
3200
3277
|
const evidence = [];
|
|
3201
3278
|
const findings = [];
|
|
3202
3279
|
const gitignore = repoFiles.find((file) => basename(file) === ".gitignore");
|
|
@@ -3219,7 +3296,8 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3219
3296
|
path,
|
|
3220
3297
|
useV36Detectors,
|
|
3221
3298
|
useV39Detectors,
|
|
3222
|
-
useV47Detectors
|
|
3299
|
+
useV47Detectors,
|
|
3300
|
+
useV23PemPrivateKeyGrammar
|
|
3223
3301
|
);
|
|
3224
3302
|
if (!found) continue;
|
|
3225
3303
|
match_ = { path, match: found };
|
|
@@ -3227,7 +3305,9 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3227
3305
|
}
|
|
3228
3306
|
const fingerprintsByPath = /* @__PURE__ */ new Map();
|
|
3229
3307
|
if (useV27Detectors) {
|
|
3230
|
-
for (const path of repoFiles.filter(
|
|
3308
|
+
for (const path of repoFiles.filter(
|
|
3309
|
+
(candidate) => isCredentialHistoryPath(candidate, useV36Detectors, useV23PemPrivateKeyGrammar)
|
|
3310
|
+
)) {
|
|
3231
3311
|
fingerprintsByPath.set(
|
|
3232
3312
|
path,
|
|
3233
3313
|
new Set(
|
|
@@ -3236,7 +3316,8 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3236
3316
|
path,
|
|
3237
3317
|
useV36Detectors,
|
|
3238
3318
|
useV39Detectors,
|
|
3239
|
-
useV47Detectors
|
|
3319
|
+
useV47Detectors,
|
|
3320
|
+
useV23PemPrivateKeyGrammar
|
|
3240
3321
|
)
|
|
3241
3322
|
)
|
|
3242
3323
|
);
|
|
@@ -3253,7 +3334,8 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3253
3334
|
useV33Detectors,
|
|
3254
3335
|
useV36Detectors,
|
|
3255
3336
|
useV39Detectors,
|
|
3256
|
-
useV47Detectors
|
|
3337
|
+
useV47Detectors,
|
|
3338
|
+
useV23PemPrivateKeyGrammar
|
|
3257
3339
|
);
|
|
3258
3340
|
const hasConfirmedSecretFinding = committedSecret != null || historySecretScan?.evidence != null;
|
|
3259
3341
|
const cryptoHygiene = withContentReadSignal(
|
|
@@ -3380,15 +3462,16 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3380
3462
|
if (committedSecret) {
|
|
3381
3463
|
const isTestPath = isTestOrFixturePath(committedSecret.path);
|
|
3382
3464
|
const isDefaultAdmin = committedSecret.match.kind === "default_admin";
|
|
3465
|
+
const isPemPrivateKey = committedSecret.match.kind === "pem_private_key";
|
|
3383
3466
|
findings.push({
|
|
3384
3467
|
severity: isTestPath ? "info" : "critical",
|
|
3385
|
-
summary: isDefaultAdmin ? isTestPath ? `Default administrative credential in a test/fixture file (${committedSecret.path}) \u2014 likely fixture data, not a production deployment default; verify.` : "An explicit default administrative credential appears committed in production configuration." : isTestPath ? `Secret-shaped value in a test/fixture file (${committedSecret.path}) \u2014 likely fixture data, not a production leak; verify.` : "Secret-shaped value appears committed in the scanned repository.",
|
|
3468
|
+
summary: isDefaultAdmin ? isTestPath ? `Default administrative credential in a test/fixture file (${committedSecret.path}) \u2014 likely fixture data, not a production deployment default; verify.` : "An explicit default administrative credential appears committed in production configuration." : isPemPrivateKey ? isTestPath ? `PEM-formatted private key in a test/fixture file (${committedSecret.path}) \u2014 likely fixture data, not a production leak; verify.` : "A PEM-formatted private key appears committed in the scanned repository." : isTestPath ? `Secret-shaped value in a test/fixture file (${committedSecret.path}) \u2014 likely fixture data, not a production leak; verify.` : "Secret-shaped value appears committed in the scanned repository.",
|
|
3386
3469
|
evidence: evidenceForRelativeAtLine(
|
|
3387
3470
|
repoPath,
|
|
3388
3471
|
committedSecret.path,
|
|
3389
3472
|
"secret_scan",
|
|
3390
3473
|
secretEvidenceLabel(
|
|
3391
|
-
isDefaultAdmin ? "Committed default administrative credential" : "Committed secret-shaped value",
|
|
3474
|
+
isDefaultAdmin ? "Committed default administrative credential" : isPemPrivateKey ? "Committed PEM-formatted private key" : "Committed secret-shaped value",
|
|
3392
3475
|
committedSecret.match
|
|
3393
3476
|
),
|
|
3394
3477
|
committedSecret.match.line
|
|
@@ -3419,9 +3502,10 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3419
3502
|
if (historySecretScan?.evidence) {
|
|
3420
3503
|
const historyPath = historySecretScan.evidence.path ?? "";
|
|
3421
3504
|
const isTestPath = isTestOrFixturePath(historyPath);
|
|
3505
|
+
const isPemPrivateKey = historySecretScan.secretKind === "pem_private_key";
|
|
3422
3506
|
findings.push({
|
|
3423
3507
|
severity: isTestPath ? "info" : "critical",
|
|
3424
|
-
summary: isTestPath ? `Secret-shaped value in git history for a test/fixture file (${historyPath}) \u2014 likely fixture data, not a production leak; verify.` : "Secret-shaped value appears in recent git history.",
|
|
3508
|
+
summary: isPemPrivateKey ? isTestPath ? `PEM-formatted private key in git history for a test/fixture file (${historyPath}) \u2014 likely fixture data, not a production leak; verify.` : "A PEM-formatted private key appears in recent git history." : isTestPath ? `Secret-shaped value in git history for a test/fixture file (${historyPath}) \u2014 likely fixture data, not a production leak; verify.` : "Secret-shaped value appears in recent git history.",
|
|
3425
3509
|
evidence: historySecretScan.evidence
|
|
3426
3510
|
});
|
|
3427
3511
|
} else if (historySecretScan?.truncated) {
|
|
@@ -4803,6 +4887,7 @@ var GATED_PRIVILEGE_CHECK_PATTERN = /pg_has_role\s*\(|has_role\s*\(|is not a mem
|
|
|
4803
4887
|
var SET_ROLE_PATTERN = /\bset\s+(local\s+)?role\b/i;
|
|
4804
4888
|
var REQUIRED_REVIEW_PATTERN = /required review|branch protection|protected branch/i;
|
|
4805
4889
|
var HISTORY_SECRET_SCAN_CREDENTIAL_BLOB_LIMIT = 5e3;
|
|
4890
|
+
var V23_PEM_WIDENED_HISTORY_BLOB_LIMIT = 500;
|
|
4806
4891
|
var SECRET_IDENTIFIER_KEYWORD_PATTERN = /secret|token|api[_-]?key|password|access[_-]?key/gi;
|
|
4807
4892
|
var SINGLE_WHITESPACE_PATTERN = /\s/u;
|
|
4808
4893
|
var SECRET_VALUE_BRANDED_PATTERN = /^(?:sk-[A-Za-z0-9_-]{24,}|sk-ant-api\d{2}-[A-Za-z0-9_-]{80,}|ghp_[A-Za-z0-9_]{30,}|xox[baprs]-[A-Za-z0-9-]{24,}|AKIA[0-9A-Z]{16}|vcp_[A-Za-z0-9_:-]{24,})$/;
|
|
@@ -4962,12 +5047,7 @@ function parseGitTrackedFiles(repoPath, output, includeHardExcluded) {
|
|
|
4962
5047
|
continue;
|
|
4963
5048
|
}
|
|
4964
5049
|
if (stat.size > MAX_REPOSITORY_CONTENT_BYTES) {
|
|
4965
|
-
|
|
4966
|
-
if (affectedCriteria.length > 0) {
|
|
4967
|
-
recordContentSkipForCriteria(fullPath, "too_large", affectedCriteria, true);
|
|
4968
|
-
} else {
|
|
4969
|
-
recordContentSkip(fullPath, "too_large", true);
|
|
4970
|
-
}
|
|
5050
|
+
recordContentSkip(fullPath, "too_large", true);
|
|
4971
5051
|
continue;
|
|
4972
5052
|
}
|
|
4973
5053
|
} catch (error) {
|
|
@@ -4981,39 +5061,6 @@ function parseGitTrackedFiles(repoPath, output, includeHardExcluded) {
|
|
|
4981
5061
|
}
|
|
4982
5062
|
return files.sort();
|
|
4983
5063
|
}
|
|
4984
|
-
function affectedCriteriaForUnavailablePath(file) {
|
|
4985
|
-
const affected = /* @__PURE__ */ new Set();
|
|
4986
|
-
if (isDependencyManifest(file) || isLockfile(file)) affected.add("A4");
|
|
4987
|
-
if (isCiWorkflow(file)) {
|
|
4988
|
-
affected.add("A1");
|
|
4989
|
-
affected.add("A3");
|
|
4990
|
-
affected.add("B2");
|
|
4991
|
-
affected.add("B3");
|
|
4992
|
-
}
|
|
4993
|
-
if (isDeployConfig(file)) {
|
|
4994
|
-
affected.add("A3");
|
|
4995
|
-
affected.add("B2");
|
|
4996
|
-
}
|
|
4997
|
-
if (isTestFile(file)) {
|
|
4998
|
-
affected.add("A1");
|
|
4999
|
-
affected.add("A2");
|
|
5000
|
-
}
|
|
5001
|
-
if (isImplementationFile(file)) {
|
|
5002
|
-
affected.add("A1");
|
|
5003
|
-
affected.add("A2");
|
|
5004
|
-
affected.add("A3");
|
|
5005
|
-
affected.add("B1");
|
|
5006
|
-
affected.add("B6");
|
|
5007
|
-
}
|
|
5008
|
-
if (/\.(?:md|mdx|rst|txt)$/i.test(file)) {
|
|
5009
|
-
affected.add("A5");
|
|
5010
|
-
affected.add("B4");
|
|
5011
|
-
affected.add("B5");
|
|
5012
|
-
affected.add("B6");
|
|
5013
|
-
}
|
|
5014
|
-
if (isAuditFile(file)) affected.add("B5");
|
|
5015
|
-
return [...affected];
|
|
5016
|
-
}
|
|
5017
5064
|
function directoryInventory(repoPath) {
|
|
5018
5065
|
const files = [];
|
|
5019
5066
|
const rootDevice = statSync(repoPath).dev;
|
|
@@ -5135,12 +5182,7 @@ function visitRepoDir(repoPath, dirPath, files, rootDevice) {
|
|
|
5135
5182
|
}
|
|
5136
5183
|
const repoRelativePath = relative(repoPath, fullPath);
|
|
5137
5184
|
if (size > MAX_REPOSITORY_CONTENT_BYTES) {
|
|
5138
|
-
|
|
5139
|
-
if (affectedCriteria.length > 0) {
|
|
5140
|
-
recordContentSkipForCriteria(fullPath, "too_large", affectedCriteria);
|
|
5141
|
-
} else {
|
|
5142
|
-
recordContentSkip(fullPath, "too_large");
|
|
5143
|
-
}
|
|
5185
|
+
recordContentSkip(fullPath, "too_large");
|
|
5144
5186
|
continue;
|
|
5145
5187
|
}
|
|
5146
5188
|
if (!isPotentialContentPath(repoRelativePath)) {
|
|
@@ -7066,7 +7108,69 @@ function fileContains(repoPath, file, pattern) {
|
|
|
7066
7108
|
if (!isRegularFile(fullPath)) return false;
|
|
7067
7109
|
return pattern.test(readRepoText(fullPath, "utf8"));
|
|
7068
7110
|
}
|
|
7069
|
-
|
|
7111
|
+
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;
|
|
7112
|
+
var BARE_PEM_PRIVATE_KEY_PATTERN = /["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\1-----/g;
|
|
7113
|
+
var BARE_PEM_KEY_FILE_PATTERN = /(?:^|\/)id_(?:rsa|dsa|ecdsa|ed25519)$|\.(?:pem|key)$/i;
|
|
7114
|
+
function isBarePemKeyFile(file) {
|
|
7115
|
+
return BARE_PEM_KEY_FILE_PATTERN.test(file);
|
|
7116
|
+
}
|
|
7117
|
+
var DEV_OR_SELF_SIGNED_KEY_CONTEXT_PATTERN = /(?<![A-Za-z0-9])(?:dev|development|local|localhost|self[-_]?signed|snakeoil|insecure|dummy|sample|demo|placeholder|fixture|generated)(?![A-Za-z0-9])/i;
|
|
7118
|
+
var ROTATED_OR_REVOKED_KEY_CONTEXT_PATTERN = /(?<![A-Za-z0-9])(?:rotated|revoked|expired|deprecated|compromised|invalidated|superseded)(?![A-Za-z0-9])|do[-_ ]not[-_ ]use|no longer valid/i;
|
|
7119
|
+
function isPlausiblePemKeyBody(rawBody) {
|
|
7120
|
+
const normalized = rawBody.replace(/\\[nr]|[\r\n\s]/g, "");
|
|
7121
|
+
if (normalized.length < 40) return false;
|
|
7122
|
+
if (!/^[A-Za-z0-9+/=]+$/.test(normalized)) return false;
|
|
7123
|
+
return !isPlaceholderSecretValue(normalized);
|
|
7124
|
+
}
|
|
7125
|
+
function scanForPemPrivateKeyMatch(pattern, contents, lines, hasIdentifierGroup) {
|
|
7126
|
+
pattern.lastIndex = 0;
|
|
7127
|
+
let match;
|
|
7128
|
+
while ((match = pattern.exec(contents)) !== null) {
|
|
7129
|
+
const full = match[0];
|
|
7130
|
+
const identifier = hasIdentifierGroup ? match[1] ?? "" : "";
|
|
7131
|
+
const body = (hasIdentifierGroup ? match[3] : match[2]) ?? "";
|
|
7132
|
+
const line = contents.slice(0, match.index).split("\n").length;
|
|
7133
|
+
const contextLines = lines.slice(Math.max(0, line - 5), line + 4).join("\n");
|
|
7134
|
+
const isDevOrSelfSigned = DEV_OR_SELF_SIGNED_KEY_CONTEXT_PATTERN.test(
|
|
7135
|
+
`${identifier}
|
|
7136
|
+
${contextLines}`
|
|
7137
|
+
);
|
|
7138
|
+
const isRotatedOrRevoked = ROTATED_OR_REVOKED_KEY_CONTEXT_PATTERN.test(contextLines);
|
|
7139
|
+
if (isPlausiblePemKeyBody(body) && !isDevOrSelfSigned && !isRotatedOrRevoked) {
|
|
7140
|
+
const value = full.slice(full.indexOf("-----BEGIN"));
|
|
7141
|
+
const characterClasses = [
|
|
7142
|
+
/[a-z]/.test(value) ? "lower" : "",
|
|
7143
|
+
/[A-Z]/.test(value) ? "upper" : "",
|
|
7144
|
+
/\d/.test(value) ? "digit" : "",
|
|
7145
|
+
/[^A-Za-z0-9]/.test(value) ? "symbol" : ""
|
|
7146
|
+
].filter(Boolean).join("+");
|
|
7147
|
+
return {
|
|
7148
|
+
identifier,
|
|
7149
|
+
line,
|
|
7150
|
+
valueLength: value.length,
|
|
7151
|
+
characterClasses: characterClasses || "other",
|
|
7152
|
+
valueFingerprint: createHash2("sha256").update(value).digest("hex"),
|
|
7153
|
+
kind: "pem_private_key"
|
|
7154
|
+
};
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
return null;
|
|
7158
|
+
}
|
|
7159
|
+
function findPemPrivateKeyAssignment(contents, file) {
|
|
7160
|
+
if (!contents.includes("-----BEGIN")) return null;
|
|
7161
|
+
if (DEV_OR_SELF_SIGNED_KEY_CONTEXT_PATTERN.test(file)) return null;
|
|
7162
|
+
const lines = contents.split("\n");
|
|
7163
|
+
const assignmentMatch = scanForPemPrivateKeyMatch(
|
|
7164
|
+
PEM_PRIVATE_KEY_ASSIGNMENT_PATTERN,
|
|
7165
|
+
contents,
|
|
7166
|
+
lines,
|
|
7167
|
+
true
|
|
7168
|
+
);
|
|
7169
|
+
if (assignmentMatch) return assignmentMatch;
|
|
7170
|
+
if (!isBarePemKeyFile(file)) return null;
|
|
7171
|
+
return scanForPemPrivateKeyMatch(BARE_PEM_PRIVATE_KEY_PATTERN, contents, lines, false);
|
|
7172
|
+
}
|
|
7173
|
+
function findCommittedSecretInFile(repoPath, file, useV36Detectors = false, useV39Detectors = false, useV47Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7070
7174
|
const fullPath = join(repoPath, file);
|
|
7071
7175
|
if (!isRegularFile(fullPath)) return null;
|
|
7072
7176
|
const contents = readRepoText(fullPath, "utf8");
|
|
@@ -7082,6 +7186,10 @@ function findCommittedSecretInFile(repoPath, file, useV36Detectors = false, useV
|
|
|
7082
7186
|
allowCredentialNamedDigest: useV36Detectors
|
|
7083
7187
|
});
|
|
7084
7188
|
if (secret) return secret;
|
|
7189
|
+
if (useV23PemPrivateKeyGrammar) {
|
|
7190
|
+
const pemSecret = findPemPrivateKeyAssignment(secretScanContents, file);
|
|
7191
|
+
if (pemSecret) return pemSecret;
|
|
7192
|
+
}
|
|
7085
7193
|
if (useV39Detectors && isEnvHistoryPath(file, useV36Detectors)) {
|
|
7086
7194
|
const weakCredential = findWeakExplicitEnvCredential(secretScanContents);
|
|
7087
7195
|
if (weakCredential) return weakCredential;
|
|
@@ -7174,7 +7282,7 @@ function hasTemplateOnlyEnvContent(repoPath, file) {
|
|
|
7174
7282
|
return value.length === 0 || isPlaceholderSecretValue(value);
|
|
7175
7283
|
});
|
|
7176
7284
|
}
|
|
7177
|
-
function findSecretFingerprintsInFile(repoPath, file, useV36Detectors = false, useV39Detectors = false, useV47Detectors = false) {
|
|
7285
|
+
function findSecretFingerprintsInFile(repoPath, file, useV36Detectors = false, useV39Detectors = false, useV47Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7178
7286
|
const fullPath = join(repoPath, file);
|
|
7179
7287
|
if (!isRegularFile(fullPath)) return [];
|
|
7180
7288
|
const contents = readRepoText(fullPath, "utf8");
|
|
@@ -7191,6 +7299,10 @@ function findSecretFingerprintsInFile(repoPath, file, useV36Detectors = false, u
|
|
|
7191
7299
|
Number.POSITIVE_INFINITY,
|
|
7192
7300
|
{ allowCredentialNamedDigest: useV36Detectors }
|
|
7193
7301
|
).map(({ valueFingerprint }) => valueFingerprint);
|
|
7302
|
+
if (useV23PemPrivateKeyGrammar) {
|
|
7303
|
+
const pemSecret = findPemPrivateKeyAssignment(scanContents, file);
|
|
7304
|
+
if (pemSecret) fingerprints.push(pemSecret.valueFingerprint);
|
|
7305
|
+
}
|
|
7194
7306
|
if (useV39Detectors && isEnvHistoryPath(file, useV36Detectors)) {
|
|
7195
7307
|
const weakCredential = findWeakExplicitEnvCredential(scanContents);
|
|
7196
7308
|
if (weakCredential) fingerprints.push(weakCredential.valueFingerprint);
|
|
@@ -7334,24 +7446,32 @@ function declareHistoryFailure(scanLimitations, reason, operation, maxBufferByte
|
|
|
7334
7446
|
`${describeGitFailure(reason, operation, maxBufferBytes)} Cejel continued with reduced Git history evidence.`
|
|
7335
7447
|
);
|
|
7336
7448
|
}
|
|
7337
|
-
function collectHistorySecretEvidence(repoPath, excludeHead, currentSecretFingerprintsByPath, scanLimitations, authoredProductionOnly = false, useV36Detectors = false, useV39Detectors = false, useV47Detectors = false) {
|
|
7449
|
+
function collectHistorySecretEvidence(repoPath, excludeHead, currentSecretFingerprintsByPath, scanLimitations, authoredProductionOnly = false, useV36Detectors = false, useV39Detectors = false, useV47Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7338
7450
|
const historyEntries = readCredentialHistoryEntries(
|
|
7339
7451
|
repoPath,
|
|
7340
7452
|
scanLimitations,
|
|
7341
|
-
useV36Detectors
|
|
7453
|
+
useV36Detectors,
|
|
7454
|
+
useV23PemPrivateKeyGrammar
|
|
7342
7455
|
);
|
|
7343
7456
|
const head = excludeHead ? readGitHead(repoPath) : null;
|
|
7344
7457
|
let scannedFiles = 0;
|
|
7458
|
+
let v23WidenedOnlyScanned = 0;
|
|
7345
7459
|
let envPathEvidence = null;
|
|
7346
7460
|
for (const entry of historyEntries) {
|
|
7347
7461
|
if (head && entry.commit === head) continue;
|
|
7348
7462
|
if (authoredProductionOnly && !(useV47Detectors ? isV47AuthoredProductionPath(entry.path) : useV39Detectors ? isV39AuthoredProductionPath(entry.path) : isAuthoredProductionPath(entry.path))) {
|
|
7349
7463
|
continue;
|
|
7350
7464
|
}
|
|
7351
|
-
|
|
7352
|
-
|
|
7465
|
+
const isV23WidenedOnly = useV23PemPrivateKeyGrammar && isV23PemCredentialHistoryPath(historyPathBasename(entry.path.toLowerCase())) && !isCredentialHistoryPath(entry.path, useV36Detectors, false);
|
|
7466
|
+
if (isV23WidenedOnly) {
|
|
7467
|
+
if (v23WidenedOnlyScanned >= V23_PEM_WIDENED_HISTORY_BLOB_LIMIT) continue;
|
|
7468
|
+
v23WidenedOnlyScanned += 1;
|
|
7469
|
+
} else {
|
|
7470
|
+
if (scannedFiles >= HISTORY_SECRET_SCAN_CREDENTIAL_BLOB_LIMIT) {
|
|
7471
|
+
return { evidence: null, secretKind: null, envPathEvidence, truncated: true };
|
|
7472
|
+
}
|
|
7473
|
+
scannedFiles += 1;
|
|
7353
7474
|
}
|
|
7354
|
-
scannedFiles += 1;
|
|
7355
7475
|
const contents = readGitBlob(repoPath, entry.commit, entry.path, scanLimitations);
|
|
7356
7476
|
if (!contents) continue;
|
|
7357
7477
|
const scanContents = prepareCredentialScanContents(
|
|
@@ -7365,6 +7485,12 @@ function collectHistorySecretEvidence(repoPath, excludeHead, currentSecretFinger
|
|
|
7365
7485
|
let secretMatch = findRealSecretAssignment(scanContents, currentFingerprints, {
|
|
7366
7486
|
allowCredentialNamedDigest: useV36Detectors
|
|
7367
7487
|
});
|
|
7488
|
+
if (!secretMatch && useV23PemPrivateKeyGrammar) {
|
|
7489
|
+
const pemMatch = findPemPrivateKeyAssignment(scanContents, entry.path);
|
|
7490
|
+
if (pemMatch && !currentFingerprints.has(pemMatch.valueFingerprint)) {
|
|
7491
|
+
secretMatch = pemMatch;
|
|
7492
|
+
}
|
|
7493
|
+
}
|
|
7368
7494
|
if (!secretMatch && useV39Detectors && isEnvHistoryPath(entry.path, useV36Detectors, useV47Detectors)) {
|
|
7369
7495
|
const weakCredential = findWeakExplicitEnvCredential(scanContents);
|
|
7370
7496
|
if (weakCredential && !currentFingerprints.has(weakCredential.valueFingerprint)) {
|
|
@@ -7375,11 +7501,15 @@ function collectHistorySecretEvidence(repoPath, excludeHead, currentSecretFinger
|
|
|
7375
7501
|
return {
|
|
7376
7502
|
evidence: {
|
|
7377
7503
|
kind: "secret_scan",
|
|
7378
|
-
label: secretEvidenceLabel(
|
|
7504
|
+
label: secretEvidenceLabel(
|
|
7505
|
+
secretMatch.kind === "pem_private_key" ? "Committed PEM-formatted private key in recent git history" : "Committed secret in recent git history",
|
|
7506
|
+
secretMatch
|
|
7507
|
+
),
|
|
7379
7508
|
path: entry.path,
|
|
7380
7509
|
line: secretMatch.line,
|
|
7381
7510
|
contentHash: entry.commit
|
|
7382
7511
|
},
|
|
7512
|
+
secretKind: secretMatch.kind ?? null,
|
|
7383
7513
|
envPathEvidence,
|
|
7384
7514
|
truncated: false
|
|
7385
7515
|
};
|
|
@@ -7393,9 +7523,9 @@ function collectHistorySecretEvidence(repoPath, excludeHead, currentSecretFinger
|
|
|
7393
7523
|
};
|
|
7394
7524
|
}
|
|
7395
7525
|
}
|
|
7396
|
-
return { evidence: null, envPathEvidence, truncated: false };
|
|
7526
|
+
return { evidence: null, secretKind: null, envPathEvidence, truncated: false };
|
|
7397
7527
|
}
|
|
7398
|
-
function readCredentialHistoryEntries(repoPath, scanLimitations, useV36Detectors = false) {
|
|
7528
|
+
function readCredentialHistoryEntries(repoPath, scanLimitations, useV36Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7399
7529
|
const entries = [];
|
|
7400
7530
|
const seen = /* @__PURE__ */ new Set();
|
|
7401
7531
|
const appendEntry = (entry) => {
|
|
@@ -7440,7 +7570,7 @@ function readCredentialHistoryEntries(repoPath, scanLimitations, useV36Detectors
|
|
|
7440
7570
|
scanLimitations.add(CONTROL_CHARACTER_HISTORY_PATH_LIMITATION);
|
|
7441
7571
|
continue;
|
|
7442
7572
|
}
|
|
7443
|
-
if (!isCredentialHistoryPath(path, useV36Detectors)) continue;
|
|
7573
|
+
if (!isCredentialHistoryPath(path, useV36Detectors, useV23PemPrivateKeyGrammar)) continue;
|
|
7444
7574
|
appendEntry({ commit: currentCommit, path });
|
|
7445
7575
|
}
|
|
7446
7576
|
}
|
|
@@ -7448,17 +7578,19 @@ function readCredentialHistoryEntries(repoPath, scanLimitations, useV36Detectors
|
|
|
7448
7578
|
for (const entry of readDeletedCredentialHistoryEntries(
|
|
7449
7579
|
repoPath,
|
|
7450
7580
|
scanLimitations,
|
|
7451
|
-
useV36Detectors
|
|
7581
|
+
useV36Detectors,
|
|
7582
|
+
useV23PemPrivateKeyGrammar
|
|
7452
7583
|
)) {
|
|
7453
7584
|
appendEntry(entry);
|
|
7454
7585
|
}
|
|
7455
7586
|
return entries;
|
|
7456
7587
|
}
|
|
7457
|
-
function readDeletedCredentialHistoryEntries(repoPath, scanLimitations, useV36Detectors = false) {
|
|
7588
|
+
function readDeletedCredentialHistoryEntries(repoPath, scanLimitations, useV36Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7458
7589
|
const deletedPaths = readDeletedCredentialHistoryPaths(
|
|
7459
7590
|
repoPath,
|
|
7460
7591
|
scanLimitations,
|
|
7461
|
-
useV36Detectors
|
|
7592
|
+
useV36Detectors,
|
|
7593
|
+
useV23PemPrivateKeyGrammar
|
|
7462
7594
|
);
|
|
7463
7595
|
const entries = [];
|
|
7464
7596
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7472,7 +7604,7 @@ function readDeletedCredentialHistoryEntries(repoPath, scanLimitations, useV36De
|
|
|
7472
7604
|
}
|
|
7473
7605
|
return entries;
|
|
7474
7606
|
}
|
|
7475
|
-
function readDeletedCredentialHistoryPaths(repoPath, scanLimitations, useV36Detectors = false) {
|
|
7607
|
+
function readDeletedCredentialHistoryPaths(repoPath, scanLimitations, useV36Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7476
7608
|
const historyResult = execGit(
|
|
7477
7609
|
[
|
|
7478
7610
|
"log",
|
|
@@ -7500,7 +7632,7 @@ function readDeletedCredentialHistoryPaths(repoPath, scanLimitations, useV36Dete
|
|
|
7500
7632
|
continue;
|
|
7501
7633
|
}
|
|
7502
7634
|
if (!path || seen.has(path)) continue;
|
|
7503
|
-
if (!isCredentialHistoryPath(path, useV36Detectors)) continue;
|
|
7635
|
+
if (!isCredentialHistoryPath(path, useV36Detectors, useV23PemPrivateKeyGrammar)) continue;
|
|
7504
7636
|
seen.add(path);
|
|
7505
7637
|
paths.push(path);
|
|
7506
7638
|
}
|
|
@@ -7518,14 +7650,24 @@ function readHistoryCommitsForPath(repoPath, path, scanLimitations) {
|
|
|
7518
7650
|
}
|
|
7519
7651
|
return historyResult.stdout.split("\n").map((line) => line.trim()).filter((line) => /^[0-9a-f]{40}$/i.test(line));
|
|
7520
7652
|
}
|
|
7521
|
-
|
|
7653
|
+
var V23_KEY_SHAPED_JSON_PATH_PATTERN = /(?<![A-Za-z0-9])(?:service[-_]?account|private[-_]?key|creds?|key)(?![A-Za-z0-9])/i;
|
|
7654
|
+
function historyPathBasename(lowerPath) {
|
|
7655
|
+
return lowerPath.split("/").at(-1) ?? lowerPath;
|
|
7656
|
+
}
|
|
7657
|
+
function isV23PemCredentialHistoryPath(basename3) {
|
|
7658
|
+
return basename3.endsWith(".json") && V23_KEY_SHAPED_JSON_PATH_PATTERN.test(basename3);
|
|
7659
|
+
}
|
|
7660
|
+
function isCredentialHistoryPath(path, _useV36Detectors = false, useV23PemPrivateKeyGrammar = false) {
|
|
7522
7661
|
if (isHardExcludedPath(path)) return false;
|
|
7523
7662
|
const lowerPath = path.toLowerCase();
|
|
7524
|
-
const basename3 = lowerPath
|
|
7663
|
+
const basename3 = historyPathBasename(lowerPath);
|
|
7525
7664
|
if (/\.env(?:\.|$)/.test(basename3)) {
|
|
7526
7665
|
return true;
|
|
7527
7666
|
}
|
|
7528
|
-
|
|
7667
|
+
if (lowerPath.includes("secret") || lowerPath.includes("credential") || basename3.endsWith(".pem") || basename3.endsWith(".key") || basename3 === "id_rsa" || basename3.endsWith(".p12") || basename3.endsWith(".pfx")) {
|
|
7668
|
+
return true;
|
|
7669
|
+
}
|
|
7670
|
+
return useV23PemPrivateKeyGrammar && isV23PemCredentialHistoryPath(basename3);
|
|
7529
7671
|
}
|
|
7530
7672
|
function isEnvHistoryPath(path, useV36Detectors = false, useV47Detectors = false) {
|
|
7531
7673
|
if (isEnvTemplatePath(path, useV47Detectors)) return false;
|
|
@@ -8300,11 +8442,11 @@ function usesV17DetectorClosure2(rubricVersion) {
|
|
|
8300
8442
|
return rubricVersion === WITAN_RUBRIC_VERSION_V17 || rubricVersion === WITAN_RUBRIC_VERSION_V18 || rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22 || rubricVersion === WITAN_RUBRIC_VERSION_V23;
|
|
8301
8443
|
}
|
|
8302
8444
|
var MAX_SIGNAL_ADJUSTMENT = 0.8;
|
|
8303
|
-
function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
|
|
8445
|
+
function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC, toolVersion) {
|
|
8304
8446
|
const parsedInput = WitanReportInputSchema.parse(input);
|
|
8305
8447
|
const signalsByCriterion = mergeSignalsByCriterion(parsedInput.signals);
|
|
8306
8448
|
const readFailureAbstentions = new Set(
|
|
8307
|
-
[...signalsByCriterion.values()].filter((signal) => signal.insufficientData === true).map((signal) => signal.criterionId)
|
|
8449
|
+
[...signalsByCriterion.values()].filter((signal) => signal.insufficientData === true && !isSelfImposedAbstention(signal)).map((signal) => signal.criterionId)
|
|
8308
8450
|
);
|
|
8309
8451
|
const hasInputSignals = inputSignals != null && inputSignals.length > 0;
|
|
8310
8452
|
const inputSignalsByDimension = /* @__PURE__ */ new Map();
|
|
@@ -8409,7 +8551,8 @@ function createWitanReport(input, inputSignals, rubric = WITAN_RUBRIC) {
|
|
|
8409
8551
|
// alter it (see classifyRepoArchetype in repo-signals.ts); per-criterion scoring above is
|
|
8410
8552
|
// unaffected by archetype, only presentation layers (badge/terminal/verdict) key off it.
|
|
8411
8553
|
...parsedInput.archetype ? { archetype: parsedInput.archetype } : {},
|
|
8412
|
-
...insufficientSourceReason ? { insufficientSourceReason } : {}
|
|
8554
|
+
...insufficientSourceReason ? { insufficientSourceReason } : {},
|
|
8555
|
+
...toolVersion ? { toolVersion } : {}
|
|
8413
8556
|
});
|
|
8414
8557
|
}
|
|
8415
8558
|
function applySignalAdjustment(nativeScore, dimension, signals) {
|
|
@@ -8479,6 +8622,14 @@ function statusAfterInputAdjustment(nativeStatus, nativeScore, adjustment, rubri
|
|
|
8479
8622
|
);
|
|
8480
8623
|
return (severity[adjustedSemanticStatus] ?? nativeSeverity) > nativeSeverity ? adjustedSemanticStatus : nativeStatus;
|
|
8481
8624
|
}
|
|
8625
|
+
var SELF_IMPOSED_ABSTENTION_REASONS = /* @__PURE__ */ new Set([
|
|
8626
|
+
"too_large",
|
|
8627
|
+
"excluded_by_extension",
|
|
8628
|
+
"non_regular_file"
|
|
8629
|
+
]);
|
|
8630
|
+
function isSelfImposedAbstention(signal) {
|
|
8631
|
+
return signal.insufficientDataReason !== void 0 && SELF_IMPOSED_ABSTENTION_REASONS.has(signal.insufficientDataReason);
|
|
8632
|
+
}
|
|
8482
8633
|
function mergeSignalsByCriterion(signals) {
|
|
8483
8634
|
const merged = /* @__PURE__ */ new Map();
|
|
8484
8635
|
for (const signal of signals) {
|
|
@@ -8487,15 +8638,19 @@ function mergeSignalsByCriterion(signals) {
|
|
|
8487
8638
|
merged.set(signal.criterionId, signal);
|
|
8488
8639
|
continue;
|
|
8489
8640
|
}
|
|
8641
|
+
const existingAbstains = existing.insufficientData === true;
|
|
8642
|
+
const signalAbstains = signal.insufficientData === true;
|
|
8643
|
+
const abstentionReason = !existingAbstains && !signalAbstains ? void 0 : [existing, signal].filter((candidate) => candidate.insufficientData === true).some((candidate) => !isSelfImposedAbstention(candidate)) ? void 0 : signal.insufficientDataReason ?? existing.insufficientDataReason;
|
|
8490
8644
|
merged.set(signal.criterionId, {
|
|
8491
8645
|
criterionId: signal.criterionId,
|
|
8492
8646
|
positiveEvidence: [...existing.positiveEvidence, ...signal.positiveEvidence],
|
|
8493
8647
|
findings: [...existing.findings, ...signal.findings],
|
|
8494
8648
|
metrics: [...existing.metrics ?? [], ...signal.metrics ?? []],
|
|
8495
8649
|
notes: signal.notes ?? existing.notes,
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8650
|
+
...existingAbstains || signalAbstains ? {
|
|
8651
|
+
insufficientData: true,
|
|
8652
|
+
...abstentionReason !== void 0 ? { insufficientDataReason: abstentionReason } : {}
|
|
8653
|
+
} : existing.notApplicable === true || signal.notApplicable === true ? { notApplicable: true } : {}
|
|
8499
8654
|
});
|
|
8500
8655
|
}
|
|
8501
8656
|
return merged;
|
|
@@ -8931,7 +9086,12 @@ function scoreRepoWithPublicCejel(options) {
|
|
|
8931
9086
|
...options.rubricVersion ? { rubricVersion: options.rubricVersion } : {}
|
|
8932
9087
|
});
|
|
8933
9088
|
const inputSignals = resolvePublicIngestSignals(options);
|
|
8934
|
-
const report = createWitanReport(
|
|
9089
|
+
const report = createWitanReport(
|
|
9090
|
+
input,
|
|
9091
|
+
inputSignals.length > 0 ? inputSignals : void 0,
|
|
9092
|
+
void 0,
|
|
9093
|
+
options.toolVersion
|
|
9094
|
+
);
|
|
8935
9095
|
if (report.verdict !== "insufficient_source" || !isWitanNoMeasurementAbstention(report)) {
|
|
8936
9096
|
return report;
|
|
8937
9097
|
}
|
|
@@ -9189,7 +9349,8 @@ function runCejelScan(options) {
|
|
|
9189
9349
|
// the repository. Direct public/leaderboard scans default this off because their targets
|
|
9190
9350
|
// are third-party repositories and must opt in explicitly.
|
|
9191
9351
|
autoDiscoverIngest: true,
|
|
9192
|
-
warnOnEmptyIngestMatch: options.warnOnEmptyIngestMatch
|
|
9352
|
+
warnOnEmptyIngestMatch: options.warnOnEmptyIngestMatch,
|
|
9353
|
+
...options.toolVersion ? { toolVersion: options.toolVersion } : {}
|
|
9193
9354
|
});
|
|
9194
9355
|
const summary = buildWitanCliSummary(report);
|
|
9195
9356
|
return { report, summary, generatedAt };
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
describeGitFailure,
|
|
6
6
|
execGit,
|
|
7
7
|
isExpectedGitAbsence
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-KNQBI3IX.js";
|
|
9
9
|
import {
|
|
10
10
|
init_esm_shims
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-V3B2V4WA.js";
|
|
12
12
|
|
|
13
13
|
// src/packs/llm/files.ts
|
|
14
14
|
init_esm_shims();
|
|
@@ -62,10 +62,35 @@ var MAX_REPOSITORY_CONTENT_BYTES = 512e3;
|
|
|
62
62
|
init_esm_shims();
|
|
63
63
|
import { closeSync, lstatSync, openSync, readFileSync, readSync } from "fs";
|
|
64
64
|
import { resolve } from "path";
|
|
65
|
+
var READ_FAILURE_REASONS = /* @__PURE__ */ new Set(["unreadable", "denied_path"]);
|
|
66
|
+
function isReadFailureReason(reason) {
|
|
67
|
+
return READ_FAILURE_REASONS.has(reason);
|
|
68
|
+
}
|
|
69
|
+
function classifyAbstentionReason(reasons) {
|
|
70
|
+
if (!reasons || reasons.size === 0) return {};
|
|
71
|
+
for (const reason of reasons) {
|
|
72
|
+
if (isReadFailureReason(reason)) return {};
|
|
73
|
+
}
|
|
74
|
+
const priority = [
|
|
75
|
+
"too_large",
|
|
76
|
+
"excluded_by_extension",
|
|
77
|
+
"non_regular_file"
|
|
78
|
+
];
|
|
79
|
+
const chosen = priority.find((reason) => reasons.has(reason));
|
|
80
|
+
return chosen ? { insufficientDataReason: chosen } : {};
|
|
81
|
+
}
|
|
65
82
|
function contentReadSignalKey(criterionId, signalId) {
|
|
66
83
|
return [criterionId, signalId].join(":");
|
|
67
84
|
}
|
|
68
85
|
var activeSession;
|
|
86
|
+
function addSkipReason(map, key, reason) {
|
|
87
|
+
const existing = map.get(key);
|
|
88
|
+
if (existing) {
|
|
89
|
+
existing.add(reason);
|
|
90
|
+
} else {
|
|
91
|
+
map.set(key, /* @__PURE__ */ new Set([reason]));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
69
94
|
function errnoClass(error) {
|
|
70
95
|
if (typeof error !== "object" || error === null || !("code" in error)) return null;
|
|
71
96
|
const code = error.code;
|
|
@@ -79,10 +104,13 @@ function recordSkip(path2, reason, errno, affectsCurrentCriterion = false, dedup
|
|
|
79
104
|
const signals = session.signals;
|
|
80
105
|
if (signals && signals.length > 0) {
|
|
81
106
|
for (const signal of signals) {
|
|
82
|
-
|
|
107
|
+
const key = contentReadSignalKey(session.criterion, signal);
|
|
108
|
+
session.affectedSignals.add(key);
|
|
109
|
+
addSkipReason(session.signalSkipReasons, key, reason);
|
|
83
110
|
}
|
|
84
111
|
} else {
|
|
85
112
|
session.unattributedCriteria.add(session.criterion);
|
|
113
|
+
addSkipReason(session.criterionSkipReasons, session.criterion, reason);
|
|
86
114
|
}
|
|
87
115
|
}
|
|
88
116
|
if (deduplicate) {
|
|
@@ -121,7 +149,9 @@ function trackContentReads(_repoPath, collect) {
|
|
|
121
149
|
summary: summaryFor(existing),
|
|
122
150
|
affectedCriteria: existing.affectedCriteria,
|
|
123
151
|
affectedSignals: existing.affectedSignals,
|
|
124
|
-
unattributedCriteria: existing.unattributedCriteria
|
|
152
|
+
unattributedCriteria: existing.unattributedCriteria,
|
|
153
|
+
criterionSkipReasons: existing.criterionSkipReasons,
|
|
154
|
+
signalSkipReasons: existing.signalSkipReasons
|
|
125
155
|
};
|
|
126
156
|
}
|
|
127
157
|
const session = {
|
|
@@ -136,7 +166,9 @@ function trackContentReads(_repoPath, collect) {
|
|
|
136
166
|
deduplicatedSkips: /* @__PURE__ */ new Set(),
|
|
137
167
|
affectedCriteria: /* @__PURE__ */ new Set(),
|
|
138
168
|
affectedSignals: /* @__PURE__ */ new Set(),
|
|
139
|
-
unattributedCriteria: /* @__PURE__ */ new Set()
|
|
169
|
+
unattributedCriteria: /* @__PURE__ */ new Set(),
|
|
170
|
+
criterionSkipReasons: /* @__PURE__ */ new Map(),
|
|
171
|
+
signalSkipReasons: /* @__PURE__ */ new Map()
|
|
140
172
|
};
|
|
141
173
|
activeSession = session;
|
|
142
174
|
try {
|
|
@@ -146,7 +178,9 @@ function trackContentReads(_repoPath, collect) {
|
|
|
146
178
|
summary: summaryFor(session),
|
|
147
179
|
affectedCriteria: session.affectedCriteria,
|
|
148
180
|
affectedSignals: session.affectedSignals,
|
|
149
|
-
unattributedCriteria: session.unattributedCriteria
|
|
181
|
+
unattributedCriteria: session.unattributedCriteria,
|
|
182
|
+
criterionSkipReasons: session.criterionSkipReasons,
|
|
183
|
+
signalSkipReasons: session.signalSkipReasons
|
|
150
184
|
};
|
|
151
185
|
} finally {
|
|
152
186
|
activeSession = void 0;
|
|
@@ -183,16 +217,6 @@ function withContentReadSignals(criterionId, signalIds, collect) {
|
|
|
183
217
|
function recordContentSkip(path2, reason, deduplicate = false) {
|
|
184
218
|
recordSkip(path2, reason, void 0, false, deduplicate);
|
|
185
219
|
}
|
|
186
|
-
function recordContentSkipForCriteria(path2, reason, criteria, deduplicate = false) {
|
|
187
|
-
const session = activeSession;
|
|
188
|
-
if (session) {
|
|
189
|
-
for (const criterion of criteria) {
|
|
190
|
-
session.affectedCriteria.add(criterion);
|
|
191
|
-
session.unattributedCriteria.add(criterion);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
recordSkip(path2, reason, void 0, false, deduplicate);
|
|
195
|
-
}
|
|
196
220
|
function recordFilesystemSkip(path2, error, deniedContext = false, deduplicate = true) {
|
|
197
221
|
const errno = errnoClass(error);
|
|
198
222
|
if (!errno) throw error;
|
|
@@ -264,13 +288,13 @@ export {
|
|
|
264
288
|
__filename,
|
|
265
289
|
init_esm_shims,
|
|
266
290
|
MAX_REPOSITORY_CONTENT_BYTES,
|
|
291
|
+
classifyAbstentionReason,
|
|
267
292
|
contentReadSignalKey,
|
|
268
293
|
trackContentReads,
|
|
269
294
|
withContentReadCriterion,
|
|
270
295
|
withContentReadSignal,
|
|
271
296
|
withContentReadSignals,
|
|
272
297
|
recordContentSkip,
|
|
273
|
-
recordContentSkipForCriteria,
|
|
274
298
|
recordFilesystemSkip,
|
|
275
299
|
readRepoText,
|
|
276
300
|
readRepoTextPrefix
|
package/dist/index.js
CHANGED
|
@@ -21,12 +21,12 @@ import {
|
|
|
21
21
|
runCejelScan,
|
|
22
22
|
serializeWitanReport,
|
|
23
23
|
verifyWitanAttestationBinding
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-
|
|
24
|
+
} from "./chunk-6R3VJGJ2.js";
|
|
25
|
+
import "./chunk-KNQBI3IX.js";
|
|
26
|
+
import "./chunk-J6R5OHN7.js";
|
|
27
27
|
import {
|
|
28
28
|
init_esm_shims
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-V3B2V4WA.js";
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
init_esm_shims();
|
|
@@ -324,7 +324,8 @@ async function runWitanCli(args, rubricVersion) {
|
|
|
324
324
|
...options.productDisplayName ? { productDisplayName: options.productDisplayName } : {},
|
|
325
325
|
...effectiveRubricVersion ? { rubricVersion: effectiveRubricVersion } : {},
|
|
326
326
|
ingestPatterns: options.ingestPatterns,
|
|
327
|
-
warnOnEmptyIngestMatch: !options.quiet
|
|
327
|
+
warnOnEmptyIngestMatch: !options.quiet,
|
|
328
|
+
toolVersion: cliVersion()
|
|
328
329
|
});
|
|
329
330
|
const attestation = createWitanAttestation(report, {
|
|
330
331
|
toolVersion: cliVersion(),
|
package/dist/mcp/index.js
CHANGED
|
@@ -5,20 +5,20 @@ import {
|
|
|
5
5
|
renderWitanBadgeSvg,
|
|
6
6
|
renderWitanHtmlReport,
|
|
7
7
|
runCejelScan
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
8
|
+
} from "../chunk-6R3VJGJ2.js";
|
|
9
|
+
import "../chunk-KNQBI3IX.js";
|
|
10
10
|
import {
|
|
11
11
|
ZodFirstPartyTypeKind,
|
|
12
12
|
ZodOptional,
|
|
13
13
|
external_exports,
|
|
14
14
|
objectType
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-J6R5OHN7.js";
|
|
16
16
|
import {
|
|
17
17
|
__commonJS,
|
|
18
18
|
__export,
|
|
19
19
|
__toESM,
|
|
20
20
|
init_esm_shims
|
|
21
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-V3B2V4WA.js";
|
|
22
22
|
|
|
23
23
|
// node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/code.js
|
|
24
24
|
var require_code = __commonJS({
|
|
@@ -17280,7 +17280,7 @@ function createCejelMcpServer(identity) {
|
|
|
17280
17280
|
async ({ path, format }) => {
|
|
17281
17281
|
let result;
|
|
17282
17282
|
try {
|
|
17283
|
-
result = runCejelScan({ repoPath: resolve(path) });
|
|
17283
|
+
result = runCejelScan({ repoPath: resolve(path), toolVersion: identity.version });
|
|
17284
17284
|
} catch (error2) {
|
|
17285
17285
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
17286
17286
|
return {
|
|
@@ -3,18 +3,18 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
|
|
|
3
3
|
const require = __cejelCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
listCejelLlmPackFiles
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-ERNYMFH4.js";
|
|
7
7
|
import {
|
|
8
8
|
execGit
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-KNQBI3IX.js";
|
|
10
10
|
import {
|
|
11
11
|
require_typescript
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-ZPQOZLB7.js";
|
|
13
13
|
import {
|
|
14
14
|
__toESM,
|
|
15
15
|
init_esm_shims,
|
|
16
16
|
readRepoText
|
|
17
|
-
} from "../../chunk-
|
|
17
|
+
} from "../../chunk-V3B2V4WA.js";
|
|
18
18
|
|
|
19
19
|
// src/packs/d-series/index.ts
|
|
20
20
|
init_esm_shims();
|
|
@@ -3,15 +3,15 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
|
|
|
3
3
|
const require = __cejelCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
external_exports
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-J6R5OHN7.js";
|
|
7
7
|
import {
|
|
8
8
|
require_typescript
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-ZPQOZLB7.js";
|
|
10
10
|
import {
|
|
11
11
|
__toESM,
|
|
12
12
|
init_esm_shims,
|
|
13
13
|
readRepoText
|
|
14
|
-
} from "../../chunk-
|
|
14
|
+
} from "../../chunk-V3B2V4WA.js";
|
|
15
15
|
|
|
16
16
|
// src/packs/decision-contracts/index.ts
|
|
17
17
|
init_esm_shims();
|