@cejel/cejel 0.6.10 → 0.6.11

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,7 +5,7 @@ import {
5
5
  renderWitanBadgeSvg,
6
6
  renderWitanHtmlReport,
7
7
  runCejelScan
8
- } from "./chunk-YHJ7ICL4.js";
8
+ } from "./chunk-4YOIR3E6.js";
9
9
  import {
10
10
  ZodFirstPartyTypeKind,
11
11
  ZodOptional,
package/dist/index.js CHANGED
@@ -3,11 +3,12 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
3
3
  const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  serveCejelMcpOverStdio
6
- } from "./chunk-U2VDSTIT.js";
6
+ } from "./chunk-S5B5KMSO.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,
@@ -44,7 +45,7 @@ import {
44
45
  verifyRecordedInputDigests,
45
46
  verifyWitanAttestationBinding,
46
47
  witanHistoryAbstentionInstruction
47
- } from "./chunk-YHJ7ICL4.js";
48
+ } from "./chunk-4YOIR3E6.js";
48
49
  import "./chunk-2UMHJ23V.js";
49
50
  import {
50
51
  external_exports
@@ -67,7 +68,7 @@ init_esm_shims();
67
68
  init_esm_shims();
68
69
  var WITAN_ISSUANCE_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
69
70
  var WITAN_ISSUANCE_PREDICATE_TYPE = "https://cejel.dev/attestations/issuance/v1";
70
- var WITAN_ISSUANCE_FORMAT_VERSION = "1.0";
71
+ var WITAN_ISSUANCE_FORMAT_VERSION = "1.1";
71
72
  var WITAN_ISSUANCE_SIGNATURE_NAMESPACE = "cejel-issuance";
72
73
  var WITAN_ISSUANCE_PRINCIPAL = "issuance@barglabs.ai";
73
74
  var WITAN_ISSUER_NAME = "Barg Labs Inc.";
@@ -95,6 +96,9 @@ var WitanIssuanceStatementSchema = external_exports.object({
95
96
  toolVersion: external_exports.string().min(1).max(80),
96
97
  rubricVersion: external_exports.string().min(1).max(120),
97
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(),
98
102
  reportByteIdentical: external_exports.literal(true)
99
103
  }).strict(),
100
104
  issuedAt: external_exports.string().datetime({ offset: true }),
@@ -159,6 +163,7 @@ function createWitanIssuanceStatement(report, options) {
159
163
  toolVersion: options.toolVersion,
160
164
  rubricVersion: report.rubricVersion,
161
165
  ...report.rubricBehaviourFingerprint ? { rubricBehaviourFingerprint: report.rubricBehaviourFingerprint } : {},
166
+ ...options.reproductionGeneratedAt ? { generatedAt: options.reproductionGeneratedAt } : {},
162
167
  reportByteIdentical: true
163
168
  },
164
169
  issuedAt: options.issuedAt,
@@ -167,6 +172,40 @@ function createWitanIssuanceStatement(report, options) {
167
172
  }
168
173
  });
