@blamejs/core 0.9.14 → 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 (112) hide show
  1. package/CHANGELOG.md +1 -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 +6 -6
  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/auth/dpop.js +3 -3
  15. package/lib/auth/sd-jwt-vc.js +2 -2
  16. package/lib/backup/bundle.js +17 -17
  17. package/lib/backup/index.js +36 -36
  18. package/lib/budr.js +3 -3
  19. package/lib/bundler.js +20 -20
  20. package/lib/circuit-breaker.js +4 -4
  21. package/lib/cli.js +25 -26
  22. package/lib/cluster.js +2 -2
  23. package/lib/compliance-sanctions.js +2 -2
  24. package/lib/config-drift.js +15 -15
  25. package/lib/content-credentials.js +4 -4
  26. package/lib/credential-hash.js +3 -3
  27. package/lib/daemon.js +19 -19
  28. package/lib/db-file-lifecycle.js +24 -24
  29. package/lib/db-schema.js +2 -2
  30. package/lib/db.js +35 -35
  31. package/lib/dev.js +10 -10
  32. package/lib/dr-runbook.js +5 -5
  33. package/lib/dual-control.js +2 -2
  34. package/lib/external-db-migrate.js +2 -2
  35. package/lib/external-db.js +2 -2
  36. package/lib/fdx.js +2 -2
  37. package/lib/file-upload.js +30 -30
  38. package/lib/flag-providers.js +4 -4
  39. package/lib/gate-contract.js +5 -5
  40. package/lib/graphql-federation.js +4 -7
  41. package/lib/honeytoken.js +6 -6
  42. package/lib/http-client-cookie-jar.js +6 -6
  43. package/lib/http-client.js +18 -18
  44. package/lib/i18n.js +5 -5
  45. package/lib/keychain.js +9 -9
  46. package/lib/legal-hold.js +2 -2
  47. package/lib/local-db-thin.js +9 -9
  48. package/lib/log-stream-local.js +17 -17
  49. package/lib/log-stream-syslog.js +2 -2
  50. package/lib/log-stream.js +3 -3
  51. package/lib/mail-bounce.js +2 -2
  52. package/lib/mail-mdn.js +2 -2
  53. package/lib/mail-srs.js +2 -2
  54. package/lib/mail.js +4 -4
  55. package/lib/mcp.js +2 -2
  56. package/lib/metrics.js +2 -2
  57. package/lib/middleware/api-encrypt.js +16 -16
  58. package/lib/middleware/body-parser.js +16 -16
  59. package/lib/middleware/compression.js +3 -3
  60. package/lib/middleware/csp-nonce.js +4 -4
  61. package/lib/middleware/health.js +7 -7
  62. package/lib/middleware/idempotency-key.js +163 -63
  63. package/lib/migrations.js +3 -3
  64. package/lib/mtls-ca.js +26 -26
  65. package/lib/mtls-engine-default.js +5 -5
  66. package/lib/network-dns.js +2 -2
  67. package/lib/network-nts.js +2 -2
  68. package/lib/network-proxy.js +3 -3
  69. package/lib/network-smtp-policy.js +2 -2
  70. package/lib/network-tls.js +17 -17
  71. package/lib/network.js +13 -13
  72. package/lib/notify.js +3 -3
  73. package/lib/object-store/gcs-bucket-ops.js +2 -2
  74. package/lib/object-store/gcs.js +5 -5
  75. package/lib/object-store/index.js +6 -6
  76. package/lib/object-store/local.js +19 -19
  77. package/lib/object-store/sigv4.js +3 -3
  78. package/lib/observability-tracer.js +4 -4
  79. package/lib/otel-export.js +3 -3
  80. package/lib/pagination.js +5 -5
  81. package/lib/parsers/safe-xml.js +3 -3
  82. package/lib/pqc-gate.js +5 -5
  83. package/lib/pubsub-redis.js +2 -2
  84. package/lib/queue-local.js +3 -3
  85. package/lib/queue.js +2 -2
  86. package/lib/redis-client.js +4 -4
  87. package/lib/restore-bundle.js +18 -18
  88. package/lib/restore-rollback.js +34 -34
  89. package/lib/restore.js +16 -16
  90. package/lib/router.js +13 -13
  91. package/lib/sandbox.js +8 -8
  92. package/lib/sec-cyber.js +3 -3
  93. package/lib/security-assert.js +2 -2
  94. package/lib/seeders.js +4 -4
  95. package/lib/self-update.js +18 -18
  96. package/lib/session-device-binding.js +2 -2
  97. package/lib/static.js +22 -22
  98. package/lib/template.js +19 -19
  99. package/lib/testing.js +7 -7
  100. package/lib/tls-exporter.js +5 -5
  101. package/lib/tracing.js +3 -3
  102. package/lib/vault/index.js +11 -11
  103. package/lib/vault/passphrase-ops.js +37 -37
  104. package/lib/vault/passphrase-source.js +2 -2
  105. package/lib/vault/rotate.js +64 -64
  106. package/lib/vault/seal-pem-file.js +26 -26
  107. package/lib/watcher.js +23 -23
  108. package/lib/webhook.js +10 -10
  109. package/lib/worker-pool.js +6 -6
  110. package/lib/ws-client.js +4 -4
  111. package/package.json +1 -1
  112. package/sbom.cdx.json +6 -6
@@ -48,18 +48,18 @@
48
48
  * sampler skips them.
49
49
  */
50
50
 
