@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
@@ -184,12 +184,12 @@
184
184
  * point. Operator wires their scanner of choice.
185
185
  */
186
186
 
187
- var fs = require("node:fs");
188
- var path = require("node:path");
189
- var stream = require("node:stream");
187
+ var nodeFs = require("node:fs");
188
+ var nodePath = require("node:path");
189
+ var nodeStream = require("node:stream");
190
190
  var atomicFile = require("./atomic-file");
191
191
  var C = require("./constants");
192
- var crypto = require("./crypto");
192
+ var bCrypto = require("./crypto");
193
193
  var gateContract = require("./gate-contract");
194
194
  var lazyRequire = require("./lazy-require");
195
195
  var numericBounds = require("./numeric-bounds");
@@ -250,7 +250,7 @@ function _validateUploadId(id) {
250
250
  function _validateCreateOpts(opts) {
251
251
  validateOpts.requireObject(opts, "fileUpload.create", FileUploadError);
252
252
  validateOpts.requireNonEmptyString(opts.stagingDir, "fileUpload.create: stagingDir", FileUploadError);
253
- if (!path.isAbsolute(opts.stagingDir)) {
253
+ if (!nodePath.isAbsolute(opts.stagingDir)) {
254
254
  throw _err("BAD_OPT", "fileUpload.create: stagingDir must be an absolute path, got " +
255
255
  JSON.stringify(opts.stagingDir));
256
256
  }
@@ -472,10 +472,10 @@ function create(opts) {
472
472
  // staging files.
473
473
  atomicFile.ensureDir(stagingDir, 0o700);
474
474
 
475
- function _uploadDir(uploadId) { return path.join(stagingDir, uploadId); }
476
- function _chunkPath(uploadId, index) { return path.join(_uploadDir(uploadId), String(index)); }
477
- function _receivedPath(uploadId) { return path.join(_uploadDir(uploadId), "_received.json"); }
478
- function _metaPath(uploadId) { return path.join(_uploadDir(uploadId), "_meta.json"); }
475
+ function _uploadDir(uploadId) { return nodePath.join(stagingDir, uploadId); }
476
+ function _chunkPath(uploadId, index) { return nodePath.join(_uploadDir(uploadId), String(index)); }
477
+ function _receivedPath(uploadId) { return nodePath.join(_uploadDir(uploadId), "_received.json"); }
478
+ function _metaPath(uploadId) { return nodePath.join(_uploadDir(uploadId), "_meta.json"); }
479
479
 
480
480
  function _checkPermission(action, actor) {
481
481
  if (!permissions) return;
@@ -491,7 +491,7 @@ function create(opts) {
491
491
 
492
492
  function _readReceivedIndices(uploadId) {
493
493
  var p = _receivedPath(uploadId);
494
- if (!fs.existsSync(p)) return [];
494
+ if (!nodeFs.existsSync(p)) return [];
495
495
  try {
496
496
  var raw = atomicFile.readSync(p, { maxBytes: SIDECAR_MAX_BYTES });
497
497
  var parsed = safeJson.parse(raw.toString("utf8"));
@@ -504,7 +504,7 @@ function create(opts) {
504
504
 
505
505
  function _readMeta(uploadId) {
506
506
  var p = _metaPath(uploadId);
507
- if (!fs.existsSync(p)) return null;
507
+ if (!nodeFs.existsSync(p)) return null;
508
508
  try {
509
509
  var raw = atomicFile.readSync(p, { maxBytes: SIDECAR_MAX_BYTES });
510
510
  return safeJson.parse(raw.toString("utf8"));
@@ -521,7 +521,7 @@ function create(opts) {
521
521
  }
522
522
 
523
523
  function _enumerateUploads() {
524
- if (!fs.existsSync(stagingDir)) return [];
524
+ if (!nodeFs.existsSync(stagingDir)) return [];
525
525
  var entries;
526
526
  try { entries = atomicFile.listDir(stagingDir, { includeStat: true }); }
527
527
  catch (_e) { return []; }
@@ -578,7 +578,7 @@ function create(opts) {
578
578
  }
579
579
 
580
580
  // Refuse re-init of an existing upload (caller-side bug).
581
- if (fs.existsSync(_uploadDir(uploadId))) {
581
+ if (nodeFs.existsSync(_uploadDir(uploadId))) {
582
582
  throw _err("UPLOAD_EXISTS",
583
583
  "fileUpload.init: upload '" + uploadId + "' already exists; cancel or finalize first");
584
584
  }
@@ -675,8 +675,8 @@ function create(opts) {
675
675
  }
676
676
 
677
677
  // Verify chunk hash matches the supplied header.
678
- var actualHex = crypto.sha3Hash(body);
679
- if (!crypto.timingSafeEqual(actualHex, sha3Hex)) {
678
+ var actualHex = bCrypto.sha3Hash(body);
679
+ if (!bCrypto.timingSafeEqual(actualHex, sha3Hex)) {
680
680
  _emitObs("fileUpload.chunk_hash_mismatch", 1);
681
681
  _emitAudit("fileUpload.chunk_received", {
682
682
  actor: requestHelpers.extractActorContext(actor),
@@ -718,9 +718,9 @@ function create(opts) {
718
718
  // Idempotent re-PUT: if this index is already received with a
719
719
  // matching body, no-op. Different body = caller bug.
720
720
  var p = _chunkPath(uploadId, index);
721
- if (fs.existsSync(p)) {
721
+ if (nodeFs.existsSync(p)) {
722
722
  var existing = atomicFile.readSync(p, { maxBytes: maxChunkBytes });
723
- if (crypto.timingSafeEqual(crypto.sha3Hash(existing), sha3Hex)) {
723
+ if (bCrypto.timingSafeEqual(bCrypto.sha3Hash(existing), sha3Hex)) {
724
724
  return {
725
725
  received: _readReceivedIndices(uploadId).length,
726
726
  totalBytesAccepted: meta.totalBytesAccepted,
@@ -744,8 +744,8 @@ function create(opts) {
744
744
  meta.lastChunkAt = clock();
745
745
  meta.totalBytesAccepted = (meta.totalBytesAccepted || 0) + body.length;
746
746
  if (meta.totalBytesAccepted > maxFileBytes) {
747
- // Reclaim staging — the upload exceeded the cap mid-stream.
748
- try { fs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
747
+ // Reclaim staging — the upload exceeded the cap mid-nodeStream.
748
+ try { nodeFs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
749
749
  catch (_e) { /* purgeIncomplete will reclaim */ }
750
750
  _emitObs("fileUpload.file_too_large", 1);
751
751
  throw _err("FILE_TOO_LARGE",
@@ -824,13 +824,13 @@ function create(opts) {
824
824
  SHA3_512_HEX_LENGTH + " chars)");
825
825
  }
826
826
  var chunkPath = _chunkPath(uploadId, ck.index);
827
- if (!fs.existsSync(chunkPath)) {
827
+ if (!nodeFs.existsSync(chunkPath)) {
828
828
  throw _err("MISSING_CHUNK",
829
829
  "fileUpload.finalize: chunk " + ck.index + " missing from staging");
830
830
  }
831
831
  var chunkBody = atomicFile.readSync(chunkPath, { maxBytes: maxChunkBytes });
832
- var actualChunkHex = crypto.sha3Hash(chunkBody);
833
- if (!crypto.timingSafeEqual(actualChunkHex, ck.sha3)) {
832
+ var actualChunkHex = bCrypto.sha3Hash(chunkBody);
833
+ if (!bCrypto.timingSafeEqual(actualChunkHex, ck.sha3)) {
834
834
  throw _err("CHUNK_HASH_MISMATCH",
835
835
  "fileUpload.finalize: chunk " + ck.index +
836
836
  " on-disk SHA3-512 doesn't match manifest");
@@ -849,7 +849,7 @@ function create(opts) {
849
849
  " bytes, manifest declares " + manifest.totalBytes);
850
850
  }
851
851
  var totalHashHex = hasher.digest("hex");
852
- if (!crypto.timingSafeEqual(totalHashHex, manifest.sha3)) {
852
+ if (!bCrypto.timingSafeEqual(totalHashHex, manifest.sha3)) {
853
853
  throw _err("MANIFEST_HASH_MISMATCH",
854
854
  "fileUpload.finalize: reassembled SHA3-512 doesn't match manifest.sha3");
855
855
  }
@@ -911,13 +911,13 @@ function create(opts) {
911
911
  // onFinalize reads through to wherever they're piping.
912
912
  async function* generate() {
913
913
  for (var i = 0; i < paths.length; i += 1) {
914
- var fh = fs.createReadStream(paths[i]);
914
+ var fh = nodeFs.createReadStream(paths[i]);
915
915
  for await (var chunk of fh) {
916
916
  yield chunk;
917
917
  }
918
918
  }
919
919
  }
920
- return stream.Readable.from(generate(), { objectMode: false });
920
+ return nodeStream.Readable.from(generate(), { objectMode: false });
921
921
  }
922
922
 
923
923
  async function finalize(callerOpts) {
@@ -1034,7 +1034,7 @@ function create(opts) {
1034
1034
  }
1035
1035
  }
1036
1036
  if (contentSafety) {
1037
- var safetyExt = path.extname(filename).toLowerCase();
1037
+ var safetyExt = nodePath.extname(filename).toLowerCase();
1038
1038
  var safetyGate = contentSafety[safetyExt];
1039
1039
  if (safetyGate && typeof safetyGate.check === "function" && bodyBuffer) {
1040
1040
  var safetyDecision;
@@ -1109,7 +1109,7 @@ function create(opts) {
1109
1109
  }
1110
1110
 
1111
1111
  // Cleanup staging on success.
1112
- try { fs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
1112
+ try { nodeFs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
1113
1113
  catch (_e) { /* best-effort */ }
1114
1114
 
1115
1115
  _emitObs("fileUpload.finalize_success", 1);
@@ -1175,7 +1175,7 @@ function create(opts) {
1175
1175
  _checkPermission("cancel", callerOpts.actor);
1176
1176
  var meta = _readMeta(uploadId);
1177
1177
  if (!meta) return { ok: false, uploadId: uploadId, reason: "not-found" };
1178
- try { fs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
1178
+ try { nodeFs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
1179
1179
  catch (_e) { /* best-effort */ }
1180
1180
  _emitObs("fileUpload.cancelled", 1);
1181
1181
  _emitAudit("fileUpload.cancelled", {
@@ -1190,7 +1190,7 @@ function create(opts) {
1190
1190
  // ---- purgeIncomplete ----
1191
1191
 
1192
1192
  function purgeIncomplete() {
1193
- if (!fs.existsSync(stagingDir)) return { purged: 0, ids: [] };
1193
+ if (!nodeFs.existsSync(stagingDir)) return { purged: 0, ids: [] };
1194
1194
  var now = clock();
1195
1195
  var entries;
1196
1196
  try { entries = atomicFile.listDir(stagingDir, { includeStat: true }); }
@@ -1211,7 +1211,7 @@ function create(opts) {
1211
1211
  }
1212
1212
  if (!purgeReason) continue;
1213
1213
  try {
1214
- fs.rmSync(e.fullPath, { recursive: true, force: true });
1214
+ nodeFs.rmSync(e.fullPath, { recursive: true, force: true });
1215
1215
  purged.push({ id: e.name, reason: purgeReason });
1216
1216
  } catch (_e2) { /* best-effort; will retry */ }
1217
1217
  }
@@ -36,7 +36,7 @@
36
36
  * provider.kind -> "local-file" | "memory" | "environment" | <operator-defined>
37
37
  */
38
38
 
39
- var fs = require("fs");
39
+ var nodeFs = require("fs");
40
40
  var validateOpts = require("./validate-opts");
41
41
  var lazyRequire = require("./lazy-require");
42
42
  var safeJson = require("./safe-json");
@@ -128,7 +128,7 @@ function localFile(opts) {
128
128
  validateOpts.requireNonEmptyString(opts.path,
129
129
  "providers.localFile: path", FlagError, "flag/bad-provider");
130
130
  var raw;
131
- try { raw = fs.readFileSync(opts.path, "utf8"); }
131
+ try { raw = nodeFs.readFileSync(opts.path, "utf8"); }
132
132
  catch (e) {
133
133
  throw new FlagError("flag/bad-provider",
134
134
  "providers.localFile: cannot read file " + JSON.stringify(opts.path) +
@@ -152,9 +152,9 @@ function localFile(opts) {
152
152
  provider._path = opts.path;
153
153
  if (opts.watch === true) {
154
154
  try {
155
- fs.watch(opts.path, { persistent: false }, function () {
155
+ nodeFs.watch(opts.path, { persistent: false }, function () {
156
156
  try {
157
- var nextRaw = fs.readFileSync(opts.path, "utf8");
157
+ var nextRaw = nodeFs.readFileSync(opts.path, "utf8");
158
158
  var nextParsed = safeJson.parse(nextRaw, { maxBytes: C.BYTES.mib(1) });
159
159
  if (nextParsed && nextParsed.flags) {
160
160
  for (var k in nextParsed.flags) {
@@ -46,7 +46,7 @@
46
46
  */
47
47
 
48
48
  var C = require("./constants");
49
- var crypto = require("./crypto");
49
+ var bCrypto = require("./crypto");
50
50
  var lazyRequire = require("./lazy-require");
51
51
  var safeAsync = require("./safe-async");
52
52
  var validateOpts = require("./validate-opts");
@@ -341,13 +341,13 @@ function defineGate(opts) {
341
341
  async function check(ctx) {
342
342
  var startedAt = Date.now();
343
343
  ctx = ctx || {};
344
- if (!ctx.forensicId) ctx.forensicId = crypto.generateToken(FORENSIC_ID_BYTES);
344
+ if (!ctx.forensicId) ctx.forensicId = bCrypto.generateToken(FORENSIC_ID_BYTES);
345
345
 
346
346
  // Decision cache lookup (memoize per-forensicHash).
347
347
  var bytes = ctx.bytes;
348
348
  var forensicHash = bytes && Buffer.isBuffer(bytes)
349
- ? crypto.sha3Hash(bytes, "hex")
350
- : (typeof bytes === "string" ? crypto.sha3Hash(Buffer.from(bytes, "utf8"), "hex") : null);
349
+ ? bCrypto.sha3Hash(bytes, "hex")
350
+ : (typeof bytes === "string" ? bCrypto.sha3Hash(Buffer.from(bytes, "utf8"), "hex") : null);
351
351
  var cacheKey = forensicHash ? (opts.name + ":" + ruleHash + ":" + forensicHash) : null;
352
352
  if (decisionCache && cacheKey) {
353
353
  try {
@@ -555,7 +555,7 @@ function _build(partial) {
555
555
  }
556
556
 
557
557
  function _hashFingerprint(obj) {
558
- return crypto.sha3Hash(JSON.stringify(obj), "hex").slice(0, FINGERPRINT_HEX_LENGTH);
558
+ return bCrypto.sha3Hash(JSON.stringify(obj), "hex").slice(0, FINGERPRINT_HEX_LENGTH);
559
559
  }
560
560
 
561
561
  // ---- Host-side helpers ----
@@ -21,9 +21,9 @@
21
21
  * GraphQL federation gateway with SDL trust boundary, sub-graph health, subgraph SDL signing, query plan caps.
22
22
  */
23
23
 
24
- var crypto = require("crypto");
24
+ var bCrypto = require("./crypto");
25
25
  var C = require("./constants");
26
- var nb = require("./numeric-bounds");
26
+ var numericBounds = require("./numeric-bounds");
27
27
  var safeJson = require("./safe-json");
28
28
  var safeBuffer = require("./safe-buffer");
29
29
  var requestHelpers = require("./request-helpers");
@@ -73,10 +73,7 @@ function _readBearer(req) {
73
73
 
74
74
  function _timingSafeEqual(a, b) {
75
75
  if (typeof a !== "string" || typeof b !== "string") return false;
76
- var ab = Buffer.from(a, "utf8");
77
- var bb = Buffer.from(b, "utf8");
78
- if (ab.length !== bb.length) return false;
79
- return crypto.timingSafeEqual(ab, bb);
76
+ return bCrypto.timingSafeEqual(a, b);
80
77
  }
81
78
 
82
79
  function _readBody(req, errorClass) {
@@ -141,7 +138,7 @@ function guardSdl(opts) {
141
138
  }
142
139
  var nonceStore = opts.nonceStore && typeof opts.nonceStore.has === "function" &&
143
140
  typeof opts.nonceStore.remember === "function" ? opts.nonceStore : null;
144
- nb.requirePositiveFiniteIntIfPresent(opts.nonceTtlMs, "graphqlFederation.guardSdl: opts.nonceTtlMs", errorClass, "BAD_TTL");
141
+ numericBounds.requirePositiveFiniteIntIfPresent(opts.nonceTtlMs, "graphqlFederation.guardSdl: opts.nonceTtlMs", errorClass, "BAD_TTL");
145
142
  var nonceTtlMs = opts.nonceTtlMs || C.TIME.minutes(5);
146
143
  var auditOn = opts.audit !== false;
147
144
 
package/lib/honeytoken.js CHANGED
@@ -30,7 +30,7 @@
30
30
  * Framework-seeded canary records that trigger an audit alert on read; integrates with sealed columns.
31
31
  */
32
32
 
33
- var crypto = require("./crypto");
33
+ var bCrypto = require("./crypto");
34
34
  var lazyRequire = require("./lazy-require");
35
35
  var validateOpts = require("./validate-opts");
36
36
  var { defineClass } = require("./framework-error");
@@ -40,10 +40,10 @@ var audit = lazyRequire(function () { return require("./audit"); });
40
40
  var HoneytokenError = defineClass("HoneytokenError", { alwaysPermanent: true });
41
41
 
42
42
  var KINDS = Object.freeze({
43
- apiKey: function () { return "bk_canary_" + crypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte (128-bit) canary entropy
44
- session: function () { return "bks_canary_" + crypto.generateToken(24); }, // allow:raw-byte-literal — 24-byte (192-bit) canary entropy
45
- url: function () { return "/admin/canary-" + crypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte canary entropy
46
- rowId: function () { return "ht_canary_" + crypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte canary entropy
43
+ apiKey: function () { return "bk_canary_" + bCrypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte (128-bit) canary entropy
44
+ session: function () { return "bks_canary_" + bCrypto.generateToken(24); }, // allow:raw-byte-literal — 24-byte (192-bit) canary entropy
45
+ url: function () { return "/admin/canary-" + bCrypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte canary entropy
46
+ rowId: function () { return "ht_canary_" + bCrypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte canary entropy
47
47
  });
48
48
 
49
49
  /**
@@ -102,7 +102,7 @@ function create(opts) {
102
102
  "(supported: " + Object.keys(KINDS).join(", ") + ")");
103
103
  }
104
104
  var value = KINDS[kind]();
105
- var id = "ht_" + crypto.generateToken(8); // allow:raw-byte-literal — 8-byte registry id
105
+ var id = "ht_" + bCrypto.generateToken(8); // allow:raw-byte-literal — 8-byte registry id
106
106
  var record = Object.freeze({
107
107
  id: id,
108
108
  kind: kind,
@@ -60,8 +60,8 @@
60
60
  * }
61
61
  */
62
62
 
63
- var fs = require("node:fs");
64
- var path = require("node:path");
63
+ var nodeFs = require("node:fs");
64
+ var nodePath = require("node:path");
65
65
  var C = require("./constants");
66
66
  var numericBounds = require("./numeric-bounds");
67
67
  var safeAsync = require("./safe-async");
@@ -181,7 +181,7 @@ function create(opts) {
181
181
  filePath = opts.file;
182
182
  // Refuse relative paths so a process running in a different cwd
183
183
  // doesn't accidentally serialize to a sibling directory.
184
- if (!path.isAbsolute(filePath)) {
184
+ if (!nodePath.isAbsolute(filePath)) {
185
185
  throw _err("BAD_OPT",
186
186
  "cookieJar.create: opts.file must be an absolute path, got " + JSON.stringify(filePath));
187
187
  }
@@ -441,7 +441,7 @@ function create(opts) {
441
441
  var rows = getAll();
442
442
  var serialized = JSON.stringify(rows);
443
443
  var blob = vault ? vault.seal(serialized) : serialized;
444
- fs.writeFileSync(filePath, blob);
444
+ nodeFs.writeFileSync(filePath, blob);
445
445
  }
446
446
  var flushScheduler = safeAsync.makeScheduledFlush(flushDebounceMs, function () {
447
447
  if (!filePath) return;
@@ -477,9 +477,9 @@ function create(opts) {
477
477
  // Persist file may be operator-tampered (or vault-sealed) — route through
478
478
  // safeJson with an explicit byte cap so a maliciously-large file can't
479
479
  // OOM the process before the parse fails.
480
- if (filePath && fs.existsSync(filePath)) {
480
+ if (filePath && nodeFs.existsSync(filePath)) {
481
481
  try {
482
- var raw = fs.readFileSync(filePath, "utf8");
482
+ var raw = nodeFs.readFileSync(filePath, "utf8");
483
483
  var serialized = vault ? vault.unseal(raw) : raw;
484
484
  if (serialized && serialized.length > 0) {
485
485
  var rows = safeJson.parse(serialized, { maxBytes: C.BYTES.mib(16) });
@@ -35,7 +35,7 @@
35
35
  * Outbound HTTP client with SSRF gate, retry, circuit breaker, wall-clock + idle timeouts, AbortSignal propagation, connection pooling, streaming, and ALPN-negotiated HTTP/2.
36
36
  */
37
37
 
38
- var fs = require("fs");
38
+ var nodeFs = require("fs");
39
39
  var http = require("http");
40
40
  var https = require("https");
41
41
  var http2 = require("http2");
@@ -46,7 +46,7 @@ var streamPromises = require("node:stream/promises");
46
46
  var { URL } = require("url");
47
47
  var atomicFile = require("./atomic-file");
48
48
  var C = require("./constants");
49
- var crypto = require("./crypto");
49
+ var bCrypto = require("./crypto");
50
50
  var pqcAgent = require("./pqc-agent");
51
51
  var safeAsync = require("./safe-async");
52
52
  var safeBuffer = require("./safe-buffer");
@@ -461,7 +461,7 @@ function _attachJarCookie(headers, jar, url) {
461
461
  // emits boundary headers + content + CRLF in order. Avoids the
462
462
  // Buffer.concat() OOM class on large uploads. contentLength is
463
463
  // a finite number when every source's size is statically
464
- // resolvable (Buffer length, fs.statSync().size, opts.size on
464
+ // resolvable (Buffer length, nodeFs.statSync().size, opts.size on
465
465
  // a stream entry); null otherwise — caller falls back to
466
466
  // chunked transfer.
467
467
  //
@@ -474,9 +474,9 @@ function _attachJarCookie(headers, jar, url) {
474
474
  // `filename` and `contentType` apply to all three shapes; for
475
475
  // `filePath` entries, `filename` defaults to path.basename(filePath).
476
476
  function _buildMultipartBody(spec) {
477
- var boundary = "----blamejs-mp-" + crypto.generateToken(C.BYTES.bytes(16));
477
+ var boundary = "----blamejs-mp-" + bCrypto.generateToken(C.BYTES.bytes(16));
478
478
  var CRLF = "\r\n";
479
- var fs = require("fs"); // allow:inline-require — only on multipart paths that touch the filesystem
479
+ var nodeFs = require("fs"); // allow:inline-require — only on multipart paths that touch the filesystem
480
480
  var path = require("path"); // allow:inline-require — same
481
481
  var nodeStream = require("stream"); // allow:inline-require — Readable subclass only when streaming
482
482
 
@@ -558,7 +558,7 @@ function _buildMultipartBody(spec) {
558
558
  } else if (hasFilePath) {
559
559
  anyStreaming = true;
560
560
  var st;
561
- try { st = fs.statSync(file.filePath); }
561
+ try { st = nodeFs.statSync(file.filePath); }
562
562
  catch (e) { throw new Error("multipart: file.filePath not readable: " + e.message); }
563
563
  if (!st.isFile()) throw new Error("multipart: file.filePath is not a regular file");
564
564
  _addEntry(head, { kind: "filePath", filePath: file.filePath, size: st.size });
@@ -613,7 +613,7 @@ function _buildMultipartBody(spec) {
613
613
  if (entry.source.kind === "buffer") {
614
614
  yield entry.source.buf;
615
615
  } else if (entry.source.kind === "filePath") {
616
- var rs = fs.createReadStream(entry.source.filePath);
616
+ var rs = nodeFs.createReadStream(entry.source.filePath);
617
617
  try {
618
618
  for await (var chunk of rs) yield chunk;
619
619
  } finally {
@@ -1699,7 +1699,7 @@ function _requestH2(transport, u, opts) {
1699
1699
  //
1700
1700
  // uploadMultipartStream — POST a file body via multipart/form-data
1701
1701
  // without buffering. Streams from disk through the request body using
1702
- // `fs.createReadStream` + `node:stream/promises` pipeline.
1702
+ // `nodeFs.createReadStream` + `node:stream/promises` pipeline.
1703
1703
  //
1704
1704
  // Both compose through `request()` (responseMode: "stream") so safeUrl,
1705
1705
  // ssrfGuard, allowedHosts, network-proxy, audit-on-host-deny, and the
@@ -1799,7 +1799,7 @@ async function downloadStream(opts) {
1799
1799
  _validateDownloadOpts(opts);
1800
1800
  var alg = opts.hash || DEFAULT_DOWNLOAD_HASH_ALG;
1801
1801
  var dest = opts.dest;
1802
- var tmpPath = dest + ".tmp-" + crypto.generateToken(C.BYTES.bytes(8));
1802
+ var tmpPath = dest + ".tmp-" + bCrypto.generateToken(C.BYTES.bytes(8));
1803
1803
  var dir = nodePath.dirname(dest);
1804
1804
 
1805
1805
  atomicFile.ensureDir(dir);
@@ -1857,13 +1857,13 @@ async function downloadStream(opts) {
1857
1857
  });
1858
1858
  counter.bytesWritten = 0;
1859
1859
 
1860
- var fileStream = fs.createWriteStream(tmpPath, { mode: DEFAULT_DOWNLOAD_FILE_MODE, flags: "w" });
1860
+ var fileStream = nodeFs.createWriteStream(tmpPath, { mode: DEFAULT_DOWNLOAD_FILE_MODE, flags: "w" });
1861
1861
 
1862
1862
  try {
1863
1863
  await streamPromises.pipeline(res.body, counter, fileStream);
1864
1864
  } catch (e) {
1865
1865
  // Pipeline failure → tmp may be partially written. Remove + audit.
1866
- try { fs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
1866
+ try { nodeFs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
1867
1867
  _emitAudit(opts, "system.httpclient.download_stream.refused", "denied", {
1868
1868
  reason: "pipeline-failed", message: e.message, code: e.code,
1869
1869
  });
@@ -1876,15 +1876,15 @@ async function downloadStream(opts) {
1876
1876
  // across platforms but matches the discipline of the rest of the
1877
1877
  // framework's atomic-write paths.
1878
1878
  try {
1879
- var fd = fs.openSync(tmpPath, "r+");
1880
- try { atomicFile.fsync(fd); } finally { try { fs.closeSync(fd); } catch (_c) { /* best-effort fd close */ } }
1879
+ var fd = nodeFs.openSync(tmpPath, "r+");
1880
+ try { atomicFile.fsync(fd); } finally { try { nodeFs.closeSync(fd); } catch (_c) { /* best-effort fd close */ } }
1881
1881
  } catch (_fe) { /* fsync best-effort */ }
1882
1882
 
1883
1883
  var actualHex = hasher.digest("hex");
1884
1884
  if (typeof opts.expected === "string" && opts.expected.length > 0) {
1885
1885
  var expected = opts.expected.toLowerCase();
1886
1886
  if (actualHex.toLowerCase() !== expected) {
1887
- try { fs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
1887
+ try { nodeFs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
1888
1888
  _emitAudit(opts, "system.httpclient.download_stream.refused", "denied", {
1889
1889
  reason: "hash-mismatch", alg: alg, expected: expected, actual: actualHex,
1890
1890
  statusCode: res.statusCode, bytesWritten: counter.bytesWritten,
@@ -1897,10 +1897,10 @@ async function downloadStream(opts) {
1897
1897
 
1898
1898
  // Atomic rename + dir fsync.
1899
1899
  try {
1900
- fs.renameSync(tmpPath, dest);
1900
+ nodeFs.renameSync(tmpPath, dest);
1901
1901
  atomicFile.fsyncDir(dir);
1902
1902
  } catch (e) {
1903
- try { fs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
1903
+ try { nodeFs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
1904
1904
  _emitAudit(opts, "system.httpclient.download_stream.refused", "denied", {
1905
1905
  reason: "rename-failed", message: e.message,
1906
1906
  });
@@ -1959,7 +1959,7 @@ function _validateUploadOpts(opts) {
1959
1959
  *
1960
1960
  * POSTs a file body via `multipart/form-data` without buffering the
1961
1961
  * file in memory. Streams from disk through the request body using
1962
- * `fs.createReadStream` + `node:stream/promises` pipeline. Throws
1962
+ * `nodeFs.createReadStream` + `node:stream/promises` pipeline. Throws
1963
1963
  * `httpclient/missing-file` when `opts.file.path` doesn't exist or
1964
1964
  * isn't a regular file. Composes through `request()` so SSRF gating,
1965
1965
  * proxy routing, and the per-origin transport cache apply unchanged.
@@ -1989,7 +1989,7 @@ async function uploadMultipartStream(opts) {
1989
1989
 
1990
1990
  var filePath = opts.file.path;
1991
1991
  var st;
1992
- try { st = fs.statSync(filePath); }
1992
+ try { st = nodeFs.statSync(filePath); }
1993
1993
  catch (e) {
1994
1994
  _emitAudit(opts, "system.httpclient.upload_stream.refused", "denied", {
1995
1995
  reason: "missing-file", path: filePath, message: e.message,
package/lib/i18n.js CHANGED
@@ -57,8 +57,8 @@
57
57
  * ICU MessageFormat + CLDR Plural Rules + locale-aware Intl formatters with translation lookup.
58
58
  */
59
59
 
60
- var fs = require("node:fs");
61
- var path = require("node:path");
60
+ var nodeFs = require("node:fs");
61
+ var nodePath = require("node:path");
62
62
  var lazyRequire = require("./lazy-require");
63
63
  var requestHelpers = require("./request-helpers");
64
64
  var safeJson = require("./safe-json");
@@ -227,13 +227,13 @@ function _loadFromDir(dir, locales) {
227
227
  var out = {};
228
228
  for (var i = 0; i < locales.length; i++) {
229
229
  var locale = locales[i];
230
- var filePath = path.join(dir, locale + ".json");
231
- if (!fs.existsSync(filePath)) {
230
+ var filePath = nodePath.join(dir, locale + ".json");
231
+ if (!nodeFs.existsSync(filePath)) {
232
232
  throw _err("LOAD_FAILED",
233
233
  "i18n: translations file not found for locale '" + locale + "': " + filePath);
234
234
  }
235
235
  var raw;
236
- try { raw = fs.readFileSync(filePath, "utf8"); }
236
+ try { raw = nodeFs.readFileSync(filePath, "utf8"); }
237
237
  catch (e) {
238
238
  throw _err("LOAD_FAILED",
239
239
  "i18n: failed to read '" + filePath + "': " + ((e && e.message) || String(e)));
package/lib/inbox.js CHANGED
@@ -143,7 +143,13 @@ function create(opts) {
143
143
  _validateTableName(opts.table);
144
144
 
145
145
  var externalDb = opts.externalDb;
146
- var table = opts.table;
146
+ var tableRaw = opts.table;
147
+ // Identifiers reach SQL through safeSql.quoteIdentifier — runs
148
+ // validateIdentifier internally + emits the dialect-correct quoted
149
+ // form. sqlite + postgres both use the double-quote dialect (per
150
+ // lib/safe-sql.js), so one quoted form serves both inbox paths.
151
+ var qTable = safeSql.quoteIdentifier(tableRaw, "sqlite");
152
+ var qIndex = safeSql.quoteIdentifier(tableRaw + "_received_at_idx", "sqlite");
147
153
  var retentionDays = (typeof opts.retentionDays === "number" && opts.retentionDays > 0) // allow:numeric-opt-Infinity
148
154
  ? opts.retentionDays : 30; // allow:raw-byte-literal — default retention days
149
155
  var auditOn = opts.audit !== false;
@@ -226,7 +232,7 @@ function create(opts) {
226
232
 
227
233
  if (dialect === "postgres") {
228
234
  var rs = await txn.query(
229
- "INSERT INTO " + table +
235
+ "INSERT INTO " + qTable +
230
236
  " (message_id, source, received_at, metadata_json) " +
231
237
  " VALUES ($1, $2, " + nowExpr + ", $3::jsonb) " +
232
238
  " ON CONFLICT (source, message_id) DO NOTHING " +
@@ -248,7 +254,7 @@ function create(opts) {
248
254
  // that the framework can't prevent. RETURNING 1 collapses both
249
255
  // round-trips into one and removes the changes() dependency.
250
256
  var sqlInsert = await txn.query(
251
- "INSERT OR IGNORE INTO " + table +
257
+ "INSERT OR IGNORE INTO " + qTable +
252
258
  " (message_id, source, received_at, metadata_json) " +
253
259
  " VALUES (?, ?, " + nowExpr + ", ?) RETURNING 1",
254
260
  [receiveOpts.messageId, receiveOpts.source, metaJson]);
@@ -268,7 +274,7 @@ function create(opts) {
268
274
  _validateReceiveOpts(receiveOpts, "markProcessed");
269
275
  var nowExpr = _utcNowExpr(externalDb);
270
276
  var dialect = (externalDb.dialect === "postgres") ? "postgres" : "sqlite";
271
- var sql = "UPDATE " + table +
277
+ var sql = "UPDATE " + qTable +
272
278
  " SET processed_at = " + nowExpr +
273
279
  " WHERE source = " + (dialect === "postgres" ? "$1" : "?") +
274
280
  " AND message_id = " + (dialect === "postgres" ? "$2" : "?");
@@ -318,7 +324,7 @@ function create(opts) {
318
324
  var dialect = (xdb && xdb.dialect === "postgres") ? "postgres" : "sqlite";
319
325
  if (dialect === "postgres") {
320
326
  await xdb.query(
321
- "CREATE TABLE IF NOT EXISTS " + table + " (" +
327
+ "CREATE TABLE IF NOT EXISTS " + qTable + " (" +
322
328
  " message_id TEXT NOT NULL," +
323
329
  " source TEXT NOT NULL," +
324
330
  " received_at TIMESTAMPTZ NOT NULL DEFAULT NOW()," +
@@ -327,11 +333,11 @@ function create(opts) {
327
333
  " PRIMARY KEY (source, message_id)" +
328
334
  ")");
329
335
  await xdb.query(
330
- "CREATE INDEX IF NOT EXISTS " + table + "_received_at_idx " +
331
- "ON " + table + " (received_at)");
336
+ "CREATE INDEX IF NOT EXISTS " + qIndex + " " +
337
+ "ON " + qTable + " (received_at)");
332
338
  } else {
333
339
  await xdb.query(
334
- "CREATE TABLE IF NOT EXISTS " + table + " (" +
340
+ "CREATE TABLE IF NOT EXISTS " + qTable + " (" +
335
341
  " message_id TEXT NOT NULL," +
336
342
  " source TEXT NOT NULL," +
337
343
  " received_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP," +
@@ -340,8 +346,8 @@ function create(opts) {
340
346
  " PRIMARY KEY (source, message_id)" +
341
347
  ")");
342
348
  await xdb.query(
343
- "CREATE INDEX IF NOT EXISTS " + table + "_received_at_idx " +
344
- "ON " + table + " (received_at)");
349
+ "CREATE INDEX IF NOT EXISTS " + qIndex + " " +
350
+ "ON " + qTable + " (received_at)");
345
351
  }
346
352
  }
347
353
 
@@ -351,7 +357,7 @@ function create(opts) {
351
357
  await externalDb.transaction(async function (xdb) {
352
358
  if (dialect === "postgres") {
353
359
  var rs = await xdb.query(
354
- "DELETE FROM " + table +
360
+ "DELETE FROM " + qTable +
355
361
  " WHERE received_at < NOW() - $1::interval " +
356
362
  " AND (processed_at IS NOT NULL OR received_at < NOW() - $2::interval)",
357
363
  [retentionDays + " days", (retentionDays * 2) + " days"]);
@@ -360,7 +366,7 @@ function create(opts) {
360
366
  var staleDate = new Date(Date.now() - retentionDays * C.TIME.days(1)).toISOString();
361
367
  var unprocStaleDate = new Date(Date.now() - retentionDays * 2 * C.TIME.days(1)).toISOString();
362
368
  await xdb.query(
363
- "DELETE FROM " + table +
369
+ "DELETE FROM " + qTable +
364
370
  " WHERE received_at < ? " +
365
371
  " AND (processed_at IS NOT NULL OR received_at < ?)",
366
372
  [staleDate, unprocStaleDate]);
@@ -378,7 +384,7 @@ function create(opts) {
378
384
  async function isFresh(receiveOpts) {
379
385
  _validateReceiveOpts(receiveOpts, "isFresh");
380
386
  var dialect = (externalDb.dialect === "postgres") ? "postgres" : "sqlite";
381
- var sql = "SELECT 1 FROM " + table +
387
+ var sql = "SELECT 1 FROM " + qTable +
382
388
  " WHERE source = " + (dialect === "postgres" ? "$1" : "?") +
383
389
  " AND message_id = " + (dialect === "postgres" ? "$2" : "?");
384
390
  var rs = await externalDb.transaction(async function (xdb) {
@@ -395,7 +401,7 @@ function create(opts) {
395
401
  var stats = await externalDb.transaction(async function (xdb) {
396
402
  var sql = "SELECT COUNT(*) AS total," +
397
403
  " COUNT(processed_at) AS processed " +
398
- " FROM " + table +
404
+ " FROM " + qTable +
399
405
  (sourceFilter ? " WHERE source = " +
400
406
  (dialect === "postgres" ? "$1" : "?") : "");
401
407
  var args = sourceFilter ? [sourceFilter] : [];
@@ -418,7 +424,7 @@ function create(opts) {
418
424
  sweep: sweep,
419
425
  isFresh: isFresh,
420
426
  getStats: getReceiveStats,
421
- table: table,
427
+ table: tableRaw,
422
428
  retentionDays: retentionDays,
423
429
  };
424
430
  }