169
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
+ }
170
209
  function verifyWitanIssuanceBinding(statement, options) {
171
210
  const parsed = WitanIssuanceStatementSchema.safeParse(statement);
172
211
  if (!parsed.success) {
@@ -506,8 +545,15 @@ function normalizeRepoRelativePath(raw) {
506
545
  if (path.split("/").some((segment) => segment === ".." || segment === "")) return void 0;
507
546
  return path;
508
547
  }
509
- function fingerprint(rule, path, line, identity) {
510
- return createHash2("sha256").update(JSON.stringify([rule, path, line, identity])).digest("hex");
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");
511
557
  }
512
558
  function exportGitLabCodeQuality(report) {
513
559
  const ingestedCount = (report.consumedSignals ?? []).reduce(
@@ -525,6 +571,7 @@ function exportGitLabCodeQuality(report) {
525
571
  const entries = [];
526
572
  let excludedUnlocated = 0;
527
573
  const seen = /* @__PURE__ */ new Set();
574
+ const groupSizes = /* @__PURE__ */ new Map();
528
575
  for (const criterion of report.criteria) {
529
576
  for (const finding of criterion.findings) {
530
577
  const path = finding.evidence.path === void 0 ? void 0 : normalizeRepoRelativePath(finding.evidence.path);
@@ -533,9 +580,14 @@ function exportGitLabCodeQuality(report) {
533
580
  excludedUnlocated += 1;
534
581
  continue;
535
582
  }
536
- const print = fingerprint(criterion.id, path, line, finding.summary);
537
- if (seen.has(print)) continue;
538
- seen.add(print);
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);
539
591
  entries.push({
540
592
  description: finding.summary,
541
593
  check_name: criterion.id,
@@ -1597,6 +1649,12 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
1597
1649
  );
1598
1650
  return 1;
1599
1651
  }
1652
+ const scanTime = readReproductionGeneratedAt(attestationArtifact.value, /* @__PURE__ */ new Date());
1653
+ if (!scanTime.ok) {
1654
+ process.stderr.write(`Cejel: refusing to issue \u2014 ${scanTime.reason}.
1655
+ `);
1656
+ return 1;
1657
+ }
1600
1658
  let keyContents;
1601
1659
  try {
1602
1660
  keyContents = readFileSync(options.keyPath, "utf8");
@@ -1619,7 +1677,8 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
1619
1677
  // comparison below, as it always has.
1620
1678
  ...suppliedReport.inputBinding?.policy === "strict" ? { strictIngest: true } : {},
1621
1679
  ...suppliedReport.inputBinding?.scannedSubject.repository ? { subjectRepository: suppliedReport.inputBinding.scannedSubject.repository } : {},
1622
- toolVersion: cliVersion()
1680
+ toolVersion: cliVersion(),
1681
+ generatedAt: scanTime.generatedAt
1623
1682
  });
1624
1683
  const reproducedBytes = serializeWitanReport(reproduction.report);
1625
1684
  const reproducedSha256 = createHash3("sha256").update(reproducedBytes, "utf8").digest("hex");
@@ -1633,6 +1692,12 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
1633
1692
  " Check that the checkout is at the certificate revision, that the @cejel/cejel version matches",
1634
1693
  " report.json's toolVersion, and that --product-name/--name/--rubric-pin/--ingest match the",
1635
1694
  " original invocation. An issuance that cannot reproduce is not issued.",
1695
+ // The reproduction replays the scan's instant, but v17's B4 reads that instant's year in
1696
+ // the local time zone. Near a new year two hosts can disagree on it; name the setting,
1697
+ // never guess a zone.
1698
+ ...isWithinDayOfNewYearUtc(scanTime.generatedAt) ? [
1699
+ ` 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.`
1700
+ ] : [],
1636
1701
  ""
1637
1702
  ].join("\n")
1638
1703
  );
@@ -1644,6 +1709,7 @@ ${attestationBinding.errors.map((error) => ` - ${error}`).join("\n")}
1644
1709
  issuerKeyFingerprint: issuerKey.fingerprint,
1645
1710
  toolVersion: cliVersion(),
1646
1711
  issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1712
+ reproductionGeneratedAt: scanTime.generatedAt,
1647
1713
  engagementRef: options.engagementRef
1648
1714
  });
1649
1715
  const issuancePath = join2(options.outDir, ISSUANCE_OUTPUT_FILE);
package/dist/mcp/index.js CHANGED
@@ -3,8 +3,8 @@ import { createRequire as __cejelCreateRequire } from 'node:module';
3
3
  const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  serveCejelMcpOverStdio
6
- } from "../chunk-U2VDSTIT.js";
7
- import "../chunk-YHJ7ICL4.js";
6
+ } from "../chunk-S5B5KMSO.js";
7
+ import "../chunk-4YOIR3E6.js";
8
8
  import "../chunk-2UMHJ23V.js";
9
9
  import "../chunk-AMVP7L26.js";
10
10
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cejel/cejel",
3
- "version": "0.6.10",
3
+ "version": "0.6.11",
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",