51
- var fs = require("fs");
52
- var path = require("path");
51
+ var nodeFs = require("fs");
52
+ var nodePath = require("path");
53
53
  var { DatabaseSync } = require("node:sqlite");
54
54
  var atomicFile = require("../atomic-file");
55
55
  var safeSql = require("../safe-sql");
56
56
  var C = require("../constants");
57
57
  var cryptoField = require("../crypto-field");
58
- var cryptoLib = require("../crypto");
58
+ var bCrypto = require("../crypto");
59
59
  var dbSchema = require("../db-schema");
60
60
  var lazyRequire = require("../lazy-require");
61
61
  var { boot } = require("../log");
62
- var nb = require("../numeric-bounds");
62
+ var numericBounds = require("../numeric-bounds");
63
63
  var safeJson = require("../safe-json");
64
64
  var validateOpts = require("../validate-opts");
65
65
  var vaultWrap = lazyRequire(function () { return require("./wrap"); });
@@ -109,7 +109,7 @@ function _knownColumnsFor(schema, infraColumns) {
109
109
 
110
110
  function validateSchemaMatch(db, opts) {
111
111
  opts = opts || {};
112
- nb.requirePositiveFiniteIntIfPresent(opts.driftSampleLimit,
112
+ numericBounds.requirePositiveFiniteIntIfPresent(opts.driftSampleLimit,
113
113
  "validateSchemaMatch: driftSampleLimit", VaultRotateError, "vault-rotate/bad-opt");
114
114
  var sampleLimit = opts.driftSampleLimit !== undefined
115
115
  ? opts.driftSampleLimit : DEFAULT_DRIFT_SAMPLE_LIMIT;
@@ -254,7 +254,7 @@ function verify(opts) {
254
254
  var keys = opts.keys;
255
255
  var db = opts.db;
256
256
  var oldKeys = opts.oldKeys || null;
257
- nb.requirePositiveFiniteIntIfPresent(opts.sampleMin,
257
+ numericBounds.requirePositiveFiniteIntIfPresent(opts.sampleMin,
258
258
  "verify: sampleMin", VaultRotateError, "vault-rotate/bad-opt");
259
259
  var sampleMin = opts.sampleMin !== undefined
260
260
  ? opts.sampleMin : DEFAULT_VERIFY_SAMPLE_MIN;
@@ -263,7 +263,7 @@ function verify(opts) {
263
263
  opts.samplePercent <= 0)) {
264
264
  throw new VaultRotateError("vault-rotate/bad-opt",
265
265
  "verify: samplePercent must be a positive finite fraction; got " +
266
- nb.shape(opts.samplePercent));
266
+ numericBounds.shape(opts.samplePercent));
267
267
  }
268
268
  var samplePct = opts.samplePercent !== undefined
269
269
  ? opts.samplePercent : DEFAULT_VERIFY_SAMPLE_FRAC;
@@ -311,7 +311,7 @@ function verify(opts) {
311
311
  if (typeof v !== "string" || v.indexOf(VAULT_PREFIX) !== 0) continue;
312
312
  var payload = v.substring(VAULT_PREFIX.length);
313
313
 
314
- try { cryptoLib.decrypt(payload, keys); }
314
+ try { bCrypto.decrypt(payload, keys); }
315
315
  catch (e) {
316
316
  rowFailed = true;
317
317
  failures.push({
@@ -324,7 +324,7 @@ function verify(opts) {
324
324
 
325
325
  if (oldKeys && !foundOldFail) {
326
326
  try {
327
- cryptoLib.decrypt(payload, oldKeys);
327
+ bCrypto.decrypt(payload, oldKeys);
328
328
  regressions.push({
329
329
  table: table,
330
330
  column: col,
@@ -391,8 +391,8 @@ function _emit(cb, ev) {
391
391
  // the original write fd around.
392
392
  function _fsyncFileByPath(p) {
393
393
  try {
394
- var fd = fs.openSync(p, "r+");
395
- try { fs.fsyncSync(fd); } finally { fs.closeSync(fd); }
394
+ var fd = nodeFs.openSync(p, "r+");
395
+ try { nodeFs.fsyncSync(fd); } finally { nodeFs.closeSync(fd); }
396
396
  } catch (_e) { /* best-effort across platforms */ }
397
397
  }
398
398
 
@@ -400,8 +400,8 @@ function _reSealValue(sealedValue, oldKeys, newKeys) {
400
400
  if (typeof sealedValue !== "string") return sealedValue;
401
401
  if (sealedValue.indexOf(C.VAULT_PREFIX) !== 0) return sealedValue;
402
402
  var payload = sealedValue.substring(VAULT_PREFIX_LEN);
403
- var plain = cryptoLib.decrypt(payload, oldKeys);
404
- return C.VAULT_PREFIX + cryptoLib.encrypt(plain, newKeys);
403
+ var plain = bCrypto.decrypt(payload, oldKeys);
404
+ return C.VAULT_PREFIX + bCrypto.encrypt(plain, newKeys);
405
405
  }
406
406
 
407
407
  // Walk a JSON-decoded value, re-sealing every vault-prefixed string.
@@ -530,12 +530,12 @@ async function rotate(opts) {
530
530
  throw new VaultRotateError("vault-rotate/no-keys",
531
531
  "rotate: opts.oldKeys and opts.newKeys are required");
532
532
  }
533
- if (typeof opts.dataDir !== "string" || !fs.existsSync(opts.dataDir)) {
533
+ if (typeof opts.dataDir !== "string" || !nodeFs.existsSync(opts.dataDir)) {
534
534
  throw new VaultRotateError("vault-rotate/no-datadir",
535
535
  "rotate: opts.dataDir is required and must exist");
536
536
  }
537
537
  validateOpts.requireNonEmptyString(opts.stagingDir, "rotate: opts.stagingDir", VaultRotateError, "vault-rotate/no-staging");
538
- if (fs.existsSync(opts.stagingDir)) {
538
+ if (nodeFs.existsSync(opts.stagingDir)) {
539
539
  throw new VaultRotateError("vault-rotate/staging-exists",
540
540
  "rotate: stagingDir already exists: " + opts.stagingDir);
541
541
  }
@@ -571,30 +571,30 @@ async function rotate(opts) {
571
571
  _emit(progress, { phase: "copy_verbatim" });
572
572
  for (var vf = 0; vf < paths.verbatimFiles.length; vf++) {
573
573
  var entry = paths.verbatimFiles[vf];
574
- var src = path.join(dataDir, entry.relativePath);
575
- if (!fs.existsSync(src)) {
574
+ var src = nodePath.join(dataDir, entry.relativePath);
575
+ if (!nodeFs.existsSync(src)) {
576
576
  if (entry.required) {
577
577
  throw new VaultRotateError("vault-rotate/missing-verbatim",
578
578
  "rotate: required verbatim file missing: " + entry.relativePath);
579
579
  }
580
580
  continue;
581
581
  }
582
- var dest = path.join(stagingDir, entry.relativePath);
583
- atomicFile.ensureDir(path.dirname(dest));
584
- fs.copyFileSync(src, dest);
582
+ var dest = nodePath.join(stagingDir, entry.relativePath);
583
+ atomicFile.ensureDir(nodePath.dirname(dest));
584
+ nodeFs.copyFileSync(src, dest);
585
585
  }
586
586
  for (var vd = 0; vd < paths.verbatimDirs.length; vd++) {
587
587
  var dent = paths.verbatimDirs[vd];
588
- var sdir = path.join(dataDir, dent.relativePath);
589
- if (!fs.existsSync(sdir)) {
588
+ var sdir = nodePath.join(dataDir, dent.relativePath);
589
+ if (!nodeFs.existsSync(sdir)) {
590
590
  if (dent.required) {
591
591
  throw new VaultRotateError("vault-rotate/missing-verbatim-dir",
592
592
  "rotate: required verbatim dir missing: " + dent.relativePath);
593
593
  }
594
594
  continue;
595
595
  }
596
- if (fs.existsSync(sdir)) {
597
- atomicFile.copyDirRecursive(sdir, path.join(stagingDir, dent.relativePath));
596
+ if (nodeFs.existsSync(sdir)) {
597
+ atomicFile.copyDirRecursive(sdir, nodePath.join(stagingDir, dent.relativePath));
598
598
  }
599
599
  }
600
600
 
@@ -603,59 +603,59 @@ async function rotate(opts) {
603
603
  var keysJson = JSON.stringify(newKeys, null, 2);
604
604
  if (mode === "wrapped") {
605
605
  var sealed = await vaultWrap().wrap(keysJson, opts.newPassphrase);
606
- fs.writeFileSync(path.join(stagingDir, paths.vaultKeySealed), sealed, { mode: 0o600 });
606
+ nodeFs.writeFileSync(nodePath.join(stagingDir, paths.vaultKeySealed), sealed, { mode: 0o600 });
607
607
  } else {
608
- fs.writeFileSync(path.join(stagingDir, paths.vaultKeyPlain), keysJson, { mode: 0o600 });
608
+ nodeFs.writeFileSync(nodePath.join(stagingDir, paths.vaultKeyPlain), keysJson, { mode: 0o600 });
609
609
  }
610
610
 
611
611
  // 3. re-seal db.key.enc + any operator-supplied additionalSealed files
612
612
  _emit(progress, { phase: "reseal_files" });
613
- var dbKeySealedPath = path.join(dataDir, paths.dbKeySealed);
613
+ var dbKeySealedPath = nodePath.join(dataDir, paths.dbKeySealed);
614
614
  var dbKey = null;
615
- if (fs.existsSync(dbKeySealedPath)) {
616
- var sealedKey = fs.readFileSync(dbKeySealedPath, "utf8").trim();
615
+ if (nodeFs.existsSync(dbKeySealedPath)) {
616
+ var sealedKey = nodeFs.readFileSync(dbKeySealedPath, "utf8").trim();
617
617
  if (sealedKey.indexOf(C.VAULT_PREFIX) !== 0) {
618
618
  throw new VaultRotateError("vault-rotate/bad-dbkey",
619
619
  "rotate: db.key.enc does not start with the vault prefix");
620
620
  }
621
- var dbKeyB64 = cryptoLib.decrypt(sealedKey.substring(VAULT_PREFIX_LEN), oldKeys);
621
+ var dbKeyB64 = bCrypto.decrypt(sealedKey.substring(VAULT_PREFIX_LEN), oldKeys);
622
622
  dbKey = Buffer.from(dbKeyB64, "base64");
623
- var resealedKey = C.VAULT_PREFIX + cryptoLib.encrypt(dbKeyB64, newKeys);
624
- fs.writeFileSync(path.join(stagingDir, paths.dbKeySealed), resealedKey, { mode: 0o600 });
623
+ var resealedKey = C.VAULT_PREFIX + bCrypto.encrypt(dbKeyB64, newKeys);
624
+ nodeFs.writeFileSync(nodePath.join(stagingDir, paths.dbKeySealed), resealedKey, { mode: 0o600 });
625
625
  }
626
626
  for (var as = 0; as < paths.additionalSealed.length; as++) {
627
627
  var ase = paths.additionalSealed[as];
628
- var asSrc = path.join(dataDir, ase.relativePath);
629
- if (!fs.existsSync(asSrc)) {
628
+ var asSrc = nodePath.join(dataDir, ase.relativePath);
629
+ if (!nodeFs.existsSync(asSrc)) {
630
630
  if (ase.required) {
631
631
  throw new VaultRotateError("vault-rotate/missing-sealed",
632
632
  "rotate: required sealed file missing: " + ase.relativePath);
633
633
  }
634
634
  continue;
635
635
  }
636
- var current = fs.readFileSync(asSrc, "utf8").trim();
636
+ var current = nodeFs.readFileSync(asSrc, "utf8").trim();
637
637
  if (current.indexOf(C.VAULT_PREFIX) !== 0) {
638
638
  throw new VaultRotateError("vault-rotate/bad-sealed",
639
639
  "rotate: sealed file does not start with the vault prefix: " + ase.relativePath);
640
640
  }
641
- var asDestDir = path.join(stagingDir, path.dirname(ase.relativePath));
642
- if (!fs.existsSync(asDestDir)) atomicFile.ensureDir(asDestDir);
643
- fs.writeFileSync(path.join(stagingDir, ase.relativePath),
641
+ var asDestDir = nodePath.join(stagingDir, nodePath.dirname(ase.relativePath));
642
+ if (!nodeFs.existsSync(asDestDir)) atomicFile.ensureDir(asDestDir);
643
+ nodeFs.writeFileSync(nodePath.join(stagingDir, ase.relativePath),
644
644
  _reSealValue(current, oldKeys, newKeys), { mode: 0o600 });
645
645
  }
646
646
 
647
647
  // 4. decrypt + rotate + re-encrypt db.enc
648
648
  _emit(progress, { phase: "rotate_db" });
649
- var encDbPath = path.join(dataDir, paths.encryptedDb);
649
+ var encDbPath = nodePath.join(dataDir, paths.encryptedDb);
650
650
  var tablesProcessed = 0;
651
651
  var totalRowsProcessed = 0;
652
652
  var verifyResult = null;
653
653
 
654
- if (fs.existsSync(encDbPath) && dbKey) {
655
- var packed = fs.readFileSync(encDbPath);
656
- var plainBytes = cryptoLib.decryptPacked(packed, dbKey);
657
- var tmpDbPath = path.join(stagingDir, "_blamejs_rotate.tmp.db");
658
- fs.writeFileSync(tmpDbPath, plainBytes, { mode: 0o600 });
654
+ if (nodeFs.existsSync(encDbPath) && dbKey) {
655
+ var packed = nodeFs.readFileSync(encDbPath);
656
+ var plainBytes = bCrypto.decryptPacked(packed, dbKey);
657
+ var tmpDbPath = nodePath.join(stagingDir, "_blamejs_rotate.tmp.db");
658
+ nodeFs.writeFileSync(tmpDbPath, plainBytes, { mode: 0o600 });
659
659
 
660
660
  var db = new DatabaseSync(tmpDbPath);
661
661
  try {
@@ -708,32 +708,32 @@ async function rotate(opts) {
708
708
  // sidecar may be absent (depending on whether journal_mode produced
709
709
  // one for this run); log at debug so the cleanup attempt isn't
710
710
  // silently swallowed when something genuinely unexpected fails.
711
- try { fs.unlinkSync(tmpDbPath + "-wal"); }
712
- catch (e) { rotateLog.debug("cleanup-failed", { op: "fs.unlinkSync", path: tmpDbPath + "-wal", error: e.message }); }
713
- try { fs.unlinkSync(tmpDbPath + "-shm"); }
714
- catch (e) { rotateLog.debug("cleanup-failed", { op: "fs.unlinkSync", path: tmpDbPath + "-shm", error: e.message }); }
711
+ try { nodeFs.unlinkSync(tmpDbPath + "-wal"); }
712
+ catch (e) { rotateLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: tmpDbPath + "-wal", error: e.message }); }
713
+ try { nodeFs.unlinkSync(tmpDbPath + "-shm"); }
714
+ catch (e) { rotateLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: tmpDbPath + "-shm", error: e.message }); }
715
715
 
716
- var rotatedBytes = fs.readFileSync(tmpDbPath);
717
- fs.writeFileSync(path.join(stagingDir, paths.encryptedDb),
718
- cryptoLib.encryptPacked(rotatedBytes, dbKey));
719
- fs.unlinkSync(tmpDbPath);
716
+ var rotatedBytes = nodeFs.readFileSync(tmpDbPath);
717
+ nodeFs.writeFileSync(nodePath.join(stagingDir, paths.encryptedDb),
718
+ bCrypto.encryptPacked(rotatedBytes, dbKey));
719
+ nodeFs.unlinkSync(tmpDbPath);
720
720
 
721
721
  // Round-trip verify on the staged DB
722
722
  _emit(progress, { phase: "verify" });
723
- var verifyTmp = path.join(stagingDir, "_blamejs_verify.tmp.db");
724
- fs.writeFileSync(verifyTmp,
725
- cryptoLib.decryptPacked(fs.readFileSync(path.join(stagingDir, paths.encryptedDb)), dbKey));
723
+ var verifyTmp = nodePath.join(stagingDir, "_blamejs_verify.tmp.db");
724
+ nodeFs.writeFileSync(verifyTmp,
725
+ bCrypto.decryptPacked(nodeFs.readFileSync(nodePath.join(stagingDir, paths.encryptedDb)), dbKey));
726
726
  var vdb = new DatabaseSync(verifyTmp);
727
727
  try {
728
728
  verifyResult = verify({ keys: newKeys, db: vdb, oldKeys: oldKeys });
729
729
  } finally {
730
730
  vdb.close();
731
- try { fs.unlinkSync(verifyTmp); }
732
- catch (e) { rotateLog.debug("cleanup-failed", { op: "fs.unlinkSync", path: verifyTmp, error: e.message }); }
733
- try { fs.unlinkSync(verifyTmp + "-wal"); }
734
- catch (e) { rotateLog.debug("cleanup-failed", { op: "fs.unlinkSync", path: verifyTmp + "-wal", error: e.message }); }
735
- try { fs.unlinkSync(verifyTmp + "-shm"); }
736
- catch (e) { rotateLog.debug("cleanup-failed", { op: "fs.unlinkSync", path: verifyTmp + "-shm", error: e.message }); }
731
+ try { nodeFs.unlinkSync(verifyTmp); }
732
+ catch (e) { rotateLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: verifyTmp, error: e.message }); }
733
+ try { nodeFs.unlinkSync(verifyTmp + "-wal"); }
734
+ catch (e) { rotateLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: verifyTmp + "-wal", error: e.message }); }
735
+ try { nodeFs.unlinkSync(verifyTmp + "-shm"); }
736
+ catch (e) { rotateLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: verifyTmp + "-shm", error: e.message }); }
737
737
  }
738
738
  if (!verifyResult.ok) {
739
739
  throw new VaultRotateError("vault-rotate/verify-failed",
@@ -747,10 +747,10 @@ async function rotate(opts) {
747
747
  // 5. fsync staging for durability before caller does the swap
748
748
  _emit(progress, { phase: "fsync" });
749
749
  function fsyncTree(dir) {
750
- var entries = fs.readdirSync(dir);
750
+ var entries = nodeFs.readdirSync(dir);
751
751
  for (var i = 0; i < entries.length; i++) {
752
- var p = path.join(dir, entries[i]);
753
- var st = fs.statSync(p);
752
+ var p = nodePath.join(dir, entries[i]);
753
+ var st = nodeFs.statSync(p);
754
754
  if (st.isFile()) _fsyncFileByPath(p);
755
755
  else if (st.isDirectory()) fsyncTree(p);
756
756
  }
@@ -19,7 +19,7 @@
19
19
  * source: "/etc/letsencrypt/live/example.com/privkey.pem",
20
20
  * destination: "/var/lib/blamejs/server.key.sealed",
21
21
  * audit: true, // default
22
- * pollInterval: b.constants.TIME.seconds(2), // fs.watchFile cadence
22
+ * pollInterval: b.constants.TIME.seconds(2), // nodeFs.watchFile cadence
23
23
  * onResealed: function (info) { ... }, // { srcPath, destPath, bytes,
24
24
  * resealedAt, generation }
25
25
  * onError: function (err) { ... }, // sealing failed
@@ -42,10 +42,10 @@
42
42
  * (rename did not happen). The recovery routine re-runs the seal from
43
43
  * source — idempotent because the source PEM is the source of truth.
44
44
  *
45
- * fs.watchFile semantics:
45
+ * nodeFs.watchFile semantics:
46
46
  *
47
- * Node's fs.watchFile is a polling stat() loop with the configured
48
- * pollInterval. It fires on mtime / size change. fs.watch (the
47
+ * Node's nodeFs.watchFile is a polling stat() loop with the configured
48
+ * pollInterval. It fires on mtime / size change. nodeFs.watch (the
49
49
  * inotify / kqueue backend) is more efficient but inconsistent across
50
50
  * platforms — single rename events surface as multiple change events
51
51
  * on Linux (events fire on the directory entry, the file, and the
@@ -54,8 +54,8 @@
54
54
  * pollInterval) is acceptable for renewal cadences measured in days.
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 atomicFile = require("../atomic-file");
60
60
  var C = require("../constants");
61
61
  var lazyRequire = require("../lazy-require");
@@ -76,7 +76,7 @@ var SealPemFileError = defineClass("SealPemFileError", { alwaysPermanent: true }
76
76
  // 2-second worst-case re-seal latency — negligible against the
77
77
  // renewal cadence. Operators with sub-second-sensitive use cases
78
78
  // override via opts.pollInterval.
79
- // H6 #6 — fs.watchFile default cadence reduced from 2s to 500ms so a
79
+ // H6 #6 — nodeFs.watchFile default cadence reduced from 2s to 500ms so a
80
80
  // fast renewal-then-revert (mtime bump then second bump within ~2s)
81
81
  // doesn't sneak past the watcher. Operators with extremely-quiet
82
82
  // renewal cycles can override via opts.pollInterval; the cost of
@@ -126,7 +126,7 @@ var DEFAULT_MAX_SOURCE_BYTES = C.BYTES.mib(1);
126
126
  * source: string, // plaintext PEM path (required)
127
127
  * destination: string, // sealed-output path (required, must differ from source)
128
128
  * audit: boolean, // emit b.audit events on every reseal (default true)
129
- * pollInterval: number, // fs.watchFile cadence in ms (default 500)
129
+ * pollInterval: number, // nodeFs.watchFile cadence in ms (default 500)
130
130
  * onResealed: function, // (info) => void — { srcPath, destPath, bytes, resealedAt, generation }
131
131
  * onError: function, // (err) => void — sealing failed
132
132
  * maxSourceBytes: number, // refuse source larger than this (default 1 MiB)
@@ -219,7 +219,7 @@ function sealPemFile(opts) {
219
219
  // marker create and marker remove, the marker remains on disk
220
220
  // and _recoverIfNeeded() detects it on the next start().
221
221
  var markerPath = destination + ".rewriting";
222
- var destDir = path.dirname(destination);
222
+ var destDir = nodePath.dirname(destination);
223
223
  atomicFile.ensureDir(destDir);
224
224
  // H6 #4 — assert parent-dir mode. If the directory is world-
225
225
  // writable, an attacker can swap the destination file or the
@@ -229,7 +229,7 @@ function sealPemFile(opts) {
229
229
  // skip the check there.
230
230
  if (process.platform !== "win32") {
231
231
  try {
232
- var dirStat = fs.statSync(destDir);
232
+ var dirStat = nodeFs.statSync(destDir);
233
233
  if ((dirStat.mode & 0o022) !== 0) { // allow:raw-byte-literal — POSIX mode mask
234
234
  throw new SealPemFileError("seal-pem-file/parent-dir-writable",
235
235
  "destination parent dir '" + destDir + "' is group/other-writable " +
@@ -242,23 +242,23 @@ function sealPemFile(opts) {
242
242
  }
243
243
  }
244
244
  var sealed = vault().seal(plaintextBytes);
245
- fs.writeFileSync(markerPath, String(Date.now()), { mode: 0o600 }); // allow:raw-byte-literal — POSIX file mode
245
+ nodeFs.writeFileSync(markerPath, String(Date.now()), { mode: 0o600 }); // allow:raw-byte-literal — POSIX file mode
246
246
  try {
247
247
  atomicFile.writeSync(destination, sealed, { fileMode: 0o600 }); // allow:raw-byte-literal — POSIX file mode
248
248
  } catch (e) {
249
- try { fs.unlinkSync(markerPath); } catch (_e) { /* best-effort */ }
249
+ try { nodeFs.unlinkSync(markerPath); } catch (_e) { /* best-effort */ }
250
250
  throw e;
251
251
  }
252
- try { fs.unlinkSync(markerPath); } catch (_e) { /* marker cleanup best-effort */ }
252
+ try { nodeFs.unlinkSync(markerPath); } catch (_e) { /* marker cleanup best-effort */ }
253
253
  // H6 #5 — fsync the destination directory so the rename + marker
254
254
  // unlink survive a power loss. Crash + backup-snapshot edge case:
255
255
  // without dir-fsync, a journaled fs may have the new file inode
256
256
  // but not the directory entry update by the time the snapshot
257
257
  // reads.
258
258
  try {
259
- var dirFd = fs.openSync(destDir, "r");
260
- try { fs.fsyncSync(dirFd); }
261
- finally { fs.closeSync(dirFd); }
259
+ var dirFd = nodeFs.openSync(destDir, "r");
260
+ try { nodeFs.fsyncSync(dirFd); }
261
+ finally { nodeFs.closeSync(dirFd); }
262
262
  } catch (_e) { /* dir fsync best-effort — Windows / non-POSIX may refuse */ }
263
263
  }
264
264
 
@@ -267,14 +267,14 @@ function sealPemFile(opts) {
267
267
  resealing = true;
268
268
  var plaintext = null;
269
269
  try {
270
- // H6 #1 — bounded read. fs.readFileSync without a size cap on a
270
+ // H6 #1 — bounded read. nodeFs.readFileSync without a size cap on a
271
271
  // file the operator's renewal process writes is an OOM vector.
272
- // H6 #3 — symlink TOCTOU defense. Open the file via fs.openSync
272
+ // H6 #3 — symlink TOCTOU defense. Open the file via nodeFs.openSync
273
273
  // with O_NOFOLLOW where possible; lstat first to verify the
274
274
  // source isn't a symlink we don't expect, then read via fd so
275
275
  // a swap-after-stat doesn't change which bytes we read.
276
276
  try {
277
- var lstat = fs.lstatSync(source);
277
+ var lstat = nodeFs.lstatSync(source);
278
278
  if (lstat.isSymbolicLink()) {
279
279
  throw new SealPemFileError("seal-pem-file/symlink-refused",
280
280
  "source is a symlink (refused; follow + re-stat opens TOCTOU)");
@@ -283,9 +283,9 @@ function sealPemFile(opts) {
283
283
  throw new SealPemFileError("seal-pem-file/source-too-large",
284
284
  "source size " + lstat.size + " exceeds maxSourceBytes " + maxSourceBytes);
285
285
  }
286
- var fd = fs.openSync(source, "r");
286
+ var fd = nodeFs.openSync(source, "r");
287
287
  try {
288
- var fstat = fs.fstatSync(fd);
288
+ var fstat = nodeFs.fstatSync(fd);
289
289
  // H6 #3 — confirm the fd points at the same inode lstat saw.
290
290
  if (fstat.ino !== lstat.ino || fstat.size > maxSourceBytes) {
291
291
  throw new SealPemFileError("seal-pem-file/toctou-detected",
@@ -294,7 +294,7 @@ function sealPemFile(opts) {
294
294
  plaintext = Buffer.alloc(fstat.size);
295
295
  var read = 0;
296
296
  while (read < fstat.size) {
297
- var n = fs.readSync(fd, plaintext, read, fstat.size - read, null);
297
+ var n = nodeFs.readSync(fd, plaintext, read, fstat.size - read, null);
298
298
  if (n === 0) break;
299
299
  read += n;
300
300
  }
@@ -303,7 +303,7 @@ function sealPemFile(opts) {
303
303
  "short read: " + read + " of " + fstat.size + " bytes");
304
304
  }
305
305
  } finally {
306
- try { fs.closeSync(fd); } catch (_e) { /* close best-effort */ }
306
+ try { nodeFs.closeSync(fd); } catch (_e) { /* close best-effort */ }
307
307
  }
308
308
  }
309
309
  catch (e) {
@@ -390,7 +390,7 @@ function sealPemFile(opts) {
390
390
  // reseal was interrupted. Re-seal from source idempotently.
391
391
  function _recoverIfNeeded() {
392
392
  var markerPath = destination + ".rewriting";
393
- if (fs.existsSync(markerPath)) {
393
+ if (nodeFs.existsSync(markerPath)) {
394
394
  log.info("vault.sealPemFile: recovery — marker '" + markerPath +
395
395
  "' present from prior crashed reseal; re-sealing from source");
396
396
  _emitAudit("recovery_started", "success", {
@@ -414,7 +414,7 @@ function sealPemFile(opts) {
414
414
  _resealNow();
415
415
  }
416
416
  };
417
- fs.watchFile(source, { persistent: false, interval: pollInterval }, listener);
417
+ nodeFs.watchFile(source, { persistent: false, interval: pollInterval }, listener);
418
418
  watching = true;
419
419
  _emitAudit("watch_started", "success", {
420
420
  source: source,
@@ -425,7 +425,7 @@ function sealPemFile(opts) {
425
425
 
426
426
  function stop() {
427
427
  if (!watching) return;
428
- fs.unwatchFile(source, listener);
428
+ nodeFs.unwatchFile(source, listener);
429
429
  listener = null;
430
430
  watching = false;
431
431
  _emitAudit("watch_stopped", "success", {
package/lib/watcher.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * b.watcher — recursive filesystem-watch primitive with cross-platform
4
4
  * event normalization.
5
5
  *
6
- * Wraps `fs.watch(root, { recursive: true })` and turns the per-platform
6
+ * Wraps `nodeFs.watch(root, { recursive: true })` and turns the per-platform
7
7
  * event soup (Linux inotify "rename" + "change", macOS FSEvents
8
8
  * coalesced "rename", Windows ReadDirectoryChangesW pure "rename" /
9
9
  * "change") into a single shape:
@@ -15,7 +15,7 @@
15
15
  * `type` is one of "file" or "dir". The watcher is build-tool-shaped:
16
16
  * use it to drive incremental rebuilds, hot-reload-on-change,
17
17
  * config-file watching, or content-store cache busts. It is NOT a
18
- * security primitive — fs.watch is best-effort across kernels and the
18
+ * security primitive — nodeFs.watch is best-effort across kernels and the
19
19
  * caller must not rely on it for audit-grade change detection.
20
20
  *
21
21
  * Cross-platform notes baked in:
@@ -45,8 +45,8 @@
45
45
  * watcher.WatcherError
46
46
  */
47
47
 
48
- var fs = require("fs");
49
- var path = require("path");
48
+ var nodeFs = require("fs");
49
+ var nodePath = require("path");
50
50
  var lazyRequire = require("./lazy-require");
51
51
  var validateOpts = require("./validate-opts");
52
52
  var { WatcherError } = require("./framework-error");
@@ -56,7 +56,7 @@ var observability = lazyRequire(function () { return require("./observability");
56
56
 
57
57
  var DEFAULT_DEBOUNCE_MS = 100;
58
58
  // Polling-mode defaults. The polling backend exists for environments
59
- // where fs.watch's native events don't reach userspace — most commonly
59
+ // where nodeFs.watch's native events don't reach userspace — most commonly
60
60
  // Docker Desktop bind-mounts on Windows / macOS hosts (where the
61
61
  // inotify events from the Linux container's mount don't propagate
62
62
  // through the gRPC-FUSE / VirtioFS bridge to the host fs), or NFS /
@@ -166,8 +166,8 @@ function _compileIgnore(patterns) {
166
166
  }
167
167
  }
168
168
  return function (relPath) {
169
- var base = path.basename(relPath);
170
- var normalized = relPath.split(path.sep).join("/");
169
+ var base = nodePath.basename(relPath);
170
+ var normalized = relPath.split(nodePath.sep).join("/");
171
171
  for (var j = 0; j < compiled.length; j += 1) {
172
172
  var c = compiled[j];
173
173
  if (c.kind === "exact" && (c.value === relPath || c.value === normalized)) return true;
@@ -212,7 +212,7 @@ function _validateOpts(opts) {
212
212
  function create(opts) {
213
213
  _validateOpts(opts);
214
214
 
215
- var root = path.resolve(opts.root);
215
+ var root = nodePath.resolve(opts.root);
216
216
  var debounceMs = (opts.debounceMs !== undefined) ? opts.debounceMs : DEFAULT_DEBOUNCE_MS;
217
217
  var maxPending = (opts.maxPending !== undefined) ? opts.maxPending : DEFAULT_MAX_PENDING;
218
218
  var mode = opts.mode || "fs";
@@ -226,7 +226,7 @@ function create(opts) {
226
226
 
227
227
  // Pre-flight: root must exist and be a directory.
228
228
  var rootStat;
229
- try { rootStat = fs.statSync(root); }
229
+ try { rootStat = nodeFs.statSync(root); }
230
230
  catch (e) {
231
231
  throw new WatcherError("watcher/root-missing",
232
232
  "watcher.create: root '" + root + "' is not accessible: " + ((e && e.message) || String(e)));
@@ -260,10 +260,10 @@ function create(opts) {
260
260
  function _normalizeAndDispatch(relPath) {
261
261
  if (stopped) return;
262
262
  if (isIgnored(relPath)) return;
263
- var fullPath = path.join(root, relPath);
263
+ var fullPath = nodePath.join(root, relPath);
264
264
  // lstat (NOT stat) — refuses to follow symlinks out of root.
265
265
  var lst;
266
- try { lst = fs.lstatSync(fullPath); }
266
+ try { lst = nodeFs.lstatSync(fullPath); }
267
267
  catch (e) {
268
268
  if (e && e.code === "ENOENT") {
269
269
  // Path is gone — delete event. Type unknown by the time we
@@ -335,9 +335,9 @@ function create(opts) {
335
335
  var stack = [""];
336
336
  while (stack.length > 0) {
337
337
  var relDir = stack.pop();
338
- var absDir = relDir === "" ? root : path.join(root, relDir);
338
+ var absDir = relDir === "" ? root : nodePath.join(root, relDir);
339
339
  var entries;
340
- try { entries = fs.readdirSync(absDir, { withFileTypes: true }); }
340
+ try { entries = nodeFs.readdirSync(absDir, { withFileTypes: true }); }
341
341
  catch (_e) {
342
342
  // Root vanished mid-walk OR an inner dir got deleted between
343
343
  // the parent listing and the descent. Skip — the next tick's
@@ -348,8 +348,8 @@ function create(opts) {
348
348
  var entry = entries[i];
349
349
  var relPath = relDir === "" ? entry.name : (relDir + "/" + entry.name);
350
350
  // Normalize to forward-slash so glob ignore-matching is
351
- // consistent with the fs.watch path the operator's hooks see.
352
- relPath = relPath.split(path.sep).join("/");
351
+ // consistent with the nodeFs.watch path the operator's hooks see.
352
+ relPath = relPath.split(nodePath.sep).join("/");
353
353
  if (isIgnored(relPath)) continue;
354
354
  if (entry.isSymbolicLink()) continue; // never follow symlinks
355
355
  fileCount += 1;
@@ -358,9 +358,9 @@ function create(opts) {
358
358
  "watcher.poll: tree exceeds pollMaxFiles=" + pollMaxFiles +
359
359
  " — narrow `ignore` patterns OR raise pollMaxFiles, OR switch to mode: \"fs\"");
360
360
  }
361
- var absPath = path.join(absDir, entry.name);
361
+ var absPath = nodePath.join(absDir, entry.name);
362
362
  var st;
363
- try { st = fs.statSync(absPath); }
363
+ try { st = nodeFs.statSync(absPath); }
364
364
  catch (_e) { continue; } // race — entry vanished
365
365
  if (entry.isDirectory()) {
366
366
  snapshot.set(relPath, { type: "dir", size: 0, mtimeMs: st.mtimeMs });
@@ -382,13 +382,13 @@ function create(opts) {
382
382
  if (pollSnapshot === null) {
383
383
  // First tick — establish the baseline without firing events.
384
384
  // Operators get add events on file CREATION after start, not on
385
- // pre-existing files (matches fs.watch semantics).
385
+ // pre-existing files (matches nodeFs.watch semantics).
386
386
  pollSnapshot = next;
387
387
  return;
388
388
  }
389
389
  // Diff: anything in `next` not in `pollSnapshot`, OR with size /
390
390
  // mtimeMs different, fires onChange via the same _enqueue path the
391
- // fs.watch backend uses (so debounce + ignore + lstat dispatch
391
+ // nodeFs.watch backend uses (so debounce + ignore + lstat dispatch
392
392
  // stay uniform). Anything in `pollSnapshot` missing from `next`
393
393
  // fires onDelete (via _normalizeAndDispatch's ENOENT branch).
394
394
  next.forEach(function (info, relPath) {
@@ -416,12 +416,12 @@ function create(opts) {
416
416
  if (typeof pollTimer.unref === "function") pollTimer.unref();
417
417
  } else {
418
418
  try {
419
- watcherHandle = fs.watch(root, { recursive: true, persistent: true }, function (eventType, filename) {
419
+ watcherHandle = nodeFs.watch(root, { recursive: true, persistent: true }, function (eventType, filename) {
420
420
  if (stopped) return;
421
421
  if (!filename) return;
422
422
  var rel = filename;
423
- if (path.isAbsolute(rel) && rel.indexOf(root) === 0) {
424
- rel = path.relative(root, rel);
423
+ if (nodePath.isAbsolute(rel) && rel.indexOf(root) === 0) {
424
+ rel = nodePath.relative(root, rel);
425
425
  }
426
426
  if (rel === "" || rel === ".") return;
427
427
  _enqueue(rel);
@@ -434,7 +434,7 @@ function create(opts) {
434
434
  ((e && e.message) || String(e)) + " — pass mode: \"poll\" to fall back to interval polling");
435
435
  }
436
436
  throw new WatcherError("watcher/start-failed",
437
- "watcher.create: fs.watch failed: " + ((e && e.message) || String(e)));
437
+ "watcher.create: nodeFs.watch failed: " + ((e && e.message) || String(e)));
438
438
  }
439
439
  }
440
440