@blamejs/core 0.14.26 → 0.15.0

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 (150) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +2 -2
  3. package/index.js +4 -0
  4. package/lib/agent-envelope-mac.js +104 -0
  5. package/lib/agent-event-bus.js +105 -4
  6. package/lib/agent-posture-chain.js +8 -42
  7. package/lib/ai-content-detect.js +9 -10
  8. package/lib/api-key.js +107 -74
  9. package/lib/atomic-file.js +62 -4
  10. package/lib/audit-chain.js +47 -11
  11. package/lib/audit-sign.js +77 -2
  12. package/lib/audit-tools.js +79 -51
  13. package/lib/audit.js +249 -123
  14. package/lib/auth/openid-federation.js +108 -47
  15. package/lib/backup/index.js +13 -10
  16. package/lib/break-glass.js +202 -144
  17. package/lib/cache.js +174 -105
  18. package/lib/chain-writer.js +38 -16
  19. package/lib/cli.js +19 -14
  20. package/lib/cluster-provider-db.js +130 -104
  21. package/lib/cluster-storage.js +119 -22
  22. package/lib/cluster.js +119 -71
  23. package/lib/compliance.js +169 -4
  24. package/lib/consent.js +73 -24
  25. package/lib/constants.js +16 -11
  26. package/lib/crypto-field.js +474 -92
  27. package/lib/db-declare-row-policy.js +35 -22
  28. package/lib/db-file-lifecycle.js +3 -2
  29. package/lib/db-query.js +497 -255
  30. package/lib/db-schema.js +209 -44
  31. package/lib/db.js +176 -95
  32. package/lib/error-page.js +14 -1
  33. package/lib/external-db-migrate.js +229 -139
  34. package/lib/external-db.js +25 -15
  35. package/lib/file-upload.js +52 -7
  36. package/lib/framework-error.js +14 -1
  37. package/lib/framework-files.js +73 -0
  38. package/lib/framework-schema.js +695 -394
  39. package/lib/gate-contract.js +649 -1
  40. package/lib/guard-agent-registry.js +26 -44
  41. package/lib/guard-all.js +1 -0
  42. package/lib/guard-auth.js +42 -112
  43. package/lib/guard-cidr.js +33 -154
  44. package/lib/guard-csv.js +46 -113
  45. package/lib/guard-domain.js +34 -157
  46. package/lib/guard-dsn.js +27 -43
  47. package/lib/guard-email.js +47 -69
  48. package/lib/guard-envelope.js +19 -32
  49. package/lib/guard-event-bus-payload.js +24 -42
  50. package/lib/guard-event-bus-topic.js +25 -43
  51. package/lib/guard-filename.js +42 -106
  52. package/lib/guard-graphql.js +42 -123
  53. package/lib/guard-html.js +53 -108
  54. package/lib/guard-idempotency-key.js +24 -42
  55. package/lib/guard-image.js +46 -103
  56. package/lib/guard-imap-command.js +18 -32
  57. package/lib/guard-jmap.js +16 -30
  58. package/lib/guard-json.js +38 -108
  59. package/lib/guard-jsonpath.js +38 -171
  60. package/lib/guard-jwt.js +49 -179
  61. package/lib/guard-list-id.js +25 -41
  62. package/lib/guard-list-unsubscribe.js +27 -43
  63. package/lib/guard-mail-compose.js +24 -42
  64. package/lib/guard-mail-move.js +26 -44
  65. package/lib/guard-mail-query.js +28 -46
  66. package/lib/guard-mail-reply.js +24 -42
  67. package/lib/guard-mail-sieve.js +24 -42
  68. package/lib/guard-managesieve-command.js +17 -31
  69. package/lib/guard-markdown.js +37 -104
  70. package/lib/guard-message-id.js +26 -45
  71. package/lib/guard-mime.js +39 -151
  72. package/lib/guard-oauth.js +54 -135
  73. package/lib/guard-pdf.js +45 -101
  74. package/lib/guard-pop3-command.js +21 -31
  75. package/lib/guard-posture-chain.js +24 -42
  76. package/lib/guard-regex.js +33 -107
  77. package/lib/guard-saga-config.js +24 -42
  78. package/lib/guard-shell.js +42 -172
  79. package/lib/guard-smtp-command.js +48 -54
  80. package/lib/guard-snapshot-envelope.js +24 -42
  81. package/lib/guard-sql.js +1491 -0
  82. package/lib/guard-stream-args.js +24 -43
  83. package/lib/guard-svg.js +47 -65
  84. package/lib/guard-template.js +35 -172
  85. package/lib/guard-tenant-id.js +26 -45
  86. package/lib/guard-time.js +32 -154
  87. package/lib/guard-trace-context.js +25 -44
  88. package/lib/guard-uuid.js +32 -153
  89. package/lib/guard-xml.js +38 -113
  90. package/lib/guard-yaml.js +51 -163
  91. package/lib/http-client.js +37 -9
  92. package/lib/inbox.js +120 -107
  93. package/lib/legal-hold.js +107 -50
  94. package/lib/log-stream-cloudwatch.js +47 -31
  95. package/lib/log-stream-otlp.js +32 -18
  96. package/lib/mail-crypto-smime.js +2 -6
  97. package/lib/mail-greylist.js +2 -6
  98. package/lib/mail-helo.js +2 -6
  99. package/lib/mail-journal.js +85 -64
  100. package/lib/mail-rbl.js +2 -6
  101. package/lib/mail-scan.js +2 -6
  102. package/lib/mail-server-jmap.js +117 -12
  103. package/lib/mail-spam-score.js +2 -6
  104. package/lib/mail-store.js +287 -154
  105. package/lib/middleware/body-parser.js +71 -25
  106. package/lib/middleware/csrf-protect.js +19 -8
  107. package/lib/middleware/fetch-metadata.js +17 -7
  108. package/lib/middleware/idempotency-key.js +54 -38
  109. package/lib/middleware/rate-limit.js +102 -32
  110. package/lib/middleware/security-headers.js +21 -5
  111. package/lib/migrations.js +108 -66
  112. package/lib/network-heartbeat.js +7 -0
  113. package/lib/nonce-store.js +31 -9
  114. package/lib/object-store/azure-blob-bucket-ops.js +9 -4
  115. package/lib/object-store/azure-blob.js +57 -3
  116. package/lib/object-store/sigv4.js +10 -0
  117. package/lib/observability.js +87 -0
  118. package/lib/otel-export.js +25 -1
  119. package/lib/outbox.js +136 -82
  120. package/lib/parsers/safe-xml.js +47 -7
  121. package/lib/pqc-agent.js +44 -0
  122. package/lib/pubsub-cluster.js +42 -20
  123. package/lib/queue-local.js +202 -139
  124. package/lib/queue-redis.js +9 -1
  125. package/lib/queue-sqs.js +6 -0
  126. package/lib/redact.js +68 -11
  127. package/lib/redis-client.js +160 -31
  128. package/lib/retention.js +82 -39
  129. package/lib/router.js +212 -5
  130. package/lib/safe-dns.js +29 -45
  131. package/lib/safe-ical.js +18 -33
  132. package/lib/safe-icap.js +27 -43
  133. package/lib/safe-sieve.js +21 -40
  134. package/lib/safe-sql.js +124 -3
  135. package/lib/safe-vcard.js +18 -33
  136. package/lib/scheduler.js +35 -12
  137. package/lib/seeders.js +122 -74
  138. package/lib/session-stores.js +42 -14
  139. package/lib/session.js +109 -72
  140. package/lib/sql.js +3885 -0
  141. package/lib/ssrf-guard.js +51 -4
  142. package/lib/static.js +177 -34
  143. package/lib/subject.js +55 -17
  144. package/lib/vault/index.js +3 -2
  145. package/lib/vault/passphrase-ops.js +3 -2
  146. package/lib/vault/rotate.js +104 -64
  147. package/lib/vendor-data.js +2 -0
  148. package/lib/websocket.js +35 -5
  149. package/package.json +1 -1
  150. package/sbom.cdx.json +6 -6
