@cejel/cejel 0.6.7 → 0.6.8

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 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.7 and may trigger Microsoft SmartScreen. The release build removes Node's inherited
95
+ > 0.6.8 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.7` is the coordinated release version for npm,
119
+ > **Distribution note:** Cejel `0.6.8` 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
 
@@ -159,6 +159,10 @@ The npm package is scoped as `@cejel/cejel`; its executable remains the short co
159
159
 
160
160
  `actions/checkout` fetches one commit by default. On a shallow clone, A2 and B2 see only that
161
161
  commit, and the certificate says so under "History limitations".
162
+ Under the prospective v27 rubric (`--rubric-pin witan-rubric-v27-prospective-2026-10-19`), a
163
+ shallow clone goes further. A2 abstains unless a confirmed critical secret finding stands or A2
164
+ does not apply. B2 abstains, where it would be scored, when fewer than 12 commits are visible. Each
165
+ abstention counts as 0 in the composite, so a shallow checkout can lower the v27 score.
162
166
 
163
167
  For an executed public example that binds a release artifact to the same commit named by its
164
168
  Cejel certificate, see [Certified releases with GitHub build provenance](./docs/certified-release-example.md).
@@ -187,16 +191,16 @@ gh attestation verify ./cejel-Windows-x86_64.exe -R BargLabs/cejel
187
191
  This is cryptographically signed provenance. It is distinct from Apple Developer ID or
188
192
  Microsoft Authenticode code-signing.
189
193
 
190
- **Docker / OCI.** The current container release is `0.6.7`:
194
+ **Docker / OCI.** The current container release is `0.6.8`:
191
195
 
192
196
  ```bash
193
- docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.6.7
197
+ docker run --rm -i -v "$PWD:/workspace:ro" ghcr.io/barglabs/cejel:0.6.8
194
198
  ```
195
199
 
196
200
  The image defaults to `cejel-mcp` over stdio. To use the CLI instead:
197
201
 
198
202
  ```bash
199
- docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.6.7 .
203
+ docker run --rm -v "$PWD:/workspace:ro" --entrypoint cejel ghcr.io/barglabs/cejel:0.6.8 .
200
204
  ```
201
205
 
202
206
  The OCI image carries an SBOM of its base-image packages, maximum-mode build provenance, and a
@@ -296,6 +300,12 @@ prints that boundary on every successful verification.
296
300
  - `--ingest <file|glob>` — fold another scanner's output into the score (repeatable). Accepts
297
301
  SARIF, OpenSSF Scorecard JSON, or the generic Cejel external-signal shape — format is
298
302
  auto-detected. See "Aggregate your scanners" below.
303
+ - `--subject-repository <repo>` — name the scanned repository (`github.com/owner/repo` or a clone
304
+ URL) so an ingested input that declares another repository is recorded as mismatched. Cejel
305
+ never infers it from clone state.
306
+ - `--strict-ingest` — refuse, before scoring, an ingested input that declares another repository or
307
+ commit, was observed before the scanned commit existed, or duplicates another input. Without it,
308
+ the binding status is recorded and scoring is unchanged. See [input binding](docs/input-binding.md).
299
309
  - `--rubric-pin <version>` — Explicit opt-in only: pin an alternate rubric instead of the
300
310
  calibrated public default. It
301
311
  accepts the calibrated version or any published prospective rubric
@@ -414,7 +424,17 @@ Three ways a scanner's output gets ingested:
414
424
  versions rather than guessing. See the [generic ingest contract](docs/ingest-contract.md), its
415
425
  [published JSON Schema](docs/schemas/cejel-ingest-v1.schema.json), and the
416
426
  [synthetic CI heal-log mapping](docs/heal-log-mapping.md). Output-field stability and version
417
- routing are documented in [format stability](docs/format-stability.md).
427
+ routing are documented in [format stability](docs/format-stability.md). Contract `1.1` adds
428
+ optional `subject`, `producer` and `observedAt` fields so the input can be bound to a revision.
429
+
430
+ Every ingested document is also recorded in `report.json` under `inputBinding`: the sha256 of its
431
+ bytes, the producer and subject its own format declares (Scorecard `repo.name`/`repo.commit`/`date`,
432
+ SARIF `versionControlProvenance` and `invocations`, generic `subject`/`observedAt`), and whether that
433
+ subject is the scanned HEAD (`matched`, `mismatched-repository`, `mismatched-commit`, `stale`,
434
+ `unbound`, `unverifiable`). By default this is a record, not a gate: it changes no score.
435
+ `--strict-ingest` refuses mismatched, stale and duplicate inputs instead. `cejel verify ... --input
436
+ <file>` checks a set of input files against the recorded digests, so a swapped input is detected.
437
+ See [input binding](docs/input-binding.md), which carries the conformance table the tests run.
418
438
 
419
439
  ## Displaying the trust badge
420
440
 
@@ -571,7 +591,7 @@ The OCI image is an alternative when Docker is the preferred execution boundary.
571
591
  the host path with the repository OpenClaw should allow Cejel to read:
572
592
 
573
593
  ```bash
