@blamejs/core 0.9.12 → 0.9.15

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.
Files changed (119) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/lib/a2a.js +11 -11
  3. package/lib/acme.js +5 -5
  4. package/lib/ai-input.js +2 -2
  5. package/lib/api-key.js +4 -4
  6. package/lib/api-snapshot.js +10 -7
  7. package/lib/app-shutdown.js +2 -2
  8. package/lib/app.js +5 -5
  9. package/lib/archive.js +8 -8
  10. package/lib/argon2-builtin.js +2 -2
  11. package/lib/atomic-file.js +53 -53
  12. package/lib/audit-sign.js +8 -8
  13. package/lib/audit-tools.js +22 -22
  14. package/lib/audit.js +29 -17
  15. package/lib/auth/dpop.js +3 -3
  16. package/lib/auth/sd-jwt-vc.js +2 -2
  17. package/lib/backup/bundle.js +17 -17
  18. package/lib/backup/index.js +36 -36
  19. package/lib/budr.js +3 -3
  20. package/lib/bundler.js +20 -20
  21. package/lib/circuit-breaker.js +24 -9
  22. package/lib/cli.js +25 -26
  23. package/lib/cluster.js +2 -2
  24. package/lib/compliance-sanctions.js +2 -2
  25. package/lib/config-drift.js +15 -15
  26. package/lib/content-credentials.js +4 -4
  27. package/lib/credential-hash.js +3 -3
  28. package/lib/crypto.js +145 -0
  29. package/lib/daemon.js +19 -19
  30. package/lib/db-file-lifecycle.js +24 -24
  31. package/lib/db-schema.js +2 -2
  32. package/lib/db.js +35 -35
  33. package/lib/dev.js +10 -10
  34. package/lib/dr-runbook.js +5 -5
  35. package/lib/dsr.js +22 -15
  36. package/lib/dual-control.js +2 -2
  37. package/lib/external-db-migrate.js +2 -2
  38. package/lib/external-db.js +2 -2
  39. package/lib/fdx.js +2 -2
  40. package/lib/file-upload.js +30 -30
  41. package/lib/flag-providers.js +4 -4
  42. package/lib/gate-contract.js +5 -5
  43. package/lib/graphql-federation.js +4 -7
  44. package/lib/honeytoken.js +6 -6
  45. package/lib/http-client-cookie-jar.js +6 -6
  46. package/lib/http-client.js +18 -18
  47. package/lib/i18n.js +5 -5
  48. package/lib/inbox.js +21 -15
  49. package/lib/keychain.js +9 -9
  50. package/lib/legal-hold.js +2 -2
  51. package/lib/local-db-thin.js +9 -9
  52. package/lib/log-stream-local.js +17 -17
  53. package/lib/log-stream-syslog.js +2 -2
  54. package/lib/log-stream.js +3 -3
  55. package/lib/mail-bounce.js +2 -2
  56. package/lib/mail-mdn.js +2 -2
  57. package/lib/mail-srs.js +2 -2
  58. package/lib/mail.js +4 -4
  59. package/lib/mcp.js +2 -2
  60. package/lib/metrics.js +249 -2
  61. package/lib/middleware/api-encrypt.js +16 -16
  62. package/lib/middleware/body-parser.js +16 -16
  63. package/lib/middleware/compression.js +3 -3
  64. package/lib/middleware/csp-nonce.js +4 -4
  65. package/lib/middleware/health.js +7 -7
  66. package/lib/middleware/idempotency-key.js +250 -0
  67. package/lib/migrations.js +3 -3
  68. package/lib/mtls-ca.js +26 -26
  69. package/lib/mtls-engine-default.js +5 -5
  70. package/lib/network-dns.js +2 -2
  71. package/lib/network-nts.js +2 -2
  72. package/lib/network-proxy.js +3 -3
  73. package/lib/network-smtp-policy.js +2 -2
  74. package/lib/network-tls.js +17 -17
  75. package/lib/network.js +13 -13
  76. package/lib/notify.js +3 -3
  77. package/lib/object-store/gcs-bucket-ops.js +2 -2
  78. package/lib/object-store/gcs.js +5 -5
  79. package/lib/object-store/index.js +6 -6
  80. package/lib/object-store/local.js +19 -19
  81. package/lib/object-store/sigv4.js +3 -3
  82. package/lib/observability-tracer.js +4 -4
  83. package/lib/otel-export.js +3 -3
  84. package/lib/pagination.js +5 -5
  85. package/lib/parsers/safe-xml.js +3 -3
  86. package/lib/pqc-agent.js +116 -26
  87. package/lib/pqc-gate.js +5 -5
  88. package/lib/pubsub-redis.js +2 -2
  89. package/lib/queue-local.js +3 -3
  90. package/lib/queue.js +2 -2
  91. package/lib/redis-client.js +4 -4
  92. package/lib/restore-bundle.js +18 -18
  93. package/lib/restore-rollback.js +34 -34
  94. package/lib/restore.js +16 -16
  95. package/lib/retry.js +50 -0
  96. package/lib/router.js +13 -13
  97. package/lib/sandbox.js +8 -8
  98. package/lib/sec-cyber.js +3 -3
  99. package/lib/security-assert.js +2 -2
  100. package/lib/seeders.js +4 -4
  101. package/lib/self-update-standalone-verifier.js +280 -0
  102. package/lib/self-update.js +32 -26
  103. package/lib/session-device-binding.js +2 -2
  104. package/lib/static.js +22 -22
  105. package/lib/template.js +19 -19
  106. package/lib/testing.js +7 -7
  107. package/lib/tls-exporter.js +5 -5
  108. package/lib/tracing.js +3 -3
  109. package/lib/vault/index.js +11 -11
  110. package/lib/vault/passphrase-ops.js +37 -37
  111. package/lib/vault/passphrase-source.js +2 -2
  112. package/lib/vault/rotate.js +70 -66
  113. package/lib/vault/seal-pem-file.js +26 -26
  114. package/lib/watcher.js +23 -23
  115. package/lib/webhook.js +10 -10
  116. package/lib/worker-pool.js +6 -6
  117. package/lib/ws-client.js +4 -4
  118. package/package.json +1 -1
  119. package/sbom.cdx.json +6 -6
