@cejel/cejel 0.4.11 → 0.6.0
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 +11 -10
- package/dist/calibration/llm-detector.js +1 -1
- package/dist/{chunk-UBCIGOIP.js → chunk-2453M7C2.js} +8409 -8129
- package/dist/index.js +152 -12
- package/dist/mcp/index.js +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -9,10 +9,12 @@ import {
|
|
|
9
9
|
WitanReportSchema,
|
|
10
10
|
assertSelectableRubricVersion,
|
|
11
11
|
buildRelyingPartySummary,
|
|
12
|
+
computeApplicableMeasuredCoverage,
|
|
12
13
|
createWitanAttestation,
|
|
13
14
|
formatCertificateMetricAppliedWeightPercent,
|
|
14
15
|
formatCertificateMetricLabel,
|
|
15
16
|
formatCertificateMetricValue,
|
|
17
|
+
formatCoverageSummary,
|
|
16
18
|
formatExternalSourceLine,
|
|
17
19
|
isProspectiveRubricVersion,
|
|
18
20
|
renderWitanBadgeEndpoint,
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
runCejelScan,
|
|
22
24
|
serializeWitanReport,
|
|
23
25
|
verifyWitanAttestationBinding
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-2453M7C2.js";
|
|
25
27
|
import "./chunk-63XQDYQ5.js";
|
|
26
28
|
import {
|
|
27
29
|
external_exports
|
|
@@ -32,7 +34,7 @@ import {
|
|
|
32
34
|
|
|
33
35
|
// src/index.ts
|
|
34
36
|
init_esm_shims();
|
|
35
|
-
import { createHash as
|
|
37
|
+
import { createHash as createHash3 } from "crypto";
|
|
36
38
|
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
|
|
37
39
|
import { dirname, join, resolve } from "path";
|
|
38
40
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
@@ -370,7 +372,7 @@ function verifyWitanIssuanceSignature(options) {
|
|
|
370
372
|
reason: `key type ${JSON.stringify(parsed.keyType)} is not one this build verifies (supported: ${WITAN_ISSUANCE_SUPPORTED_KEY_TYPES.join(", ")}); verify with OpenSSH instead \u2014 see docs/issuance.md`
|
|
371
373
|
};
|
|
372
374
|
}
|
|
373
|
-
const
|
|
375
|
+
const fingerprint2 = sshPublicKeyFingerprint(parsed.publicKeyBlob);
|
|
374
376
|
let signatureVerified;
|
|
375
377
|
try {
|
|
376
378
|
const publicKey = ed25519PublicKeyFromBlob(parsed.publicKeyBlob);
|
|
@@ -405,16 +407,16 @@ function verifyWitanIssuanceSignature(options) {
|
|
|
405
407
|
return { status: "invalid", reason: error instanceof Error ? error.message : String(error) };
|
|
406
408
|
}
|
|
407
409
|
if (signers.length === 0) {
|
|
408
|
-
return { status: "unlisted_key", keyType: parsed.keyType, fingerprint };
|
|
410
|
+
return { status: "unlisted_key", keyType: parsed.keyType, fingerprint: fingerprint2 };
|
|
409
411
|
}
|
|
410
412
|
const signedKeyBase64 = parsed.publicKeyBlob.toString("base64");
|
|
411
413
|
const listed = signers.some(
|
|
412
414
|
(entry) => entry.uninterpretedOptions === void 0 && entry.keyType === parsed.keyType && normalizeBase64(entry.keyBase64) === signedKeyBase64 && entry.principals.includes(expectedPrincipal) && (entry.namespaces === void 0 || entry.namespaces.includes(expectedNamespace))
|
|
413
415
|
);
|
|
414
416
|
if (!listed) {
|
|
415
|
-
return { status: "unlisted_key", keyType: parsed.keyType, fingerprint };
|
|
417
|
+
return { status: "unlisted_key", keyType: parsed.keyType, fingerprint: fingerprint2 };
|
|
416
418
|
}
|
|
417
|
-
return { status: "valid", principal: expectedPrincipal, keyType: parsed.keyType, fingerprint };
|
|
419
|
+
return { status: "valid", principal: expectedPrincipal, keyType: parsed.keyType, fingerprint: fingerprint2 };
|
|
418
420
|
}
|
|
419
421
|
function normalizeBase64(value) {
|
|
420
422
|
return Buffer.from(value, "base64").toString("base64");
|
|
@@ -468,6 +470,71 @@ function parseIssuerPublicKey(contents) {
|
|
|
468
470
|
return { keyType, keyBase64, fingerprint: sshPublicKeyFingerprint(blob) };
|
|
469
471
|
}
|
|
470
472
|
|
|
473
|
+
// src/export/gitlab-codequality.ts
|
|
474
|
+
init_esm_shims();
|
|
475
|
+
import { createHash as createHash2 } from "crypto";
|
|
476
|
+
var GITLAB_CODEQUALITY_SEVERITY = {
|
|
477
|
+
critical: "critical",
|
|
478
|
+
warning: "major",
|
|
479
|
+
info: "info"
|
|
480
|
+
};
|
|
481
|
+
function normalizeRepoRelativePath(raw) {
|
|
482
|
+
let path = raw.replaceAll("\\", "/");
|
|
483
|
+
while (path.startsWith("./")) path = path.slice(2);
|
|
484
|
+
if (path.length === 0 || path.startsWith("/") || /^[A-Za-z]:/.test(path)) return void 0;
|
|
485
|
+
if (path.split("/").some((segment) => segment === ".." || segment === "")) return void 0;
|
|
486
|
+
return path;
|
|
487
|
+
}
|
|
488
|
+
function fingerprint(rule, path, line, identity) {
|
|
489
|
+
return createHash2("sha256").update(JSON.stringify([rule, path, line, identity])).digest("hex");
|
|
490
|
+
}
|
|
491
|
+
function exportGitLabCodeQuality(report) {
|
|
492
|
+
const ingestedCount = (report.consumedSignals ?? []).reduce(
|
|
493
|
+
(total, signal) => total + signal.findings.length,
|
|
494
|
+
0
|
|
495
|
+
);
|
|
496
|
+
if (report.verdict === "insufficient_source") {
|
|
497
|
+
return {
|
|
498
|
+
entries: [],
|
|
499
|
+
excludedUnlocated: 0,
|
|
500
|
+
excludedIngested: ingestedCount,
|
|
501
|
+
abstainedReason: report.insufficientSourceReason
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
const entries = [];
|
|
505
|
+
let excludedUnlocated = 0;
|
|
506
|
+
const seen = /* @__PURE__ */ new Set();
|
|
507
|
+
for (const criterion of report.criteria) {
|
|
508
|
+
for (const finding of criterion.findings) {
|
|
509
|
+
const path = finding.evidence.path === void 0 ? void 0 : normalizeRepoRelativePath(finding.evidence.path);
|
|
510
|
+
const line = finding.evidence.line;
|
|
511
|
+
if (path === void 0 || line == null) {
|
|
512
|
+
excludedUnlocated += 1;
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const print = fingerprint(criterion.id, path, line, finding.summary);
|
|
516
|
+
if (seen.has(print)) continue;
|
|
517
|
+
seen.add(print);
|
|
518
|
+
entries.push({
|
|
519
|
+
description: finding.summary,
|
|
520
|
+
check_name: criterion.id,
|
|
521
|
+
fingerprint: print,
|
|
522
|
+
location: { path, lines: { begin: line } },
|
|
523
|
+
severity: GITLAB_CODEQUALITY_SEVERITY[finding.severity]
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return { entries, excludedUnlocated, excludedIngested: ingestedCount };
|
|
528
|
+
}
|
|
529
|
+
function renderGitLabCodeQualityFooter(result) {
|
|
530
|
+
if (result.abstainedReason !== void 0) {
|
|
531
|
+
return `Cejel: abstained report (insufficient_source): exported 0 entries. ${result.abstainedReason}
|
|
532
|
+
`;
|
|
533
|
+
}
|
|
534
|
+
return `Cejel: exported ${result.entries.length} located finding(s); ${result.excludedUnlocated} without a file and line excluded; ${result.excludedIngested} ingested third-party finding(s) not exported.
|
|
535
|
+
`;
|
|
536
|
+
}
|
|
537
|
+
|
|
471
538
|
// src/terminal.ts
|
|
472
539
|
init_esm_shims();
|
|
473
540
|
function renderMinScoreAbstentionFailure(summary, minScore) {
|
|
@@ -484,6 +551,13 @@ function renderMinScoreLimitationFailure(summary, minScore) {
|
|
|
484
551
|
return `Cejel: cannot evaluate the required minimum ${minScore.toFixed(1)}/4.0 because the scan has limited evidence (${summary.scanLimitations.length} limitation${summary.scanLimitations.length === 1 ? "" : "s"}). Resolve the reported limitations and re-scan.
|
|
485
552
|
`;
|
|
486
553
|
}
|
|
554
|
+
function renderMinScoreCoverageFailure(coverage, minScore) {
|
|
555
|
+
if (!coverage.lowConfidence) {
|
|
556
|
+
throw new Error("A minimum-score coverage message requires low measurement coverage.");
|
|
557
|
+
}
|
|
558
|
+
return `Cejel: cannot evaluate the required minimum ${minScore.toFixed(1)}/4.0 because fewer than half of the applicable dimensions were measured in at least one score (${formatCoverageSummary(coverage)}). Resolve the reported evidence gaps and re-scan.
|
|
559
|
+
`;
|
|
560
|
+
}
|
|
487
561
|
function renderTerminalCertificate(summary, report) {
|
|
488
562
|
const abstained = summary.verdict === "Insufficient source" || summary.verdict === "Insufficient evidence";
|
|
489
563
|
if (!abstained && (summary.overallScore === null || summary.codeTrustScore === null || summary.processTrustScore === null)) {
|
|
@@ -636,7 +710,7 @@ var CLI_FLAG_SPECS = [
|
|
|
636
710
|
{
|
|
637
711
|
tokens: ["--min-score"],
|
|
638
712
|
value: "<0-4>",
|
|
639
|
-
description: "exit nonzero below this overall score",
|
|
713
|
+
description: "exit nonzero below this overall score or with insufficient measurement coverage",
|
|
640
714
|
kind: "minScore"
|
|
641
715
|
},
|
|
642
716
|
{
|
|
@@ -730,12 +804,14 @@ Usage:
|
|
|
730
804
|
npx ${NPX_PACKAGE_NAME} [path] [options]
|
|
731
805
|
npx ${NPX_PACKAGE_NAME} scan [path] [options]
|
|
732
806
|
npx ${NPX_PACKAGE_NAME} verify <report.json> <attestation.json> [issuance.json issuance.json.sig]
|
|
807
|
+
npx ${NPX_PACKAGE_NAME} export gitlab-codequality <report.json> [-o gl-code-quality-report.json]
|
|
733
808
|
npx ${NPX_PACKAGE_NAME} issue [path] --report <report.json> --attestation <attestation.json> --key <key.pub> --engagement-ref <ref>
|
|
734
809
|
|
|
735
810
|
Commands:
|
|
736
811
|
scan score a repository (default when the command is omitted)
|
|
737
812
|
verify verify the report/attestation binding; with an issuance pair, also check the issuer
|
|
738
813
|
signature against docs/security/${ISSUER_SIGNERS_FILE} and docs/security/${ISSUER_REVOCATIONS_FILE}
|
|
814
|
+
export write report.json's located findings as a GitLab Code Quality report (offline)
|
|
739
815
|
issue re-run this version at the certificate's revision and, only if report.json reproduces
|
|
740
816
|
byte for byte, write an unsigned issuance.json for the issuer to sign through ssh-agent
|
|
741
817
|
|
|
@@ -780,6 +856,9 @@ async function runWitanCli(args, rubricVersion) {
|
|
|
780
856
|
if (invocation.command === "issue") {
|
|
781
857
|
return runIssue(invocation.options);
|
|
782
858
|
}
|
|
859
|
+
if (invocation.command === "export") {
|
|
860
|
+
return runExport(invocation);
|
|
861
|
+
}
|
|
783
862
|
const options = invocation.options;
|
|
784
863
|
if (options.showVersion) {
|
|
785
864
|
process.stdout.write(`${cliVersion()}
|
|
@@ -851,6 +930,11 @@ Wrote:
|
|
|
851
930
|
process.stderr.write(renderMinScoreAbstentionFailure(summary, options.minScore));
|
|
852
931
|
return 1;
|
|
853
932
|
}
|
|
933
|
+
const coverage = computeApplicableMeasuredCoverage(report);
|
|
934
|
+
if (coverage.lowConfidence) {
|
|
935
|
+
process.stderr.write(renderMinScoreCoverageFailure(coverage, options.minScore));
|
|
936
|
+
return 1;
|
|
937
|
+
}
|
|
854
938
|
if (report.overallScore < options.minScore) {
|
|
855
939
|
process.stderr.write(
|
|
856
940
|
`Cejel: overall score ${report.overallScore.toFixed(1)}/4.0 is below the required minimum ${options.minScore.toFixed(1)}/4.0
|
|
@@ -876,6 +960,13 @@ function parseCliInvocation(args) {
|
|
|
876
960
|
}
|
|
877
961
|
return parseVerifyArgs(verifyArgs);
|
|
878
962
|
}
|
|
963
|
+
if (command === "export") {
|
|
964
|
+
const exportArgs = args.slice(1);
|
|
965
|
+
if (exportArgs.some((arg) => arg === "-h" || arg === "--help")) {
|
|
966
|
+
return { command: "scan", options: parseArgs(["--help"]) };
|
|
967
|
+
}
|
|
968
|
+
return parseExportArgs(exportArgs);
|
|
969
|
+
}
|
|
879
970
|
if (command === "issue") {
|
|
880
971
|
const issueArgs = args.slice(1);
|
|
881
972
|
if (issueArgs.some((arg) => arg === "-h" || arg === "--help")) {
|
|
@@ -888,6 +979,54 @@ function parseCliInvocation(args) {
|
|
|
888
979
|
}
|
|
889
980
|
return { command: "scan", options: parseArgs(args) };
|
|
890
981
|
}
|
|
982
|
+
var EXPORT_USAGE = () => `Usage: npx ${NPX_PACKAGE_NAME} export gitlab-codequality <report.json> [-o gl-code-quality-report.json]`;
|
|
983
|
+
function parseExportArgs(args) {
|
|
984
|
+
const [format, ...rest] = args;
|
|
985
|
+
if (format !== "gitlab-codequality") throw new Error(EXPORT_USAGE());
|
|
986
|
+
const positionals = [];
|
|
987
|
+
let outPath = "gl-code-quality-report.json";
|
|
988
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
989
|
+
const arg = rest[index];
|
|
990
|
+
if (arg === void 0) continue;
|
|
991
|
+
if (arg === "-o" || arg === "--out") {
|
|
992
|
+
const value = rest[index + 1];
|
|
993
|
+
if (!value) throw new Error(`Missing value for ${arg}`);
|
|
994
|
+
outPath = value;
|
|
995
|
+
index += 1;
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
if (arg.startsWith("-")) throw new Error(`Unknown Cejel export flag: ${arg}`);
|
|
999
|
+
positionals.push(arg);
|
|
1000
|
+
}
|
|
1001
|
+
const [reportPath] = positionals;
|
|
1002
|
+
if (positionals.length !== 1 || !reportPath) throw new Error(EXPORT_USAGE());
|
|
1003
|
+
return {
|
|
1004
|
+
command: "export",
|
|
1005
|
+
format,
|
|
1006
|
+
reportPath: resolve(reportPath),
|
|
1007
|
+
outPath: resolve(outPath)
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
function runExport(invocation) {
|
|
1011
|
+
const reportArtifact = readJsonArtifact(invocation.reportPath, "report");
|
|
1012
|
+
const reportResult = WitanReportSchema.safeParse(reportArtifact.value);
|
|
1013
|
+
if (!reportResult.success) {
|
|
1014
|
+
const members = reportResult.error.issues.map(
|
|
1015
|
+
(issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`
|
|
1016
|
+
);
|
|
1017
|
+
process.stderr.write(
|
|
1018
|
+
`Cejel: report validation failed:
|
|
1019
|
+
${members.map((member) => ` - ${member}`).join("\n")}
|
|
1020
|
+
`
|
|
1021
|
+
);
|
|
1022
|
+
return 1;
|
|
1023
|
+
}
|
|
1024
|
+
const result = exportGitLabCodeQuality(reportResult.data);
|
|
1025
|
+
writeFileSync(invocation.outPath, `${JSON.stringify(result.entries, null, 2)}
|
|
1026
|
+
`, "utf8");
|
|
1027
|
+
process.stderr.write(renderGitLabCodeQualityFooter(result));
|
|
1028
|
+
return 0;
|
|
1029
|
+
}
|
|
891
1030
|
var VERIFY_USAGE = () => `Usage: npx ${NPX_PACKAGE_NAME} verify <report.json> <attestation.json> [issuance.json issuance.json.sig] [--signers <file>] [--revocations <file>]`;
|
|
892
1031
|
function parseVerifyArgs(args) {
|
|
893
1032
|
const positionals = [];
|
|
@@ -1029,7 +1168,7 @@ ${members.map((member) => ` - ${member}`).join("\n")}
|
|
|
1029
1168
|
return 1;
|
|
1030
1169
|
}
|
|
1031
1170
|
const statement = readJsonArtifact(invocation.attestationPath, "attestation");
|
|
1032
|
-
const reportSha256 =
|
|
1171
|
+
const reportSha256 = createHash3("sha256").update(reportArtifact.contents).digest("hex");
|
|
1033
1172
|
const result = verifyWitanAttestationBinding(statement.value, reportResult.data, {
|
|
1034
1173
|
reportSha256
|
|
1035
1174
|
});
|
|
@@ -1046,7 +1185,7 @@ ${result.errors.map((error) => ` - ${error}`).join("\n")}
|
|
|
1046
1185
|
process.stdout.write("Cejel: signature and signer identity were not verified.\n");
|
|
1047
1186
|
return 0;
|
|
1048
1187
|
}
|
|
1049
|
-
const attestationSha256 =
|
|
1188
|
+
const attestationSha256 = createHash3("sha256").update(statement.contents).digest("hex");
|
|
1050
1189
|
return runVerifyIssuance(invocation, {
|
|
1051
1190
|
reportSha256,
|
|
1052
1191
|
attestationSha256,
|
|
@@ -1149,8 +1288,8 @@ ${members.map((member) => ` - ${member}`).join("\n")}
|
|
|
1149
1288
|
}
|
|
1150
1289
|
const suppliedReport = reportResult.data;
|
|
1151
1290
|
const attestationArtifact = readJsonArtifact(options.attestationPath, "attestation");
|
|
1152
|
-
const reportSha256 =
|
|
1153
|
-
const attestationSha256 =
|
|
1291
|
+
const reportSha256 = createHash3("sha256").update(reportArtifact.contents).digest("hex");
|
|
1292
|
+
const attestationSha256 = createHash3("sha256").update(attestationArtifact.contents).digest("hex");
|
|
1154
1293
|
const attestationBinding = verifyWitanAttestationBinding(
|
|
1155
1294
|
attestationArtifact.value,
|
|
1156
1295
|
suppliedReport,
|
|
@@ -1182,7 +1321,7 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
|
|
|
1182
1321
|
toolVersion: cliVersion()
|
|
1183
1322
|
});
|
|
1184
1323
|
const reproducedBytes = serializeWitanReport(reproduction.report);
|
|
1185
|
-
const reproducedSha256 =
|
|
1324
|
+
const reproducedSha256 = createHash3("sha256").update(reproducedBytes, "utf8").digest("hex");
|
|
1186
1325
|
if (reproducedSha256 !== reportSha256) {
|
|
1187
1326
|
process.stderr.write(
|
|
1188
1327
|
[
|
|
@@ -1403,6 +1542,7 @@ export {
|
|
|
1403
1542
|
WitanReportSchema,
|
|
1404
1543
|
parseArgs,
|
|
1405
1544
|
parseCliInvocation,
|
|
1545
|
+
parseExportArgs,
|
|
1406
1546
|
parseIssueArgs,
|
|
1407
1547
|
parseVerifyArgs,
|
|
1408
1548
|
runWitanFreeCli,
|
package/dist/mcp/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cejel/cejel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"mcpName": "io.github.BargLabs/cejel",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"gate:d5": "pnpm run build && node scripts/d5-precision-gate.mjs",
|
|
64
64
|
"gate:d6": "pnpm run build && node scripts/d6-precision-gate.mjs",
|
|
65
65
|
"validate:distribution": "node scripts/validate-distribution-metadata.mjs",
|
|
66
|
+
"test:release": "bash scripts/release_tests.sh",
|
|
66
67
|
"pnpm:devPreinstall": "node scripts/assert-dev-environment.mjs --phase=preinstall",
|
|
67
68
|
"prepublishOnly": "pnpm run build",
|
|
68
69
|
"prepack": "pnpm run build",
|