574
- 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.7"]}'
594
+ 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.8"]}'
575
595
  npx -y openclaw@latest mcp doctor cejel-oci --probe
576
596
  ```
577
597
 
@@ -5,20 +5,20 @@ import {
5
5
  deriveProductIdentity,
6
6
  runCejelScan,
7
7
  serializeWitanReport
8
- } from "../chunk-ZZHQZW26.js";
8
+ } from "../chunk-KJVFCZZ3.js";
9
9
  import {
10
10
  listCejelLlmPackFiles
11
- } from "../chunk-5PFYOIOS.js";
12
- import "../chunk-VGBFZBZQ.js";
11
+ } from "../chunk-WXPGEQBE.js";
12
+ import "../chunk-NIWSYJ6N.js";
13
13
  import {
14
14
  external_exports
15
- } from "../chunk-FSMZV3Y6.js";
15
+ } from "../chunk-AMVP7L26.js";
16
16
  import {
17
17
  init_esm_shims,
18
18
  readRepoText,
19
19
  recordContentSkip,
20
20
  recordFilesystemSkip
21
- } from "../chunk-O3MHSK25.js";
21
+ } from "../chunk-AWLKQUI4.js";
22
22
 
23
23
  // src/calibration/llm-detector.ts
24
24
  init_esm_shims();
@@ -7,7 +7,7 @@ import {
7
7
  __filename,
8
8
  __require,
9
9
  init_esm_shims
10
- } from "./chunk-O3MHSK25.js";
10
+ } from "./chunk-AWLKQUI4.js";
11
11
 
12
12
  // node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/typescript.js
13
13
  var require_typescript = __commonJS({
@@ -4,7 +4,7 @@ const require = __cejelCreateRequire(import.meta.url);
4
4
  import {
5
5
  __export,
6
6
  init_esm_shims
7
- } from "./chunk-O3MHSK25.js";
7
+ } from "./chunk-AWLKQUI4.js";
8
8
 
9
9
  // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
10
10
  var external_exports = {};
@@ -62,7 +62,11 @@ var MAX_REPOSITORY_CONTENT_BYTES = 512e3;
62
62
  init_esm_shims();
63
63
  import { closeSync, lstatSync, openSync, readFileSync, readSync } from "fs";
64
64
  import { resolve } from "path";
65
- var READ_FAILURE_REASONS = /* @__PURE__ */ new Set(["unreadable", "denied_path"]);
65
+ var READ_FAILURE_REASONS = /* @__PURE__ */ new Set([
66
+ "unreadable",
67
+ "denied_path",
68
+ "lfs_pointer"
69
+ ]);
66
70
  function isReadFailureReason(reason) {
67
71
  return READ_FAILURE_REASONS.has(reason);
68
72
  }
@@ -131,7 +135,10 @@ function summaryFor(session) {
131
135
  tooLarge: session.counts.too_large,
132
136
  excludedByExtension: session.counts.excluded_by_extension,
133
137
  deniedPath: session.counts.denied_path,
134
- nonRegularFile: session.counts.non_regular_file
138
+ nonRegularFile: session.counts.non_regular_file,
139
+ // Present only when non-zero, so every report without an LFS pointer is byte-identical to one
140
+ // produced before this reason existed (report format 1.6).
141
+ ...session.counts.lfs_pointer > 0 ? { lfsPointer: session.counts.lfs_pointer } : {}
135
142
  };
136
143
  const sortedErrnos = Object.fromEntries(
137
144
  [...session.unreadableByErrno.entries()].sort(([left], [right]) => left.localeCompare(right))
@@ -143,7 +150,7 @@ function summaryFor(session) {
143
150
  affectedCriteria: [...session.affectedCriteria].sort()
144
151
  };
145
152
  }
146
- function trackContentReads(_repoPath, collect) {
153
+ function trackContentReads(_repoPath, collect, options = {}) {
147
154
  const existing = activeSession;
148
155
  if (existing) {
149
156
  return {
@@ -162,8 +169,10 @@ function trackContentReads(_repoPath, collect) {
162
169
  too_large: 0,
163
170
  excluded_by_extension: 0,
164
171
  denied_path: 0,
165
- non_regular_file: 0
172
+ non_regular_file: 0,
173
+ lfs_pointer: 0
166
174
  },
175
+ lfsPointerAbstention: options.lfsPointerAbstention === true,
167
176
  unreadableByErrno: /* @__PURE__ */ new Map(),
168
177
  deduplicatedSkips: /* @__PURE__ */ new Set(),
169
178
  affectedCriteria: /* @__PURE__ */ new Set(),
@@ -218,6 +227,20 @@ function withContentReadSignals(criterionId, signalIds, collect) {
218
227
  session.signals = previous;
219
228
  }
220
229
  }
230
+ function withoutContentReadAttribution(collect) {
231
+ const session = activeSession;
232
+ if (!session) return collect();
233
+ const previousCriterion = session.criterion;
234
+ const previousSignals = session.signals;
235
+ session.criterion = void 0;
236
+ session.signals = void 0;
237
+ try {
238
+ return collect();
239
+ } finally {
240
+ session.criterion = previousCriterion;
241
+ session.signals = previousSignals;
242
+ }
243
+ }
221
244
  function recordContentSkip(path2, reason, deduplicate = false) {
222
245
  recordSkip(path2, reason, void 0, false, deduplicate);
223
246
  }
@@ -287,12 +310,33 @@ function readRepoText(path2, encoding = "utf8") {
287
310
  recordFilesystemSkip(path2, error);
288
311
  return "";
289
312
  }
313
+ let contents;
290
314
  try {
291
- return readFileSync(path2, encoding);
315
+ contents = readFileSync(path2, encoding);
292
316
  } catch (error) {
293
317
  recordFilesystemSkip(path2, error);
294
318
  return "";
295
319
  }
320
+ if (isGitLfsPointerText(contents)) {
321
+ recordSkip(path2, "lfs_pointer", void 0, activeSession?.lfsPointerAbstention === true, true);
322
+ }
323
+ return contents;
324
+ }
325
+ var GIT_LFS_POINTER_MAX_BYTES = 1024;
326
+ var GIT_LFS_POINTER_VERSION_LINE = /^version https:\/\/(?:git-lfs\.github\.com\/spec\/v1|hawser\.github\.com\/spec\/v1)\n/;
327
+ var GIT_LFS_POINTER_OID_LINE = /^oid sha256:[0-9a-f]{64}$/m;
328
+ var GIT_LFS_POINTER_SIZE_LINE = /^size [0-9]+$/m;
329
+ var GIT_LFS_POINTER_KEY_LINE = /^[a-z0-9.-]+ \S[^\n]*$/;
330
+ function isGitLfsPointerText(contents) {
331
+ if (isLfGitLfsPointerText(contents)) return true;
332
+ return contents.length < 2 * GIT_LFS_POINTER_MAX_BYTES && contents.includes("\r\n") && !/\r(?!\n)/.test(contents) && !/(?:^|[^\r])\n/.test(contents) && isLfGitLfsPointerText(contents.replaceAll("\r\n", "\n"));
333
+ }
334
+ function isLfGitLfsPointerText(contents) {
335
+ if (contents.length >= GIT_LFS_POINTER_MAX_BYTES || !contents.startsWith("version ")) return false;
336
+ if (!GIT_LFS_POINTER_VERSION_LINE.test(contents)) return false;
337
+ const lines = contents.replace(/\n$/, "").split("\n");
338
+ if (!lines.slice(1).every((line) => GIT_LFS_POINTER_KEY_LINE.test(line))) return false;
339
+ return GIT_LFS_POINTER_OID_LINE.test(contents) && GIT_LFS_POINTER_SIZE_LINE.test(contents);
296
340
  }
297
341
  function readRepoTextPrefix(path2, byteLimit) {
298
342
  try {
@@ -310,7 +354,11 @@ function readRepoTextPrefix(path2, byteLimit) {
310
354
  descriptor = openSync(path2, "r");
311
355
  const buffer = Buffer.allocUnsafe(byteLimit);
312
356
  const bytesRead = readSync(descriptor, buffer, 0, byteLimit, 0);
313
- return buffer.subarray(0, bytesRead).toString("utf8");
357
+ const contents = buffer.subarray(0, bytesRead).toString("utf8");
358
+ if (bytesRead < byteLimit && isGitLfsPointerText(contents)) {
359
+ recordSkip(path2, "lfs_pointer", void 0, activeSession?.lfsPointerAbstention === true, true);
360
+ }
361
+ return contents;
314
362
  } catch (error) {
315
363
  recordFilesystemSkip(path2, error);
316
364
  return "";
@@ -340,11 +388,13 @@ export {
340
388
  withContentReadCriterion,
341
389
  withContentReadSignal,
342
390
  withContentReadSignals,
391
+ withoutContentReadAttribution,
343
392
  recordContentSkip,
344
393
  registerWithheldRepoPath,
345
394
  withheldRepoPaths,
346
395
  abstainSignalOnWithheldPaths,
347
396
  recordFilesystemSkip,
348
397
  readRepoText,
398
+ isGitLfsPointerText,
349
399
  readRepoTextPrefix
350
400
  };