@cejel/cejel 0.7.0-rc.1 → 0.7.0-rc.2
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 +131 -27
- package/dist/calibration/llm-detector.js +28 -7
- package/dist/{chunk-D7MWEPPX.js → chunk-2UMHJ23V.js} +9 -4
- package/dist/{chunk-SNRQAKKE.js → chunk-G6AN4EDL.js} +9682 -1130
- package/dist/{chunk-LBJFDK4R.js → chunk-IJYGVKYU.js} +1 -1
- package/dist/{chunk-UMGZWGOI.js → chunk-RRPQHEBU.js} +1 -1
- package/dist/index.js +217 -21
- package/dist/mcp/index.js +3 -3
- package/dist/packs/d-series/index.js +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3,15 +3,17 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
|
|
|
3
3
|
const require = __cejelCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
serveCejelMcpOverStdio
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IJYGVKYU.js";
|
|
7
7
|
import {
|
|
8
8
|
CALLER_CONTEXT_PRODUCT_IDENTITY_NOTICE,
|
|
9
9
|
CERTIFICATE_GLOSSARY,
|
|
10
10
|
EXTERNAL_FINDINGS_DISPLAY_LIMIT,
|
|
11
|
+
METRIC_DERIVED_FINDING_PATTERN,
|
|
11
12
|
PROSPECTIVE_RUBRIC_NOTICE,
|
|
12
13
|
WITAN_RUBRIC_VERSION_V22,
|
|
13
14
|
WitanReportSchema,
|
|
14
15
|
assertSelectableRubricVersion,
|
|
16
|
+
buildPlainSummary,
|
|
15
17
|
buildRelyingPartySummary,
|
|
16
18
|
buildVerdictExplanation,
|
|
17
19
|
computeApplicableMeasuredCoverage,
|
|
@@ -26,10 +28,13 @@ import {
|
|
|
26
28
|
formatExternalSourceLine,
|
|
27
29
|
formatLowConfidenceSentence,
|
|
28
30
|
formatVerdictCapSentence,
|
|
31
|
+
glossaryEntryForRubric,
|
|
29
32
|
importedAdjustmentSentence,
|
|
30
33
|
inheritedFindingNote,
|
|
31
34
|
inputBindingHeadline,
|
|
32
35
|
isProspectiveRubricVersion,
|
|
36
|
+
isWitanAbstentionLabel,
|
|
37
|
+
renderWitanAbstentionHeadline,
|
|
33
38
|
renderWitanBadgeEndpoint,
|
|
34
39
|
renderWitanBadgeSvg,
|
|
35
40
|
renderWitanHtmlReport,
|
|
@@ -38,9 +43,10 @@ import {
|
|
|
38
43
|
serializeWitanReport,
|
|
39
44
|
stripTerminalControls,
|
|
40
45
|
verifyRecordedInputDigests,
|
|
41
|
-
verifyWitanAttestationBinding
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
verifyWitanAttestationBinding,
|
|
47
|
+
witanHistoryAbstentionInstruction
|
|
48
|
+
} from "./chunk-G6AN4EDL.js";
|
|
49
|
+
import "./chunk-2UMHJ23V.js";
|
|
44
50
|
import {
|
|
45
51
|
external_exports
|
|
46
52
|
} from "./chunk-AMVP7L26.js";
|
|
@@ -62,7 +68,7 @@ init_esm_shims();
|
|
|
62
68
|
init_esm_shims();
|
|
63
69
|
var WITAN_ISSUANCE_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
64
70
|
var WITAN_ISSUANCE_PREDICATE_TYPE = "https://cejel.dev/attestations/issuance/v1";
|
|
65
|
-
var WITAN_ISSUANCE_FORMAT_VERSION = "1.
|
|
71
|
+
var WITAN_ISSUANCE_FORMAT_VERSION = "1.1";
|
|
66
72
|
var WITAN_ISSUANCE_SIGNATURE_NAMESPACE = "cejel-issuance";
|
|
67
73
|
var WITAN_ISSUANCE_PRINCIPAL = "issuance@barglabs.ai";
|
|
68
74
|
var WITAN_ISSUER_NAME = "Barg Labs Inc.";
|
|
@@ -90,6 +96,9 @@ var WitanIssuanceStatementSchema = external_exports.object({
|
|
|
90
96
|
toolVersion: external_exports.string().min(1).max(80),
|
|
91
97
|
rubricVersion: external_exports.string().min(1).max(120),
|
|
92
98
|
rubricBehaviourFingerprint: external_exports.string().regex(/^sha256:[a-f0-9]{64}$/).optional(),
|
|
99
|
+
// The scan time the reproduction replayed: the attested scan's predicate.generatedAt
|
|
100
|
+
// (issuance format 1.1 and later). Optional on read, so a 1.0 issuance still verifies.
|
|
101
|
+
generatedAt: external_exports.string().datetime({ offset: true }).optional(),
|
|
93
102
|
reportByteIdentical: external_exports.literal(true)
|
|
94
103
|
}).strict(),
|
|
95
104
|
issuedAt: external_exports.string().datetime({ offset: true }),
|
|
@@ -154,6 +163,7 @@ function createWitanIssuanceStatement(report, options) {
|
|
|
154
163
|
toolVersion: options.toolVersion,
|
|
155
164
|
rubricVersion: report.rubricVersion,
|
|
156
165
|
...report.rubricBehaviourFingerprint ? { rubricBehaviourFingerprint: report.rubricBehaviourFingerprint } : {},
|
|
166
|
+
...options.reproductionGeneratedAt ? { generatedAt: options.reproductionGeneratedAt } : {},
|
|
157
167
|
reportByteIdentical: true
|
|
158
168
|
},
|
|
159
169
|
issuedAt: options.issuedAt,
|
|
@@ -162,6 +172,40 @@ function createWitanIssuanceStatement(report, options) {
|
|
|
162
172
|
}
|
|
163
173
|
});
|
|
164
174
|
}
|
|
175
|
+
var ISO_8601_INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
176
|
+
function readReproductionGeneratedAt(attestation, now) {
|
|
177
|
+
const predicate = typeof attestation === "object" && attestation !== null ? attestation.predicate : void 0;
|
|
178
|
+
const value = typeof predicate === "object" && predicate !== null ? predicate.generatedAt : void 0;
|
|
179
|
+
if (value === void 0 || value === null) {
|
|
180
|
+
return {
|
|
181
|
+
ok: false,
|
|
182
|
+
reason: "the attestation's predicate.generatedAt is missing, so the reproduction has no scan time to replay"
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const instant = typeof value === "string" ? Date.parse(value) : Number.NaN;
|
|
186
|
+
if (typeof value !== "string" || !ISO_8601_INSTANT.test(value) || !Number.isFinite(instant)) {
|
|
187
|
+
return {
|
|
188
|
+
ok: false,
|
|
189
|
+
reason: `the attestation's predicate.generatedAt is not a valid ISO 8601 time: ${JSON.stringify(value)}`
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (instant > now.getTime()) {
|
|
193
|
+
return {
|
|
194
|
+
ok: false,
|
|
195
|
+
reason: `the attestation's predicate.generatedAt (${value}) is later than this issuer's clock (${now.toISOString()})`
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return { ok: true, generatedAt: value };
|
|
199
|
+
}
|
|
200
|
+
var NEW_YEAR_WINDOW_MS = 24 * 60 * 60 * 1e3;
|
|
201
|
+
function isWithinDayOfNewYearUtc(generatedAt) {
|
|
202
|
+
const instant = Date.parse(generatedAt);
|
|
203
|
+
if (!Number.isFinite(instant)) return false;
|
|
204
|
+
const year = new Date(instant).getUTCFullYear();
|
|
205
|
+
const sinceNewYear = instant - Date.UTC(year, 0, 1);
|
|
206
|
+
const untilNewYear = Date.UTC(year + 1, 0, 1) - instant;
|
|
207
|
+
return sinceNewYear <= NEW_YEAR_WINDOW_MS || untilNewYear <= NEW_YEAR_WINDOW_MS;
|
|
208
|
+
}
|
|
165
209
|
function verifyWitanIssuanceBinding(statement, options) {
|
|
166
210
|
const parsed = WitanIssuanceStatementSchema.safeParse(statement);
|
|
167
211
|
if (!parsed.success) {
|
|
@@ -501,8 +545,15 @@ function normalizeRepoRelativePath(raw) {
|
|
|
501
545
|
if (path.split("/").some((segment) => segment === ".." || segment === "")) return void 0;
|
|
502
546
|
return path;
|
|
503
547
|
}
|
|
504
|
-
|
|
505
|
-
|
|
548
|
+
var METRIC_DERIVED_IDENTITY = "metric-derived";
|
|
549
|
+
var NUMBER_PLACEHOLDER = "<n>";
|
|
550
|
+
var NUMBER_PATTERN = /\d+(?:\.\d+)?/g;
|
|
551
|
+
function findingIdentity(summary) {
|
|
552
|
+
if (METRIC_DERIVED_FINDING_PATTERN.test(summary)) return METRIC_DERIVED_IDENTITY;
|
|
553
|
+
return summary.replace(NUMBER_PATTERN, NUMBER_PLACEHOLDER);
|
|
554
|
+
}
|
|
555
|
+
function fingerprint(rule, path, line, identity, ordinal) {
|
|
556
|
+
return createHash2("sha256").update(JSON.stringify([rule, path, line, identity, ordinal])).digest("hex");
|
|
506
557
|
}
|
|
507
558
|
function exportGitLabCodeQuality(report) {
|
|
508
559
|
const ingestedCount = (report.consumedSignals ?? []).reduce(
|
|
@@ -520,6 +571,7 @@ function exportGitLabCodeQuality(report) {
|
|
|
520
571
|
const entries = [];
|
|
521
572
|
let excludedUnlocated = 0;
|
|
522
573
|
const seen = /* @__PURE__ */ new Set();
|
|
574
|
+
const groupSizes = /* @__PURE__ */ new Map();
|
|
523
575
|
for (const criterion of report.criteria) {
|
|
524
576
|
for (const finding of criterion.findings) {
|
|
525
577
|
const path = finding.evidence.path === void 0 ? void 0 : normalizeRepoRelativePath(finding.evidence.path);
|
|
@@ -528,9 +580,14 @@ function exportGitLabCodeQuality(report) {
|
|
|
528
580
|
excludedUnlocated += 1;
|
|
529
581
|
continue;
|
|
530
582
|
}
|
|
531
|
-
const
|
|
532
|
-
if (seen.has(
|
|
533
|
-
seen.add(
|
|
583
|
+
const exact = JSON.stringify([criterion.id, path, line, finding.summary]);
|
|
584
|
+
if (seen.has(exact)) continue;
|
|
585
|
+
seen.add(exact);
|
|
586
|
+
const identity = findingIdentity(finding.summary);
|
|
587
|
+
const group = JSON.stringify([criterion.id, path, line, identity]);
|
|
588
|
+
const ordinal = groupSizes.get(group) ?? 0;
|
|
589
|
+
groupSizes.set(group, ordinal + 1);
|
|
590
|
+
const print = fingerprint(criterion.id, path, line, identity, ordinal);
|
|
534
591
|
entries.push({
|
|
535
592
|
description: finding.summary,
|
|
536
593
|
check_name: criterion.id,
|
|
@@ -551,6 +608,64 @@ function renderGitLabCodeQualityFooter(result) {
|
|
|
551
608
|
`;
|
|
552
609
|
}
|
|
553
610
|
|
|
611
|
+
// src/export/gitlab-metrics.ts
|
|
612
|
+
init_esm_shims();
|
|
613
|
+
var UNMEASURED_STATUSES = /* @__PURE__ */ new Set([
|
|
614
|
+
"insufficient_data",
|
|
615
|
+
"not_applicable"
|
|
616
|
+
]);
|
|
617
|
+
function formatGitLabMetricScore(score) {
|
|
618
|
+
return score.toFixed(1);
|
|
619
|
+
}
|
|
620
|
+
function gitLabCriterionMetricName(id) {
|
|
621
|
+
return `cejel_criterion_${id.toLowerCase().replaceAll("-", "_")}_score`;
|
|
622
|
+
}
|
|
623
|
+
function isMeasured(criterion) {
|
|
624
|
+
return !UNMEASURED_STATUSES.has(criterion.status);
|
|
625
|
+
}
|
|
626
|
+
function exportGitLabMetrics(report) {
|
|
627
|
+
if (report.verdict === "insufficient_source") {
|
|
628
|
+
return { lines: [], leftOut: [], abstainedReason: report.insufficientSourceReason };
|
|
629
|
+
}
|
|
630
|
+
const lines = [];
|
|
631
|
+
const leftOut = [];
|
|
632
|
+
const add = (name, score, measured) => {
|
|
633
|
+
if (score === null || !measured) {
|
|
634
|
+
leftOut.push(name);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
lines.push(`${name} ${formatGitLabMetricScore(score)}`);
|
|
638
|
+
};
|
|
639
|
+
const criteria = report.criteria;
|
|
640
|
+
add("cejel_overall_score", report.overallScore, criteria.some(isMeasured));
|
|
641
|
+
const trust = [
|
|
642
|
+
["cejel_process_trust_score", report.processTrustScore, "process_trust"],
|
|
643
|
+
["cejel_code_trust_score", report.codeTrustScore, "code_trust"]
|
|
644
|
+
];
|
|
645
|
+
for (const [name, score, category] of trust) {
|
|
646
|
+
const inCategory = criteria.filter((criterion) => criterion.category === category);
|
|
647
|
+
if (inCategory.length === 0) continue;
|
|
648
|
+
add(name, score, inCategory.some(isMeasured));
|
|
649
|
+
}
|
|
650
|
+
for (const criterion of criteria) {
|
|
651
|
+
add(gitLabCriterionMetricName(criterion.id), criterion.score, isMeasured(criterion));
|
|
652
|
+
}
|
|
653
|
+
return { lines, leftOut };
|
|
654
|
+
}
|
|
655
|
+
function renderGitLabMetricsFile(result) {
|
|
656
|
+
return result.lines.length === 0 ? "" : `${result.lines.join("\n")}
|
|
657
|
+
`;
|
|
658
|
+
}
|
|
659
|
+
function renderGitLabMetricsFooter(result) {
|
|
660
|
+
if (result.abstainedReason !== void 0) {
|
|
661
|
+
return `Cejel: abstained report (insufficient_source): wrote 0 metrics. ${result.abstainedReason}
|
|
662
|
+
`;
|
|
663
|
+
}
|
|
664
|
+
const named = result.leftOut.length === 0 ? "" : ` (${result.leftOut.join(", ")})`;
|
|
665
|
+
return `Cejel: wrote ${result.lines.length} metric(s); ${result.leftOut.length} value(s) left out because Cejel did not measure them${named}.
|
|
666
|
+
`;
|
|
667
|
+
}
|
|
668
|
+
|
|
554
669
|
// src/npx-version-guard.ts
|
|
555
670
|
init_esm_shims();
|
|
556
671
|
import { delimiter, resolve } from "path";
|
|
@@ -653,9 +768,14 @@ function writeOutputFiles(dir, files, what) {
|
|
|
653
768
|
// src/terminal.ts
|
|
654
769
|
init_esm_shims();
|
|
655
770
|
function renderMinScoreAbstentionFailure(summary, minScore) {
|
|
656
|
-
if (summary.verdict
|
|
771
|
+
if (!isWitanAbstentionLabel(summary.verdict)) {
|
|
657
772
|
throw new Error("A minimum-score abstention message requires an abstained Cejel summary.");
|
|
658
773
|
}
|
|
774
|
+
const instruction = witanHistoryAbstentionInstruction(summary.verdict);
|
|
775
|
+
if (instruction !== void 0) {
|
|
776
|
+
return `Cejel: cannot evaluate the required minimum ${minScore.toFixed(1)}/4.0 because the scan is ${summary.verdict.toLowerCase()}, with no composite score and no verdict. ${instruction}
|
|
777
|
+
`;
|
|
778
|
+
}
|
|
659
779
|
return `Cejel: cannot evaluate the required minimum ${minScore.toFixed(1)}/4.0 because this repository has ${summary.verdict.toLowerCase()}.
|
|
660
780
|
`;
|
|
661
781
|
}
|
|
@@ -674,14 +794,14 @@ function renderMinScoreCoverageFailure(coverage, minScore) {
|
|
|
674
794
|
`;
|
|
675
795
|
}
|
|
676
796
|
function renderTerminalCertificate(summary, report) {
|
|
677
|
-
const abstained = summary.verdict
|
|
797
|
+
const abstained = isWitanAbstentionLabel(summary.verdict);
|
|
678
798
|
if (!abstained && (summary.overallScore === null || summary.codeTrustScore === null || summary.processTrustScore === null)) {
|
|
679
799
|
throw new Error("A scored Cejel summary must carry numeric headline scores.");
|
|
680
800
|
}
|
|
681
801
|
const lines = abstained ? [
|
|
682
802
|
`Cejel Trust Certificate \u2014 ${stripTerminalControls(summary.productDisplayName)}`,
|
|
683
803
|
"",
|
|
684
|
-
|
|
804
|
+
renderWitanAbstentionHeadline(summary.verdict),
|
|
685
805
|
` ${summary.insufficientSourceReason === void 0 ? "No ratable source or measurable evidence was found." : stripTerminalControls(summary.insufficientSourceReason)}`,
|
|
686
806
|
""
|
|
687
807
|
] : [
|
|
@@ -709,6 +829,7 @@ function renderTerminalCertificate(summary, report) {
|
|
|
709
829
|
""
|
|
710
830
|
);
|
|
711
831
|
}
|
|
832
|
+
if (report) lines.push(...renderPlainSummaryLines(buildPlainSummary(report), summary, abstained));
|
|
712
833
|
if (summary.scanLimitations.length > 0) {
|
|
713
834
|
lines.push(
|
|
714
835
|
"LIMITED EVIDENCE \u2014 the score and verdict are qualified by scan limitations:",
|
|
@@ -718,7 +839,7 @@ function renderTerminalCertificate(summary, report) {
|
|
|
718
839
|
}
|
|
719
840
|
if (summary.historyLimitations?.length) {
|
|
720
841
|
lines.push(
|
|
721
|
-
"HISTORY LIMITATION \u2014 scored as usual, but some criteria read truncated history:",
|
|
842
|
+
abstained ? "HISTORY LIMITATION \u2014 some criteria read truncated history:" : "HISTORY LIMITATION \u2014 scored as usual, but some criteria read truncated history:",
|
|
722
843
|
...summary.historyLimitations.map((limitation) => ` - ${stripTerminalControls(limitation)}`),
|
|
723
844
|
""
|
|
724
845
|
);
|
|
@@ -814,7 +935,7 @@ function renderTerminalCertificate(summary, report) {
|
|
|
814
935
|
}
|
|
815
936
|
}
|
|
816
937
|
lines.push("", "Plain-language glossary:");
|
|
817
|
-
for (const entry of CERTIFICATE_GLOSSARY) {
|
|
938
|
+
for (const entry of CERTIFICATE_GLOSSARY.map((curated) => glossaryEntryForRubric(curated, report?.rubricVersion))) {
|
|
818
939
|
lines.push(` ${entry.term} \u2014 ${entry.definition}`);
|
|
819
940
|
}
|
|
820
941
|
if (report && isProspectiveRubricVersion(report.rubricVersion)) {
|
|
@@ -823,6 +944,43 @@ function renderTerminalCertificate(summary, report) {
|
|
|
823
944
|
return `${lines.join("\n")}
|
|
824
945
|
`;
|
|
825
946
|
}
|
|
947
|
+
var PLAIN_TERMINAL_ITEM_LIMIT = 3;
|
|
948
|
+
function renderPlainSummaryLines(plain, summary, abstained) {
|
|
949
|
+
const section = (heading, leads, items, empty) => {
|
|
950
|
+
const shown = items.slice(0, PLAIN_TERMINAL_ITEM_LIMIT);
|
|
951
|
+
const remaining = items.length - shown.length;
|
|
952
|
+
return [
|
|
953
|
+
`${heading} (${items.length}):`,
|
|
954
|
+
...leads.filter((lead) => lead !== void 0).map((lead) => ` ${lead}`),
|
|
955
|
+
...items.length === 0 ? [` ${empty}`] : shown.map((item) => ` - ${stripTerminalControls(item)}`),
|
|
956
|
+
...remaining > 0 ? [` ...and ${remaining} more in certificate.html.`] : []
|
|
957
|
+
];
|
|
958
|
+
};
|
|
959
|
+
const itemLine = (item) => `${item.area} \u2014 ${item.text}${item.location !== void 0 ? ` (${item.location})` : ""}`;
|
|
960
|
+
const wrongFindings = plain.wrong.filter((item) => item.source.kind === "finding").length;
|
|
961
|
+
const otherFindings = summary.findingCount - wrongFindings;
|
|
962
|
+
const topFindingsNote = !abstained && otherFindings > 0 ? `Top findings below counts ${summary.findingCount} finding${summary.findingCount === 1 ? "" : "s"} in all, including ${otherFindings} informational finding${otherFindings === 1 ? "" : "s"} not listed here.` : void 0;
|
|
963
|
+
return [
|
|
964
|
+
"In plain words (the full lists, with links to the evidence, are in certificate.html):",
|
|
965
|
+
...plain.prospectiveNotice !== void 0 ? [` ${plain.prospectiveNotice}`] : [],
|
|
966
|
+
...section("What holds", [plain.areasLine, plain.holdsLead], plain.holds.map(itemLine), plain.holdsNote ?? ""),
|
|
967
|
+
...section(
|
|
968
|
+
"What is wrong",
|
|
969
|
+
[plain.wrongNote, topFindingsNote],
|
|
970
|
+
plain.wrong.map((item) => `${item.level === "critical" ? "Critical" : "Warning"}: ${itemLine(item)}`),
|
|
971
|
+
"Cejel recorded no critical or warning-level problem in what it checked."
|
|
972
|
+
),
|
|
973
|
+
...section("What is not known", [], plain.notKnown.map(itemLine), "Cejel listed no gap here; anything it did not measure is still not established."),
|
|
974
|
+
...section(
|
|
975
|
+
"What to check next",
|
|
976
|
+
[],
|
|
977
|
+
plain.checkNext.map((check) => `${check.area} \u2014 ${check.text}`),
|
|
978
|
+
"Nothing to check from these lists."
|
|
979
|
+
),
|
|
980
|
+
plain.notCovered,
|
|
981
|
+
""
|
|
982
|
+
];
|
|
983
|
+
}
|
|
826
984
|
function formatScore(score) {
|
|
827
985
|
if (score === null) throw new Error("A scored Cejel summary must carry numeric scores.");
|
|
828
986
|
return score.toFixed(1);
|
|
@@ -970,6 +1128,7 @@ Usage:
|
|
|
970
1128
|
npx ${NPX_PACKAGE_NAME} scan [path] [options]
|
|
971
1129
|
npx ${NPX_PACKAGE_NAME} verify <report.json> <attestation.json> [issuance.json issuance.json.sig]
|
|
972
1130
|
npx ${NPX_PACKAGE_NAME} export gitlab-codequality <report.json> [-o gl-code-quality-report.json]
|
|
1131
|
+
npx ${NPX_PACKAGE_NAME} export gitlab-metrics <report.json> [-o metrics.txt]
|
|
973
1132
|
npx ${NPX_PACKAGE_NAME} mcp
|
|
974
1133
|
npx ${NPX_PACKAGE_NAME} issue [path] --report <report.json> --attestation <attestation.json> --key <key.pub> --engagement-ref <ref>
|
|
975
1134
|
|
|
@@ -977,7 +1136,8 @@ Commands:
|
|
|
977
1136
|
scan score a repository (default when the command is omitted)
|
|
978
1137
|
verify verify the report/attestation binding; with an issuance pair, also check the issuer
|
|
979
1138
|
signature against docs/security/${ISSUER_SIGNERS_FILE} and docs/security/${ISSUER_REVOCATIONS_FILE}
|
|
980
|
-
export write report.json's located findings as a GitLab Code Quality report
|
|
1139
|
+
export write report.json's located findings as a GitLab Code Quality report, or its scores
|
|
1140
|
+
as a GitLab metrics report (offline)
|
|
981
1141
|
mcp serve the MCP server over stdio (the same server as the cejel-mcp bin)
|
|
982
1142
|
issue re-run this version at the certificate's revision and, only if report.json reproduces
|
|
983
1143
|
byte for byte, write an unsigned issuance.json for the issuer to sign through ssh-agent
|
|
@@ -1097,7 +1257,13 @@ async function runWitanCli(args, rubricVersion) {
|
|
|
1097
1257
|
},
|
|
1098
1258
|
{ name: "badge.json", contents: JSON.stringify(renderWitanBadgeEndpoint(report), null, 2) },
|
|
1099
1259
|
{ name: "badge.svg", contents: renderWitanBadgeSvg(report) },
|
|
1100
|
-
|
|
1260
|
+
// plainSummary (goal_cejel_four_question_summary_2026-11-02) is added here, at the one place
|
|
1261
|
+
// summary.json is written, so buildWitanCliSummary's shape (the MCP and HTTP responses) and
|
|
1262
|
+
// every key before it are unchanged.
|
|
1263
|
+
{
|
|
1264
|
+
name: "summary.json",
|
|
1265
|
+
contents: JSON.stringify({ ...summary, plainSummary: buildPlainSummary(report) }, null, 2)
|
|
1266
|
+
}
|
|
1101
1267
|
],
|
|
1102
1268
|
"scan output"
|
|
1103
1269
|
);
|
|
@@ -1186,12 +1352,22 @@ function parseCliInvocation(args) {
|
|
|
1186
1352
|
}
|
|
1187
1353
|
return { command: "scan", options: parseArgs(args) };
|
|
1188
1354
|
}
|
|
1189
|
-
var
|
|
1355
|
+
var EXPORT_DEFAULT_OUT = {
|
|
1356
|
+
"gitlab-codequality": "gl-code-quality-report.json",
|
|
1357
|
+
"gitlab-metrics": "metrics.txt"
|
|
1358
|
+
};
|
|
1359
|
+
function isExportFormat(value) {
|
|
1360
|
+
return value !== void 0 && Object.hasOwn(EXPORT_DEFAULT_OUT, value);
|
|
1361
|
+
}
|
|
1362
|
+
var EXPORT_USAGE = () => [
|
|
1363
|
+
`Usage: npx ${NPX_PACKAGE_NAME} export gitlab-codequality <report.json> [-o gl-code-quality-report.json]`,
|
|
1364
|
+
` npx ${NPX_PACKAGE_NAME} export gitlab-metrics <report.json> [-o metrics.txt]`
|
|
1365
|
+
].join("\n");
|
|
1190
1366
|
function parseExportArgs(args) {
|
|
1191
1367
|
const [format, ...rest] = args;
|
|
1192
|
-
if (format
|
|
1368
|
+
if (!isExportFormat(format)) throw new Error(EXPORT_USAGE());
|
|
1193
1369
|
const positionals = [];
|
|
1194
|
-
let outPath =
|
|
1370
|
+
let outPath = EXPORT_DEFAULT_OUT[format];
|
|
1195
1371
|
for (let index = 0; index < rest.length; index += 1) {
|
|
1196
1372
|
const arg = rest[index];
|
|
1197
1373
|
if (arg === void 0) continue;
|
|
@@ -1228,6 +1404,12 @@ ${members.map((member) => ` - ${member}`).join("\n")}
|
|
|
1228
1404
|
);
|
|
1229
1405
|
return 1;
|
|
1230
1406
|
}
|
|
1407
|
+
if (invocation.format === "gitlab-metrics") {
|
|
1408
|
+
const metrics = exportGitLabMetrics(reportResult.data);
|
|
1409
|
+
writeOutputFile(invocation.outPath, renderGitLabMetricsFile(metrics), "export output");
|
|
1410
|
+
process.stderr.write(renderGitLabMetricsFooter(metrics));
|
|
1411
|
+
return 0;
|
|
1412
|
+
}
|
|
1231
1413
|
const result = exportGitLabCodeQuality(reportResult.data);
|
|
1232
1414
|
writeOutputFile(
|
|
1233
1415
|
invocation.outPath,
|
|
@@ -1543,6 +1725,12 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
|
|
|
1543
1725
|
);
|
|
1544
1726
|
return 1;
|
|
1545
1727
|
}
|
|
1728
|
+
const scanTime = readReproductionGeneratedAt(attestationArtifact.value, /* @__PURE__ */ new Date());
|
|
1729
|
+
if (!scanTime.ok) {
|
|
1730
|
+
process.stderr.write(`Cejel: refusing to issue \u2014 ${scanTime.reason}.
|
|
1731
|
+
`);
|
|
1732
|
+
return 1;
|
|
1733
|
+
}
|
|
1546
1734
|
let keyContents;
|
|
1547
1735
|
try {
|
|
1548
1736
|
keyContents = readFileSync(options.keyPath, "utf8");
|
|
@@ -1565,7 +1753,8 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
|
|
|
1565
1753
|
// comparison below, as it always has.
|
|
1566
1754
|
...suppliedReport.inputBinding?.policy === "strict" ? { strictIngest: true } : {},
|
|
1567
1755
|
...suppliedReport.inputBinding?.scannedSubject.repository ? { subjectRepository: suppliedReport.inputBinding.scannedSubject.repository } : {},
|
|
1568
|
-
toolVersion: cliVersion()
|
|
1756
|
+
toolVersion: cliVersion(),
|
|
1757
|
+
generatedAt: scanTime.generatedAt
|
|
1569
1758
|
});
|
|
1570
1759
|
const reproducedBytes = serializeWitanReport(reproduction.report);
|
|
1571
1760
|
const reproducedSha256 = createHash3("sha256").update(reproducedBytes, "utf8").digest("hex");
|
|
@@ -1579,6 +1768,12 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
|
|
|
1579
1768
|
" Check that the checkout is at the certificate revision, that the @cejel/cejel version matches",
|
|
1580
1769
|
" report.json's toolVersion, and that --product-name/--name/--rubric-pin/--ingest match the",
|
|
1581
1770
|
" original invocation. An issuance that cannot reproduce is not issued.",
|
|
1771
|
+
// The reproduction replays the scan's instant, but v17's B4 reads that instant's year in
|
|
1772
|
+
// the local time zone. Near a new year two hosts can disagree on it; name the setting,
|
|
1773
|
+
// never guess a zone.
|
|
1774
|
+
...isWithinDayOfNewYearUtc(scanTime.generatedAt) ? [
|
|
1775
|
+
` The scan's generatedAt (${scanTime.generatedAt}) is within 24 hours of a new year in UTC, and v17's B4 freshness year is read in the scan host's local time: an issuer in another time zone may need to re-run with the TZ environment variable set to the scan host's.`
|
|
1776
|
+
] : [],
|
|
1582
1777
|
""
|
|
1583
1778
|
].join("\n")
|
|
1584
1779
|
);
|
|
@@ -1590,6 +1785,7 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
|
|
|
1590
1785
|
issuerKeyFingerprint: issuerKey.fingerprint,
|
|
1591
1786
|
toolVersion: cliVersion(),
|
|
1592
1787
|
issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1788
|
+
reproductionGeneratedAt: scanTime.generatedAt,
|
|
1593
1789
|
engagementRef: options.engagementRef
|
|
1594
1790
|
});
|
|
1595
1791
|
const issuancePath = join2(options.outDir, ISSUANCE_OUTPUT_FILE);
|
package/dist/mcp/index.js
CHANGED
|
@@ -3,9 +3,9 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
|
|
|
3
3
|
const require = __cejelCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
serveCejelMcpOverStdio
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-IJYGVKYU.js";
|
|
7
|
+
import "../chunk-G6AN4EDL.js";
|
|
8
|
+
import "../chunk-2UMHJ23V.js";
|
|
9
9
|
import "../chunk-AMVP7L26.js";
|
|
10
10
|
import {
|
|
11
11
|
init_esm_shims
|
|
@@ -3,10 +3,10 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
|
|
|
3
3
|
const require = __cejelCreateRequire(import.meta.url);
|
|
4
4
|
import {
|
|
5
5
|
listCejelLlmPackFiles
|
|
6
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-RRPQHEBU.js";
|
|
7
7
|
import {
|
|
8
8
|
execGit
|
|
9
|
-
} from "../../chunk-
|
|
9
|
+
} from "../../chunk-2UMHJ23V.js";
|
|
10
10
|
import {
|
|
11
11
|
require_typescript
|
|
12
12
|
} from "../../chunk-6LVNNJTG.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cejel/cejel",
|
|
3
|
-
"version": "0.7.0-rc.
|
|
3
|
+
"version": "0.7.0-rc.2",
|
|
4
4
|
"mcpName": "io.github.BargLabs/cejel",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"type": "git",
|
|
32
32
|
"url": "git+https://github.com/BargLabs/cejel.git"
|
|
33
33
|
},
|
|
34
|
-
"packageManager": "pnpm@9.0.0",
|
|
34
|
+
"packageManager": "pnpm@9.0.0+sha512.b4106707c7225b1748b61595953ccbebff97b54ad05d002aa3635f633b9c53cd666f7ce9b8bc44704f1fa048b9a49b55371ab2d9e9d667d1efe2ef1514bcd513",
|
|
35
35
|
"type": "module",
|
|
36
36
|
"bin": {
|
|
37
37
|
"cejel": "dist/index.js",
|