@@ -11,8 +11,8 @@
11
11
  * Every write goes through the same crash-safe sequence:
12
12
  * 1. write payload to a sibling temp file (`<filepath>.tmp-<token>`)
13
13
  * 2. fsync the file descriptor before close
14
- * 3. fs.rename() the temp file over the destination — POSIX rename
15
- * is atomic on the same filesystem; on Windows, fs.rename uses
14
+ * 3. nodeFs.rename() the temp file over the destination — POSIX rename
15
+ * is atomic on the same filesystem; on Windows, nodeFs.rename uses
16
16
  * MoveFileEx with REPLACE_EXISTING for the same guarantee
17
17
  * 4. fsync the parent directory so the rename itself is durable
18
18
  *
@@ -38,8 +38,8 @@
38
38
  * @card
39
39
  * Atomic file I/O with integrity verification, retry on transient errors, and cross-process locking.
40
40
  */
41
- var fs = require("fs");
42
- var path = require("path");
41
+ var nodeFs = require("fs");
42
+ var nodePath = require("path");
43
43
  var { generateToken, sha3Hash } = require("./crypto");
44
44
  var safeJson = require("./safe-json");
45
45
  var C = require("./constants");
@@ -47,7 +47,7 @@ var { boot } = require("./log");
47
47
  var safeBuffer = require("./safe-buffer");
48
48
  var numericBounds = require("./numeric-bounds");
49
49
  var safeAsync = require("./safe-async");
50
- var retry = require("./retry");
50
+ var retryHelper = require("./retry");
51
51
  var { FrameworkError } = require("./framework-error");
52
52
 
53
53
  var log = boot("atomic-file");
