cabloy 5.1.228 → 5.1.229

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/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.228
1
+ 5.1.229
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.229
4
+
5
+ ### Features
6
+
7
+ - Update functionality.
8
+
9
+ ### Improvements
10
+
11
+ - Update `source-map-js` to version 1.2.2 in the documentation dependencies.
12
+ - Refresh the pnpm lockfile.
13
+
3
14
  ## 5.1.228
4
15
 
5
16
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.228",
3
+ "version": "5.1.229",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -366,81 +366,45 @@ test('stream fatal UTF-8 and line limit never emit raw fragments of the failing
366
366
  await assert.rejects(streamed(['/Users/Secret/raw']), /^Error: INVALID_INPUT$/u);
367
367
  });
368
368
 
369
- test('stream rejects binary control bytes on complete and final lines without raw fragments', async () => {
370
- for (const byte of [0, 1, 27, 127]) {
371
- for (const ending of ['', '\n']) {
372
- for (const prefix of ['', '/Users/Secret/file ']) {
373
- const bytes = Buffer.concat([
374
- Buffer.from(prefix),
375
- Buffer.from([byte]),
376
- Buffer.from(ending),
377
- ]);
378
- const bytePosition = Buffer.byteLength(prefix) + 1;
379
- const output = sink();
380
- await assert.rejects(
381
- sanitizeStream(Readable.from([bytes]), output.stream, { matcher }),
382
- error => {
383
- assert.equal(String(error), 'Error: BINARY');
384
- assert.equal(error.code, 'BINARY');
385
- assert.deepEqual(error.details, { byte, line: 1, bytePosition });
386
- return true;
387
- },
388
- );
389
- assert.equal(output.text(), '');
390
- const result = cli(['sanitize'], bytes);
391
- assert.equal(result.status, 2);
392
- assert.equal(result.stdout, '');
393
- assert.equal(
394
- result.stderr,
395
- `evidence-paths: BINARY byte=${byte} line=1 byte-position=${bytePosition}\n`,
396
- );
397
- }
398
- }
369
+ test('stream preserves controls on complete and final lines while sanitizing paths', async () => {
370
+ const controls = '\u0000\u0001\u001B\u007F';
371
+ const input = `${controls}/Users/Secret/file${controls} /Users/Secret${controls} /synthetic/checkout/a${controls} file:///Users/Secret/file${controls}`;
372
+ const expected = `${controls}<PRIVATE_PATH>${controls} <PRIVATE_PATH>${controls} a${controls} <PRIVATE_PATH>${controls}`;
373
+ for (const ending of ['', '\n']) {
374
+ const bytes = Buffer.from(input + ending);
375
+ assert.equal(await streamed([bytes]), expected + ending);
376
+ const result = cli(['sanitize', '--repo-root', '/synthetic/checkout'], bytes);
377
+ assert.equal(result.status, 0);
378
+ assert.equal(result.stdout, expected + ending);
379
+ assert.equal(result.stderr, '');
399
380
  }
400
381
  assert.equal(await streamed([Buffer.from('plain\ttext\f\r\n')]), 'plain\ttext\f\r\n');
401
382
  });
402
383
 
403
- test('binary diagnostics use original byte positions across chunks and input lines', async () => {
384
+ test('stream preserves ANSI sequences and controls across UTF-8 chunks and lines', async () => {
385
+ const input = 'ok\r\n\r\né🙂\u001B[31m /Users/Secret/file\u001B[0m\u0000';
386
+ const expected = 'ok\r\n\r\né🙂\u001B[31m <PRIVATE_PATH>\u001B[0m\u0000';
404
387
  for (const ending of ['', '\n']) {
405
- const bytes = Buffer.concat([
406
- Buffer.from('ok\r\n\r\né🙂'),
407
- Buffer.from([27, 0]),
408
- Buffer.from('/Users/Secret/file'),
409
- Buffer.from(ending),
410
- ]);
388
+ const bytes = Buffer.from(input + ending);
411
389
  for (const chunks of [[bytes], Array.from(bytes, byte => Buffer.from([byte]))]) {
390
+ assert.equal(await streamed(chunks), expected + ending);
412
391
  const output = sink();
413
- await assert.rejects(
414
- sanitizeStream(Readable.from(chunks), output.stream, { matcher }),
415
- error => {
416
- assert.equal(String(error), 'Error: BINARY');
417
- assert.equal(error.code, 'BINARY');
418
- assert.deepEqual(error.details, { byte: 27, line: 3, bytePosition: 7 });
419
- return true;
420
- },
421
- );
422
- assert.equal(output.text(), 'ok\r\n\r\n');
423
- const cliOutput = sink();
424
392
  const errors = sink();
425
393
  assert.equal(
426
394
  await runCli(['sanitize'], {
427
395
  input: Readable.from(chunks),
428
- output: cliOutput.stream,
396
+ output: output.stream,
429
397
  error: errors.stream,
430
398
  }),
431
- 2,
399
+ 0,
432
400
  );
433
- assert.equal(cliOutput.text(), 'ok\r\n\r\n');
434
- assert.equal(errors.text(), 'evidence-paths: BINARY byte=27 line=3 byte-position=7\n');
401
+ assert.equal(output.text(), expected + ending);
402
+ assert.equal(errors.text(), '');
435
403
  }
436
- const result = cli(['sanitize'], bytes);
437
- assert.equal(result.status, 2);
438
- assert.equal(result.stdout, 'ok\r\n\r\n');
439
- assert.equal(result.stderr, 'evidence-paths: BINARY byte=27 line=3 byte-position=7\n');
440
404
  }
441
405
  });
442
406
 
443
- test('line limits retain precedence over binary diagnostics', async () => {
407
+ test('line byte limits still apply to control-containing input', async () => {
444
408
  for (const ending of ['', '\n']) {
445
409
  const bytes = Buffer.concat([
446
410
  Buffer.from([27]),
@@ -9,15 +9,14 @@ export const LINE_LIMIT = 1024 * 1024;
9
9
  export const FILE_LIMIT = 8 * 1024 * 1024;
10
10
 
11
11
  class SafeError extends Error {
12
- constructor(code, details) {
12
+ constructor(code) {
13
13
  super(code);
14
14
  this.code = code;
15
- this.details = details;
16
15
  }
17
16
  }
18
17
 
19
- function fail(code, details) {
20
- throw new SafeError(code, details);
18
+ function fail(code) {
19
+ throw new SafeError(code);
21
20
  }
22
21
 
23
22
  function rootInfo(value) {
@@ -70,7 +69,7 @@ function pathEnd(text, start, quoted) {
70
69
  let end = start;
71
70
  while (end < text.length) {
72
71
  const char = text[end];
73
- if (/[\r\n\t"'`<>(){}[\]|,;!?]/u.test(char) || (!quoted && /\s/u.test(char))) break;
72
+ if (/[\p{Cc}"'`<>(){}[\]|,;!?]/u.test(char) || (!quoted && /\s/u.test(char))) break;
74
73
  // Preserve stack line/column suffixes (and keep other colon-delimited prose out).
75
74
  if (char === ':') break;
76
75
  end++;
@@ -120,14 +119,15 @@ export function createMatcher({ repoRoot = DEFAULT_REPO_ROOT, maps = [] } = {})
120
119
  );
121
120
 
122
121
  const sep = '(?:\\\\{1,2}|/)';
123
- const account = '[^/\\\\\\s"\'`<>(){}[\\]|,;:!?]+(?: [^/\\\\\\s"\'`<>(){}[\\]|,;:!?]+)*';
122
+ const account =
123
+ '[^/\\\\\\s\\p{Cc}"\'`<>(){}[\\]|,;:!?]+(?: [^/\\\\\\s\\p{Cc}"\'`<>(){}[\\]|,;:!?]+)*';
124
124
  const known = [
125
- new RegExp(`/(?:Users|home)/${account}`, 'y'),
125
+ new RegExp(`/(?:Users|home)/${account}`, 'uy'),
126
126
  /\/(?:private\/)?tmp/y,
127
127
  /\/(?:private\/)?var\/folders/y,
128
- new RegExp(`[A-Z]:${sep}(?:Users${sep}${account}|(?:Windows${sep})?Temp)`, 'iy'),
128
+ new RegExp(`[A-Z]:${sep}(?:Users${sep}${account}|(?:Windows${sep})?Temp)`, 'iuy'),
129
129
  // UNC is a filesystem convention; forward-slash protocol-relative URLs are not.
130
- /\\{2,4}[^\\/\s"'`<>(){}[\]|,;:!?]+\\{1,2}[^\\/\s"'`<>(){}[\]|,;:!?]+/y,
130
+ /\\{2,4}[^\\/\s\p{Cc}"'`<>(){}[\]|,;:!?]+\\{1,2}[^\\/\s\p{Cc}"'`<>(){}[\]|,;:!?]+/uy,
131
131
  ];
132
132
 
133
133
  function recognizeAt(text, start, quoted = false) {
@@ -136,7 +136,7 @@ export function createMatcher({ repoRoot = DEFAULT_REPO_ROOT, maps = [] } = {})
136
136
  const match = entry.regex.exec(text);
137
137
  if (!match) continue;
138
138
  const after = start + match[0].length;
139
- if (after < text.length && !/[/\\\s"'`<>(){}[\]|,;:!?]/u.test(text[after])) continue;
139
+ if (after < text.length && !/[/\\\s\p{Cc}"'`<>(){}[\]|,;:!?]/u.test(text[after])) continue;
140
140
  const end = pathEnd(text, after, quoted);
141
141
  const tail = normalizeTail(text.slice(after, end), entry.windows).replace(/^\/+|\/+$/gu, '');
142
142
  const replacement =
@@ -154,7 +154,7 @@ export function createMatcher({ repoRoot = DEFAULT_REPO_ROOT, maps = [] } = {})
154
154
  const match = regex.exec(text);
155
155
  if (!match) continue;
156
156
  const after = start + match[0].length;
157
- if (after < text.length && !/[/\\\s"'`<>(){}[\]|,;:!?]/u.test(text[after])) continue;
157
+ if (after < text.length && !/[/\\\s\p{Cc}"'`<>(){}[\]|,;:!?]/u.test(text[after])) continue;
158
158
  return { end: pathEnd(text, after, quoted), replacement: '<PRIVATE_PATH>' };
159
159
  }
160
160
  return null;
@@ -164,7 +164,7 @@ export function createMatcher({ repoRoot = DEFAULT_REPO_ROOT, maps = [] } = {})
164
164
  if (typeof text !== 'string') fail('INVALID_TEXT');
165
165
  const matches = [];
166
166
  // Skip non-file URLs as units, including their paths, query strings and fragments.
167
- const urls = /[A-Za-z][A-Za-z0-9+.-]{0,31}:\/\/[^\s"'`<>(){}[\]|]+/gu;
167
+ const urls = /[A-Za-z][A-Za-z0-9+.-]{0,31}:\/\/[^\s\p{Cc}"'`<>(){}[\]|]+/gu;
168
168
  const protectedUrls = [...text.matchAll(urls)];
169
169
  let urlIndex = 0;
170
170
  for (let index = 0; index < text.length; index++) {
@@ -248,7 +248,6 @@ export async function sanitizeStream(input, output, { matcher = createMatcher()
248
248
  output.on('error', onError);
249
249
  let parts = [];
250
250
  let length = 0;
251
- let lineNumber = 1;
252
251
  try {
253
252
  for await (const chunk of input) {
254
253
  if (outputFailed) fail('OUTPUT_ERROR');
@@ -264,10 +263,8 @@ export async function sanitizeStream(input, output, { matcher = createMatcher()
264
263
  parts.push(bytes.subarray(start, end));
265
264
  if (newline < 0) break;
266
265
  const line = Buffer.concat(parts, length);
267
- rejectBinary(line, lineNumber);
268
266
  const text = decode(line);
269
267
  await write(output, `${matcher.sanitize(text)}\n`);
270
- lineNumber++;
271
268
  parts = [];
272
269
  length = 0;
273
270
  start = newline + 1;
@@ -275,7 +272,6 @@ export async function sanitizeStream(input, output, { matcher = createMatcher()
275
272
  }
276
273
  if (length) {
277
274
  const line = Buffer.concat(parts, length);
278
- rejectBinary(line, lineNumber);
279
275
  await write(output, matcher.sanitize(decode(line)));
280
276
  }
281
277
  if (outputFailed) fail('OUTPUT_ERROR');
@@ -296,21 +292,12 @@ function safeFilename(value, matcher) {
296
292
  );
297
293
  }
298
294
 
299
- function binaryIndex(bytes) {
300
- return bytes.findIndex(
295
+ function binary(bytes) {
296
+ return bytes.some(
301
297
  byte => byte === 0 || byte === 127 || (byte < 32 && ![9, 10, 12, 13].includes(byte)),
302
298
  );
303
299
  }
304
300
 
305
- function binary(bytes) {
306
- return binaryIndex(bytes) >= 0;
307
- }
308
-
309
- function rejectBinary(bytes, line) {
310
- const index = binaryIndex(bytes);
311
- if (index >= 0) fail('BINARY', { byte: bytes[index], line, bytePosition: index + 1 });
312
- }
313
-
314
301
  async function ancestorExclusion(path) {
315
302
  const root = parse(path).root;
316
303
  let current = root;
@@ -476,13 +463,11 @@ Stack :line:column suffixes are preserved. Non-file URLs, unrelated API/containe
476
463
  paths and unknown absolute roots are left unchanged: this is NOT a universal secret
477
464
  scanner. Use explicit maps for other local roots; inspect output before publication.
478
465
  UTF-8 decoding is fatal, with a 1 MiB byte limit per line (including CR, excluding LF).
479
- NUL, DEL and non-text C0 controls are rejected; tab, LF, form feed and CR are allowed.
480
- sanitize BINARY diagnostics report only the first rejected decimal byte, 1-based input
481
- line and byte-position within that original UTF-8 line. LF advances the line; CR counts
482
- as a byte, not a line boundary. No text from the rejected line is reported.
466
+ sanitize preserves control characters without interpreting or stripping them; they do
467
+ not cause BINARY rejection. LF separates lines; CR counts toward the line byte limit.
483
468
  Only complete validated lines are emitted; earlier sanitized lines may survive failure.
484
- check limits each file to 8 MiB, treats NUL/non-text controls as binary, and reports only
485
- scope-N/relative-filename:line CODE, never source lines or absolute scan roots.
469
+ check limits each file to 8 MiB and treats NUL, DEL and non-text C0 controls as binary.
470
+ It reports only scope-N/relative-filename:line CODE, never source lines or absolute scan roots.
486
471
  Symlinks (including scope ancestors), special files, .env* files/directories, unreadable,
487
472
  binary, invalid UTF-8 and over-limit files make checking incomplete. Scope trees must
488
473
  remain stable while scanning; this is not an adversarial filesystem snapshot.
@@ -545,23 +530,8 @@ export async function runCli(
545
530
  return result.status;
546
531
  } catch (caught) {
547
532
  const code = caught instanceof SafeError ? caught.code : 'INTERNAL_ERROR';
548
- let diagnostic = code;
549
- if (caught instanceof SafeError && code === 'BINARY') {
550
- const { byte, line, bytePosition } = caught.details ?? {};
551
- if (
552
- Number.isInteger(byte) &&
553
- byte >= 0 &&
554
- byte <= 255 &&
555
- Number.isSafeInteger(line) &&
556
- line > 0 &&
557
- Number.isSafeInteger(bytePosition) &&
558
- bytePosition > 0
559
- ) {
560
- diagnostic += ` byte=${byte} line=${line} byte-position=${bytePosition}`;
561
- }
562
- }
563
533
  try {
564
- await write(error, `evidence-paths: ${diagnostic}\n`);
534
+ await write(error, `evidence-paths: ${code}\n`);
565
535
  } catch {
566
536
  /* no unsafe fallback */
567
537
  }
@@ -697,8 +697,8 @@ packages:
697
697
  shiki@2.5.0:
698
698
  resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==}
699
699
 
700
- source-map-js@1.2.1:
701
- resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
700
+ source-map-js@1.2.2:
701
+ resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==}
702
702
  engines: {node: '>=0.10.0'}
703
703
 
704
704
  space-separated-tokens@2.0.2:
@@ -1175,7 +1175,7 @@ snapshots:
1175
1175
  '@vue/shared': 3.5.35
1176
1176
  entities: 7.0.1
1177
1177
  estree-walker: 2.0.2
1178
- source-map-js: 1.2.1
1178
+ source-map-js: 1.2.2
1179
1179
 
1180
1180
  '@vue/compiler-dom@3.5.35':
1181
1181
  dependencies:
@@ -1192,7 +1192,7 @@ snapshots:
1192
1192
  estree-walker: 2.0.2
1193
1193
  magic-string: 0.30.21
1194
1194
  postcss: 8.5.15
1195
- source-map-js: 1.2.1
1195
+ source-map-js: 1.2.2
1196
1196
 
1197
1197
  '@vue/compiler-ssr@3.5.35':
1198
1198
  dependencies:
@@ -1425,7 +1425,7 @@ snapshots:
1425
1425
  dependencies:
1426
1426
  nanoid: 3.3.12
1427
1427
  picocolors: 1.1.1
1428
- source-map-js: 1.2.1
1428
+ source-map-js: 1.2.2
1429
1429
 
1430
1430
  preact@10.29.2: {}
1431
1431
 
@@ -1487,7 +1487,7 @@ snapshots:
1487
1487
  '@shikijs/vscode-textmate': 10.0.2
1488
1488
  '@types/hast': 3.0.4
1489
1489
 
1490
- source-map-js@1.2.1: {}
1490
+ source-map-js@1.2.2: {}
1491
1491
 
1492
1492
  space-separated-tokens@2.0.2: {}
1493
1493
 
@@ -610,7 +610,7 @@ importers:
610
610
  specifier: ^7.6.2
611
611
  version: 7.8.5
612
612
  zova-cli-set-front:
613
- specifier: ^1.2.118
613
+ specifier: ^1.2.119
614
614
  version: link:../cli-set-front
615
615
  devDependencies:
616
616
  clean-package: