@cejel/cejel 0.4.5 → 0.4.6
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 +16 -6
- package/dist/calibration/llm-detector.js +5 -5
- package/dist/{chunk-EW6NAHE4.js → chunk-2IBG5OBF.js} +1 -1
- package/dist/{chunk-EPK7UTIK.js → chunk-BZXBLDCJ.js} +1 -1
- package/dist/{chunk-R4DPQLS2.js → chunk-H55KECVT.js} +17 -0
- package/dist/{chunk-RW6YJVHZ.js → chunk-QRWLHTGR.js} +2 -2
- package/dist/{chunk-4IXESHG7.js → chunk-VEJLHO5F.js} +565 -144
- package/dist/{chunk-KGFGX3TP.js → chunk-Y6NO5HAI.js} +1 -1
- package/dist/index.js +4 -4
- 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/package.json +1 -1
|
@@ -5,19 +5,21 @@ import {
|
|
|
5
5
|
describeGitFailure,
|
|
6
6
|
execGit,
|
|
7
7
|
isExpectedGitAbsence
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-BZXBLDCJ.js";
|
|
9
9
|
import {
|
|
10
10
|
external_exports
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-Y6NO5HAI.js";
|
|
12
12
|
import {
|
|
13
|
+
MAX_REPOSITORY_CONTENT_BYTES,
|
|
13
14
|
init_esm_shims,
|
|
14
15
|
readRepoText,
|
|
15
16
|
readRepoTextPrefix,
|
|
16
17
|
recordContentSkip,
|
|
18
|
+
recordContentSkipForCriteria,
|
|
17
19
|
recordFilesystemSkip,
|
|
18
20
|
trackContentReads,
|
|
19
21
|
withContentReadCriterion
|
|
20
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-H55KECVT.js";
|
|
21
23
|
|
|
22
24
|
// src/witan/schemas.ts
|
|
23
25
|
init_esm_shims();
|
|
@@ -117,10 +119,26 @@ var WitanEvidencePointerSchema = external_exports.object({
|
|
|
117
119
|
}).strict().refine((value) => Boolean(value.url ?? value.path ?? value.contentHash), {
|
|
118
120
|
message: "Witan evidence must include a path, url, or content hash."
|
|
119
121
|
});
|
|
122
|
+
var WitanInventoryScanDerivationSchema = external_exports.object({
|
|
123
|
+
kind: external_exports.literal("inventory-scan"),
|
|
124
|
+
inventoryScope: external_exports.enum(["tracked-scan-eligible", "tracked-complete"]),
|
|
125
|
+
inventoryCount: external_exports.number().int().nonnegative(),
|
|
126
|
+
patternSetId: external_exports.string().min(1).max(180),
|
|
127
|
+
patternCount: external_exports.number().int().positive(),
|
|
128
|
+
matchCount: external_exports.number().int().nonnegative()
|
|
129
|
+
}).strict();
|
|
130
|
+
var WitanFindingDerivationSchema = external_exports.discriminatedUnion("kind", [
|
|
131
|
+
WitanInventoryScanDerivationSchema
|
|
132
|
+
]);
|
|
120
133
|
var WitanFindingSchema = external_exports.object({
|
|
121
134
|
severity: WitanFindingSeveritySchema,
|
|
122
135
|
summary: external_exports.string().min(1).max(500),
|
|
123
|
-
evidence: WitanEvidencePointerSchema
|
|
136
|
+
evidence: WitanEvidencePointerSchema,
|
|
137
|
+
// Absence findings emitted by the native repository detector must name the exact
|
|
138
|
+
// inventory and versioned pattern set whose zero result supports the proposition.
|
|
139
|
+
// This remains optional at the shared schema boundary because positive findings and
|
|
140
|
+
// semantic derivations are separate contracts; native absence emitters require it.
|
|
141
|
+
derivation: WitanFindingDerivationSchema.optional()
|
|
124
142
|
}).strict();
|
|
125
143
|
var WitanCriterionMetricSchema = external_exports.object({
|
|
126
144
|
name: external_exports.string().min(1).max(80),
|
|
@@ -321,6 +339,7 @@ var WitanReportSchema = external_exports.preprocess((value) => {
|
|
|
321
339
|
}, StrictWitanReportSchema);
|
|
322
340
|
var WITAN_ATTESTATION_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
323
341
|
var WITAN_ATTESTATION_PREDICATE_TYPE = "https://cejel.dev/attestations/scan/v1";
|
|
342
|
+
var WITAN_REPORT_FORMAT_VERSION = "1.0";
|
|
324
343
|
var WitanAttestationOutcomeSchema = external_exports.discriminatedUnion("status", [
|
|
325
344
|
external_exports.object({
|
|
326
345
|
status: external_exports.literal("scored"),
|
|
@@ -350,6 +369,10 @@ var WitanAttestationStatementSchema = external_exports.object({
|
|
|
350
369
|
version: external_exports.string().min(1).max(80)
|
|
351
370
|
}).strict(),
|
|
352
371
|
generatedAt: external_exports.string().datetime({ offset: true }),
|
|
372
|
+
// Optional on read for attestations emitted before Evidence Seam v1. New attestations
|
|
373
|
+
// always carry it, keeping report.json byte-stable while giving paired consumers an
|
|
374
|
+
// explicit report contract version.
|
|
375
|
+
reportFormatVersion: external_exports.string().regex(/^1\.\d+$/).optional(),
|
|
353
376
|
rubricVersion: external_exports.string().min(1).max(120),
|
|
354
377
|
repository: external_exports.object({
|
|
355
378
|
productSlug: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/),
|
|
@@ -421,7 +444,9 @@ init_esm_shims();
|
|
|
421
444
|
// src/witan/abstention.ts
|
|
422
445
|
init_esm_shims();
|
|
423
446
|
var WITAN_NO_MEASUREMENT_REASON = "No free-core rubric criterion produced a measurable signal. Cejel abstains rather than publish a numeric zero for an entirely unmeasured repository.";
|
|
424
|
-
var WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY = "Reviewable source is present, but the
|
|
447
|
+
var WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY = "Reviewable source is present, but the tracked scan-eligible inventory matched no concrete test file, configured test runner, CI test command, or coverage configuration.";
|
|
448
|
+
var WITAN_DIRECTORY_WALK_AUTHENTICATED_A1_ABSENCE_SUMMARY = "Reviewable source is present, but the bounded directory walk matched no concrete test file, configured test runner, CI test command, or coverage configuration.";
|
|
449
|
+
var WITAN_LEGACY_AUTHENTICATED_A1_ABSENCE_SUMMARY = "Reviewable source is present, but the complete tracked inventory contains no concrete test file, configured test runner, CI test command, or coverage configuration.";
|
|
425
450
|
function isWitanNoMeasurementAbstention(report) {
|
|
426
451
|
return report.verdict === "insufficient_source" && report.insufficientSourceReason.startsWith(WITAN_NO_MEASUREMENT_REASON) && report.criteria.every(
|
|
427
452
|
({ status }) => status === "not_applicable" || status === "insufficient_data"
|
|
@@ -1027,6 +1052,7 @@ function formatMetricMember(criterion, metric2) {
|
|
|
1027
1052
|
}
|
|
1028
1053
|
|
|
1029
1054
|
// src/witan/html.ts
|
|
1055
|
+
var CEJEL_CERTIFICATE_FORMAT_VERSION = "1.0";
|
|
1030
1056
|
function renderWitanHtmlReport(report, options = {}) {
|
|
1031
1057
|
const codeCriteria = criteriaByCategory(report, "code_trust");
|
|
1032
1058
|
const processCriteria = criteriaByCategory(report, "process_trust");
|
|
@@ -1045,6 +1071,7 @@ function renderWitanHtmlReport(report, options = {}) {
|
|
|
1045
1071
|
<head>
|
|
1046
1072
|
<meta charset="utf-8">
|
|
1047
1073
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1074
|
+
<meta name="cejel-certificate-format" content="${CEJEL_CERTIFICATE_FORMAT_VERSION}">
|
|
1048
1075
|
<title>Cejel Trust Certificate - ${escapeHtml(report.productDisplayName)}</title>
|
|
1049
1076
|
<style>${CERTIFICATE_CSS}</style>
|
|
1050
1077
|
</head>
|
|
@@ -1639,6 +1666,7 @@ function createWitanAttestation(report, options) {
|
|
|
1639
1666
|
predicate: {
|
|
1640
1667
|
tool: { name: "cejel", version: options.toolVersion },
|
|
1641
1668
|
generatedAt: options.generatedAt,
|
|
1669
|
+
reportFormatVersion: WITAN_REPORT_FORMAT_VERSION,
|
|
1642
1670
|
rubricVersion: report.rubricVersion,
|
|
1643
1671
|
repository: {
|
|
1644
1672
|
productSlug: report.productSlug,
|
|
@@ -1724,6 +1752,23 @@ init_esm_shims();
|
|
|
1724
1752
|
// src/witan/markdown.ts
|
|
1725
1753
|
init_esm_shims();
|
|
1726
1754
|
|
|
1755
|
+
// src/presentation-safety.ts
|
|
1756
|
+
init_esm_shims();
|
|
1757
|
+
var PRESENTATION_CONTROL_PATTERN = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
|
|
1758
|
+
function sanitizePresentationLine(value, options) {
|
|
1759
|
+
const withoutControls = value.replace(PRESENTATION_CONTROL_PATTERN, "").trim();
|
|
1760
|
+
const safeValue = withoutControls.length > 0 ? withoutControls : options.fallback;
|
|
1761
|
+
if (safeValue.length <= options.maxLength) return safeValue;
|
|
1762
|
+
const ellipsis = ".".repeat(Math.min(3, options.maxLength));
|
|
1763
|
+
const contentLimit = Math.max(options.maxLength - ellipsis.length, 0);
|
|
1764
|
+
let prefix = "";
|
|
1765
|
+
for (const character of safeValue) {
|
|
1766
|
+
if (prefix.length + character.length > contentLimit) break;
|
|
1767
|
+
prefix += character;
|
|
1768
|
+
}
|
|
1769
|
+
return `${prefix}${ellipsis}`;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1727
1772
|
// src/witan/repo-signals.ts
|
|
1728
1773
|
init_esm_shims();
|
|
1729
1774
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -1745,6 +1790,71 @@ function stripBom(text) {
|
|
|
1745
1790
|
function usesV17DetectorClosure(rubricVersion) {
|
|
1746
1791
|
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;
|
|
1747
1792
|
}
|
|
1793
|
+
var LEGACY_INVENTORY_ABSENCE_CONTEXT = {
|
|
1794
|
+
rubricGeneration: "legacy",
|
|
1795
|
+
inventoryProvenance: "directory-walk"
|
|
1796
|
+
};
|
|
1797
|
+
var INVENTORY_SCAN_PATTERN_SETS = {
|
|
1798
|
+
a1AuthenticatedAbsence: {
|
|
1799
|
+
id: "cejel.core-a1.test-integrity-surface.v1",
|
|
1800
|
+
count: 4
|
|
1801
|
+
},
|
|
1802
|
+
a1ConcreteTests: {
|
|
1803
|
+
id: "cejel.core-a1.concrete-test-files.v1",
|
|
1804
|
+
count: 16
|
|
1805
|
+
},
|
|
1806
|
+
a1CoverageConfiguration: {
|
|
1807
|
+
id: "cejel.core-a1.coverage-configuration.v1",
|
|
1808
|
+
count: 10
|
|
1809
|
+
},
|
|
1810
|
+
a2CurrentSecret: {
|
|
1811
|
+
id: "cejel.core-a2.current-secret-shape.v1",
|
|
1812
|
+
count: 3
|
|
1813
|
+
},
|
|
1814
|
+
a2RlsPolicy: {
|
|
1815
|
+
id: "cejel.core-a2.rls-policy.v1",
|
|
1816
|
+
count: 3
|
|
1817
|
+
},
|
|
1818
|
+
a3CiOrReleaseDeploy: {
|
|
1819
|
+
id: "cejel.core-a3.ci-or-release-deploy.v1",
|
|
1820
|
+
count: 12
|
|
1821
|
+
},
|
|
1822
|
+
a4Lockfile: {
|
|
1823
|
+
id: "cejel.core-a4.lockfile.v1",
|
|
1824
|
+
count: 11
|
|
1825
|
+
},
|
|
1826
|
+
a5ClaimRealityArtifact: {
|
|
1827
|
+
id: "cejel.core-a5.claim-reality-artifact.v1",
|
|
1828
|
+
count: 3
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
function inventoryScanDerivation(inventoryCount, patternSet, matchCount) {
|
|
1832
|
+
return {
|
|
1833
|
+
kind: "inventory-scan",
|
|
1834
|
+
inventoryScope: "tracked-scan-eligible",
|
|
1835
|
+
inventoryCount,
|
|
1836
|
+
patternSetId: patternSet.id,
|
|
1837
|
+
patternCount: patternSet.count,
|
|
1838
|
+
matchCount
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
function inventoryAbsenceFinding(options) {
|
|
1842
|
+
const { context, summaries } = options;
|
|
1843
|
+
const summary = context.rubricGeneration === "legacy" ? summaries.legacy : context.inventoryProvenance === "tracked-scan-eligible" ? summaries.currentTracked : summaries.currentDirectoryWalk;
|
|
1844
|
+
const emitsDerivation = context.rubricGeneration === "current" && context.inventoryProvenance === "tracked-scan-eligible";
|
|
1845
|
+
return {
|
|
1846
|
+
severity: options.severity,
|
|
1847
|
+
summary,
|
|
1848
|
+
evidence: options.evidence,
|
|
1849
|
+
...emitsDerivation ? {
|
|
1850
|
+
derivation: inventoryScanDerivation(
|
|
1851
|
+
options.inventoryCount,
|
|
1852
|
+
options.patternSet,
|
|
1853
|
+
options.matchCount
|
|
1854
|
+
)
|
|
1855
|
+
} : {}
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1748
1858
|
function buildWitanInputFromRepo(options) {
|
|
1749
1859
|
const tracked = trackContentReads(options.repoPath, () => buildWitanInputFromRepoUntracked(options));
|
|
1750
1860
|
const affectedCriteria = tracked.affectedCriteria;
|
|
@@ -1783,6 +1893,10 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
1783
1893
|
const inventoryFiles = fileInventory.inventoryFiles;
|
|
1784
1894
|
const ignoredTargetReason = repoFiles.length === 0 ? explainIgnoredScanTarget(options.repoPath) : void 0;
|
|
1785
1895
|
const usesV17Detectors = usesV17DetectorClosure(rubricVersion);
|
|
1896
|
+
const inventoryAbsenceContext = {
|
|
1897
|
+
rubricGeneration: usesV17Detectors ? "current" : "legacy",
|
|
1898
|
+
inventoryProvenance: fileInventory.repoFileInventoryScope === "tracked-scan-eligible" ? "tracked-scan-eligible" : "directory-walk"
|
|
1899
|
+
};
|
|
1786
1900
|
const usesV18NativeRls = 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;
|
|
1787
1901
|
const usesV19CommitYear = rubricVersion === WITAN_RUBRIC_VERSION_V19 || rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
|
|
1788
1902
|
const usesV20A3ExplicitGaps = rubricVersion === WITAN_RUBRIC_VERSION_V20 || rubricVersion === WITAN_RUBRIC_VERSION_V21 || rubricVersion === WITAN_RUBRIC_VERSION_V22;
|
|
@@ -1824,6 +1938,7 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
1824
1938
|
usesV21ExecutedEscalations,
|
|
1825
1939
|
usesV22PackageStartEntrypoint,
|
|
1826
1940
|
reviewableSourceProof,
|
|
1941
|
+
inventoryAbsenceContext,
|
|
1827
1942
|
scanLimitations
|
|
1828
1943
|
);
|
|
1829
1944
|
const archetype = usesV17Detectors && reviewableSourceProof?.passes === true && isV17RescuableAbstention(gatedArchetype.abstentionKind) && coreSignals.some(isMeasuredCriterionSignal) ? {
|
|
@@ -1833,7 +1948,10 @@ function buildWitanInputFromRepoUntracked(options) {
|
|
|
1833
1948
|
} : gatedArchetype;
|
|
1834
1949
|
return {
|
|
1835
1950
|
productSlug: options.productSlug,
|
|
1836
|
-
productDisplayName: options.productDisplayName,
|
|
1951
|
+
productDisplayName: sanitizePresentationLine(options.productDisplayName, {
|
|
1952
|
+
fallback: "Repository",
|
|
1953
|
+
maxLength: 120
|
|
1954
|
+
}),
|
|
1837
1955
|
repo: {
|
|
1838
1956
|
...headSha ? { headSha } : {}
|
|
1839
1957
|
},
|
|
@@ -2430,7 +2548,7 @@ function buildNotApplicableSignal(criterionId, reason) {
|
|
|
2430
2548
|
notApplicable: true
|
|
2431
2549
|
};
|
|
2432
2550
|
}
|
|
2433
|
-
function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, reviewableSourceProof, scanLimitations = /* @__PURE__ */ new Set()) {
|
|
2551
|
+
function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, useV19CommitYear, useV20A3ExplicitGaps, useV21ExecutedEscalations, useV22PackageStartEntrypoint, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT, scanLimitations = /* @__PURE__ */ new Set()) {
|
|
2434
2552
|
const signals = [];
|
|
2435
2553
|
const mono = resolveMonorepoContext(repoPath, scanLimitations);
|
|
2436
2554
|
const collectCriterion = (criterionId, collect) => withContentReadCriterion(criterionId, collect);
|
|
@@ -2442,7 +2560,8 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2442
2560
|
useV27Detectors,
|
|
2443
2561
|
useV33Detectors,
|
|
2444
2562
|
useV36Detectors,
|
|
2445
|
-
reviewableSourceProof
|
|
2563
|
+
reviewableSourceProof,
|
|
2564
|
+
inventoryAbsenceContext
|
|
2446
2565
|
)
|
|
2447
2566
|
);
|
|
2448
2567
|
const a2Signal = collectCriterion(
|
|
@@ -2456,6 +2575,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2456
2575
|
useV39Detectors,
|
|
2457
2576
|
useV47Detectors,
|
|
2458
2577
|
useV18NativeRls,
|
|
2578
|
+
inventoryAbsenceContext,
|
|
2459
2579
|
scanLimitations
|
|
2460
2580
|
)
|
|
2461
2581
|
);
|
|
@@ -2466,7 +2586,8 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2466
2586
|
repoFiles,
|
|
2467
2587
|
useV27Detectors,
|
|
2468
2588
|
useV20A3ExplicitGaps,
|
|
2469
|
-
useV22PackageStartEntrypoint
|
|
2589
|
+
useV22PackageStartEntrypoint,
|
|
2590
|
+
inventoryAbsenceContext
|
|
2470
2591
|
)
|
|
2471
2592
|
);
|
|
2472
2593
|
const a4Signal = collectCriterion(
|
|
@@ -2476,12 +2597,18 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2476
2597
|
repoFiles,
|
|
2477
2598
|
mono,
|
|
2478
2599
|
useV27Detectors,
|
|
2479
|
-
useV47Detectors
|
|
2600
|
+
useV47Detectors,
|
|
2601
|
+
inventoryAbsenceContext
|
|
2480
2602
|
)
|
|
2481
2603
|
);
|
|
2482
2604
|
const a5Signal = collectCriterion(
|
|
2483
2605
|
"A5",
|
|
2484
|
-
() => collectA5ClaimRealityEvidence(
|
|
2606
|
+
() => collectA5ClaimRealityEvidence(
|
|
2607
|
+
repoPath,
|
|
2608
|
+
repoFiles,
|
|
2609
|
+
useV27Detectors,
|
|
2610
|
+
inventoryAbsenceContext
|
|
2611
|
+
)
|
|
2485
2612
|
);
|
|
2486
2613
|
const b1Signal = buildNotApplicableSignal(
|
|
2487
2614
|
"B1",
|
|
@@ -2532,7 +2659,7 @@ function collectRepoSignals(repoPath, generatedAt, repoFiles, useV27Detectors, u
|
|
|
2532
2659
|
}
|
|
2533
2660
|
return signals;
|
|
2534
2661
|
}
|
|
2535
|
-
function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, reviewableSourceProof) {
|
|
2662
|
+
function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, reviewableSourceProof, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
|
|
2536
2663
|
const evidence = [];
|
|
2537
2664
|
const findings = [];
|
|
2538
2665
|
const allTestFiles = findConcreteTestFiles(
|
|
@@ -2629,7 +2756,12 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
2629
2756
|
}
|
|
2630
2757
|
}
|
|
2631
2758
|
if (evidence.length === 0 && findings.length === 0 && configuredRunnerFiles.length === 0) {
|
|
2632
|
-
return reviewableSourceProof?.passes === true && reviewableSourceProof.firstReadablePath ? buildA1AuthenticatedAbsenceSignal(
|
|
2759
|
+
return reviewableSourceProof?.passes === true && reviewableSourceProof.firstReadablePath ? buildA1AuthenticatedAbsenceSignal(
|
|
2760
|
+
repoPath,
|
|
2761
|
+
reviewableSourceProof,
|
|
2762
|
+
repoFiles.length,
|
|
2763
|
+
inventoryAbsenceContext
|
|
2764
|
+
) : null;
|
|
2633
2765
|
}
|
|
2634
2766
|
if (testFiles.length === 0) {
|
|
2635
2767
|
const fallback = configuredRunnerFiles[0] ? evidenceForRelative(
|
|
@@ -2639,11 +2771,21 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
2639
2771
|
"Configured test runner"
|
|
2640
2772
|
) : useV33Detectors ? void 0 : evidence[0] ?? findings[0]?.evidence;
|
|
2641
2773
|
if (fallback) {
|
|
2642
|
-
findings.push(
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2774
|
+
findings.push(
|
|
2775
|
+
inventoryAbsenceFinding({
|
|
2776
|
+
severity: "warning",
|
|
2777
|
+
summaries: {
|
|
2778
|
+
legacy: "A test runner is configured, but no concrete test files were detected.",
|
|
2779
|
+
currentTracked: "A test runner is configured, but the tracked scan-eligible inventory matched no concrete test file.",
|
|
2780
|
+
currentDirectoryWalk: "A test runner is configured, but the bounded directory walk matched no concrete test file."
|
|
2781
|
+
},
|
|
2782
|
+
evidence: fallback,
|
|
2783
|
+
context: inventoryAbsenceContext,
|
|
2784
|
+
inventoryCount: repoFiles.length,
|
|
2785
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a1ConcreteTests,
|
|
2786
|
+
matchCount: 0
|
|
2787
|
+
})
|
|
2788
|
+
);
|
|
2647
2789
|
}
|
|
2648
2790
|
} else if (coverageFiles.length === 0 && (!useV33Detectors || configuredRunnerFiles.length > 0)) {
|
|
2649
2791
|
if (isLeanTestToolchain) {
|
|
@@ -2661,11 +2803,21 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
2661
2803
|
} else {
|
|
2662
2804
|
const firstEvidence = evidence[0];
|
|
2663
2805
|
if (!firstEvidence) return null;
|
|
2664
|
-
findings.push(
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2806
|
+
findings.push(
|
|
2807
|
+
inventoryAbsenceFinding({
|
|
2808
|
+
severity: "info",
|
|
2809
|
+
summaries: {
|
|
2810
|
+
legacy: "Test suite files are present, but no coverage configuration was detected.",
|
|
2811
|
+
currentTracked: "Test suite files are present, but the tracked scan-eligible inventory matched no coverage configuration.",
|
|
2812
|
+
currentDirectoryWalk: "Test suite files are present, but the bounded directory walk matched no coverage configuration."
|
|
2813
|
+
},
|
|
2814
|
+
evidence: firstEvidence,
|
|
2815
|
+
context: inventoryAbsenceContext,
|
|
2816
|
+
inventoryCount: repoFiles.length,
|
|
2817
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a1CoverageConfiguration,
|
|
2818
|
+
matchCount: 0
|
|
2819
|
+
})
|
|
2820
|
+
);
|
|
2669
2821
|
}
|
|
2670
2822
|
}
|
|
2671
2823
|
const nonHollowShare = measureNonHollowTestShare(
|
|
@@ -2738,7 +2890,7 @@ function collectA1TestIntegrityEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
2738
2890
|
notes: "A1 is detected from real test files, test runner configuration, and optional coverage configuration."
|
|
2739
2891
|
};
|
|
2740
2892
|
}
|
|
2741
|
-
function buildA1AuthenticatedAbsenceSignal(repoPath, proof) {
|
|
2893
|
+
function buildA1AuthenticatedAbsenceSignal(repoPath, proof, inventoryCount, inventoryAbsenceContext) {
|
|
2742
2894
|
const sourcePath = proof.firstReadablePath;
|
|
2743
2895
|
if (!sourcePath) {
|
|
2744
2896
|
throw new Error("Cejel invariant: a passing reviewable-source proof must have a readable path");
|
|
@@ -2753,11 +2905,19 @@ function buildA1AuthenticatedAbsenceSignal(repoPath, proof) {
|
|
|
2753
2905
|
criterionId: "A1",
|
|
2754
2906
|
positiveEvidence: [anchor],
|
|
2755
2907
|
findings: [
|
|
2756
|
-
{
|
|
2908
|
+
inventoryAbsenceFinding({
|
|
2757
2909
|
severity: "critical",
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2910
|
+
summaries: {
|
|
2911
|
+
legacy: WITAN_LEGACY_AUTHENTICATED_A1_ABSENCE_SUMMARY,
|
|
2912
|
+
currentTracked: WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY,
|
|
2913
|
+
currentDirectoryWalk: WITAN_DIRECTORY_WALK_AUTHENTICATED_A1_ABSENCE_SUMMARY
|
|
2914
|
+
},
|
|
2915
|
+
evidence: anchor,
|
|
2916
|
+
context: inventoryAbsenceContext,
|
|
2917
|
+
inventoryCount,
|
|
2918
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a1AuthenticatedAbsence,
|
|
2919
|
+
matchCount: 0
|
|
2920
|
+
})
|
|
2761
2921
|
],
|
|
2762
2922
|
metrics: [
|
|
2763
2923
|
metric(
|
|
@@ -2811,7 +2971,7 @@ function buildA1AuthenticatedAbsenceSignal(repoPath, proof) {
|
|
|
2811
2971
|
notes: `V17 authenticated absence: ${proof.eligibleSourceFileCount} criterion-ratable of ${proof.sourceShapedFileCount} source-shaped files; sampled ${proof.sampledFileCount}; ${proof.readableSampledFileCount} representative-text files.`
|
|
2812
2972
|
};
|
|
2813
2973
|
}
|
|
2814
|
-
function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, scanLimitations) {
|
|
2974
|
+
function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33Detectors, useV36Detectors, useV39Detectors, useV47Detectors, useV18NativeRls, inventoryAbsenceContext, scanLimitations) {
|
|
2815
2975
|
const evidence = [];
|
|
2816
2976
|
const findings = [];
|
|
2817
2977
|
const gitignore = repoFiles.find((file) => basename(file) === ".gitignore");
|
|
@@ -2964,16 +3124,26 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
2964
3124
|
)
|
|
2965
3125
|
});
|
|
2966
3126
|
} else if (committedEnvFilePath) {
|
|
2967
|
-
findings.push(
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
3127
|
+
findings.push(
|
|
3128
|
+
inventoryAbsenceFinding({
|
|
3129
|
+
severity: useV33Detectors ? "info" : "warning",
|
|
3130
|
+
summaries: {
|
|
3131
|
+
legacy: "A non-template .env file is committed in the current repository tree; no secret-shaped value was detected.",
|
|
3132
|
+
currentTracked: "A tracked non-template .env file is present, while the bounded scan over the tracked scan-eligible inventory matched no secret-shaped value.",
|
|
3133
|
+
currentDirectoryWalk: "A non-template .env file is present in the bounded directory walk, which matched no secret-shaped value."
|
|
3134
|
+
},
|
|
3135
|
+
evidence: evidenceForRelative(
|
|
3136
|
+
repoPath,
|
|
3137
|
+
committedEnvFilePath,
|
|
3138
|
+
"secret_scan",
|
|
3139
|
+
"Committed .env file (no confirmed secret value found)"
|
|
3140
|
+
),
|
|
3141
|
+
context: inventoryAbsenceContext,
|
|
3142
|
+
inventoryCount: repoFiles.length,
|
|
3143
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a2CurrentSecret,
|
|
3144
|
+
matchCount: 0
|
|
3145
|
+
})
|
|
3146
|
+
);
|
|
2977
3147
|
}
|
|
2978
3148
|
if (historySecretScan?.evidence) {
|
|
2979
3149
|
const historyPath = historySecretScan.evidence.path ?? "";
|
|
@@ -3005,16 +3175,26 @@ function collectA2IsolationEvidence(repoPath, repoFiles, useV27Detectors, useV33
|
|
|
3005
3175
|
if (hasTenantWithoutRlsGap) {
|
|
3006
3176
|
const gapEvidence = rlsMigration ?? tenantScopedFile ?? tenantWithoutRlsPremiseFiles[0];
|
|
3007
3177
|
if (gapEvidence) {
|
|
3008
|
-
findings.push(
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3178
|
+
findings.push(
|
|
3179
|
+
inventoryAbsenceFinding({
|
|
3180
|
+
severity: "warning",
|
|
3181
|
+
summaries: {
|
|
3182
|
+
legacy: "Tenant-scoped schema detected but no row-level-security policies found \u2014 isolation gap.",
|
|
3183
|
+
currentTracked: "A tenant-scoped schema signal is present, while the tracked scan-eligible inventory matched no row-level-security policy.",
|
|
3184
|
+
currentDirectoryWalk: "A tenant-scoped schema signal is present, while the bounded directory walk matched no row-level-security policy."
|
|
3185
|
+
},
|
|
3186
|
+
evidence: evidenceForRelative(
|
|
3187
|
+
repoPath,
|
|
3188
|
+
gapEvidence,
|
|
3189
|
+
"artifact",
|
|
3190
|
+
"Tenant schema without RLS policies"
|
|
3191
|
+
),
|
|
3192
|
+
context: inventoryAbsenceContext,
|
|
3193
|
+
inventoryCount: repoFiles.length,
|
|
3194
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a2RlsPolicy,
|
|
3195
|
+
matchCount: 0
|
|
3196
|
+
})
|
|
3197
|
+
);
|
|
3018
3198
|
}
|
|
3019
3199
|
}
|
|
3020
3200
|
evidence.push(...cryptoHygiene.evidence);
|
|
@@ -3199,7 +3379,7 @@ function collectCryptoHygieneEvidence(repoPath, implFiles, useV39Detectors = fal
|
|
|
3199
3379
|
}
|
|
3200
3380
|
return { evidence, findings, hasSurface, clean: hasSurface && !insecureFound };
|
|
3201
3381
|
}
|
|
3202
|
-
function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, useV20ExplicitGaps = false, useV22PackageStartEntrypoint = false) {
|
|
3382
|
+
function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, useV20ExplicitGaps = false, useV22PackageStartEntrypoint = false, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
|
|
3203
3383
|
const v20DirectHttpEntrypoint = useV20ExplicitGaps ? findV20DirectHttpServerEntrypointFile(repoPath, repoFiles) : null;
|
|
3204
3384
|
const v22PackageStartHttpEntrypoint = useV22PackageStartEntrypoint ? findV22PackageStartHttpServerEntrypointFile(repoPath, repoFiles) : null;
|
|
3205
3385
|
const v20RuntimeContainer = useV20ExplicitGaps ? findRuntimeContainerEntrypointFile(repoPath, repoFiles, true) : null;
|
|
@@ -3361,11 +3541,21 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
3361
3541
|
if (!workflow && releaseDeployConfigs.length === 0) {
|
|
3362
3542
|
const firstEvidence = evidence[0];
|
|
3363
3543
|
if (!firstEvidence) return null;
|
|
3364
|
-
findings.push(
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3544
|
+
findings.push(
|
|
3545
|
+
inventoryAbsenceFinding({
|
|
3546
|
+
severity: "info",
|
|
3547
|
+
summaries: {
|
|
3548
|
+
legacy: "A deployable service surface exists, but no CI or release-deployment automation was detected.",
|
|
3549
|
+
currentTracked: "A deployable service signal is present, while the tracked scan-eligible inventory matched no CI workflow or release-deployment automation.",
|
|
3550
|
+
currentDirectoryWalk: "A deployable service signal is present, while the bounded directory walk matched no CI workflow or release-deployment automation."
|
|
3551
|
+
},
|
|
3552
|
+
evidence: firstEvidence,
|
|
3553
|
+
context: inventoryAbsenceContext,
|
|
3554
|
+
inventoryCount: repoFiles.length,
|
|
3555
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a3CiOrReleaseDeploy,
|
|
3556
|
+
matchCount: 0
|
|
3557
|
+
})
|
|
3558
|
+
);
|
|
3369
3559
|
}
|
|
3370
3560
|
return {
|
|
3371
3561
|
criterionId: "A3",
|
|
@@ -3435,7 +3625,7 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
3435
3625
|
]
|
|
3436
3626
|
};
|
|
3437
3627
|
}
|
|
3438
|
-
function collectA4DependencyEvidence(repoPath, repoFiles, mono, useV27Detectors, useV47Detectors) {
|
|
3628
|
+
function collectA4DependencyEvidence(repoPath, repoFiles, mono, useV27Detectors, useV47Detectors, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
|
|
3439
3629
|
const evidence = [];
|
|
3440
3630
|
const findings = [];
|
|
3441
3631
|
const manifests = repoFiles.filter(isDependencyManifest);
|
|
@@ -3488,11 +3678,26 @@ function collectA4DependencyEvidence(repoPath, repoFiles, mono, useV27Detectors,
|
|
|
3488
3678
|
const deployableServiceFiles = useV47Detectors ? findDeployableServicePremiseFiles(repoPath, repoFiles, true) : [];
|
|
3489
3679
|
const isAppOrService = (useV47Detectors ? deployableServiceFiles.length > 0 : isDeployableService(repoPath, repoFiles, useV27Detectors)) || packagedApplicationFiles.length > 0;
|
|
3490
3680
|
if (isAppOrService && !hasLockfile && manifest) {
|
|
3491
|
-
findings.push(
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3681
|
+
findings.push(
|
|
3682
|
+
inventoryAbsenceFinding({
|
|
3683
|
+
severity: "critical",
|
|
3684
|
+
summaries: {
|
|
3685
|
+
legacy: "Dependency manifest is present without a detected lockfile \u2014 non-reproducible installs.",
|
|
3686
|
+
currentTracked: "Dependency manifest is present, but the tracked scan-eligible inventory matched no recognized lockfile.",
|
|
3687
|
+
currentDirectoryWalk: "Dependency manifest is present, but the bounded directory walk matched no recognized lockfile."
|
|
3688
|
+
},
|
|
3689
|
+
evidence: evidenceForRelative(
|
|
3690
|
+
repoPath,
|
|
3691
|
+
manifest,
|
|
3692
|
+
"dependency_report",
|
|
3693
|
+
"Dependency manifest"
|
|
3694
|
+
),
|
|
3695
|
+
context: inventoryAbsenceContext,
|
|
3696
|
+
inventoryCount: repoFiles.length + (mono?.sharedFiles.length ?? 0),
|
|
3697
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a4Lockfile,
|
|
3698
|
+
matchCount: 0
|
|
3699
|
+
})
|
|
3700
|
+
);
|
|
3496
3701
|
}
|
|
3497
3702
|
if (manifest && hasSuspiciousDependencies(repoPath, manifest, useV47Detectors)) {
|
|
3498
3703
|
findings.push({
|
|
@@ -3600,7 +3805,7 @@ function hasDeclaredVersionConstraint(dependency) {
|
|
|
3600
3805
|
if (/[~^<>=!]/.test(trimmed)) return true;
|
|
3601
3806
|
return /^v?\d/.test(trimmed);
|
|
3602
3807
|
}
|
|
3603
|
-
function collectA5ClaimRealityEvidence(repoPath, repoFiles, useV27Detectors) {
|
|
3808
|
+
function collectA5ClaimRealityEvidence(repoPath, repoFiles, useV27Detectors, inventoryAbsenceContext = LEGACY_INVENTORY_ABSENCE_CONTEXT) {
|
|
3604
3809
|
const reconciliationArtifacts = useV27Detectors ? findClaimRealityReconciliationArtifacts(repoPath, repoFiles) : [];
|
|
3605
3810
|
const claimSourceFiles = useV27Detectors ? findClaimSourceFiles(repoFiles) : repoFiles.filter(
|
|
3606
3811
|
(file) => /^(?:README|readme)\.md$/.test(file) || /^docs\/[^/]+\.(?:md|mdx)$/.test(file)
|
|
@@ -3665,15 +3870,23 @@ function collectA5ClaimRealityEvidence(repoPath, repoFiles, useV27Detectors) {
|
|
|
3665
3870
|
criterionId: "A5",
|
|
3666
3871
|
positiveEvidence: evidence,
|
|
3667
3872
|
findings: reconciliationArtifacts.length > 0 ? [] : [
|
|
3668
|
-
{
|
|
3873
|
+
inventoryAbsenceFinding({
|
|
3669
3874
|
// Proxy-only A5: cap at warning (never critical) — a static proxy is lower-confidence
|
|
3670
3875
|
// than a curated registry or reconciliation artifact. Downgraded to info when the repo
|
|
3671
3876
|
// documents honest negative space: that scoping IS a form of claim-reality discipline,
|
|
3672
3877
|
// not an additional gap on top of the missing dedicated artifact.
|
|
3673
3878
|
severity: negativeSpaceDoc ? "info" : "warning",
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3879
|
+
summaries: {
|
|
3880
|
+
legacy: negativeSpaceDoc ? "Claim source and implementation files are present; no dedicated claim-reality report artifact was supplied, but the repo explicitly documents what it does NOT cover/protect against \u2014 honest scoping, not overclaiming." : "Claim source and implementation files are present, but no dedicated claim-reality report artifact was supplied.",
|
|
3881
|
+
currentTracked: negativeSpaceDoc ? "Claim-source and implementation-file signals are present, while the tracked scan-eligible inventory matched no dedicated claim-reality report artifact; the repo separately documents what it does NOT cover or protect against." : "Claim-source and implementation-file signals are present, while the tracked scan-eligible inventory matched no dedicated claim-reality report artifact.",
|
|
3882
|
+
currentDirectoryWalk: negativeSpaceDoc ? "Claim-source and implementation-file signals are present, while the bounded directory walk matched no dedicated claim-reality report artifact; the repo separately documents what it does NOT cover or protect against." : "Claim-source and implementation-file signals are present, while the bounded directory walk matched no dedicated claim-reality report artifact."
|
|
3883
|
+
},
|
|
3884
|
+
evidence: firstEvidence,
|
|
3885
|
+
context: inventoryAbsenceContext,
|
|
3886
|
+
inventoryCount: repoFiles.length,
|
|
3887
|
+
patternSet: INVENTORY_SCAN_PATTERN_SETS.a5ClaimRealityArtifact,
|
|
3888
|
+
matchCount: 0
|
|
3889
|
+
})
|
|
3677
3890
|
],
|
|
3678
3891
|
// Recalibrated (goal_cejel_launch_hardening_combined_2026-07-06, Phase 3 H1):
|
|
3679
3892
|
// reconciliation_artifact_depth is usually 0 on the generic-proxy path, so its prior 0.3
|
|
@@ -4021,7 +4234,7 @@ function collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles, useV39Detecto
|
|
|
4021
4234
|
const isV21ExecutedShape = v21ExecutedEscalationFileSet.has(file);
|
|
4022
4235
|
findings.push({
|
|
4023
4236
|
severity: "critical",
|
|
4024
|
-
summary: isV21ExecutedShape ? "
|
|
4237
|
+
summary: isV21ExecutedShape ? "This file contains an authored administrative SQL statement \u2014 role-membership grant, SUPERUSER escalation, or schema-wide table privilege grant \u2014 or executes one through a direct database-driver call, and does not itself contain text matching the human-gate marker pattern." : "This file executes a role-membership GRANT or SUPERUSER escalation and does not itself contain text matching the human-gate marker pattern.",
|
|
4025
4238
|
evidence: evidenceForRelative(
|
|
4026
4239
|
repoPath,
|
|
4027
4240
|
file,
|
|
@@ -4348,10 +4561,20 @@ function parseGitTrackedFiles(repoPath, output, includeHardExcluded) {
|
|
|
4348
4561
|
continue;
|
|
4349
4562
|
}
|
|
4350
4563
|
try {
|
|
4351
|
-
|
|
4564
|
+
const stat = lstatSync(fullPath);
|
|
4565
|
+
if (!stat.isFile()) {
|
|
4352
4566
|
recordContentSkip(fullPath, "non_regular_file", true);
|
|
4353
4567
|
continue;
|
|
4354
4568
|
}
|
|
4569
|
+
if (stat.size > MAX_REPOSITORY_CONTENT_BYTES) {
|
|
4570
|
+
const affectedCriteria = affectedCriteriaForUnavailablePath(file);
|
|
4571
|
+
if (affectedCriteria.length > 0) {
|
|
4572
|
+
recordContentSkipForCriteria(fullPath, "too_large", affectedCriteria, true);
|
|
4573
|
+
} else {
|
|
4574
|
+
recordContentSkip(fullPath, "too_large", true);
|
|
4575
|
+
}
|
|
4576
|
+
continue;
|
|
4577
|
+
}
|
|
4355
4578
|
} catch (error) {
|
|
4356
4579
|
recordFilesystemSkip(fullPath, error, false, true);
|
|
4357
4580
|
continue;
|
|
@@ -4363,6 +4586,39 @@ function parseGitTrackedFiles(repoPath, output, includeHardExcluded) {
|
|
|
4363
4586
|
}
|
|
4364
4587
|
return files.sort();
|
|
4365
4588
|
}
|
|
4589
|
+
function affectedCriteriaForUnavailablePath(file) {
|
|
4590
|
+
const affected = /* @__PURE__ */ new Set();
|
|
4591
|
+
if (isDependencyManifest(file) || isLockfile(file)) affected.add("A4");
|
|
4592
|
+
if (isCiWorkflow(file)) {
|
|
4593
|
+
affected.add("A1");
|
|
4594
|
+
affected.add("A3");
|
|
4595
|
+
affected.add("B2");
|
|
4596
|
+
affected.add("B3");
|
|
4597
|
+
}
|
|
4598
|
+
if (isDeployConfig(file)) {
|
|
4599
|
+
affected.add("A3");
|
|
4600
|
+
affected.add("B2");
|
|
4601
|
+
}
|
|
4602
|
+
if (isTestFile(file)) {
|
|
4603
|
+
affected.add("A1");
|
|
4604
|
+
affected.add("A2");
|
|
4605
|
+
}
|
|
4606
|
+
if (isImplementationFile(file)) {
|
|
4607
|
+
affected.add("A1");
|
|
4608
|
+
affected.add("A2");
|
|
4609
|
+
affected.add("A3");
|
|
4610
|
+
affected.add("B1");
|
|
4611
|
+
affected.add("B6");
|
|
4612
|
+
}
|
|
4613
|
+
if (/\.(?:md|mdx|rst|txt)$/i.test(file)) {
|
|
4614
|
+
affected.add("A5");
|
|
4615
|
+
affected.add("B4");
|
|
4616
|
+
affected.add("B5");
|
|
4617
|
+
affected.add("B6");
|
|
4618
|
+
}
|
|
4619
|
+
if (isAuditFile(file)) affected.add("B5");
|
|
4620
|
+
return [...affected];
|
|
4621
|
+
}
|
|
4366
4622
|
function directoryInventory(repoPath) {
|
|
4367
4623
|
const files = [];
|
|
4368
4624
|
const rootDevice = statSync(repoPath).dev;
|
|
@@ -4375,6 +4631,7 @@ function collectRepoFileInventory(repoPath) {
|
|
|
4375
4631
|
return {
|
|
4376
4632
|
repoFiles: parseGitTrackedFiles(repoPath, tracked.stdout, false),
|
|
4377
4633
|
inventoryFiles: parseGitTrackedFiles(repoPath, tracked.stdout, true),
|
|
4634
|
+
repoFileInventoryScope: "tracked-scan-eligible",
|
|
4378
4635
|
scanLimitations: []
|
|
4379
4636
|
};
|
|
4380
4637
|
}
|
|
@@ -4482,8 +4739,13 @@ function visitRepoDir(repoPath, dirPath, files, rootDevice) {
|
|
|
4482
4739
|
continue;
|
|
4483
4740
|
}
|
|
4484
4741
|
const repoRelativePath = relative(repoPath, fullPath);
|
|
4485
|
-
if (size >
|
|
4486
|
-
|
|
4742
|
+
if (size > MAX_REPOSITORY_CONTENT_BYTES) {
|
|
4743
|
+
const affectedCriteria = affectedCriteriaForUnavailablePath(repoRelativePath);
|
|
4744
|
+
if (affectedCriteria.length > 0) {
|
|
4745
|
+
recordContentSkipForCriteria(fullPath, "too_large", affectedCriteria);
|
|
4746
|
+
} else {
|
|
4747
|
+
recordContentSkip(fullPath, "too_large");
|
|
4748
|
+
}
|
|
4487
4749
|
continue;
|
|
4488
4750
|
}
|
|
4489
4751
|
if (!isPotentialContentPath(repoRelativePath)) {
|
|
@@ -6475,12 +6737,11 @@ function hasEnvPathInGitHistory(repoPath, scanLimitations) {
|
|
|
6475
6737
|
|
|
6476
6738
|
// src/witan/public-scan.ts
|
|
6477
6739
|
init_esm_shims();
|
|
6478
|
-
import { resolve as resolve2 } from "path";
|
|
6479
6740
|
|
|
6480
6741
|
// src/witan/ingest.ts
|
|
6481
6742
|
init_esm_shims();
|
|
6482
|
-
import { readFileSync as readFileSync4,
|
|
6483
|
-
import { join as join2 } from "path";
|
|
6743
|
+
import { lstatSync as lstatSync3, opendirSync, readFileSync as readFileSync4, realpathSync as realpathSync3 } from "fs";
|
|
6744
|
+
import { join as join2, resolve as resolve3 } from "path";
|
|
6484
6745
|
|
|
6485
6746
|
// src/witan/finding-limits.ts
|
|
6486
6747
|
init_esm_shims();
|
|
@@ -6504,49 +6765,118 @@ function clampFinding(finding) {
|
|
|
6504
6765
|
// src/witan/generic-adapter.ts
|
|
6505
6766
|
init_esm_shims();
|
|
6506
6767
|
import { readFileSync } from "fs";
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
6510
|
-
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
"
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
"
|
|
6519
|
-
|
|
6520
|
-
|
|
6768
|
+
|
|
6769
|
+
// src/witan/ingest-files.ts
|
|
6770
|
+
init_esm_shims();
|
|
6771
|
+
import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "fs";
|
|
6772
|
+
import { dirname as dirname2, isAbsolute, relative as relative2, resolve as resolve2, sep as sep2 } from "path";
|
|
6773
|
+
var MAX_INGEST_DOCUMENT_BYTES = 8 * 1024 * 1024;
|
|
6774
|
+
function isContainedPath(candidate, directory) {
|
|
6775
|
+
const relativePath = relative2(directory, candidate);
|
|
6776
|
+
return relativePath === "" || relativePath !== ".." && !relativePath.startsWith(`..${sep2}`) && !isAbsolute(relativePath);
|
|
6777
|
+
}
|
|
6778
|
+
function isMissingIngestPath(error) {
|
|
6779
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
6780
|
+
}
|
|
6781
|
+
function resolveIngestFilePath(filePath, inputDirectory = dirname2(filePath)) {
|
|
6782
|
+
const lexicalDirectory = resolve2(inputDirectory);
|
|
6783
|
+
const lexicalCandidate = resolve2(filePath);
|
|
6784
|
+
const directoryStat = lstatSync2(lexicalDirectory);
|
|
6785
|
+
if (directoryStat.isSymbolicLink()) {
|
|
6786
|
+
throw new Error(`Cejel: refusing symlinked ingest directory: ${inputDirectory}`);
|
|
6787
|
+
}
|
|
6788
|
+
if (!directoryStat.isDirectory()) {
|
|
6789
|
+
throw new Error(`Cejel: refusing non-directory ingest boundary: ${inputDirectory}`);
|
|
6790
|
+
}
|
|
6791
|
+
const candidateStat = lstatSync2(lexicalCandidate);
|
|
6792
|
+
if (candidateStat.isSymbolicLink()) {
|
|
6793
|
+
throw new Error(`Cejel: refusing symlinked ingest file: ${filePath}`);
|
|
6794
|
+
}
|
|
6795
|
+
if (!candidateStat.isFile()) {
|
|
6796
|
+
throw new Error(`Cejel: refusing non-regular ingest file: ${filePath}`);
|
|
6797
|
+
}
|
|
6798
|
+
if (candidateStat.size > MAX_INGEST_DOCUMENT_BYTES) {
|
|
6799
|
+
throw new Error(
|
|
6800
|
+
`Cejel: ingest file exceeds the ${MAX_INGEST_DOCUMENT_BYTES.toLocaleString("en-US")} byte budget: ${filePath}`
|
|
6801
|
+
);
|
|
6802
|
+
}
|
|
6803
|
+
const resolvedDirectory = realpathSync2(lexicalDirectory);
|
|
6804
|
+
const resolvedCandidate = realpathSync2(lexicalCandidate);
|
|
6805
|
+
if (!isContainedPath(resolvedCandidate, resolvedDirectory)) {
|
|
6806
|
+
throw new Error(`Cejel: refusing ingest file outside its resolved input directory: ${filePath}`);
|
|
6807
|
+
}
|
|
6808
|
+
return resolvedCandidate;
|
|
6809
|
+
}
|
|
6810
|
+
function isResolvedIngestPathContained(candidate, directory) {
|
|
6811
|
+
return isContainedPath(candidate, directory);
|
|
6812
|
+
}
|
|
6813
|
+
|
|
6814
|
+
// src/witan/generic-adapter.ts
|
|
6815
|
+
var CEJEL_GENERIC_INGEST_CONTRACT_VERSION = "1.0";
|
|
6816
|
+
var CEJEL_GENERIC_INGEST_CONTRACT_MAJOR = 1;
|
|
6817
|
+
var GenericFindingV1Schema = external_exports.object({
|
|
6818
|
+
ruleId: external_exports.string().min(1),
|
|
6819
|
+
severity: external_exports.enum(["critical", "warning", "info"]),
|
|
6820
|
+
message: external_exports.string().min(1),
|
|
6821
|
+
location: external_exports.string().min(1).optional()
|
|
6822
|
+
}).passthrough();
|
|
6823
|
+
var GenericSignalV1Schema = external_exports.object({
|
|
6824
|
+
dimension: external_exports.enum(["A1", "A2", "A3", "A4", "A5", "B1", "B2", "B3", "B4", "B5", "B6"]),
|
|
6825
|
+
weight: external_exports.number().min(0).max(1).default(0.5),
|
|
6826
|
+
findings: external_exports.array(GenericFindingV1Schema).min(1)
|
|
6827
|
+
}).passthrough();
|
|
6828
|
+
var GenericSignalDocumentV1Schema = external_exports.object({
|
|
6829
|
+
version: external_exports.string().regex(/^1\.\d+$/),
|
|
6830
|
+
tool: external_exports.string().trim().min(1),
|
|
6831
|
+
signals: external_exports.array(GenericSignalV1Schema)
|
|
6832
|
+
}).passthrough();
|
|
6521
6833
|
function isGenericSignalDocument(raw) {
|
|
6522
6834
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return false;
|
|
6523
6835
|
const doc = raw;
|
|
6524
6836
|
return typeof doc.tool === "string" && doc.tool.length > 0 && Array.isArray(doc.signals);
|
|
6525
6837
|
}
|
|
6838
|
+
function assertSupportedContractVersion(version) {
|
|
6839
|
+
if (typeof version !== "string") {
|
|
6840
|
+
throw new Error(
|
|
6841
|
+
`Cejel: generic ingest contract version is required; expected "version": "${CEJEL_GENERIC_INGEST_CONTRACT_VERSION}". Refusing to guess an unversioned contract.`
|
|
6842
|
+
);
|
|
6843
|
+
}
|
|
6844
|
+
const match = /^(\d+)\.(\d+)$/.exec(version);
|
|
6845
|
+
if (!match) {
|
|
6846
|
+
throw new Error(
|
|
6847
|
+
`Cejel: invalid generic ingest contract version "${version}"; expected <major>.<minor> (current ${CEJEL_GENERIC_INGEST_CONTRACT_VERSION}).`
|
|
6848
|
+
);
|
|
6849
|
+
}
|
|
6850
|
+
const major = Number(match[1]);
|
|
6851
|
+
if (major !== CEJEL_GENERIC_INGEST_CONTRACT_MAJOR) {
|
|
6852
|
+
throw new Error(
|
|
6853
|
+
`Cejel: unsupported generic ingest contract major version ${major} (declared "${version}"); this Cejel build supports major ${CEJEL_GENERIC_INGEST_CONTRACT_MAJOR}. Refusing to guess.`
|
|
6854
|
+
);
|
|
6855
|
+
}
|
|
6856
|
+
}
|
|
6857
|
+
function formatValidationIssues(error) {
|
|
6858
|
+
return error.issues.map((issue) => `${issue.path.length > 0 ? issue.path.join(".") : "<document>"}: ${issue.message}`).join("; ");
|
|
6859
|
+
}
|
|
6526
6860
|
function parseGenericJson(raw) {
|
|
6527
6861
|
if (!isGenericSignalDocument(raw)) return [];
|
|
6528
|
-
|
|
6529
|
-
const
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
const findings = [];
|
|
6535
|
-
for (const finding of signal.findings ?? []) {
|
|
6536
|
-
const severity = finding.severity;
|
|
6537
|
-
if (!severity || !VALID_SEVERITIES.has(severity)) continue;
|
|
6538
|
-
const ruleId = finding.ruleId ?? "unknown";
|
|
6539
|
-
findings.push({
|
|
6540
|
-
ruleId,
|
|
6541
|
-
severity,
|
|
6542
|
-
message: finding.message ?? ruleId,
|
|
6543
|
-
...finding.location ? { location: finding.location } : {}
|
|
6544
|
-
});
|
|
6545
|
-
}
|
|
6546
|
-
if (findings.length === 0) continue;
|
|
6547
|
-
signals.push({ source, dimension, weight, findings });
|
|
6862
|
+
assertSupportedContractVersion(raw.version);
|
|
6863
|
+
const parsed = GenericSignalDocumentV1Schema.safeParse(raw);
|
|
6864
|
+
if (!parsed.success) {
|
|
6865
|
+
throw new Error(
|
|
6866
|
+
`Cejel: invalid generic ingest contract v${raw.version}: ${formatValidationIssues(parsed.error)}`
|
|
6867
|
+
);
|
|
6548
6868
|
}
|
|
6549
|
-
return signals
|
|
6869
|
+
return parsed.data.signals.map((signal) => ({
|
|
6870
|
+
source: parsed.data.tool,
|
|
6871
|
+
dimension: signal.dimension,
|
|
6872
|
+
weight: signal.weight,
|
|
6873
|
+
findings: signal.findings.map((finding) => ({
|
|
6874
|
+
ruleId: finding.ruleId,
|
|
6875
|
+
severity: finding.severity,
|
|
6876
|
+
message: finding.message,
|
|
6877
|
+
...finding.location ? { location: finding.location } : {}
|
|
6878
|
+
}))
|
|
6879
|
+
}));
|
|
6550
6880
|
}
|
|
6551
6881
|
|
|
6552
6882
|
// src/witan/sarif-adapter.ts
|
|
@@ -6808,30 +7138,35 @@ function rawToolName(raw) {
|
|
|
6808
7138
|
}
|
|
6809
7139
|
var MAX_INGEST_SOURCE_LENGTH = 120;
|
|
6810
7140
|
function sanitizeIngestSource(source) {
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
return `${codePoints.slice(0, MAX_INGEST_SOURCE_LENGTH - 3).join("")}...`;
|
|
7141
|
+
return sanitizePresentationLine(source, {
|
|
7142
|
+
fallback: "unknown",
|
|
7143
|
+
maxLength: MAX_INGEST_SOURCE_LENGTH
|
|
7144
|
+
});
|
|
6816
7145
|
}
|
|
6817
7146
|
function parseIngestFile(filePath, options = {}) {
|
|
7147
|
+
const resolvedFilePath = resolveIngestFilePath(filePath);
|
|
6818
7148
|
let raw;
|
|
6819
7149
|
try {
|
|
6820
|
-
raw = JSON.parse(stripBom(readFileSync4(
|
|
7150
|
+
raw = JSON.parse(stripBom(readFileSync4(resolvedFilePath, "utf8")));
|
|
6821
7151
|
} catch (error) {
|
|
6822
7152
|
const message = error instanceof Error ? error.message : String(error);
|
|
6823
7153
|
throw new Error(`Cejel: could not parse ingest file as JSON: ${filePath} (${message})`);
|
|
6824
7154
|
}
|
|
7155
|
+
const rawCount = countRawEntries(raw);
|
|
7156
|
+
if (options.maxFindingCandidates !== void 0 && rawCount > options.maxFindingCandidates) {
|
|
7157
|
+
throw new Error(
|
|
7158
|
+
`Cejel: ${rawCount.toLocaleString("en-US")} ingest finding candidates exceed the ${options.maxFindingCandidates.toLocaleString("en-US")} remaining retained ingest finding budget; refusing before adapter materialization.`
|
|
7159
|
+
);
|
|
7160
|
+
}
|
|
6825
7161
|
let signals;
|
|
6826
7162
|
if (looksLikeSarif(raw)) signals = parseSarifJson(raw, options.extraSarifDimensionRules);
|
|
6827
7163
|
else if (looksLikeScorecard(raw)) signals = parseScorecardJson(raw);
|
|
6828
7164
|
else if (isGenericSignalDocument(raw)) signals = parseGenericJson(raw);
|
|
6829
7165
|
if (!signals) {
|
|
6830
7166
|
throw new Error(
|
|
6831
|
-
`Cejel: unrecognized ingest file format: ${filePath} \u2014 expected SARIF (a "runs" array), OpenSSF Scorecard JSON (a "checks" array), or the generic Cejel external-signal shape (a "tool" string + "signals" array). See the cejel README "Aggregate your scanners".`
|
|
7167
|
+
`Cejel: unrecognized ingest file format: ${filePath} \u2014 expected SARIF (a "runs" array), OpenSSF Scorecard JSON (a "checks" array), or the versioned generic Cejel external-signal shape (a "version" string + "tool" string + "signals" array). See the cejel README "Aggregate your scanners".`
|
|
6832
7168
|
);
|
|
6833
7169
|
}
|
|
6834
|
-
const rawCount = countRawEntries(raw);
|
|
6835
7170
|
const mappedCount = signals.reduce((sum, signal) => sum + signal.findings.length, 0);
|
|
6836
7171
|
if (rawCount > 0 && mappedCount === 0) {
|
|
6837
7172
|
process.stderr.write(
|
|
@@ -6846,30 +7181,83 @@ function parseIngestFile(filePath, options = {}) {
|
|
|
6846
7181
|
findings: signal.findings.map(clampFinding)
|
|
6847
7182
|
}));
|
|
6848
7183
|
}
|
|
6849
|
-
function expandIngestPattern(pattern) {
|
|
7184
|
+
function expandIngestPattern(pattern, maxMatches = Number.POSITIVE_INFINITY) {
|
|
6850
7185
|
if (!pattern.includes("*")) return [pattern];
|
|
6851
7186
|
const lastSlash = pattern.lastIndexOf("/");
|
|
6852
7187
|
const dir = lastSlash === -1 ? "." : pattern.slice(0, lastSlash);
|
|
6853
7188
|
const filePattern = lastSlash === -1 ? pattern : pattern.slice(lastSlash + 1);
|
|
6854
7189
|
const regexSource = filePattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
6855
7190
|
const regex = new RegExp(`^${regexSource}$`);
|
|
6856
|
-
|
|
7191
|
+
const entries = [];
|
|
6857
7192
|
try {
|
|
6858
|
-
|
|
6859
|
-
|
|
7193
|
+
const directoryStat = lstatSync3(resolve3(dir));
|
|
7194
|
+
if (directoryStat.isSymbolicLink()) {
|
|
7195
|
+
throw new Error(`Cejel: refusing symlinked ingest directory: ${dir}`);
|
|
7196
|
+
}
|
|
7197
|
+
if (!directoryStat.isDirectory()) return [];
|
|
7198
|
+
const directory = opendirSync(dir);
|
|
7199
|
+
try {
|
|
7200
|
+
for (; ; ) {
|
|
7201
|
+
const entry = directory.readSync();
|
|
7202
|
+
if (!entry) break;
|
|
7203
|
+
if (!regex.test(entry.name)) continue;
|
|
7204
|
+
entries.push(entry.name);
|
|
7205
|
+
if (entries.length >= maxMatches) break;
|
|
7206
|
+
}
|
|
7207
|
+
} finally {
|
|
7208
|
+
directory.closeSync();
|
|
7209
|
+
}
|
|
7210
|
+
} catch (error) {
|
|
7211
|
+
if (!isMissingIngestPath(error)) throw error;
|
|
6860
7212
|
return [];
|
|
6861
7213
|
}
|
|
6862
|
-
return entries.
|
|
7214
|
+
return entries.sort().map((name) => {
|
|
7215
|
+
const candidate = join2(dir, name);
|
|
7216
|
+
resolveIngestFilePath(candidate, dir);
|
|
7217
|
+
return candidate;
|
|
7218
|
+
});
|
|
6863
7219
|
}
|
|
6864
|
-
function discoverIngestInputs(repoPath) {
|
|
7220
|
+
function discoverIngestInputs(repoPath, maxMatches = Number.POSITIVE_INFINITY) {
|
|
7221
|
+
const resolvedRoot = realpathSync3(repoPath);
|
|
7222
|
+
const cejelDir = join2(repoPath, ".cejel");
|
|
6865
7223
|
const inputsDir = join2(repoPath, ".cejel", "inputs");
|
|
6866
|
-
|
|
7224
|
+
const entries = [];
|
|
6867
7225
|
try {
|
|
6868
|
-
|
|
6869
|
-
|
|
7226
|
+
const cejelStat = lstatSync3(cejelDir);
|
|
7227
|
+
if (cejelStat.isSymbolicLink()) {
|
|
7228
|
+
throw new Error(`Cejel: refusing symlinked ingest path component: ${cejelDir}`);
|
|
7229
|
+
}
|
|
7230
|
+
if (!cejelStat.isDirectory()) {
|
|
7231
|
+
throw new Error(`Cejel: refusing non-directory ingest path component: ${cejelDir}`);
|
|
7232
|
+
}
|
|
7233
|
+
const inputsStat = lstatSync3(inputsDir);
|
|
7234
|
+
if (inputsStat.isSymbolicLink()) {
|
|
7235
|
+
throw new Error(`Cejel: refusing symlinked ingest path component: ${inputsDir}`);
|
|
7236
|
+
}
|
|
7237
|
+
if (!inputsStat.isDirectory()) {
|
|
7238
|
+
throw new Error(`Cejel: refusing non-directory ingest path component: ${inputsDir}`);
|
|
7239
|
+
}
|
|
7240
|
+
const resolvedInputs = realpathSync3(inputsDir);
|
|
7241
|
+
if (!isResolvedIngestPathContained(resolvedInputs, resolvedRoot)) {
|
|
7242
|
+
throw new Error(`Cejel: refusing auto-discovery directory outside the repository root.`);
|
|
7243
|
+
}
|
|
7244
|
+
const directory = opendirSync(inputsDir);
|
|
7245
|
+
try {
|
|
7246
|
+
for (; ; ) {
|
|
7247
|
+
const entry = directory.readSync();
|
|
7248
|
+
if (!entry) break;
|
|
7249
|
+
if (!/\.(sarif|json)$/i.test(entry.name)) continue;
|
|
7250
|
+
entries.push(entry.name);
|
|
7251
|
+
if (entries.length >= maxMatches) break;
|
|
7252
|
+
}
|
|
7253
|
+
} finally {
|
|
7254
|
+
directory.closeSync();
|
|
7255
|
+
}
|
|
7256
|
+
} catch (error) {
|
|
7257
|
+
if (!isMissingIngestPath(error)) throw error;
|
|
6870
7258
|
return [];
|
|
6871
7259
|
}
|
|
6872
|
-
return entries.
|
|
7260
|
+
return entries.sort().map((name) => resolveIngestFilePath(join2(inputsDir, name), inputsDir));
|
|
6873
7261
|
}
|
|
6874
7262
|
|
|
6875
7263
|
// src/witan/scoring.ts
|
|
@@ -7281,7 +7669,11 @@ function scoreMetrics(metrics) {
|
|
|
7281
7669
|
function capScoreForFindings(score, findings, rubricVersion, criterionId) {
|
|
7282
7670
|
if (findings.some((finding) => finding.severity === "critical")) {
|
|
7283
7671
|
const criticalFindings = findings.filter(({ severity }) => severity === "critical");
|
|
7284
|
-
const authenticatedA1Absence = usesV17DetectorClosure2(rubricVersion) && criterionId === "A1" && criticalFindings.length === 1 &&
|
|
7672
|
+
const authenticatedA1Absence = usesV17DetectorClosure2(rubricVersion) && criterionId === "A1" && criticalFindings.length === 1 && [
|
|
7673
|
+
WITAN_AUTHENTICATED_A1_ABSENCE_SUMMARY,
|
|
7674
|
+
WITAN_DIRECTORY_WALK_AUTHENTICATED_A1_ABSENCE_SUMMARY,
|
|
7675
|
+
WITAN_LEGACY_AUTHENTICATED_A1_ABSENCE_SUMMARY
|
|
7676
|
+
].includes(criticalFindings[0]?.summary ?? "");
|
|
7285
7677
|
return authenticatedA1Absence ? Math.min(score, 1.4) : 1.4;
|
|
7286
7678
|
}
|
|
7287
7679
|
if (findings.some((finding) => finding.severity === "warning")) return Math.min(score, 2.4);
|
|
@@ -7553,6 +7945,8 @@ function roundScore(score) {
|
|
|
7553
7945
|
}
|
|
7554
7946
|
|
|
7555
7947
|
// src/witan/public-scan.ts
|
|
7948
|
+
var MAX_INGEST_DOCUMENTS = 128;
|
|
7949
|
+
var MAX_RETAINED_INGEST_FINDINGS = 1e4;
|
|
7556
7950
|
function scoreRepoWithPublicCejel(options) {
|
|
7557
7951
|
if (options.rubricVersion !== void 0) {
|
|
7558
7952
|
assertSelectableRubricVersion(options.rubricVersion);
|
|
@@ -7580,36 +7974,58 @@ function scoreRepoWithPublicCejel(options) {
|
|
|
7580
7974
|
}
|
|
7581
7975
|
function resolvePublicIngestSignals(options) {
|
|
7582
7976
|
const seen = /* @__PURE__ */ new Set();
|
|
7583
|
-
const
|
|
7977
|
+
const documents = [];
|
|
7978
|
+
const addDocument = (file, provenance) => {
|
|
7979
|
+
const resolved = resolveIngestFilePath(file);
|
|
7980
|
+
if (seen.has(resolved)) return;
|
|
7981
|
+
seen.add(resolved);
|
|
7982
|
+
documents.push({ file: resolved, provenance });
|
|
7983
|
+
if (documents.length > MAX_INGEST_DOCUMENTS) {
|
|
7984
|
+
throw new Error(
|
|
7985
|
+
`Cejel: ${documents.length} unique ingest documents exceed the ${MAX_INGEST_DOCUMENTS} ingest document budget; refusing to produce a partial certificate.`
|
|
7986
|
+
);
|
|
7987
|
+
}
|
|
7988
|
+
};
|
|
7584
7989
|
for (const pattern of options.ingestPatterns ?? []) {
|
|
7585
|
-
const matches = expandIngestPattern(pattern);
|
|
7990
|
+
const matches = expandIngestPattern(pattern, MAX_INGEST_DOCUMENTS + 1);
|
|
7586
7991
|
if (matches.length === 0 && options.warnOnEmptyIngestMatch) {
|
|
7587
7992
|
process.stderr.write(`Cejel: --ingest pattern matched no files: ${pattern}
|
|
7588
7993
|
`);
|
|
7589
7994
|
}
|
|
7590
7995
|
for (const match of matches) {
|
|
7591
|
-
|
|
7592
|
-
if (seen.has(resolved)) continue;
|
|
7593
|
-
seen.add(resolved);
|
|
7594
|
-
explicitFiles.push(match);
|
|
7996
|
+
addDocument(match, "operator_supplied");
|
|
7595
7997
|
}
|
|
7596
7998
|
}
|
|
7597
|
-
|
|
7598
|
-
|
|
7599
|
-
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
|
|
7604
|
-
|
|
7605
|
-
|
|
7999
|
+
if (options.autoDiscoverIngest) {
|
|
8000
|
+
for (const discovered of discoverIngestInputs(
|
|
8001
|
+
options.repoPath,
|
|
8002
|
+
MAX_INGEST_DOCUMENTS + 1
|
|
8003
|
+
)) {
|
|
8004
|
+
addDocument(discovered, "auto_discovered");
|
|
8005
|
+
}
|
|
8006
|
+
}
|
|
8007
|
+
const signals = [];
|
|
8008
|
+
let retainedFindings = 0;
|
|
8009
|
+
for (const document of documents) {
|
|
8010
|
+
const parsed = parseIngestFile(document.file, {
|
|
8011
|
+
maxFindingCandidates: MAX_RETAINED_INGEST_FINDINGS - retainedFindings,
|
|
8012
|
+
provenance: document.provenance
|
|
8013
|
+
});
|
|
8014
|
+
const documentFindings = parsed.reduce((count, signal) => count + signal.findings.length, 0);
|
|
8015
|
+
retainedFindings += documentFindings;
|
|
8016
|
+
if (retainedFindings > MAX_RETAINED_INGEST_FINDINGS) {
|
|
8017
|
+
throw new Error(
|
|
8018
|
+
`Cejel: ${retainedFindings.toLocaleString("en-US")} retained findings exceed the ${MAX_RETAINED_INGEST_FINDINGS.toLocaleString("en-US")} retained ingest finding budget; refusing to truncate evidence.`
|
|
8019
|
+
);
|
|
8020
|
+
}
|
|
8021
|
+
signals.push(...parsed);
|
|
7606
8022
|
}
|
|
7607
8023
|
return signals;
|
|
7608
8024
|
}
|
|
7609
8025
|
|
|
7610
8026
|
// src/product-identity.ts
|
|
7611
8027
|
init_esm_shims();
|
|
7612
|
-
import { existsSync, readFileSync as readFileSync5 } from "fs";
|
|
8028
|
+
import { existsSync, lstatSync as lstatSync4, readFileSync as readFileSync5 } from "fs";
|
|
7613
8029
|
import { basename as basename2, join as join3 } from "path";
|
|
7614
8030
|
var SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
|
|
7615
8031
|
var FALLBACK_SLUG = "repo";
|
|
@@ -7617,7 +8033,10 @@ var FALLBACK_DISPLAY_NAME = "Repository";
|
|
|
7617
8033
|
function deriveProductIdentity(repoPath, productName) {
|
|
7618
8034
|
const packageJsonPath = join3(repoPath, "package.json");
|
|
7619
8035
|
const raw = productName ?? readPackageName(packageJsonPath) ?? basename2(repoPath);
|
|
7620
|
-
const displayName =
|
|
8036
|
+
const displayName = sanitizePresentationLine(raw, {
|
|
8037
|
+
fallback: FALLBACK_DISPLAY_NAME,
|
|
8038
|
+
maxLength: 120
|
|
8039
|
+
});
|
|
7621
8040
|
return {
|
|
7622
8041
|
productSlug: slugify(raw),
|
|
7623
8042
|
productDisplayName: displayName
|
|
@@ -7626,6 +8045,8 @@ function deriveProductIdentity(repoPath, productName) {
|
|
|
7626
8045
|
function readPackageName(packageJsonPath) {
|
|
7627
8046
|
if (!existsSync(packageJsonPath)) return void 0;
|
|
7628
8047
|
try {
|
|
8048
|
+
const stat = lstatSync4(packageJsonPath);
|
|
8049
|
+
if (!stat.isFile() || stat.size > MAX_REPOSITORY_CONTENT_BYTES) return void 0;
|
|
7629
8050
|
const parsed = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
|
|
7630
8051
|
if (typeof parsed === "object" && parsed !== null && "name" in parsed) {
|
|
7631
8052
|
const name = parsed.name;
|