@cejel/cejel 0.6.1 → 0.6.3
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 +52 -6
- package/dist/calibration/llm-detector.js +1 -1
- package/dist/{chunk-UBU3PP7P.js → chunk-FA72HEWJ.js} +85 -28
- package/dist/chunk-KAMWXGY7.js +17358 -0
- package/dist/index.js +30 -1
- package/dist/mcp/index.js +7 -17332
- package/dist/packs/d-series/index.js +6 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -92,7 +92,7 @@ if (-not $expected -or $actual -ne $expected.ToLowerInvariant()) {
|
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
> **Windows signing status:** `cejel-Windows-x86_64.exe` is intentionally unsigned in
|
|
95
|
-
> 0.6.
|
|
95
|
+
> 0.6.3 and may trigger Microsoft SmartScreen. The release build removes Node's inherited
|
|
96
96
|
> signature before SEA injection and fails unless Windows reports the result as `NotSigned`;
|
|
97
97
|
> it does not ship an invalid signature. Before running it, verify `SHA256SUMS` and the
|
|
98
98
|
> GitHub build-provenance attestation. Each binary also has an attached SPDX SBOM (the packages
|
|
@@ -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.6.
|
|
119
|
+
> **Distribution note:** Cejel `0.6.3` is the coordinated release version for npm,
|
|
120
120
|
> standalone binaries, Docker/OCI, GitHub Action, Homebrew, and MCP Registry. A GitLab CI
|
|
121
121
|
> template and Code Quality export live in [`docs/gitlab-ci.md`](./docs/gitlab-ci.md).
|
|
122
122
|
|
|
@@ -149,11 +149,17 @@ The npm package is scoped as `@cejel/cejel`; its executable remains the short co
|
|
|
149
149
|
**GitHub Action** — score every PR and publish the badge:
|
|
150
150
|
|
|
151
151
|
```yaml
|
|
152
|
+
- uses: actions/checkout@v4
|
|
153
|
+
with:
|
|
154
|
+
fetch-depth: 0 # full history: A2 and B2 read commit history
|
|
152
155
|
- uses: BargLabs/cejel/action@v1
|
|
153
156
|
with:
|
|
154
157
|
min-score: "2.5" # optional: fail the build below this
|
|
155
158
|
```
|
|
156
159
|
|
|
160
|
+
`actions/checkout` fetches one commit by default. On a shallow clone, A2 and B2 see only that
|
|
161
|
+
commit, and the certificate says so under "History limitations".
|
|
162
|
+
|
|
157
163
|
For an executed public example that binds a release artifact to the same commit named by its
|
|
158
164
|
Cejel certificate, see [Certified releases with GitHub build provenance](./docs/certified-release-example.md).
|
|
159
165
|
|
|
@@ -181,16 +187,16 @@ gh attestation verify ./cejel-Windows-x86_64.exe -R BargLabs/cejel
|
|
|
181
187
|
This is cryptographically signed provenance. It is distinct from Apple Developer ID or
|
|
182
188
|
Microsoft Authenticode code-signing.
|
|
183
189
|
|
|
184
|
-
**Docker / OCI.** The current container release is `0.6.
|
|
190
|
+
**Docker / OCI.** The current container release is `0.6.3`:
|
|
185
191
|
|
|
186
192
|
```bash
|
|
187
|
-
docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.6.
|
|
193
|
+
docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.6.3
|
|
188
194
|
```
|
|
189
195
|
|
|
190
196
|
The image defaults to `cejel-mcp` over stdio. To use the CLI instead:
|
|
191
197
|
|
|
192
198
|
```bash
|
|
193
|
-
docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.6.
|
|
199
|
+
docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.6.3 .
|
|
194
200
|
```
|
|
195
201
|
|
|
196
202
|
The OCI image carries an SBOM of its base-image packages, maximum-mode build provenance, and a
|
|
@@ -429,11 +435,51 @@ a configurable `min-score` threshold. The scoring step makes no network calls an
|
|
|
429
435
|
secrets.
|
|
430
436
|
|
|
431
437
|
```yaml
|
|
438
|
+
- uses: actions/checkout@v4
|
|
439
|
+
with:
|
|
440
|
+
fetch-depth: 0 # full history: A2 and B2 read commit history
|
|
432
441
|
- uses: BargLabs/cejel/action@v1
|
|
433
442
|
with:
|
|
434
443
|
min-score: '2.5' # optional; omit to never fail the check
|
|
435
444
|
```
|
|
436
445
|
|
|
446
|
+
Check out with `fetch-depth: 0`: the default depth of 1 truncates the history A2 and B2 read, and
|
|
447
|
+
the certificate then lists a history limitation.
|
|
448
|
+
|
|
449
|
+
## Use with Claude
|
|
450
|
+
|
|
451
|
+
Cejel ships as a Claude Code plugin in [`plugins/cejel/`](./plugins/cejel/). The plugin gives
|
|
452
|
+
Claude the `scan` tool below, started through `npx` and pinned to this release, plus a
|
|
453
|
+
`cejel-scan` skill. The skill tells Claude when a scan answers the question and how to read the
|
|
454
|
+
certificate. It also tells Claude to present a score as evidence for your own judgment, never as
|
|
455
|
+
an audit or a guarantee.
|
|
456
|
+
|
|
457
|
+
Install it from this repository's marketplace, inside Claude Code:
|
|
458
|
+
|
|
459
|
+
```text
|
|
460
|
+
/plugin marketplace add BargLabs/cejel
|
|
461
|
+
/plugin install cejel@cejel
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
From a shell, the same two steps are `claude plugin marketplace add BargLabs/cejel` and
|
|
465
|
+
`claude plugin install cejel@cejel`.
|
|
466
|
+
|
|
467
|
+
Scanning stays offline: the `scan` tool makes no network calls and writes no files. The first
|
|
468
|
+
time the server starts, `npx` fetches the pinned `@cejel/cejel` package from npm. When you name
|
|
469
|
+
a repository that isn't on your machine, Claude asks before cloning it with your `git`. Neither
|
|
470
|
+
fetch is part of the scan.
|
|
471
|
+
|
|
472
|
+
Example prompts that work on a public repository:
|
|
473
|
+
|
|
474
|
+
- "Clone https://github.com/BargLabs/cejel into a temporary folder and give me its Cejel trust
|
|
475
|
+
score."
|
|
476
|
+
- "Scan https://github.com/BargLabs/cejel with Cejel and explain the top findings and the criteria
|
|
477
|
+
they come from."
|
|
478
|
+
- "Run Cejel on https://github.com/BargLabs/cejel and save its trust badge to ./cejel-badge.svg."
|
|
479
|
+
|
|
480
|
+
Support: [GitHub issues](https://github.com/BargLabs/cejel/issues). Privacy policy:
|
|
481
|
+
<https://barglabs.ai/privacy>. Terms: <https://barglabs.ai/terms>.
|
|
482
|
+
|
|
437
483
|
## MCP server (for agents)
|
|
438
484
|
|
|
439
485
|
The same package ships a second bin, `cejel-mcp` — a thin MCP (Model Context Protocol)
|
|
@@ -521,7 +567,7 @@ The OCI image is an alternative when Docker is the preferred execution boundary.
|
|
|
521
567
|
the host path with the repository OpenClaw should allow Cejel to read:
|
|
522
568
|
|
|
523
569
|
```bash
|
|
524
|
-
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.6.
|
|
570
|
+
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.6.3"]}'
|
|
525
571
|
npx -y openclaw@latest mcp doctor cejel-oci --probe
|
|
526
572
|
```
|
|
527
573
|
|
|
@@ -345,6 +345,10 @@ var WitanReportCommonSchema = external_exports.object({
|
|
|
345
345
|
consumedSignals: external_exports.array(WitanConsumedSignalSummarySchema).optional(),
|
|
346
346
|
archetype: WitanRepoArchetypeSchema.optional(),
|
|
347
347
|
scanLimitations: external_exports.array(external_exports.string().min(1).max(1e3)).max(16).optional(),
|
|
348
|
+
// Additive-optional (report format 1.5, issue #428): disclosures about the commit history the
|
|
349
|
+
// scan read, such as a shallow clone. Declared, never scored: unlike scanLimitations it gates
|
|
350
|
+
// nothing (no score, abstention, badge or --min-score effect). Absent when history is complete.
|
|
351
|
+
historyLimitations: external_exports.array(external_exports.string().min(1).max(1e3)).max(4).optional(),
|
|
348
352
|
contentReadSummary: WitanContentReadSummarySchema.optional(),
|
|
349
353
|
// Additive-optional (report format 1.3): absent means a report produced before this field
|
|
350
354
|
// existed, never "nothing withheld" — createWitanReport always sets it (possibly to []) on any
|
|
@@ -397,7 +401,7 @@ var WitanReportSchema = external_exports.preprocess((value) => {
|
|
|
397
401
|
}, StrictWitanReportSchema);
|
|
398
402
|
var WITAN_ATTESTATION_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
399
403
|
var WITAN_ATTESTATION_PREDICATE_TYPE = "https://cejel.dev/attestations/scan/v1";
|
|
400
|
-
var WITAN_REPORT_FORMAT_VERSION = "1.
|
|
404
|
+
var WITAN_REPORT_FORMAT_VERSION = "1.5";
|
|
401
405
|
var WitanAttestationOutcomeSchema = external_exports.discriminatedUnion("status", [
|
|
402
406
|
external_exports.object({
|
|
403
407
|
status: external_exports.literal("scored"),
|
|
@@ -638,14 +642,14 @@ init_esm_shims();
|
|
|
638
642
|
init_esm_shims();
|
|
639
643
|
import { createHash } from "crypto";
|
|
640
644
|
var WITAN_RUBRIC_BEHAVIOUR_FINGERPRINTS = Object.freeze({
|
|
641
|
-
[WITAN_RUBRIC_VERSION_V17]: "sha256:
|
|
642
|
-
[WITAN_RUBRIC_VERSION_V18]: "sha256:
|
|
643
|
-
[WITAN_RUBRIC_VERSION_V19]: "sha256:
|
|
644
|
-
[WITAN_RUBRIC_VERSION_V20]: "sha256:
|
|
645
|
-
[WITAN_RUBRIC_VERSION_V21]: "sha256:
|
|
646
|
-
[WITAN_RUBRIC_VERSION_V22]: "sha256:
|
|
647
|
-
[WITAN_RUBRIC_VERSION_V23]: "sha256:
|
|
648
|
-
[WITAN_RUBRIC_VERSION_V24]: "sha256:
|
|
645
|
+
[WITAN_RUBRIC_VERSION_V17]: "sha256:2da86acf64c39d2edb7824df46250ea23c396264266a2cd925953e9c6ad25858",
|
|
646
|
+
[WITAN_RUBRIC_VERSION_V18]: "sha256:60917d1d81efe7cda6f6adbfaa05293e2d9bf1db37b39d084e47f9011a996b67",
|
|
647
|
+
[WITAN_RUBRIC_VERSION_V19]: "sha256:e050ce8563de0736d9cfb6c23e2ba04725d88465d378a8632576269b9bc9f269",
|
|
648
|
+
[WITAN_RUBRIC_VERSION_V20]: "sha256:0433adc34b58c0fd5500a0bdeaa910c854eedbadbeaabb9045238d4ba9be8878",
|
|
649
|
+
[WITAN_RUBRIC_VERSION_V21]: "sha256:d3796b78a45ea3593f6abe71c77c2d00a0056d0f2d88476a01329dc342166aa6",
|
|
650
|
+
[WITAN_RUBRIC_VERSION_V22]: "sha256:932bc09cbe555c6058a95be581ccf7a286c43887f90d32698f5273b7623824bf",
|
|
651
|
+
[WITAN_RUBRIC_VERSION_V23]: "sha256:db97dc999d37e3c39c3bd3e4b1778c94eb9ad3176fb5c7d7264a7e510b162fc1",
|
|
652
|
+
[WITAN_RUBRIC_VERSION_V24]: "sha256:e313fab25ae1779a11e86b23696881cd6f8fe2a845609dc93e2ff6f3ff8c16d5"
|
|
649
653
|
});
|
|
650
654
|
function rubricBehaviourFingerprint(rubricVersion) {
|
|
651
655
|
return WITAN_RUBRIC_BEHAVIOUR_FINGERPRINTS[rubricVersion];
|
|
@@ -2179,6 +2183,13 @@ function renderWitanHtmlReport(report, options = {}) {
|
|
|
2179
2183
|
).join("")}</ul>
|
|
2180
2184
|
</section>` : ""}
|
|
2181
2185
|
|
|
2186
|
+
${(report.historyLimitations?.length ?? 0) > 0 ? `<section class="scan-limitations-section evidence-section" aria-label="History limitations">
|
|
2187
|
+
<h2>History limitations</h2>
|
|
2188
|
+
<ul>${(report.historyLimitations ?? []).map(
|
|
2189
|
+
(limitation) => `<li class="scan-warning"><strong>History limitation</strong><span>${escapeHtml(limitation)}</span></li>`
|
|
2190
|
+
).join("")}</ul>
|
|
2191
|
+
</section>` : ""}
|
|
2192
|
+
|
|
2182
2193
|
${renderContentReadSummary(report)}
|
|
2183
2194
|
|
|
2184
2195
|
<section class="trust-grid" aria-label="Rubric criteria">
|
|
@@ -4800,14 +4811,14 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
4800
4811
|
const serverEntrypoint = v22PackageStartHttpEntrypoint ?? findServerEntrypointFile(repoPath, repoFiles, useV27Detectors) ?? v20DirectHttpEntrypoint ?? null;
|
|
4801
4812
|
const runtimeContainer = useV27Detectors ? findRuntimeContainerEntrypointFile(repoPath, repoFiles, useV20ExplicitGaps) : null;
|
|
4802
4813
|
const errorBoundaries = repoFiles.filter(
|
|
4803
|
-
(file) => (!useV27Detectors || isAuthoredProductionPath(file)) &&
|
|
4814
|
+
(file) => (!useV27Detectors || isAuthoredProductionPath(file)) && isImplementationEvidencePath(file) && ERROR_BOUNDARY_FILENAME_PATTERN.test(file)
|
|
4804
4815
|
);
|
|
4805
|
-
const errorBoundaryFileReads = (file) => observabilityDepthReads(file) || (!useV27Detectors || isAuthoredProductionPath(file)) && /(^|\/)server\//.test(file) && /\.(?:mjs|cjs)$/.test(file);
|
|
4816
|
+
const errorBoundaryFileReads = (file) => isImplementationEvidencePath(file) && (observabilityDepthReads(file) || (!useV27Detectors || isAuthoredProductionPath(file)) && /(^|\/)server\//.test(file) && /\.(?:mjs|cjs)$/.test(file));
|
|
4806
4817
|
const expressErrorMiddlewareFile = errorBoundaries.length > 0 ? void 0 : withContentReadSignal(
|
|
4807
4818
|
"A3",
|
|
4808
4819
|
"prod_readiness_primitives",
|
|
4809
4820
|
() => repoFiles.find(
|
|
4810
|
-
(file) => errorBoundaryFileReads(file) &&
|
|
4821
|
+
(file) => errorBoundaryFileReads(file) && fileMatchesOutsideComments(repoPath, file, EXPRESS_ERROR_MIDDLEWARE_PATTERN) && fileMatchesOutsideComments(repoPath, file, EXPRESS_MIDDLEWARE_REACHABLE_PATTERN)
|
|
4811
4822
|
)
|
|
4812
4823
|
);
|
|
4813
4824
|
const errorBoundary = errorBoundaries[0] ?? expressErrorMiddlewareFile;
|
|
@@ -4886,7 +4897,11 @@ function collectA3ProdReadinessEvidence(repoPath, repoFiles, useV27Detectors, us
|
|
|
4886
4897
|
errorBoundary,
|
|
4887
4898
|
"prod_check",
|
|
4888
4899
|
"Error boundary",
|
|
4889
|
-
|
|
4900
|
+
findFirstMatchingLineOutsideComments(
|
|
4901
|
+
repoPath,
|
|
4902
|
+
errorBoundary,
|
|
4903
|
+
EXPRESS_ERROR_MIDDLEWARE_PATTERN
|
|
4904
|
+
)
|
|
4890
4905
|
) : evidenceForRelative(repoPath, errorBoundary, "prod_check", "Error boundary")
|
|
4891
4906
|
);
|
|
4892
4907
|
}
|
|
@@ -5425,7 +5440,7 @@ function collectB2PrTraceEvidence(repoPath, repoFiles) {
|
|
|
5425
5440
|
(file) => /(^|\/)(pull_request_template|PULL_REQUEST_TEMPLATE)(\.md$|\/[^/]+\.md$)/.test(file)
|
|
5426
5441
|
);
|
|
5427
5442
|
const branchProtectionDoc = repoFiles.find(
|
|
5428
|
-
(file) => /branch.*protection|review.*gate
|
|
5443
|
+
(file) => /branch.*protection|review.*gate/i.test(file) || GITHUB_CODEOWNERS_PATH_PATTERN.test(file)
|
|
5429
5444
|
);
|
|
5430
5445
|
const evidence = [
|
|
5431
5446
|
...workflows.slice(0, 3).map((file) => evidenceForRelative(repoPath, file, "ci_run", "Pull-request CI workflow")),
|
|
@@ -5678,32 +5693,36 @@ function collectB4AuditEvidence(repoPath, repoFiles, generatedAt, useV19CommitYe
|
|
|
5678
5693
|
function collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles, useV39Detectors = false, useV21ExecutedEscalations = false) {
|
|
5679
5694
|
const evidence = [];
|
|
5680
5695
|
const findings = [];
|
|
5681
|
-
const
|
|
5696
|
+
const policyDocFiles = repoFiles.filter(
|
|
5697
|
+
(file) => /\.(md|mdx)$/i.test(file) && isPolicyDocumentEvidencePath(file)
|
|
5698
|
+
);
|
|
5699
|
+
const isPolicyDocMatching = (file, pattern) => fileContains(repoPath, file, pattern) && !isCejelCertificateText(repoPath, file);
|
|
5682
5700
|
const humanGateDoc = withContentReadSignal(
|
|
5683
5701
|
"B6",
|
|
5684
5702
|
"human_gate_documented",
|
|
5685
|
-
() =>
|
|
5703
|
+
() => policyDocFiles.find((file) => isPolicyDocMatching(file, HUMAN_GATE_MARKER_PATTERN))
|
|
5686
5704
|
);
|
|
5687
5705
|
const implFiles = repoFiles.filter(isImplementationFile);
|
|
5706
|
+
const implementationEvidenceFiles = implFiles.filter(isImplementationEvidencePath);
|
|
5688
5707
|
const gatedPrivilegeCheckFile = withContentReadSignal(
|
|
5689
5708
|
"B6",
|
|
5690
5709
|
"fail_closed_privilege_check",
|
|
5691
|
-
() =>
|
|
5692
|
-
(file) =>
|
|
5710
|
+
() => implementationEvidenceFiles.find(
|
|
5711
|
+
(file) => fileMatchesOutsideComments(repoPath, file, GATED_PRIVILEGE_CHECK_PATTERN) && fileMatchesOutsideComments(repoPath, file, SET_ROLE_PATTERN)
|
|
5693
5712
|
)
|
|
5694
5713
|
);
|
|
5695
5714
|
const killSwitchFile = withContentReadSignal(
|
|
5696
5715
|
"B6",
|
|
5697
5716
|
"kill_switch_fail_safe_present",
|
|
5698
|
-
() =>
|
|
5699
|
-
(file) =>
|
|
5717
|
+
() => implementationEvidenceFiles.find(
|
|
5718
|
+
(file) => fileMatchesOutsideComments(repoPath, file, KILL_SWITCH_NAME_PATTERN) && fileMatchesOutsideComments(repoPath, file, KILL_SWITCH_FAIL_CLOSED_PATTERN)
|
|
5700
5719
|
)
|
|
5701
5720
|
);
|
|
5702
|
-
const codeownersFile = repoFiles.find((file) =>
|
|
5721
|
+
const codeownersFile = repoFiles.find((file) => GITHUB_CODEOWNERS_PATH_PATTERN.test(file));
|
|
5703
5722
|
const reviewGateDoc = withContentReadSignal(
|
|
5704
5723
|
"B6",
|
|
5705
5724
|
"protected_path_review_gate",
|
|
5706
|
-
() =>
|
|
5725
|
+
() => policyDocFiles.find((file) => isPolicyDocMatching(file, REQUIRED_REVIEW_PATTERN))
|
|
5707
5726
|
);
|
|
5708
5727
|
const protectedPathReviewGate = codeownersFile ?? reviewGateDoc;
|
|
5709
5728
|
const { executableFiles, v21ExecutedEscalationFileSet, productionUngatedEscalationFiles } = withContentReadSignal("B6", "privilege_escalation_cleanliness", () => {
|
|
@@ -5781,7 +5800,7 @@ function collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles, useV39Detecto
|
|
|
5781
5800
|
repoPath,
|
|
5782
5801
|
protectedPathReviewGate,
|
|
5783
5802
|
"artifact",
|
|
5784
|
-
"CODEOWNERS
|
|
5803
|
+
protectedPathReviewGate === codeownersFile ? "CODEOWNERS file in a location GitHub reads (protected-path review gate)" : "Documented required-review/branch-protection policy"
|
|
5785
5804
|
)
|
|
5786
5805
|
)
|
|
5787
5806
|
);
|
|
@@ -5872,7 +5891,7 @@ function collectB6PrivilegedOpsGatingEvidence(repoPath, repoFiles, useV39Detecto
|
|
|
5872
5891
|
1,
|
|
5873
5892
|
hasPrivilegedOpsSurface ? 0.2 : 0.6,
|
|
5874
5893
|
"present",
|
|
5875
|
-
"Credits a CODEOWNERS file or documented required-review/branch-protection policy \u2014 the general OSS-observable analogue of human-gating changes to sensitive paths."
|
|
5894
|
+
"Credits a CODEOWNERS file where GitHub reads it (root, .github/ or docs/) or a documented required-review/branch-protection policy (not a changelog, record, review note or report) \u2014 the general OSS-observable analogue of human-gating changes to sensitive paths."
|
|
5876
5895
|
),
|
|
5877
5896
|
...killSwitchMetrics
|
|
5878
5897
|
],
|
|
@@ -5918,6 +5937,19 @@ function isTestFile(file, rubricVersion = WITAN_RUBRIC_VERSION) {
|
|
|
5918
5937
|
}
|
|
5919
5938
|
var NAME_SHAPED_TEST_FILE_PATTERN = /^test(?:-[^/]+)?\.[cm]?[jt]sx?$|\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)__tests__\/|(^|\/)test_[^/]*\.py$|(^|\/)tests?\.py$|_test\.go$|(^|\/)test_[^/]*\.(cpp|cc|cxx)$|_(test|tests)\.(cpp|cc|cxx)$|Tests?\.(java|kt)$|(_test|_spec)\.(rs|rb|php|swift|kt)$/;
|
|
5920
5939
|
var NAME_SHAPED_TEST_FILE_PATTERN_V17 = /\.tftest\.hcl$|\.bats$|(^|\/)test[^/]*\.m$|_test\.m$/i;
|
|
5940
|
+
var CALIBRATION_RECORD_DIR_PATTERN = /(^|\/)calibrations?(\/|$)/i;
|
|
5941
|
+
function isImplementationEvidencePath(path) {
|
|
5942
|
+
return isProductionSourcePath(path) && !CALIBRATION_RECORD_DIR_PATTERN.test(path);
|
|
5943
|
+
}
|
|
5944
|
+
var CHANGELOG_FILE_PATTERN = /(^|\/)(?:CHANGELOG|CHANGES|HISTORY|NEWS|RELEASE[-_]?NOTES|RELEASES)(?:\.[^/]*)?$/i;
|
|
5945
|
+
var NON_POLICY_RECORD_DIR_PATTERN = /(^|\/)(?:\.changesets?|changelogs?|changelog\.d|experiments?|calibrations?|reviews|review[-_]notes?|reports)(\/|$)/i;
|
|
5946
|
+
function isPolicyDocumentEvidencePath(path) {
|
|
5947
|
+
return isProductionSourcePath(path) && !CHANGELOG_FILE_PATTERN.test(path) && !NON_POLICY_RECORD_DIR_PATTERN.test(path);
|
|
5948
|
+
}
|
|
5949
|
+
var CEJEL_CERTIFICATE_TEXT_PATTERN = /^# Cejel Trust Report\b[\s\S]*?^- Rubric: /m;
|
|
5950
|
+
function isCejelCertificateText(repoPath, file) {
|
|
5951
|
+
return fileContains(repoPath, file, CEJEL_CERTIFICATE_TEXT_PATTERN);
|
|
5952
|
+
}
|
|
5921
5953
|
var TEST_RUNNER_PATTERN = /\b(vitest|jest|mocha|ava|tap|pytest|go test|cargo test|rspec|phpunit|gradle test|mvn test|node\s+--test|node:test)\b/i;
|
|
5922
5954
|
function hasCredibleTestScript(scripts) {
|
|
5923
5955
|
const script = scripts.get("test");
|
|
@@ -5969,6 +6001,7 @@ function fileHasV21RawSqlEscalation(repoPath, file) {
|
|
|
5969
6001
|
var GATED_PRIVILEGE_CHECK_PATTERN = /pg_has_role\s*\(|has_role\s*\(|is not a member of/i;
|
|
5970
6002
|
var SET_ROLE_PATTERN = /\bset\s+(local\s+)?role\b/i;
|
|
5971
6003
|
var REQUIRED_REVIEW_PATTERN = /required review|branch protection|protected branch/i;
|
|
6004
|
+
var GITHUB_CODEOWNERS_PATH_PATTERN = /^(?:\.github\/|docs\/)?CODEOWNERS$/;
|
|
5972
6005
|
var HISTORY_SECRET_SCAN_CREDENTIAL_BLOB_LIMIT = 5e3;
|
|
5973
6006
|
var V23_PEM_WIDENED_HISTORY_BLOB_LIMIT = 500;
|
|
5974
6007
|
var SECRET_IDENTIFIER_KEYWORD_PATTERN = /secret|token|api[_-]?key|password|access[_-]?key/gi;
|
|
@@ -8027,6 +8060,7 @@ var HEALTH_ROUTE_FUNCTIONS_DIR_PATTERN = /(^|\/)functions\/.*\.(?:ts|tsx|js|jsx|
|
|
|
8027
8060
|
var V20_HEALTH_OR_READINESS_ROUTE_PATTERN = /["'`](?:[\w${}./-]{0,60}\/[_-]?(?:(?:health|ready|live)z?|readiness|liveness|up)(?=["'`/])|(?:healthz|readiness|liveness)(?=["'`]))|@Get\s*\(\s*(["'`])health\1\s*\)/i;
|
|
8028
8061
|
var EXPRESS_ERROR_MIDDLEWARE_PATTERN = /\(\s*(?:_?err|_?error)(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*,\s*(?:req|request)(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*,\s*(?:res|response)(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*,\s*_?next(?:\s*:\s*(?:[^,()<]|<[^<>]*>)*)?\s*\)\s*(?:=>|\{)/i;
|
|
8029
8062
|
var EXPRESS_MIDDLEWARE_REACHABLE_PATTERN = /\.use\s*\(|\bmodule\.exports\b|\bexport\s+(?:default\b|function\b|const\b|class\b)/;
|
|
8063
|
+
var ERROR_BOUNDARY_FILENAME_PATTERN = /(^|\/)(?:error-boundary|error)\.(?:tsx|jsx|ts|js)$/;
|
|
8030
8064
|
var RACK_SERVER_ENTRYPOINT_PATTERN = /Rack::(?:Server|Handler(?:::\w+)?)\.(?:start|run)\s*\(/;
|
|
8031
8065
|
var RACK_CONFIG_RUN_PATTERN = /^\s*run\s+(?:(?:[A-Z]\w*(?:::\w+)*(?:\.new)?|lambda)\b|->)/m;
|
|
8032
8066
|
var RUNTIME_CONTAINER_COMMAND_PATTERN = /^\s*(?:CMD|ENTRYPOINT)\s+.*(?:\b(?:start|serve|server|qgis|nginx|apache|gunicorn|uvicorn)\b|manage\.py\s+runserver).*$/im;
|
|
@@ -8221,11 +8255,22 @@ function fileContains(repoPath, file, pattern) {
|
|
|
8221
8255
|
if (!isRegularFile(fullPath)) return false;
|
|
8222
8256
|
return pattern.test(readRepoText(fullPath, "utf8"));
|
|
8223
8257
|
}
|
|
8224
|
-
|
|
8258
|
+
var HASH_COMMENT_SOURCE_PATTERN = /\.(?:py|rb)$/i;
|
|
8259
|
+
function stripCodeComments(file, contents) {
|
|
8260
|
+
const withoutComments = contents.replace(/\/\*[\s\S]*?\*\//g, (block) => block.replace(/[^\n]/g, "")).replace(/(?<!:)\/\/.*$/gm, "");
|
|
8261
|
+
return HASH_COMMENT_SOURCE_PATTERN.test(file) ? withoutComments.replace(/^[ \t]*#.*$/gm, "") : withoutComments;
|
|
8262
|
+
}
|
|
8263
|
+
function fileMatchesOutsideComments(repoPath, file, pattern) {
|
|
8225
8264
|
const fullPath = join(repoPath, file);
|
|
8226
8265
|
if (!isRegularFile(fullPath)) return false;
|
|
8227
|
-
|
|
8228
|
-
|
|
8266
|
+
return pattern.test(stripCodeComments(file, readRepoText(fullPath, "utf8")));
|
|
8267
|
+
}
|
|
8268
|
+
function findFirstMatchingLineOutsideComments(repoPath, file, pattern) {
|
|
8269
|
+
const fullPath = join(repoPath, file);
|
|
8270
|
+
if (!isRegularFile(fullPath)) return null;
|
|
8271
|
+
const lines = stripCodeComments(file, readRepoText(fullPath, "utf8")).split("\n");
|
|
8272
|
+
const index = lines.findIndex((line) => pattern.test(line));
|
|
8273
|
+
return index === -1 ? null : index + 1;
|
|
8229
8274
|
}
|
|
8230
8275
|
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;
|
|
8231
8276
|
var BARE_PEM_PRIVATE_KEY_PATTERN = /["'`]?-----BEGIN((?: (?:RSA|DSA|EC|OPENSSH|ENCRYPTED))? PRIVATE KEY)-----([\s\S]{0,32768}?)-----END\1-----/g;
|
|
@@ -9218,6 +9263,15 @@ function hasEnvPathInGitHistory(repoPath, scanLimitations) {
|
|
|
9218
9263
|
// src/witan/public-scan.ts
|
|
9219
9264
|
init_esm_shims();
|
|
9220
9265
|
|
|
9266
|
+
// src/witan/history-depth.ts
|
|
9267
|
+
init_esm_shims();
|
|
9268
|
+
var SHALLOW_HISTORY_LIMITATION = "This repository is a shallow clone (git rev-parse --is-shallow-repository reports true), so its commit history is truncated. A2 (credentials and .env files in earlier commits) and B2 (pull-request merge ratio over recent commits) read history and saw only the commits present; on a full clone their results may differ. Scores are computed exactly as for any other scan. Re-scan a full clone (for actions/checkout, set fetch-depth: 0).";
|
|
9269
|
+
function detectHistoryLimitations(repoPath) {
|
|
9270
|
+
const result = execGit(["rev-parse", "--is-shallow-repository"], { cwd: repoPath });
|
|
9271
|
+
if (!result.ok || result.stdout.trim() !== "true") return void 0;
|
|
9272
|
+
return [SHALLOW_HISTORY_LIMITATION];
|
|
9273
|
+
}
|
|
9274
|
+
|
|
9221
9275
|
// src/witan/ingest.ts
|
|
9222
9276
|
init_esm_shims();
|
|
9223
9277
|
import { lstatSync as lstatSync3, opendirSync, readFileSync as readFileSync4, realpathSync as realpathSync3 } from "fs";
|
|
@@ -9755,12 +9809,14 @@ function scoreRepoWithPublicCejel(options) {
|
|
|
9755
9809
|
...options.rubricVersion ? { rubricVersion: options.rubricVersion } : {}
|
|
9756
9810
|
});
|
|
9757
9811
|
const inputSignals = resolvePublicIngestSignals(options);
|
|
9758
|
-
const
|
|
9812
|
+
const scored = createWitanReport(
|
|
9759
9813
|
input,
|
|
9760
9814
|
inputSignals.length > 0 ? inputSignals : void 0,
|
|
9761
9815
|
void 0,
|
|
9762
9816
|
options.toolVersion
|
|
9763
9817
|
);
|
|
9818
|
+
const historyLimitations = detectHistoryLimitations(options.repoPath);
|
|
9819
|
+
const report = historyLimitations ? { ...scored, historyLimitations } : scored;
|
|
9764
9820
|
if (report.verdict !== "insufficient_source" || !isWitanNoMeasurementAbstention(report)) {
|
|
9765
9821
|
return report;
|
|
9766
9822
|
}
|
|
@@ -9982,6 +10038,7 @@ function buildWitanCliSummary(report) {
|
|
|
9982
10038
|
externalFindingCount: allExternalFindings.length,
|
|
9983
10039
|
topExternalFindings: allExternalFindings.slice(0, EXTERNAL_FINDINGS_DISPLAY_LIMIT),
|
|
9984
10040
|
scanLimitations: [...report.scanLimitations ?? []],
|
|
10041
|
+
...report.historyLimitations?.length ? { historyLimitations: [...report.historyLimitations] } : {},
|
|
9985
10042
|
...report.contentReadSummary ? { contentReadSummary: report.contentReadSummary } : {}
|
|
9986
10043
|
};
|
|
9987
10044
|
if (report.verdict === "insufficient_source") {
|