@@ -89,7 +89,7 @@ function _isFsRetryable(e) {
89
89
 
90
90
  async function _withRetry(fn, opts) {
91
91
  opts = Object.assign({}, DEFAULTS, opts || {});
92
- return retry.withRetry(function () { return fn(); }, {
92
+ return retryHelper.withRetry(function () { return fn(); }, {
93
93
  maxAttempts: opts.retryAttempts,
94
94
  baseDelayMs: opts.retryBaseMs,
95
95
  maxDelayMs: opts.retryMaxMs,
@@ -114,7 +114,7 @@ async function _withRetry(fn, opts) {
114
114
  * @status stable
115
115
  * @related b.atomicFile.fsyncDir, b.atomicFile.write
116
116
  *
117
- * Best-effort fs.fsyncSync wrapper. Silently swallows errors because
117
+ * Best-effort nodeFs.fsyncSync wrapper. Silently swallows errors because
118
118
  * not every platform / fd type supports fsync (some FUSE mounts, some
119
119
  * device fds). Use this when you want the durability hint but don't
120
120
  * want a non-fsyncable target to crash the caller.
@@ -124,10 +124,10 @@ async function _withRetry(fn, opts) {
124
124
  * var fd = fs.openSync("/tmp/note.txt", "w");
125
125
  * fs.writeSync(fd, "hello\n");
126
126
  * b.atomicFile.fsync(fd);
127
- * fs.closeSync(fd);
127
+ * nodeFs.closeSync(fd);
128
128
  */
129
129
  function fsync(fd) {
130
- try { fs.fsyncSync(fd); } catch (_e) { /* not all platforms support fsync on every fd type */ }
130
+ try { nodeFs.fsyncSync(fd); } catch (_e) { /* not all platforms support fsync on every fd type */ }
131
131
  }
132
132
 
133
133
  /**
@@ -147,9 +147,9 @@ function fsync(fd) {
147
147
  */
148
148
  function fsyncDir(dirPath) {
149
149
  try {
150
- var fd = fs.openSync(dirPath, "r");
151
- try { fs.fsyncSync(fd); } catch (_e) { /* Windows rejects directory fsync */ }
152
- finally { fs.closeSync(fd); }
150
+ var fd = nodeFs.openSync(dirPath, "r");
151
+ try { nodeFs.fsyncSync(fd); } catch (_e) { /* Windows rejects directory fsync */ }
152
+ finally { nodeFs.closeSync(fd); }
153
153
  } catch (_e) { /* dir fsync is best-effort across filesystems */ }
154
154
  }
155
155
 
@@ -180,7 +180,7 @@ function ensureDir(dirPath, mode) {
180
180
  if (typeof dirPath !== "string" || dirPath.length === 0) {
181
181
  throw new AtomicFileError("ensureDir: path must be a non-empty string", "atomic-file/bad-path");
182
182
  }
183
- fs.mkdirSync(dirPath, { recursive: true, mode: typeof mode === "number" ? mode : 0o700 });
183
+ nodeFs.mkdirSync(dirPath, { recursive: true, mode: typeof mode === "number" ? mode : 0o700 });
184
184
  return dirPath;
185
185
  }
186
186
 
@@ -217,29 +217,29 @@ function copyDirRecursive(src, dest, opts) {
217
217
  if (typeof dest !== "string" || dest.length === 0) {
218
218
  throw new AtomicFileError("copyDirRecursive: dest must be a non-empty string", "atomic-file/bad-path");
219
219
  }
220
- if (!fs.existsSync(src)) {
220
+ if (!nodeFs.existsSync(src)) {
221
221
  throw new AtomicFileError("copyDirRecursive: src does not exist: " + src, "atomic-file/missing-src");
222
222
  }
223
223
  opts = opts || {};
224
224
  var dirMode = typeof opts.dirMode === "number" ? opts.dirMode : 0o700;
225
225
  var overwrite = !!opts.overwrite;
226
- var copyFlags = overwrite ? 0 : fs.constants.COPYFILE_EXCL;
226
+ var copyFlags = overwrite ? 0 : nodeFs.constants.COPYFILE_EXCL;
227
227
 
228
228
  ensureDir(dest, dirMode);
229
- var entries = fs.readdirSync(src, { withFileTypes: true });
229
+ var entries = nodeFs.readdirSync(src, { withFileTypes: true });
230
230
  var fileCount = 0;
231
231
  var byteCount = 0;
232
232
  for (var i = 0; i < entries.length; i++) {
233
233
  var name = entries[i].name;
234
- var s = path.join(src, name);
235
- var d = path.join(dest, name);
234
+ var s = nodePath.join(src, name);
235
+ var d = nodePath.join(dest, name);
236
236
  if (entries[i].isDirectory()) {
237
237
  var sub = copyDirRecursive(s, d, opts);
238
238
  fileCount += sub.fileCount;
239
239
  byteCount += sub.byteCount;
240
240
  } else if (entries[i].isFile()) {
241
- fs.copyFileSync(s, d, copyFlags);
242
- try { byteCount += fs.statSync(d).size; } catch (_e) { /* size best-effort */ }
241
+ nodeFs.copyFileSync(s, d, copyFlags);
242
+ try { byteCount += nodeFs.statSync(d).size; } catch (_e) { /* size best-effort */ }
243
243
  fileCount++;
244
244
  }
245
245
  // Symlinks, sockets, devices: deliberately skipped
@@ -308,30 +308,30 @@ function writeSync(filepath, data, opts) {
308
308
  typeMessage: "data must be Buffer, Uint8Array, or string",
309
309
  });
310
310
 
311
- var dir = path.dirname(filepath);
312
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
311
+ var dir = nodePath.dirname(filepath);
312
+ if (!nodeFs.existsSync(dir)) nodeFs.mkdirSync(dir, { recursive: true, mode: 0o700 });
313
313
 
314
314
  var tmpPath = filepath + ".tmp-" + generateToken(C.BYTES.bytes(8));
315
315
  var renamed = false;
316
316
  try {
317
- var fd = fs.openSync(tmpPath, "w", opts.fileMode);
317
+ var fd = nodeFs.openSync(tmpPath, "w", opts.fileMode);
318
318
  try {
319
319
  var pos = 0;
320
320
  while (pos < buf.length) {
321
- pos += fs.writeSync(fd, buf, pos, buf.length - pos, null);
321
+ pos += nodeFs.writeSync(fd, buf, pos, buf.length - pos, null);
322
322
  }
323
323
  _fsync(fd);
324
324
  } finally {
325
- try { fs.closeSync(fd); } catch (_e) { /* already closed? */ }
325
+ try { nodeFs.closeSync(fd); } catch (_e) { /* already closed? */ }
326
326
  }
327
- fs.renameSync(tmpPath, filepath);
327
+ nodeFs.renameSync(tmpPath, filepath);
328
328
  renamed = true;
329
329
  _fsyncDir(dir);
330
330
  } finally {
331
331
  if (!renamed) {
332
332
  // Either the write or the rename failed — remove the tmp so the next
333
333
  // boot doesn't see a leaked partial file.
334
- try { fs.unlinkSync(tmpPath); } catch (_e) { /* may not exist */ }
334
+ try { nodeFs.unlinkSync(tmpPath); } catch (_e) { /* may not exist */ }
335
335
  }
336
336
  }
337
337
 
@@ -354,7 +354,7 @@ function writeSync(filepath, data, opts) {
354
354
  * predict — only glob-by-prefix and prune by age. Operators should
355
355
  * call this at boot for every "important" filepath (vault.key.sealed,
356
356
  * audit-sign.key.sealed, db.enc, ...) BEFORE the first atomic write
357
- * to that path. Returns the number of orphans removed.
357
+ * to that nodePath. Returns the number of orphans removed.
358
358
  *
359
359
  * @opts
360
360
  * olderThanMs: 300000, // only prune temp files older than this many ms (default 5 minutes)
@@ -369,8 +369,8 @@ function writeSync(filepath, data, opts) {
369
369
  function cleanOrphans(filepath, opts) {
370
370
  opts = opts || {};
371
371
  var olderThanMs = opts.olderThanMs != null ? opts.olderThanMs : C.TIME.minutes(5);
372
- var dir = path.dirname(filepath);
373
- var basename = path.basename(filepath);
372
+ var dir = nodePath.dirname(filepath);
373
+ var basename = nodePath.basename(filepath);
374
374
  var prefix = basename + ".tmp-";
375
375
  var nowMs = Date.now();
376
376
  var removed = 0;
@@ -382,7 +382,7 @@ function cleanOrphans(filepath, opts) {
382
382
  var entry = entries[i];
383
383
  try {
384
384
  if (nowMs - entry.mtimeMs >= olderThanMs) {
385
- fs.unlinkSync(entry.fullPath);
385
+ nodeFs.unlinkSync(entry.fullPath);
386
386
  removed += 1;
387
387
  }
388
388
  } catch (_e) { /* concurrent cleanup or permission — best effort */ }
@@ -433,24 +433,24 @@ async function write(filepath, data, opts) {
433
433
 
434
434
  return await _withRetry(function () {
435
435
  return new Promise(function (resolve, reject) {
436
- var dir = path.dirname(filepath);
437
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
436
+ var dir = nodePath.dirname(filepath);
437
+ if (!nodeFs.existsSync(dir)) nodeFs.mkdirSync(dir, { recursive: true, mode: 0o700 });
438
438
  var tmpPath = filepath + ".tmp-" + generateToken(C.BYTES.bytes(8));
439
439
  var renamed = false;
440
440
  try {
441
- var fd = fs.openSync(tmpPath, "w", opts.fileMode);
441
+ var fd = nodeFs.openSync(tmpPath, "w", opts.fileMode);
442
442
  try {
443
443
  var pos = 0;
444
444
  while (pos < buf.length) {
445
- pos += fs.writeSync(fd, buf, pos, buf.length - pos, null);
445
+ pos += nodeFs.writeSync(fd, buf, pos, buf.length - pos, null);
446
446
  }
447
447
  _fsync(fd);
448
448
  } finally {
449
- try { fs.closeSync(fd); } catch (_e) { /* already closed? */ }
449
+ try { nodeFs.closeSync(fd); } catch (_e) { /* already closed? */ }
450
450
  }
451
451
  // Atomic rename — POSIX rename is atomic on the same FS; on Windows,
452
- // fs.renameSync uses MoveFileEx with REPLACE_EXISTING.
453
- fs.renameSync(tmpPath, filepath);
452
+ // nodeFs.renameSync uses MoveFileEx with REPLACE_EXISTING.
453
+ nodeFs.renameSync(tmpPath, filepath);
454
454
  renamed = true;
455
455
  _fsyncDir(dir);
456
456
  var hash = opts.computeHash ? sha3Hash(buf) : null;
@@ -459,7 +459,7 @@ async function write(filepath, data, opts) {
459
459
  reject(e);
460
460
  } finally {
461
461
  if (!renamed) {
462
- try { fs.unlinkSync(tmpPath); } catch (_e) { /* may not exist */ }
462
+ try { nodeFs.unlinkSync(tmpPath); } catch (_e) { /* may not exist */ }
463
463
  }
464
464
  }
465
465
  });
@@ -560,20 +560,20 @@ function _validateMaxBytes(maxBytes) {
560
560
  }
561
561
 
562
562
  function _readSyncCore(filepath, opts) {
563
- if (!fs.existsSync(filepath)) {
563
+ if (!nodeFs.existsSync(filepath)) {
564
564
  var e = new AtomicFileError("file not found: " + filepath, "atomic-file/not-found");
565
565
  e.code = "ENOENT";
566
566
  throw e;
567
567
  }
568
568
  _validateMaxBytes(opts.maxBytes);
569
- var stat = fs.statSync(filepath);
569
+ var stat = nodeFs.statSync(filepath);
570
570
  if (stat.size > opts.maxBytes) {
571
571
  throw new AtomicFileError(
572
572
  "file size " + stat.size + " > maxBytes " + opts.maxBytes,
573
573
  "atomic-file/too-large"
574
574
  );
575
575
  }
576
- var buf = fs.readFileSync(filepath);
576
+ var buf = nodeFs.readFileSync(filepath);
577
577
  if (opts.expectedHash) {
578
578
  var actual = sha3Hash(buf);
579
579
  if (actual !== opts.expectedHash) {
@@ -706,7 +706,7 @@ async function copy(src, dst, opts) {
706
706
  * @status stable
707
707
  * @related b.atomicFile.read, b.atomicFile.readSync
708
708
  *
709
- * Synchronous existence check. Thin wrapper over `fs.existsSync` that
709
+ * Synchronous existence check. Thin wrapper over `nodeFs.existsSync` that
710
710
  * normalises the answer for callers that already require this module
711
711
  * — saves an additional `require("fs")` in modules that otherwise
712
712
  * only need atomicFile.
@@ -717,7 +717,7 @@ async function copy(src, dst, opts) {
717
717
  * }
718
718
  */
719
719
  function exists(filepath) {
720
- return fs.existsSync(filepath);
720
+ return nodeFs.existsSync(filepath);
721
721
  }
722
722
 
723
723
  /**
@@ -767,16 +767,16 @@ async function lock(filepath, fn, opts) {
767
767
  while (Date.now() < deadline) {
768
768
  try {
769
769
  // O_CREAT | O_EXCL — fails if file exists
770
- fd = fs.openSync(lockPath, "wx", opts.fileMode);
770
+ fd = nodeFs.openSync(lockPath, "wx", opts.fileMode);
771
771
  break;
772
772
  } catch (e) {
773
773
  if (e.code !== "EEXIST") throw e;
774
774
  // Stale lock detection: if the .lock file is older than 5 minutes,
775
775
  // assume the holding process crashed and remove it.
776
776
  try {
777
- var stat = fs.statSync(lockPath);
777
+ var stat = nodeFs.statSync(lockPath);
778
778
  if (Date.now() - stat.mtimeMs > C.TIME.minutes(5)) {
779
- try { fs.unlinkSync(lockPath); }
779
+ try { nodeFs.unlinkSync(lockPath); }
780
780
  catch (uerr) { log.debug("stale-lock unlink failed", { path: lockPath, error: uerr.message }); }
781
781
  continue;
782
782
  }
@@ -791,7 +791,7 @@ async function lock(filepath, fn, opts) {
791
791
  );
792
792
  }
793
793
  try {
794
- fs.writeSync(fd, Buffer.from(JSON.stringify({
794
+ nodeFs.writeSync(fd, Buffer.from(JSON.stringify({
795
795
  pid: process.pid,
796
796
  acquiredAt: Date.now(),
797
797
  }), "utf8"));
@@ -801,9 +801,9 @@ async function lock(filepath, fn, opts) {
801
801
  try {
802
802
  return await fn();
803
803
  } finally {
804
- try { fs.closeSync(fd); }
804
+ try { nodeFs.closeSync(fd); }
805
805
  catch (cerr) { log.debug("lock fd close failed", { error: cerr.message }); }
806
- try { fs.unlinkSync(lockPath); }
806
+ try { nodeFs.unlinkSync(lockPath); }
807
807
  catch (uerr) { log.debug("lock release unlink failed", { path: lockPath, error: uerr.message }); }
808
808
  }
809
809
  }
@@ -848,7 +848,7 @@ function listDir(dir, opts) {
848
848
 
849
849
  var entries;
850
850
  try {
851
- entries = fs.readdirSync(dir);
851
+ entries = nodeFs.readdirSync(dir);
852
852
  } catch (e) {
853
853
  if (missingOk && e.code === "ENOENT") return [];
854
854
  throw new AtomicFileError(
@@ -861,11 +861,11 @@ function listDir(dir, opts) {
861
861
  for (var i = 0; i < entries.length; i++) {
862
862
  var name = entries[i];
863
863
  if (filter && !filter(name)) continue;
864
- var fullPath = path.join(dir, name);
864
+ var fullPath = nodePath.join(dir, name);
865
865
  var entry = { name: name, fullPath: fullPath };
866
866
  if (includeStat) {
867
867
  try {
868
- var stat = fs.statSync(fullPath);
868
+ var stat = nodeFs.statSync(fullPath);
869
869
  entry.mtimeMs = stat.mtimeMs;
870
870
  entry.sizeBytes = stat.size;
871
871
  entry.isDirectory = stat.isDirectory();
package/lib/audit-sign.js CHANGED
@@ -58,8 +58,8 @@
58
58
  * @card
59
59
  * SLH-DSA-SHAKE-256f post-quantum signature for audit-chain checkpoints.
60
60
  */
61
- var fs = require("fs");
62
- var path = require("path");
61
+ var nodeFs = require("fs");
62
+ var nodePath = require("path");
63
63
  var nodeCrypto = require("crypto");
64
64
  var atomicFile = require("./atomic-file");
65
65
  var { sha3Hash } = require("./crypto");
@@ -118,8 +118,8 @@ var log = boot("audit-sign");
118
118
  function resolvePaths(dataDir) {
119
119
  return {
120
120
  dataDir: dataDir,
121
- plaintext: path.join(dataDir, "audit-sign.key"),
122
- sealed: path.join(dataDir, "audit-sign.key.sealed"),
121
+ plaintext: nodePath.join(dataDir, "audit-sign.key"),
122
+ sealed: nodePath.join(dataDir, "audit-sign.key.sealed"),
123
123
  };
124
124
  }
125
125
 
@@ -200,13 +200,13 @@ async function init(opts) {
200
200
  currentMode = mode;
201
201
  paths = resolvePaths(opts.dataDir);
202
202
 
203
- if (!fs.existsSync(paths.dataDir)) fs.mkdirSync(paths.dataDir, { recursive: true });
203
+ if (!nodeFs.existsSync(paths.dataDir)) nodeFs.mkdirSync(paths.dataDir, { recursive: true });
204
204
  // Sweep tmp files from any prior crashed write
205
205
  atomicFile.cleanOrphans(paths.sealed);
206
206
  atomicFile.cleanOrphans(paths.plaintext);
207
207
 
208
- var hasPlaintext = fs.existsSync(paths.plaintext);
209
- var hasSealed = fs.existsSync(paths.sealed);
208
+ var hasPlaintext = nodeFs.existsSync(paths.plaintext);
209
+ var hasSealed = nodeFs.existsSync(paths.sealed);
210
210
  if (hasPlaintext && hasSealed) {
211
211
  throw _err("KEY_FILE_CONFLICT",
212
212
  "both audit-sign.key and audit-sign.key.sealed exist; resolve manually");
@@ -233,7 +233,7 @@ async function init(opts) {
233
233
  }
234
234
 
235
235
  function _initPlaintext() {
236
- if (fs.existsSync(paths.plaintext)) {
236
+ if (nodeFs.existsSync(paths.plaintext)) {
237
237
  var loaded;
238
238
  try { loaded = safeJson.parse(atomicFile.readSync(paths.plaintext), { schema: SIGNING_KEY_SCHEMA }); }
239
239
  catch (e) {
@@ -54,8 +54,8 @@
54
54
  * Operator-side audit-chain inspection / export — verify chain integrity end-to-end, export RFC 8785 canonical-JSON slices, format rows for downstream SIEM (CADF / ISO 19395), and generate tamper-evident compliance-evidence bundles auditors can verify off-line.
55
55
  */
56
56
 
57
- var fs = require("fs");
58
- var path = require("path");
57
+ var nodeFs = require("fs");
58
+ var nodePath = require("path");
59
59
  var pkg = require("../package.json");
60
60
  var atomicFile = require("./atomic-file");
61
61
  var auditChain = require("./audit-chain");
@@ -65,7 +65,7 @@ var backupCrypto = require("./backup/crypto");
65
65
  var clusterStorage = require("./cluster-storage");
66
66
  var lazyRequire = require("./lazy-require");
67
67
  var validateOpts = require("./validate-opts");
68
- var jsonSafe = require("./safe-json");
68
+ var safeJson = require("./safe-json");
69
69
  var { defineClass } = require("./framework-error");
70
70
 
71
71
  var FRAMEWORK_VERSION = (pkg && pkg.version) || "unknown";
@@ -117,7 +117,7 @@ function _requireOutDir(outDir, kind) {
117
117
  throw new AuditToolsError("audit-tools/no-outdir",
118
118
  kind + ": opts.out is required");
119
119
  }
120
- if (fs.existsSync(outDir)) {
120
+ if (nodeFs.existsSync(outDir)) {
121
121
  throw new AuditToolsError("audit-tools/outdir-exists",
122
122
  kind + ": out already exists: " + outDir +
123
123
  " (refusing to overwrite — pick a fresh path)");
@@ -128,7 +128,7 @@ function _requireOutDir(outDir, kind) {
128
128
  // as audit-chain.canonicalize, config-drift._stableStringify, and
129
129
  // pagination._canonicalize for the same input. Pre-v0.6.67 each site
130
130
  // had its own copy of the walk, all carrying the same silent-loss bug
131
- // for Date / Buffer / Map / Set / BigInt / circular refs.
131
+ // for Date / Buffer / Map / Set / BigInt / circular renodeFs.
132
132
  function _canonicalize(value) { return canonicalJson.stringify(value); }
133
133
 
134
134
  // Convert a single audit_log row to its on-disk-canonical JSON shape.
@@ -309,14 +309,14 @@ async function _writeBundle(args) {
309
309
  return JSON.stringify(_rowToWireForm(r));
310
310
  }).join("\n") + "\n";
311
311
  var rowsEnc = await backupCrypto.encryptWithFreshSalt(jsonl, passphrase);
312
- atomicFile.writeSync(path.join(outDir, "rows.enc"), rowsEnc.encrypted, { fileMode: 0o600 });
312
+ atomicFile.writeSync(nodePath.join(outDir, "rows.enc"), rowsEnc.encrypted, { fileMode: 0o600 });
313
313
 
314
314
  // 2. (archive) Encrypt the checkpoint JSON
315
315
  var checkpointSalt = null;
316
316
  if (checkpoint) {
317
317
  var ckptJson = _canonicalize(_rowToWireForm(checkpoint));
318
318
  var ckptEnc = await backupCrypto.encryptWithFreshSalt(ckptJson, passphrase);
319
- atomicFile.writeSync(path.join(outDir, "checkpoint.enc"), ckptEnc.encrypted, { fileMode: 0o600 });
319
+ atomicFile.writeSync(nodePath.join(outDir, "checkpoint.enc"), ckptEnc.encrypted, { fileMode: 0o600 });
320
320
  checkpointSalt = ckptEnc.salt;
321
321
  }
322
322
 
@@ -343,7 +343,7 @@ async function _writeBundle(args) {
343
343
  checksum: {
344
344
  rowsSha3_512: backupCrypto.checksum(rowsEnc.encrypted),
345
345
  checkpointSha3_512: checkpointSalt
346
- ? backupCrypto.checksum(fs.readFileSync(path.join(outDir, "checkpoint.enc")))
346
+ ? backupCrypto.checksum(nodeFs.readFileSync(nodePath.join(outDir, "checkpoint.enc")))
347
347
  : null,
348
348
  },
349
349
  };
@@ -355,7 +355,7 @@ async function _writeBundle(args) {
355
355
  checkpointId: String(checkpoint._id),
356
356
  };
357
357
  }
358
- var manifestPath = path.join(outDir, "manifest.json");
358
+ var manifestPath = nodePath.join(outDir, "manifest.json");
359
359
  atomicFile.writeSync(manifestPath, _canonicalize(manifest), { fileMode: 0o600 });
360
360
  return { manifest: manifest, manifestPath: manifestPath };
361
361
  }
@@ -363,16 +363,16 @@ async function _writeBundle(args) {
363
363
  // ---- Bundle reader ----
364
364
 
365
365
  async function _readBundle(inDir, passphrase) {
366
- if (typeof inDir !== "string" || !fs.existsSync(inDir)) {
366
+ if (typeof inDir !== "string" || !nodeFs.existsSync(inDir)) {
367
367
  throw new AuditToolsError("audit-tools/no-bundle",
368
368
  "bundle directory does not exist: " + inDir);
369
369
  }
370
- var manifestPath = path.join(inDir, "manifest.json");
371
- if (!fs.existsSync(manifestPath)) {
370
+ var manifestPath = nodePath.join(inDir, "manifest.json");
371
+ if (!nodeFs.existsSync(manifestPath)) {
372
372
  throw new AuditToolsError("audit-tools/no-manifest",
373
373
  "manifest.json missing in " + inDir);
374
374
  }
375
- var manifest = jsonSafe.parse(fs.readFileSync(manifestPath, "utf8"));
375
+ var manifest = safeJson.parse(nodeFs.readFileSync(manifestPath, "utf8"));
376
376
  if (!manifest || manifest.format !== BUNDLE_FORMAT) {
377
377
  throw new AuditToolsError("audit-tools/bad-format",
378
378
  "manifest.format is not " + BUNDLE_FORMAT);
@@ -382,12 +382,12 @@ async function _readBundle(inDir, passphrase) {
382
382
  "manifest.kind must be one of " + Object.keys(VALID_KINDS).join(", "));
383
383
  }
384
384
 
385
- var rowsEncPath = path.join(inDir, "rows.enc");
386
- if (!fs.existsSync(rowsEncPath)) {
385
+ var rowsEncPath = nodePath.join(inDir, "rows.enc");
386
+ if (!nodeFs.existsSync(rowsEncPath)) {
387
387
  throw new AuditToolsError("audit-tools/no-rows-blob",
388
388
  "rows.enc missing in " + inDir);
389
389
  }
390
- var rowsEnc = fs.readFileSync(rowsEncPath);
390
+ var rowsEnc = nodeFs.readFileSync(rowsEncPath);
391
391
  if (manifest.checksum && manifest.checksum.rowsSha3_512 &&
392
392
  backupCrypto.checksum(rowsEnc) !== manifest.checksum.rowsSha3_512) {
393
393
  throw new AuditToolsError("audit-tools/rows-checksum-mismatch",
@@ -396,16 +396,16 @@ async function _readBundle(inDir, passphrase) {
396
396
  var rowsPlainBuf = await backupCrypto.decryptWithPassphrase(rowsEnc, passphrase, manifest.salts.rows);
397
397
  var rowsPlain = rowsPlainBuf.toString("utf8");
398
398
  var lines = rowsPlain.split("\n").filter(function (l) { return l.length > 0; });
399
- var rows = lines.map(function (l) { return _wireFormToRow(jsonSafe.parse(l)); });
399
+ var rows = lines.map(function (l) { return _wireFormToRow(safeJson.parse(l)); });
400
400
 
401
401
  var checkpoint = null;
402
402
  if (manifest.kind === KIND_ARCHIVE) {
403
- var ckptPath = path.join(inDir, "checkpoint.enc");
404
- if (!fs.existsSync(ckptPath)) {
403
+ var ckptPath = nodePath.join(inDir, "checkpoint.enc");
404
+ if (!nodeFs.existsSync(ckptPath)) {
405
405
  throw new AuditToolsError("audit-tools/no-checkpoint-blob",
406
406
  "checkpoint.enc missing in " + inDir + " (archive bundles must include the covering checkpoint)");
407
407
  }
408
- var ckptEnc = fs.readFileSync(ckptPath);
408
+ var ckptEnc = nodeFs.readFileSync(ckptPath);
409
409
  if (manifest.checksum && manifest.checksum.checkpointSha3_512 &&
410
410
  backupCrypto.checksum(ckptEnc) !== manifest.checksum.checkpointSha3_512) {
411
411
  throw new AuditToolsError("audit-tools/checkpoint-checksum-mismatch",
@@ -413,7 +413,7 @@ async function _readBundle(inDir, passphrase) {
413
413
  }
414
414
  var ckptPlain = (await backupCrypto.decryptWithPassphrase(ckptEnc, passphrase, manifest.salts.checkpoint))
415
415
  .toString("utf8");
416
- checkpoint = _wireFormToRow(jsonSafe.parse(ckptPlain));
416
+ checkpoint = _wireFormToRow(safeJson.parse(ckptPlain));
417
417
  }
418
418
 
419
419
  return { manifest: manifest, rows: rows, checkpoint: checkpoint };
@@ -956,7 +956,7 @@ function _toCadfOutcome(outcome) {
956
956
  function _toCadfEvent(row) {
957
957
  var meta = null;
958
958
  if (row.metadata) {
959
- try { meta = typeof row.metadata === "string" ? jsonSafe.parse(row.metadata) : row.metadata; }
959
+ try { meta = typeof row.metadata === "string" ? safeJson.parse(row.metadata) : row.metadata; }
960
960
  catch (_e) { meta = { raw: String(row.metadata) }; }
961
961
  }
962
962
  var ev = {
package/lib/audit.js CHANGED
@@ -55,6 +55,7 @@ var cluster = require("./cluster");
55
55
  var clusterStorage = require("./cluster-storage");
56
56
  var { generateToken } = require("./crypto");
57
57
  var cryptoField = require("./crypto-field");
58
+ var safeSql = require("./safe-sql");
58
59
  var dbRoleContext = require("./db-role-context");
59
60
  var handlers = require("./handlers");
60
61
  var { boot } = require("./log");
@@ -1334,37 +1335,48 @@ function bindActor(actorId, opts) {
1334
1335
  */
1335
1336
  function generateActorBindingTriggerSql(opts) {
1336
1337
  opts = opts || {};
1337
- var column = opts.column || "actorUserId";
1338
- var tableName = opts.tableName || "_blamejs_audit_log";
1339
- var allowRoles = Array.isArray(opts.allowRoles) ? opts.allowRoles : [];
1340
- var fnName = "_blamejs_audit_actor_binding_check";
1341
- var trigName = "_blamejs_audit_actor_binding_trig";
1338
+ var columnRaw = opts.column || "actorUserId";
1339
+ var tableNameRaw = opts.tableName || "_blamejs_audit_log";
1340
+ var allowRoles = Array.isArray(opts.allowRoles) ? opts.allowRoles : [];
1341
+ var fnNameRaw = "_blamejs_audit_actor_binding_check";
1342
+ var trigNameRaw = "_blamejs_audit_actor_binding_trig";
1343
+ // Quote-and-validate every identifier through safeSql.quoteIdentifier
1344
+ // so operator-supplied opts.column / opts.tableName / opts.roleMappingFn
1345
+ // can't reach raw concatenation. PostgreSQL + SQLite both use the
1346
+ // double-quote dialect.
1347
+ var qColumn = safeSql.quoteIdentifier(columnRaw, "postgres");
1348
+ var qTable = safeSql.quoteIdentifier(tableNameRaw, "postgres");
1349
+ var qFn = safeSql.quoteIdentifier(fnNameRaw, "postgres");
1350
+ var qTrig = safeSql.quoteIdentifier(trigNameRaw, "postgres");
1351
+ var qRoleMapFn = opts.roleMappingFn
1352
+ ? safeSql.quoteIdentifier(opts.roleMappingFn, "postgres")
1353
+ : null;
1342
1354
  var allowList = allowRoles.length === 0 ? "" :
1343
1355
  " IF current_user IN (" +
1344
1356
  allowRoles.map(function (r) { return "'" + r.replace(/'/g, "''") + "'"; }).join(", ") +
1345
1357
  ") THEN RETURN NEW; END IF;\n";
1346
- var roleMatch = opts.roleMappingFn
1347
- ? " IF " + opts.roleMappingFn + "(NEW.\"" + column + "\") IS DISTINCT FROM current_user THEN\n"
1348
- : " IF NEW.\"" + column + "\" IS DISTINCT FROM current_user THEN\n";
1358
+ var roleMatch = qRoleMapFn
1359
+ ? " IF " + qRoleMapFn + "(NEW." + qColumn + ") IS DISTINCT FROM current_user THEN\n"
1360
+ : " IF NEW." + qColumn + " IS DISTINCT FROM current_user THEN\n";
1349
1361
  var up =
1350
- "CREATE OR REPLACE FUNCTION " + fnName + "() RETURNS trigger AS $$\n" +
1362
+ "CREATE OR REPLACE FUNCTION " + qFn + "() RETURNS trigger AS $$\n" +
1351
1363
  "BEGIN\n" +
1352
1364
  allowList +
1353
1365
  roleMatch +
1354
- " RAISE EXCEPTION 'segregation-of-duties violation: actor=% does not match current_user=%', NEW.\"" + column + "\", current_user\n" +
1366
+ " RAISE EXCEPTION 'segregation-of-duties violation: actor=% does not match current_user=%', NEW." + qColumn + ", current_user\n" +
1355
1367
  " USING ERRCODE = 'P0001';\n" +
1356
1368
  " END IF;\n" +
1357
1369
  " RETURN NEW;\n" +
1358
1370
  "END;\n" +
1359
1371
  "$$ LANGUAGE plpgsql;\n" +
1360
- "DROP TRIGGER IF EXISTS " + trigName + " ON " + tableName + ";\n" +
1361
- "CREATE TRIGGER " + trigName + "\n" +
1362
- " BEFORE INSERT ON " + tableName + "\n" +
1363
- " FOR EACH ROW EXECUTE FUNCTION " + fnName + "();\n";
1372
+ "DROP TRIGGER IF EXISTS " + qTrig + " ON " + qTable + ";\n" +
1373
+ "CREATE TRIGGER " + qTrig + "\n" +
1374
+ " BEFORE INSERT ON " + qTable + "\n" +
1375
+ " FOR EACH ROW EXECUTE FUNCTION " + qFn + "();\n";
1364
1376
  var down =
1365
- "DROP TRIGGER IF EXISTS " + trigName + " ON " + tableName + ";\n" +
1366
- "DROP FUNCTION IF EXISTS " + fnName + "();\n";
1367
- return { up: up, down: down, functionName: fnName, triggerName: trigName };
1377
+ "DROP TRIGGER IF EXISTS " + qTrig + " ON " + qTable + ";\n" +
1378
+ "DROP FUNCTION IF EXISTS " + qFn + "();\n";
1379
+ return { up: up, down: down, functionName: fnNameRaw, triggerName: trigNameRaw };
1368
1380
  }
1369
1381
 
1370
1382
  // Boot-time check operators wire under sox-404 / soc2 posture. Verifies
package/lib/auth/dpop.js CHANGED
@@ -27,7 +27,7 @@
27
27
  */
28
28
 
29
29
  var nodeCrypto = require("crypto");
30
- var blamejsCrypto = require("../crypto");
30
+ var bCrypto = require("../crypto");
31
31
  var safeJson = require("../safe-json");
32
32
  var safeUrl = require("../safe-url");
33
33
  var validateOpts = require("../validate-opts");
@@ -417,7 +417,7 @@ async function verify(proof, opts) {
417
417
  // Compute thumbprint for downstream binding (jkt → access-token cnf claim)
418
418
  var jkt = thumbprint(header.jwk);
419
419
  if (typeof opts.expectedThumbprint === "string" && opts.expectedThumbprint.length > 0) {
420
- if (!blamejsCrypto.timingSafeEqual(jkt, opts.expectedThumbprint)) {
420
+ if (!bCrypto.timingSafeEqual(jkt, opts.expectedThumbprint)) {
421
421
  throw new AuthError("auth-dpop/thumbprint-mismatch",
422
422
  "proof key thumbprint does not match expected");
423
423
  }
@@ -461,7 +461,7 @@ async function verify(proof, opts) {
461
461
  throw new AuthError("auth-dpop/missing-ath",
462
462
  "accessToken supplied but proof has no ath claim");
463
463
  }
464
- if (!blamejsCrypto.timingSafeEqual(payload.ath, expectedAth)) {
464
+ if (!bCrypto.timingSafeEqual(payload.ath, expectedAth)) {
465
465
  throw new AuthError("auth-dpop/ath-mismatch",
466
466
  "payload.ath does not match SHA-256 of access token");
467
467
  }
@@ -61,14 +61,14 @@
61
61
  */
62
62
 
63
63
  var nodeCrypto = require("node:crypto");
64
- var blamejsCrypto = require("../crypto");
64
+ var bCrypto = require("../crypto");
65
65
  var safeBuffer = require("../safe-buffer");
66
66
  var safeJson = require("../safe-json");
67
67
  var validateOpts = require("../validate-opts");
68
68
 
69
69
  function _timingSafeEqStr(a, b) {
70
70
  if (typeof a !== "string" || typeof b !== "string") return false;
71
- return blamejsCrypto.timingSafeEqual(a, b);
71
+ return bCrypto.timingSafeEqual(a, b);
72
72
  }
73
73
  var disclosure = require("./sd-jwt-vc-disclosure");
74
74
  var sdJwtVcIssuer = require("./sd-jwt-vc-issuer");