@cejel/cejel 0.6.7 → 0.7.0-rc.1

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.
@@ -5,10 +5,10 @@ import {
5
5
  describeGitFailure,
6
6
  execGit,
7
7
  isExpectedGitAbsence
8
- } from "./chunk-VGBFZBZQ.js";
8
+ } from "./chunk-D7MWEPPX.js";
9
9
  import {
10
10
  init_esm_shims
11
- } from "./chunk-O3MHSK25.js";
11
+ } from "./chunk-AWLKQUI4.js";
12
12
 
13
13
  // src/packs/llm/files.ts
14
14
  init_esm_shims();
package/dist/index.js CHANGED
@@ -3,10 +3,11 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
3
3
  const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  serveCejelMcpOverStdio
6
- } from "./chunk-TOQPQPDF.js";
6
+ } from "./chunk-LBJFDK4R.js";
7
7
  import {
8
8
  CALLER_CONTEXT_PRODUCT_IDENTITY_NOTICE,
9
9
  CERTIFICATE_GLOSSARY,
10
+ EXTERNAL_FINDINGS_DISPLAY_LIMIT,
10
11
  PROSPECTIVE_RUBRIC_NOTICE,
11
12
  WITAN_RUBRIC_VERSION_V22,
12
13
  WitanReportSchema,
@@ -15,6 +16,8 @@ import {
15
16
  buildVerdictExplanation,
16
17
  computeApplicableMeasuredCoverage,
17
18
  createWitanAttestation,
19
+ describeInputBindingRecord,
20
+ describeScannedSubject,
18
21
  formatCertificateMetricAppliedWeightPercent,
19
22
  formatCertificateMetricLabel,
20
23
  formatCertificateMetricValue,
@@ -22,21 +25,28 @@ import {
22
25
  formatDimensionsMeasuredSentence,
23
26
  formatExternalSourceLine,
24
27
  formatLowConfidenceSentence,
28
+ formatVerdictCapSentence,
29
+ importedAdjustmentSentence,
30
+ inheritedFindingNote,
31
+ inputBindingHeadline,
25
32
  isProspectiveRubricVersion,
26
33
  renderWitanBadgeEndpoint,
27
34
  renderWitanBadgeSvg,
28
35
  renderWitanHtmlReport,
36
+ resolveIngestFilePath,
29
37
  runCejelScan,
30
38
  serializeWitanReport,
39
+ stripTerminalControls,
40
+ verifyRecordedInputDigests,
31
41
  verifyWitanAttestationBinding
32
- } from "./chunk-ZZHQZW26.js";
33
- import "./chunk-VGBFZBZQ.js";
42
+ } from "./chunk-SNRQAKKE.js";
43
+ import "./chunk-D7MWEPPX.js";
34
44
  import {
35
45
  external_exports
36
- } from "./chunk-FSMZV3Y6.js";
46
+ } from "./chunk-AMVP7L26.js";
37
47
  import {
38
48
  init_esm_shims
39
- } from "./chunk-O3MHSK25.js";
49
+ } from "./chunk-AWLKQUI4.js";
40
50
 
41
51
  // src/index.ts
42
52
  init_esm_shims();
@@ -663,9 +673,6 @@ function renderMinScoreCoverageFailure(coverage, minScore) {
663
673
  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.
664
674
  `;
665
675
  }
666
- function stripTerminalControls(text) {
667
- return text.replace(/[\t\n]/g, " ").replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
668
- }
669
676
  function renderTerminalCertificate(summary, report) {
670
677
  const abstained = summary.verdict === "Insufficient source" || summary.verdict === "Insufficient evidence";
671
678
  if (!abstained && (summary.overallScore === null || summary.codeTrustScore === null || summary.processTrustScore === null)) {
@@ -681,6 +688,7 @@ function renderTerminalCertificate(summary, report) {
681
688
  `Cejel Trust Certificate \u2014 ${stripTerminalControls(summary.productDisplayName)}`,
682
689
  "",
683
690
  `Overall: ${formatScore(summary.overallScore)}/4.0 (${summary.verdict})`,
691
+ ...report && formatVerdictCapSentence(report) ? [` ${formatVerdictCapSentence(report)}`] : [],
684
692
  ` Code trust: ${formatScore(summary.codeTrustScore)}/4.0`,
685
693
  ` Process trust: ${formatScore(summary.processTrustScore)}/4.0`,
686
694
  ...report ? [` ${formatDimensionsMeasuredSentence(report)}`] : [],
@@ -696,7 +704,7 @@ function renderTerminalCertificate(summary, report) {
696
704
  `Scope: ${relyingPartySummary.scope}`,
697
705
  `What was examined: ${stripTerminalControls(relyingPartySummary.examined)}`,
698
706
  `What was established: ${relyingPartySummary.established}`,
699
- `What was not established: ${relyingPartySummary.notEstablished}`,
707
+ `What was not established: ${stripTerminalControls(relyingPartySummary.notEstablished)}`,
700
708
  `What to do next: ${relyingPartySummary.next}`,
701
709
  ""
702
710
  );
@@ -719,7 +727,7 @@ function renderTerminalCertificate(summary, report) {
719
727
  const reads = summary.contentReadSummary;
720
728
  const errnoCounts = Object.entries(reads.unreadableByErrno).map(([errno, count]) => `${errno}: ${count}`).join(", ");
721
729
  lines.push(
722
- `Skipped content entries: ${reads.skipped} (unreadable: ${reads.byReason.unreadable}${errnoCounts ? ` [${errnoCounts}]` : ""}; too large: ${reads.byReason.tooLarge}; excluded by extension: ${reads.byReason.excludedByExtension}; denied path: ${reads.byReason.deniedPath}; non-regular file: ${reads.byReason.nonRegularFile}).`
730
+ `Skipped content entries: ${reads.skipped} (unreadable: ${reads.byReason.unreadable}${errnoCounts ? ` [${errnoCounts}]` : ""}; too large: ${reads.byReason.tooLarge}; excluded by extension: ${reads.byReason.excludedByExtension}; denied path: ${reads.byReason.deniedPath}; non-regular file: ${reads.byReason.nonRegularFile}${reads.byReason.lfsPointer ? `; Git LFS pointer: ${reads.byReason.lfsPointer}` : ""}).`
723
731
  );
724
732
  if (reads.affectedCriteria.length > 0) {
725
733
  lines.push(
@@ -737,6 +745,20 @@ function renderTerminalCertificate(summary, report) {
737
745
  }
738
746
  lines.push("");
739
747
  }
748
+ if (report?.inputBinding) {
749
+ lines.push(stripTerminalControls(inputBindingHeadline(report.inputBinding)));
750
+ lines.push(` ${stripTerminalControls(describeScannedSubject(report.inputBinding))}`);
751
+ for (const input of report.inputBinding.inputs.slice(0, EXTERNAL_FINDINGS_DISPLAY_LIMIT)) {
752
+ lines.push(` ${stripTerminalControls(describeInputBindingRecord(input))}`);
753
+ }
754
+ const remaining = report.inputBinding.inputs.length - EXTERNAL_FINDINGS_DISPLAY_LIMIT;
755
+ if (remaining > 0) lines.push(` ...and ${remaining} more \u2014 see report.json inputBinding.`);
756
+ for (const criterion of report.criteria) {
757
+ const sentence = importedAdjustmentSentence(criterion);
758
+ if (sentence) lines.push(` ${criterion.id}: ${sentence}`);
759
+ }
760
+ lines.push("");
761
+ }
740
762
  const measurements = report?.criteria.flatMap(
741
763
  (criterion) => criterion.metrics.map((metric) => ({
742
764
  criterionId: criterion.id,
@@ -749,7 +771,7 @@ function renderTerminalCertificate(summary, report) {
749
771
  lines.push("Measurements:");
750
772
  for (const measurement of measurements ?? []) {
751
773
  lines.push(
752
- ` ${measurement.criterionId} \u2014 ${measurement.label}: ${measurement.value} (weight ${measurement.weightPercent}% of ${measurement.criterionId})`
774
+ ` ${measurement.criterionId} \u2014 ${measurement.label}: ${stripTerminalControls(measurement.value)} (weight ${measurement.weightPercent}% of ${measurement.criterionId})`
753
775
  );
754
776
  }
755
777
  lines.push("");
@@ -783,6 +805,8 @@ function renderTerminalCertificate(summary, report) {
783
805
  lines.push(
784
806
  ` [${finding.severity}] ${stripTerminalControls(finding.label)} \u2192 ${finding.dimension}: ${stripTerminalControls(finding.ruleId)} \u2014 ${stripTerminalControls(finding.message)}${finding.location ? ` (${stripTerminalControls(finding.location)})` : ""}`
785
807
  );
808
+ const note = inheritedFindingNote(finding.ruleId);
809
+ if (note) lines.push(` Note: ${note}`);
786
810
  }
787
811
  const remaining = summary.externalFindingCount - summary.topExternalFindings.length;
788
812
  if (remaining > 0) {
@@ -861,6 +885,17 @@ var CLI_FLAG_SPECS = [
861
885
  description: "fold in a SARIF/JSON scanner report (repeatable)",
862
886
  kind: "ingest"
863
887
  },
888
+ {
889
+ tokens: ["--subject-repository"],
890
+ value: "<repo>",
891
+ description: "name this repository (e.g. github.com/owner/repo) so ingested inputs declaring another repository are recorded as mismatched",
892
+ kind: "subjectRepository"
893
+ },
894
+ {
895
+ tokens: ["--strict-ingest"],
896
+ description: "refuse ingested inputs for another repository or commit, observed before this commit, or duplicated (default: record only)",
897
+ kind: "strictIngest"
898
+ },
864
899
  {
865
900
  tokens: ["--rubric-pin"],
866
901
  value: "<version>",
@@ -953,6 +988,8 @@ ${usageOptions}
953
988
  Verify options:
954
989
  --signers <file> use this allowed-signers file instead of the published one
955
990
  --revocations <file> use this revocations file instead of the published one
991
+ --input <file> an ingested input (repeatable); fails unless the supplied set of files
992
+ has exactly the sha256 digests report.json records in inputBinding
956
993
 
957
994
  Issue options:
958
995
  --report <file> the report.json being countersigned (required)
@@ -964,6 +1001,7 @@ Issue options:
964
1001
  --name <display> pass exactly what the original scan used, or the bytes will not reproduce
965
1002
  --rubric-pin <version> pass exactly what the original scan used, or the bytes will not reproduce
966
1003
  --ingest <file> pass exactly what the original scan used, or the bytes will not reproduce
1004
+ (--strict-ingest and --subject-repository are read from report.json's inputBinding)
967
1005
 
968
1006
  cejel never signs and never reads private key material.
969
1007
 
@@ -1035,6 +1073,8 @@ async function runWitanCli(args, rubricVersion) {
1035
1073
  ...effectiveRubricVersion ? { rubricVersion: effectiveRubricVersion } : {},
1036
1074
  ingestPatterns: options.ingestPatterns,
1037
1075
  warnOnEmptyIngestMatch: !options.quiet,
1076
+ ...options.strictIngest ? { strictIngest: true } : {},
1077
+ ...options.subjectRepository ? { subjectRepository: options.subjectRepository } : {},
1038
1078
  toolVersion: cliVersion()
1039
1079
  });
1040
1080
  const attestation = createWitanAttestation(report, {
@@ -1198,14 +1238,22 @@ ${members.map((member) => ` - ${member}`).join("\n")}
1198
1238
  process.stderr.write(renderGitLabCodeQualityFooter(result));
1199
1239
  return 0;
1200
1240
  }
1201
- var VERIFY_USAGE = () => `Usage: npx ${NPX_PACKAGE_NAME} verify <report.json> <attestation.json> [issuance.json issuance.json.sig] [--signers <file>] [--revocations <file>]`;
1241
+ var VERIFY_USAGE = () => `Usage: npx ${NPX_PACKAGE_NAME} verify <report.json> <attestation.json> [issuance.json issuance.json.sig] [--signers <file>] [--revocations <file>] [--input <file>...]`;
1202
1242
  function parseVerifyArgs(args) {
1203
1243
  const positionals = [];
1204
1244
  let signersPath;
1205
1245
  let revocationsPath;
1246
+ const inputPaths = [];
1206
1247
  for (let index = 0; index < args.length; index += 1) {
1207
1248
  const arg = args[index];
1208
1249
  if (arg === void 0) continue;
1250
+ if (arg === "--input") {
1251
+ const value = args[index + 1];
1252
+ if (!value) throw new Error(`Missing value for ${arg}`);
1253
+ inputPaths.push(resolve3(value));
1254
+ index += 1;
1255
+ continue;
1256
+ }
1209
1257
  if (arg === "--signers" || arg === "--revocations") {
1210
1258
  const value = args[index + 1];
1211
1259
  if (!value) throw new Error(`Missing value for ${arg}`);
@@ -1227,7 +1275,8 @@ function parseVerifyArgs(args) {
1227
1275
  ...issuancePath ? { issuancePath: resolve3(issuancePath) } : {},
1228
1276
  ...issuanceSignaturePath ? { issuanceSignaturePath: resolve3(issuanceSignaturePath) } : {},
1229
1277
  ...signersPath ? { signersPath } : {},
1230
- ...revocationsPath ? { revocationsPath } : {}
1278
+ ...revocationsPath ? { revocationsPath } : {},
1279
+ ...inputPaths.length > 0 ? { inputPaths } : {}
1231
1280
  };
1232
1281
  }
1233
1282
  function parseIssueArgs(args) {
@@ -1352,6 +1401,26 @@ ${result.errors.map((error) => ` - ${error}`).join("\n")}
1352
1401
  return 1;
1353
1402
  }
1354
1403
  process.stdout.write("Cejel: report/attestation binding verified.\n");
1404
+ if (invocation.inputPaths) {
1405
+ let inputCheck;
1406
+ try {
1407
+ inputCheck = verifyRecordedInputDigests(
1408
+ reportResult.data,
1409
+ invocation.inputPaths,
1410
+ (path) => readFileSync(resolveIngestFilePath(path))
1411
+ );
1412
+ } catch (error) {
1413
+ const message = error instanceof Error ? error.message : String(error);
1414
+ process.stderr.write(
1415
+ `Cejel: input digests not verified \u2014 ${stripTerminalControls(message.replace(/^Cejel: /, ""))}
1416
+ `
1417
+ );
1418
+ return 1;
1419
+ }
1420
+ for (const line of inputCheck.lines) process.stdout.write(`${stripTerminalControls(line)}
1421
+ `);
1422
+ if (!inputCheck.valid) return 1;
1423
+ }
1355
1424
  if (!invocation.issuancePath || !invocation.issuanceSignaturePath) {
1356
1425
  process.stdout.write("Cejel: signature and signer identity were not verified.\n");
1357
1426
  return 0;
@@ -1490,6 +1559,12 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
1490
1559
  ...options.rubricPin ? { rubricVersion: options.rubricPin } : {},
1491
1560
  ingestPatterns: options.ingestPatterns,
1492
1561
  warnOnEmptyIngestMatch: true,
1562
+ // The binding policy and the named subject repository are part of the invocation the report
1563
+ // records, so the reproduction reads them from the report it is reproducing rather than from
1564
+ // flags an issuer could forget. Anything else a report states differently fails the byte
1565
+ // comparison below, as it always has.
1566
+ ...suppliedReport.inputBinding?.policy === "strict" ? { strictIngest: true } : {},
1567
+ ...suppliedReport.inputBinding?.scannedSubject.repository ? { subjectRepository: suppliedReport.inputBinding.scannedSubject.repository } : {},
1493
1568
  toolVersion: cliVersion()
1494
1569
  });
1495
1570
  const reproducedBytes = serializeWitanReport(reproduction.report);
@@ -1568,6 +1643,8 @@ function parseArgs(args) {
1568
1643
  let showVersion = false;
1569
1644
  let rubricPin;
1570
1645
  let runAttempt;
1646
+ let strictIngest = false;
1647
+ let subjectRepository;
1571
1648
  const ingestPatterns = [];
1572
1649
  for (let index = 0; index < args.length; index += 1) {
1573
1650
  const arg = args[index];
@@ -1646,6 +1723,16 @@ function parseArgs(args) {
1646
1723
  index += 1;
1647
1724
  break;
1648
1725
  }
1726
+ case "strictIngest":
1727
+ strictIngest = true;
1728
+ break;
1729
+ case "subjectRepository": {
1730
+ const value = args[index + 1];
1731
+ if (!value) throw new Error("Missing value for --subject-repository");
1732
+ subjectRepository = value;
1733
+ index += 1;
1734
+ break;
1735
+ }
1649
1736
  case "runAttempt": {
1650
1737
  const value = args[index + 1];
1651
1738
  if (!value) throw new Error("Missing value for --run-attempt");
@@ -1678,6 +1765,8 @@ function parseArgs(args) {
1678
1765
  showHelp,
1679
1766
  showVersion,
1680
1767
  ingestPatterns,
1768
+ ...strictIngest ? { strictIngest } : {},
1769
+ ...subjectRepository ? { subjectRepository } : {},
1681
1770
  ...rubricPin ? { rubricPin } : {},
1682
1771
  ...runAttempt ? { runAttempt } : {}
1683
1772
  };
package/dist/mcp/index.js CHANGED
@@ -3,13 +3,13 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
3
3
  const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  serveCejelMcpOverStdio
6
- } from "../chunk-TOQPQPDF.js";
7
- import "../chunk-ZZHQZW26.js";
8
- import "../chunk-VGBFZBZQ.js";
9
- import "../chunk-FSMZV3Y6.js";
6
+ } from "../chunk-LBJFDK4R.js";
7
+ import "../chunk-SNRQAKKE.js";
8
+ import "../chunk-D7MWEPPX.js";
9
+ import "../chunk-AMVP7L26.js";
10
10
  import {
11
11
  init_esm_shims
12
- } from "../chunk-O3MHSK25.js";
12
+ } from "../chunk-AWLKQUI4.js";
13
13
 
14
14
  // src/mcp/index.ts
15
15
  init_esm_shims();
@@ -3,18 +3,18 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
3
3
  const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  listCejelLlmPackFiles
6
- } from "../../chunk-5PFYOIOS.js";
6
+ } from "../../chunk-UMGZWGOI.js";
7
7
  import {
8
8
  execGit
9
- } from "../../chunk-VGBFZBZQ.js";
9
+ } from "../../chunk-D7MWEPPX.js";
10
10
  import {
11
11
  require_typescript
12
- } from "../../chunk-RH6FKREX.js";
12
+ } from "../../chunk-6LVNNJTG.js";
13
13
  import {
14
14
  __toESM,
15
15
  init_esm_shims,
16
16
  readRepoText
17
- } from "../../chunk-O3MHSK25.js";
17
+ } from "../../chunk-AWLKQUI4.js";
18
18
 
19
19
  // src/packs/d-series/index.ts
20
20
  init_esm_shims();
@@ -3,15 +3,15 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
3
3
  const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  external_exports
6
- } from "../../chunk-FSMZV3Y6.js";
6
+ } from "../../chunk-AMVP7L26.js";
7
7
  import {
8
8
  require_typescript
9
- } from "../../chunk-RH6FKREX.js";
9
+ } from "../../chunk-6LVNNJTG.js";
10
10
  import {
11
11
  __toESM,
12
12
  init_esm_shims,
13
13
  readRepoText
14
- } from "../../chunk-O3MHSK25.js";
14
+ } from "../../chunk-AWLKQUI4.js";
15
15
 
16
16
  // src/packs/decision-contracts/index.ts
17
17
  init_esm_shims();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cejel/cejel",
3
- "version": "0.6.7",
3
+ "version": "0.7.0-rc.1",
4
4
  "mcpName": "io.github.BargLabs/cejel",
5
5
  "private": false,
6
6
  "publishConfig": {