@@ -52,6 +52,10 @@ var log = boot("external-db");
52
52
  var audit = lazyRequire(function () { return require("./audit"); });
53
53
  var db = lazyRequire(function () { return require("./db"); });
54
54
  var observability = lazyRequire(function () { return require("./observability"); });
55
+ // b.sql composes the framework's own internal queries against a backend
56
+ // (e.g. the pg_roles hardening scan). Lazy because b.sql -> framework-schema
57
+ // -> external-db would cycle at module load; resolved when a query runs.
58
+ var sql = lazyRequire(function () { return require("./sql"); });
55
59
 
56
60
  function _emitMetric(name, value, labels) {
57
61
  try { observability().event(name, value, labels || {}); }
@@ -79,7 +83,7 @@ function _emitMetric(name, value, labels) {
79
83
  // cannot backtrack polynomially (CWE-1333 ReDoS).
80
84
  var _STATEMENT_CLASS_RE = /^(?:\s|\/\*(?:[^*]|\*(?!\/))*\*\/|--[^\n]*\n)*([A-Za-z]+)/;
81
85
  var _STATEMENT_CLASS_MAP = Object.freeze({
82
- SELECT: "SELECT", VALUES: "SELECT", TABLE: "SELECT",
86
+ SELECT: "SELECT", VALUES: "SELECT", TABLE: "SELECT", // allow:hand-rolled-sql — leading-keyword classifier table, not composed SQL
83
87
  SHOW: "READ_INFO", DESCRIBE: "READ_INFO", DESC: "READ_INFO",
84
88
  PRAGMA: "READ_INFO", USE: "READ_INFO",
85
89
  INSERT: "DML", UPDATE: "DML", DELETE: "DML", MERGE: "DML",
@@ -762,7 +766,7 @@ function init(opts) {
762
766
  return async function () {
763
767
  var client = await cn();
764
768
  try {
765
- await qn(client, "SET application_name TO " + quotedAppName, []);
769
+ await qn(client, "SET application_name TO " + quotedAppName, []); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
766
770
  } catch (_e) {
767
771
  // Best-effort. Real Postgres always supports SET
768
772
  // application_name; a driver that refuses it is a shim
@@ -1210,10 +1214,10 @@ async function transaction(fn, opts) {
1210
1214
  try {
1211
1215
  await b.beginTx(client);
1212
1216
  if (typeof stmtTimeoutMs === "number" && isFinite(stmtTimeoutMs) && stmtTimeoutMs > 0) {
1213
- await b.query(client, "SET LOCAL statement_timeout = " + Math.floor(stmtTimeoutMs), []);
1217
+ await b.query(client, "SET LOCAL statement_timeout = " + Math.floor(stmtTimeoutMs), []); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
1214
1218
  }
1215
1219
  if (typeof idleTimeoutMs === "number" && isFinite(idleTimeoutMs) && idleTimeoutMs > 0) {
1216
- await b.query(client, "SET LOCAL idle_in_transaction_session_timeout = " + Math.floor(idleTimeoutMs), []);
1220
+ await b.query(client, "SET LOCAL idle_in_transaction_session_timeout = " + Math.floor(idleTimeoutMs), []); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
1217
1221
  }
1218
1222
  for (var gi = 0; gi < prebuiltGucs.length; gi++) {
1219
1223
  await b.query(client, prebuiltGucs[gi], []);
@@ -1312,7 +1316,7 @@ async function _pingBackend(b) {
1312
1316
  var client = await b.pool.acquire();
1313
1317
  try {
1314
1318
  if (b.ping) await b.ping(client);
1315
- else await b.query(client, "SELECT 1", []);
1319
+ else await b.query(client, "SELECT 1", []); // allow:hand-rolled-sql — fixed connectivity ping (no table / b.sql verb)
1316
1320
  b.pool.release(client);
1317
1321
  return { ok: true, breakerState: b.breaker.getState(), pool: b.pool.stats() };
1318
1322
  } catch (e) {
@@ -1451,7 +1455,7 @@ function _buildSessionGucsStatements(sessionGucs) {
1451
1455
  "sessionGucs['" + name + "']: value must be a string, finite number, or boolean (got " +
1452
1456
  typeof value + ")", true);
1453
1457
  }
1454
- out.push("SET LOCAL " + qName + " = " + literal);
1458
+ out.push("SET LOCAL " + qName + " = " + literal); // allow:hand-rolled-sql — Postgres session GUC SET (no table; not a b.sql verb)
1455
1459
  }
1456
1460
  return out;
1457
1461
  }
@@ -2143,22 +2147,27 @@ function _connectAs(rawConnect, query, opts) {
2143
2147
 
2144
2148
  // Pre-compute the SET statements once — every fresh client runs the
2145
2149
  // same list, so building it per-connect would burn microbenchmarks.
2150
+ // Postgres session-config statements (SET ROLE / search_path /
2151
+ // application_name / statement_timeout / GUCs). These are session-state
2152
+ // commands, not table DML — b.sql has no SET verb, so they stay
2153
+ // hand-composed (identifiers double-quoted, string values single-quote
2154
+ // escaped, numerics rendered after a finite-check below).
2146
2155
  var stmts = [];
2147
2156
  if (opts.role) {
2148
- stmts.push('SET ROLE "' + opts.role + '"');
2157
+ stmts.push('SET ROLE "' + opts.role + '"'); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
2149
2158
  }
2150
2159
  if (pathSegments) {
2151
2160
  var pathSql = pathSegments.map(function (s) { return '"' + s + '"'; }).join(", ");
2152
- stmts.push("SET search_path TO " + pathSql);
2161
+ stmts.push("SET search_path TO " + pathSql); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
2153
2162
  }
2154
2163
  if (opts.applicationName !== undefined) {
2155
2164
  // Single-quoted string literal — SQL-standard escape doubles embedded
2156
2165
  // single quotes.
2157
2166
  var an = String(opts.applicationName).replace(/'/g, "''");
2158
- stmts.push("SET application_name TO '" + an + "'");
2167
+ stmts.push("SET application_name TO '" + an + "'"); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
2159
2168
  }
2160
2169
  if (opts.statementTimeoutMs !== undefined) {
2161
- stmts.push("SET statement_timeout TO " + opts.statementTimeoutMs);
2170
+ stmts.push("SET statement_timeout TO " + opts.statementTimeoutMs); // allow:hand-rolled-sql — Postgres session SET (no table; not a b.sql verb)
2162
2171
  }
2163
2172
  if (opts.gucs) {
2164
2173
  for (var gn in opts.gucs) {
@@ -2467,11 +2476,12 @@ async function assertRoleHardening(opts) {
2467
2476
  var ignoreSystem = opts.ignoreSystem !== false; // default true
2468
2477
  var rows;
2469
2478
  try {
2470
- var res = await query(
2471
- "SELECT rolname FROM pg_roles ORDER BY rolname",
2472
- [],
2473
- { backend: backendName }
2474
- );
2479
+ // pg_roles is a Postgres system catalog (this path is Postgres-only —
2480
+ // guarded above), so b.sql emits the bare unquoted catalog name with a
2481
+ // quoted projection. No params, no `?`, so no placeholder translation.
2482
+ var rolesBuilt = sql().select("pg_roles", { dialect: "postgres" })
2483
+ .columns(["rolname"]).orderBy("rolname", "asc").toSql();
2484
+ var res = await query(rolesBuilt.sql, rolesBuilt.params, { backend: backendName });
2475
2485
  rows = (res && res.rows) || [];
2476
2486
  } catch (e) {
2477
2487
  audit().safeEmit({
@@ -22,8 +22,12 @@
22
22
  * content gate inspects the reassembled buffer, so it runs on uploads
23
23
  * up to `maxStreamReassemblyBytes` (default 64 MiB); a larger upload
24
24
  * is handed to `onFinalize` as a stream and the byte-content gate is
25
- * skipped (MIME-sniff + filename gates still run, and the skip emits a
26
- * `fileUpload.content_safety_skipped` warning audit). To guarantee
25
+ * skipped (MIME-sniff + filename gates still run). Every skip path
26
+ * the upload streamed past the reassembly cap, no gate is registered
27
+ * for the file's extension, or `contentSafety: null` disabled scanning
28
+ * — emits a `fileUpload.content_safety_skipped` audit whose `reason`
29
+ * names the cause, so a security review of the audit log can tell which
30
+ * uploads reached storage without a content scan and why. To guarantee
27
31
  * content-gating of a type, cap `maxFileBytes` at or below
28
32
  * `maxStreamReassemblyBytes`. Per-chunk hooks
29
33
  * (`onChunk`) are the integration point for virus scanners and
@@ -475,6 +479,32 @@ function create(opts) {
475
479
  if (opts.observability) opts.observability.safeEvent(name, value, labels || {});
476
480
  }
477
481
 
482
+ // Emit an audit row whenever the byte-level content-safety scan is
483
+ // SKIPPED for a finalized upload — so a security review of the audit
484
+ // log can tell that bytes reached storage without passing the
485
+ // content gate, and WHY. Without this, every skip path (operator
486
+ // opt-out, no gate registered for the file's extension, or the upload
487
+ // streamed past maxStreamReassemblyBytes) was silent: the audit log
488
+ // showed a clean `fileUpload.finalize` success indistinguishable from
489
+ // a scanned upload. `reason` names the skip cause so operators can
490
+ // alert / lower maxStreamReassemblyBytes / register the missing gate.
491
+ // Observability-only: `_emitAudit` wraps audit.safeEmit in try/catch
492
+ // (drop-silent — by design) so a throwing sink never breaks the upload.
493
+ function _emitContentSafetySkipped(uploadId, actor, reason, ext, size) {
494
+ _emitObs("fileUpload.content_safety_skipped", 1, { reason: reason, ext: ext || "" });
495
+ // outcome "success" — the upload itself finalized; the audit records
496
+ // that the byte-level scan did NOT run, with `reason` naming why
497
+ // (the only outcomes the audit chain accepts are success / failure /
498
+ // denied, so the skip-cause lives in `reason` + `metadata`).
499
+ _emitAudit("fileUpload.content_safety_skipped", {
500
+ actor: requestHelpers.extractActorContext(actor),
501
+ resource: { kind: "fileUpload", id: uploadId },
502
+ outcome: "success",
503
+ reason: reason,
504
+ metadata: { uploadId: uploadId, ext: ext || null, size: size, reason: reason },
505
+ });
506
+ }
507
+
478
508
  // Staging dir mode 0o700 — only the framework process reads its own
479
509
  // staging files.
480
510
  atomicFile.ensureDir(stagingDir, 0o700);
@@ -1088,12 +1118,27 @@ function create(opts) {
1088
1118
  // upload streamed past maxStreamReassemblyBytes and was never
1089
1119
  // reassembled into a buffer the byte-level gate can inspect. The
1090
1120
  // MIME-sniff and filename gates still ran; the per-extension
1091
- // content gate did NOT. Surface it (rather than skipping silently)
1092
- // via an observability counter so operators can alert, lower
1093
- // maxStreamReassemblyBytes, or cap maxFileBytes to force
1094
- // content-gating of this type.
1095
- _emitObs("fileUpload.content_safety_skipped_streamed", 1, { ext: safetyExt });
1121
+ // content gate did NOT. Audit the skip (with the streamed reason)
1122
+ // so operators can alert, lower maxStreamReassemblyBytes, or cap
1123
+ // maxFileBytes to force content-gating of this type.
1124
+ _emitContentSafetySkipped(uploadId, actor, "streamed-over-reassembly-cap",
1125
+ safetyExt, verified.totalBytes);
1126
+ } else {
1127
+ // contentSafety is wired but no gate is registered for this file's
1128
+ // extension — the byte-level scan does not run. Audit the skip so
1129
+ // a review can tell the upload bypassed content scanning (and
1130
+ // register a gate for the extension if it should be scanned).
1131
+ _emitContentSafetySkipped(uploadId, actor, "no-gate-for-extension",
1132
+ safetyExt, verified.totalBytes);
1096
1133
  }
1134
+ } else {
1135
+ // Content-safety scanning is disabled for this upload manager
1136
+ // (contentSafety: null opt-out at create()). The create-time audit
1137
+ // recorded the disable; this per-upload audit makes the bypass
1138
+ // visible at the point bytes reached storage.
1139
+ _emitContentSafetySkipped(uploadId, actor, "content-safety-disabled",
1140
+ nodePath.extname(filename).toLowerCase(),
1141
+ verified.totalBytes);
1097
1142
  }
1098
1143
 
1099
1144
  // Hand to operator's onFinalize.
@@ -215,6 +215,16 @@ var GuardSvgError = defineClass("GuardSvgError", { alwaysPermane
215
215
  // (Windows strips them silently), unicode bidi/RTLO file-name spoofing,
216
216
  // overlong UTF-8 encoding, length caps. alwaysPermanent.
217
217
  var GuardFilenameError = defineClass("GuardFilenameError", { alwaysPermanent: true });
218
+ // GuardSqlError covers raw-SQL refusals from the b.guardSql guard: the
219
+ // OS-reach floor (file / exec / FDW / extension / privilege-pivot
220
+ // across Postgres / SQLite / MySQL), stacked statements, comment
221
+ // smuggling, embedded string literals in a fragment, invalid UTF-8
222
+ // (CVE-2025-1094 encoding-bypass class), time-based probes, schema
223
+ // recon, and the migration DDL-verb allowlist. DOT-style codes
224
+ // (sql.refuse / sql.stacked / sql.file-access / ...) so they don't
225
+ // collide with SafeSqlError's slash codes (sql/bad-shape / ...).
226
+ // alwaysPermanent.
227
+ var GuardSqlError = defineClass("GuardSqlError", { alwaysPermanent: true });
218
228
  // GuardArchiveError covers archive-shape violations: zip-slip path
219
229
  // traversal, symlink + hardlink escape, decompression-ratio bombs,
220
230
  // nested-archive depth, file-count + total-size + per-entry-size caps,
@@ -557,7 +567,9 @@ var WatcherError = defineClass("WatcherError", { alwaysPermane
557
567
  // caller-shape misuse or an irrecoverable on-disk condition.
558
568
  var LocalDbThinError = defineClass("LocalDbThinError", { alwaysPermanent: true });
559
569
  // RouterError covers operator-shape violations on the router primitive:
560
- // invalid `allowedRedirectOrigins` opt at create time, and cross-origin
570
+ // invalid `allowedRedirectOrigins` opt at create time, a malformed
571
+ // `use()` mount (non-string / non-array prefix, a prefix not beginning
572
+ // with "/", a missing or non-function middleware), and cross-origin
561
573
  // `res.redirect()` targets that are not on the allowlist. alwaysPermanent
562
574
  // — every case is config-time programming bug or an outbound-redirect
563
575
  // shape error that retry will not recover.
@@ -685,6 +697,7 @@ module.exports = {
685
697
  GuardHtmlError: GuardHtmlError,
686
698
  GuardSvgError: GuardSvgError,
687
699
  GuardFilenameError: GuardFilenameError,
700
+ GuardSqlError: GuardSqlError,
688
701
  GuardArchiveError: GuardArchiveError,
689
702
  GuardJsonError: GuardJsonError,
690
703
  GuardYamlError: GuardYamlError,
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ // framework-files — the single source of truth for the framework's on-disk
4
+ // state file names. Centralized (mirroring framework-schema's table-name
5
+ // registry) so a rename / relocation is a one-line change and no module
6
+ // hardcodes the literal. Every owner resolves its file name through
7
+ // fileName(logical) instead of embedding the string; the codebase-patterns
8
+ // `no-hardcoded-framework-file-name` detector drives the remaining owners
9
+ // onto this registry in reverse (a file that still hardcodes a registered
10
+ // name fails the gate once it leaves the migration backlog).
11
+ //
12
+ // Internal infrastructure (not a public b.* namespace) — consumed by db /
13
+ // vault / audit / backup the way constants.js is.
14
+
15
+ var { FrameworkError } = require("./framework-error");
16
+
17
+ // Canonical state file names. Each is a BARE file name (no path) joined onto
18
+ // the operator's dataDir / a sub-path by the owner. Security-/durability-
19
+ // sensitive files only — templated names (e.g. the hashed working-db file)
20
+ // are not registered here.
21
+ var DEFAULT_FILE_NAMES = Object.freeze({
22
+ dbEnc: "db.enc", // encrypted-at-rest database ciphertext
23
+ dbKeyEnc: "db.key.enc", // sealed database encryption key
24
+ vaultKey: "vault.key", // sealed vault keypair
25
+ auditTip: "audit.tip", // audit rollback-detection sidecar
26
+ auditSignKey: "audit-sign.key", // sealed audit-signing keypair
27
+ rowsEnc: "rows.enc", // archive/backup rows ciphertext member
28
+ checkpointEnc: "checkpoint.enc", // archive/backup checkpoint ciphertext member
29
+ });
30
+
31
+ var _overrides = {};
32
+
33
+ // fileName(logical) — resolve a logical file key to its configured (or
34
+ // default) bare file name. Defensive request-shape reader: throws on an
35
+ // unknown logical key (a typo is a boot-time bug, not a runtime default).
36
+ function fileName(logical) {
37
+ if (Object.prototype.hasOwnProperty.call(_overrides, logical)) return _overrides[logical];
38
+ if (Object.prototype.hasOwnProperty.call(DEFAULT_FILE_NAMES, logical)) {
39
+ return DEFAULT_FILE_NAMES[logical];
40
+ }
41
+ throw new FrameworkError(
42
+ "frameworkFiles.fileName: unknown logical file '" + logical + "'",
43
+ "framework-files/unknown");
44
+ }
45
+
46
+ // setFileName(logical, name) — config-time override of a state file name.
47
+ // THROW on bad input (entry-point tier): the override must name a known
48
+ // logical key and be a bare file name (no path separators, no '..') so it
49
+ // can't redirect a sealed-key write outside the data dir.
50
+ function setFileName(logical, name) {
51
+ if (!Object.prototype.hasOwnProperty.call(DEFAULT_FILE_NAMES, logical)) {
52
+ throw new FrameworkError(
53
+ "frameworkFiles.setFileName: unknown logical file '" + logical + "'",
54
+ "framework-files/unknown");
55
+ }
56
+ if (typeof name !== "string" || name.length === 0 ||
57
+ name.indexOf("/") !== -1 || name.indexOf("\\") !== -1 || name.indexOf("..") !== -1) {
58
+ throw new FrameworkError(
59
+ "frameworkFiles.setFileName: name must be a non-empty bare file name " +
60
+ "(no path separators or '..')", "framework-files/bad-name");
61
+ }
62
+ _overrides[logical] = name;
63
+ }
64
+
65
+ // Test/boot helper — drop all overrides back to the defaults.
66
+ function _resetForTest() { _overrides = {}; }
67
+
68
+ module.exports = {
69
+ fileName: fileName,
70
+ setFileName: setFileName,
71
+ DEFAULT_FILE_NAMES: DEFAULT_FILE_NAMES,
72
+ _resetForTest: _resetForTest,
73
+ };