@blamejs/core 0.9.14 → 0.9.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/a2a-tasks.js +2 -2
- package/lib/a2a.js +11 -11
- package/lib/acme.js +5 -5
- package/lib/ai-input.js +2 -2
- package/lib/api-key.js +4 -4
- package/lib/api-snapshot.js +6 -6
- package/lib/app-shutdown.js +2 -2
- package/lib/app.js +5 -5
- package/lib/archive.js +8 -8
- package/lib/argon2-builtin.js +2 -2
- package/lib/atomic-file.js +53 -53
- package/lib/audit-sign.js +8 -8
- package/lib/audit-tools.js +22 -22
- package/lib/auth/dpop.js +3 -3
- package/lib/auth/elevation-grant.js +3 -3
- package/lib/auth/fido-mds3.js +6 -6
- package/lib/auth/jwt-external.js +2 -2
- package/lib/auth/sd-jwt-vc.js +2 -2
- package/lib/backup/bundle.js +17 -17
- package/lib/backup/index.js +36 -36
- package/lib/budr.js +3 -3
- package/lib/bundler.js +20 -20
- package/lib/circuit-breaker.js +4 -4
- package/lib/cli.js +25 -26
- package/lib/cluster.js +2 -2
- package/lib/compliance-sanctions.js +2 -2
- package/lib/compliance.js +6 -7
- package/lib/config-drift.js +15 -15
- package/lib/config.js +6 -6
- package/lib/content-credentials.js +4 -4
- package/lib/credential-hash.js +7 -7
- package/lib/crypto-field.js +9 -9
- package/lib/daemon.js +19 -19
- package/lib/db-file-lifecycle.js +24 -24
- package/lib/db-schema.js +2 -2
- package/lib/db.js +34 -34
- package/lib/dev.js +10 -10
- package/lib/dr-runbook.js +5 -5
- package/lib/dual-control.js +2 -2
- package/lib/external-db-migrate.js +17 -17
- package/lib/external-db.js +2 -2
- package/lib/fdx.js +2 -2
- package/lib/file-upload.js +30 -30
- package/lib/flag-evaluation-context.js +2 -2
- package/lib/flag-providers.js +4 -4
- package/lib/gate-contract.js +5 -5
- package/lib/graphql-federation.js +4 -7
- package/lib/honeytoken.js +6 -6
- package/lib/http-client-cookie-jar.js +6 -6
- package/lib/http-client.js +18 -18
- package/lib/i18n.js +5 -5
- package/lib/keychain.js +5 -5
- package/lib/legal-hold.js +2 -2
- package/lib/local-db-thin.js +9 -9
- package/lib/log-stream-local.js +17 -17
- package/lib/log-stream-syslog.js +2 -2
- package/lib/log-stream.js +3 -3
- package/lib/log.js +2 -2
- package/lib/mail-bounce.js +2 -2
- package/lib/mail-mdn.js +2 -2
- package/lib/mail-srs.js +2 -2
- package/lib/mail.js +7 -7
- package/lib/mcp-tool-registry.js +6 -6
- package/lib/mcp.js +2 -2
- package/lib/metrics.js +2 -2
- package/lib/middleware/api-encrypt.js +16 -16
- package/lib/middleware/body-parser.js +18 -18
- package/lib/middleware/compression.js +3 -3
- package/lib/middleware/csp-nonce.js +4 -4
- package/lib/middleware/health.js +7 -7
- package/lib/middleware/idempotency-key.js +163 -63
- package/lib/middleware/require-bound-key.js +4 -4
- package/lib/middleware/require-mtls.js +4 -4
- package/lib/migrations.js +5 -5
- package/lib/mtls-ca.js +26 -26
- package/lib/mtls-engine-default.js +5 -5
- package/lib/network-byte-quota.js +2 -2
- package/lib/network-dns.js +2 -2
- package/lib/network-nts.js +2 -2
- package/lib/network-proxy.js +3 -3
- package/lib/network-smtp-policy.js +2 -2
- package/lib/network-tls.js +17 -17
- package/lib/network.js +25 -25
- package/lib/notify.js +11 -11
- package/lib/object-store/gcs-bucket-ops.js +2 -2
- package/lib/object-store/gcs.js +5 -5
- package/lib/object-store/index.js +6 -6
- package/lib/object-store/local.js +19 -19
- package/lib/object-store/sigv4.js +3 -3
- package/lib/observability-tracer.js +4 -4
- package/lib/otel-export.js +3 -3
- package/lib/pagination.js +5 -5
- package/lib/parsers/safe-env.js +3 -3
- package/lib/parsers/safe-xml.js +3 -3
- package/lib/pqc-gate.js +5 -5
- package/lib/pubsub-redis.js +2 -2
- package/lib/queue-local.js +3 -3
- package/lib/queue.js +2 -2
- package/lib/redis-client.js +4 -4
- package/lib/restore-bundle.js +17 -17
- package/lib/restore-rollback.js +34 -34
- package/lib/restore.js +16 -16
- package/lib/router.js +25 -25
- package/lib/sandbox.js +8 -8
- package/lib/sec-cyber.js +3 -3
- package/lib/security-assert.js +2 -2
- package/lib/seeders.js +6 -6
- package/lib/self-update.js +18 -18
- package/lib/session-device-binding.js +2 -2
- package/lib/static.js +22 -22
- package/lib/template.js +19 -19
- package/lib/testing.js +9 -9
- package/lib/tls-exporter.js +5 -5
- package/lib/tracing.js +3 -3
- package/lib/vault/index.js +11 -11
- package/lib/vault/passphrase-ops.js +37 -37
- package/lib/vault/passphrase-source.js +2 -2
- package/lib/vault/rotate.js +64 -64
- package/lib/vault/seal-pem-file.js +26 -26
- package/lib/vault-aad.js +5 -5
- package/lib/watcher.js +22 -22
- package/lib/webhook.js +10 -10
- package/lib/worker-pool.js +6 -6
- package/lib/ws-client.js +6 -6
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/restore-rollback.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @intro
|
|
8
8
|
* Backup-restore safety net — atomic dataDir swap with a versioned
|
|
9
|
-
* rollback
|
|
9
|
+
* rollback nodePath. The primitive `b.restore` calls to put a
|
|
10
10
|
* freshly-decrypted bundle into place: filesystem rename is atomic
|
|
11
11
|
* on POSIX (and on Windows when nothing has the dir open), so the
|
|
12
12
|
* swap either fully completes or the previous `dataDir` is
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
* corrupting state.
|
|
40
40
|
*
|
|
41
41
|
* @card
|
|
42
|
-
* Backup-restore safety net — atomic dataDir swap with a versioned rollback
|
|
42
|
+
* Backup-restore safety net — atomic dataDir swap with a versioned rollback nodePath.
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
var
|
|
46
|
-
var
|
|
45
|
+
var nodeFs = require("fs");
|
|
46
|
+
var nodePath = require("path");
|
|
47
47
|
var atomicFile = require("./atomic-file");
|
|
48
48
|
var C = require("./constants");
|
|
49
|
-
var
|
|
49
|
+
var numericBounds = require("./numeric-bounds");
|
|
50
50
|
var safeJson = require("./safe-json");
|
|
51
51
|
var { defineClass } = require("./framework-error");
|
|
52
52
|
|
|
@@ -94,7 +94,7 @@ function _resolveRollbackRoot(opts) {
|
|
|
94
94
|
*/
|
|
95
95
|
function swap(opts) {
|
|
96
96
|
opts = opts || {};
|
|
97
|
-
if (typeof opts.stagingDir !== "string" || !
|
|
97
|
+
if (typeof opts.stagingDir !== "string" || !nodeFs.existsSync(opts.stagingDir)) {
|
|
98
98
|
throw new RestoreRollbackError("restore-rollback/no-staging",
|
|
99
99
|
"swap: opts.stagingDir is required and must exist");
|
|
100
100
|
}
|
|
@@ -106,20 +106,20 @@ function swap(opts) {
|
|
|
106
106
|
atomicFile.ensureDir(rollbackRoot);
|
|
107
107
|
|
|
108
108
|
var swappedAt = atomicFile.pathTimestamp();
|
|
109
|
-
var rollbackPath =
|
|
110
|
-
var markerPath =
|
|
109
|
+
var rollbackPath = nodePath.join(rollbackRoot, swappedAt);
|
|
110
|
+
var markerPath = nodePath.join(rollbackRoot, swappedAt + ".marker.json");
|
|
111
111
|
|
|
112
|
-
if (
|
|
112
|
+
if (nodeFs.existsSync(rollbackPath) || nodeFs.existsSync(markerPath)) {
|
|
113
113
|
throw new RestoreRollbackError("restore-rollback/collision",
|
|
114
114
|
"swap: a rollback at " + rollbackPath + " already exists — refusing to overwrite");
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
var hadDataDir =
|
|
117
|
+
var hadDataDir = nodeFs.existsSync(opts.dataDir);
|
|
118
118
|
|
|
119
|
-
// Step 1: rename current dataDir → rollback
|
|
119
|
+
// Step 1: rename current dataDir → rollback nodePath. Skipped on first
|
|
120
120
|
// restore (no existing dataDir).
|
|
121
121
|
if (hadDataDir) {
|
|
122
|
-
try {
|
|
122
|
+
try { nodeFs.renameSync(opts.dataDir, rollbackPath); }
|
|
123
123
|
catch (e) {
|
|
124
124
|
throw new RestoreRollbackError("restore-rollback/rename-existing-failed",
|
|
125
125
|
"swap: could not move existing dataDir to rollback: " + ((e && e.message) || String(e)));
|
|
@@ -127,11 +127,11 @@ function swap(opts) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// Step 2: rename staging → dataDir
|
|
130
|
-
try {
|
|
130
|
+
try { nodeFs.renameSync(opts.stagingDir, opts.dataDir); }
|
|
131
131
|
catch (e) {
|
|
132
132
|
// Step 2 failed — try to undo step 1 so the operator's dataDir is back
|
|
133
133
|
if (hadDataDir) {
|
|
134
|
-
try {
|
|
134
|
+
try { nodeFs.renameSync(rollbackPath, opts.dataDir); }
|
|
135
135
|
catch (_e) { /* dataDir is now in rollbackPath; operator must recover manually */ }
|
|
136
136
|
}
|
|
137
137
|
throw new RestoreRollbackError("restore-rollback/rename-staging-failed",
|
|
@@ -148,7 +148,7 @@ function swap(opts) {
|
|
|
148
148
|
operator: opts.marker || null,
|
|
149
149
|
};
|
|
150
150
|
try {
|
|
151
|
-
|
|
151
|
+
nodeFs.writeFileSync(markerPath, JSON.stringify(marker, null, 2) + "\n", { mode: 0o600 });
|
|
152
152
|
} catch (_e) { /* marker write is best-effort */ }
|
|
153
153
|
|
|
154
154
|
return {
|
|
@@ -193,19 +193,19 @@ async function rollback(opts) {
|
|
|
193
193
|
throw new RestoreRollbackError("restore-rollback/no-datadir",
|
|
194
194
|
"rollback: opts.dataDir is required");
|
|
195
195
|
}
|
|
196
|
-
if (typeof opts.rollbackPath !== "string" || !
|
|
196
|
+
if (typeof opts.rollbackPath !== "string" || !nodeFs.existsSync(opts.rollbackPath)) {
|
|
197
197
|
throw new RestoreRollbackError("restore-rollback/no-rollback",
|
|
198
198
|
"rollback: opts.rollbackPath is required and must exist");
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
// Move the current dataDir aside (so the rollback's rename target is empty)
|
|
202
202
|
var discardedAt = null;
|
|
203
|
-
if (
|
|
203
|
+
if (nodeFs.existsSync(opts.dataDir)) {
|
|
204
204
|
var rollbackRoot = _resolveRollbackRoot(opts);
|
|
205
205
|
atomicFile.ensureDir(rollbackRoot);
|
|
206
206
|
discardedAt = atomicFile.pathTimestamp();
|
|
207
|
-
var discardedPath =
|
|
208
|
-
try {
|
|
207
|
+
var discardedPath = nodePath.join(rollbackRoot, "discarded-" + discardedAt);
|
|
208
|
+
try { nodeFs.renameSync(opts.dataDir, discardedPath); }
|
|
209
209
|
catch (e) {
|
|
210
210
|
throw new RestoreRollbackError("restore-rollback/rename-existing-failed",
|
|
211
211
|
"rollback: could not move current dataDir aside: " + ((e && e.message) || String(e)));
|
|
@@ -214,7 +214,7 @@ async function rollback(opts) {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
// Rename the rollback dir back into dataDir's place
|
|
217
|
-
try {
|
|
217
|
+
try { nodeFs.renameSync(opts.rollbackPath, opts.dataDir); }
|
|
218
218
|
catch (e) {
|
|
219
219
|
throw new RestoreRollbackError("restore-rollback/rollback-rename-failed",
|
|
220
220
|
"rollback: could not move rollback into dataDir: " + ((e && e.message) || String(e)) +
|
|
@@ -223,7 +223,7 @@ async function rollback(opts) {
|
|
|
223
223
|
|
|
224
224
|
// Best-effort: clean up the marker file alongside the rollback path
|
|
225
225
|
var markerPath = opts.rollbackPath + ".marker.json";
|
|
226
|
-
try { if (
|
|
226
|
+
try { if (nodeFs.existsSync(markerPath)) nodeFs.unlinkSync(markerPath); }
|
|
227
227
|
catch (_e) { /* marker cleanup is best-effort */ }
|
|
228
228
|
|
|
229
229
|
return {
|
|
@@ -258,22 +258,22 @@ async function rollback(opts) {
|
|
|
258
258
|
function list(opts) {
|
|
259
259
|
opts = opts || {};
|
|
260
260
|
var rollbackRoot = _resolveRollbackRoot(opts);
|
|
261
|
-
if (!
|
|
262
|
-
var entries =
|
|
261
|
+
if (!nodeFs.existsSync(rollbackRoot)) return [];
|
|
262
|
+
var entries = nodeFs.readdirSync(rollbackRoot, { withFileTypes: true });
|
|
263
263
|
var out = [];
|
|
264
264
|
for (var i = 0; i < entries.length; i++) {
|
|
265
265
|
if (!entries[i].isDirectory()) continue;
|
|
266
266
|
var name = entries[i].name;
|
|
267
267
|
if (name.indexOf("discarded-") === 0) continue; // discarded dirs aren't restore points
|
|
268
|
-
var p =
|
|
268
|
+
var p = nodePath.join(rollbackRoot, name);
|
|
269
269
|
var markerPath = p + ".marker.json";
|
|
270
270
|
var marker = null;
|
|
271
|
-
if (
|
|
272
|
-
try { marker = safeJson.parse(
|
|
271
|
+
if (nodeFs.existsSync(markerPath)) {
|
|
272
|
+
try { marker = safeJson.parse(nodeFs.readFileSync(markerPath, "utf8"), { maxBytes: C.BYTES.kib(64) }); }
|
|
273
273
|
catch (_e) { marker = null; }
|
|
274
274
|
}
|
|
275
275
|
var stat;
|
|
276
|
-
try { stat =
|
|
276
|
+
try { stat = nodeFs.statSync(p); } catch (_e) { continue; }
|
|
277
277
|
out.push({
|
|
278
278
|
rollbackPath: p,
|
|
279
279
|
swappedAt: (marker && marker.swappedAt) || stat.mtime.toISOString(),
|
|
@@ -311,18 +311,18 @@ function list(opts) {
|
|
|
311
311
|
*/
|
|
312
312
|
function purge(opts) {
|
|
313
313
|
opts = opts || {};
|
|
314
|
-
|
|
314
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.keep,
|
|
315
315
|
"restore-rollback.purge: opts.keep", RestoreRollbackError, "restore-rollback/bad-keep");
|
|
316
316
|
var keep = opts.keep !== undefined ? opts.keep : 0;
|
|
317
317
|
var rollbackRoot = _resolveRollbackRoot(opts);
|
|
318
|
-
if (!
|
|
318
|
+
if (!nodeFs.existsSync(rollbackRoot)) return { kept: keep, deleted: [] };
|
|
319
319
|
// Always sweep "discarded-*" dirs — they're never restore points
|
|
320
|
-
var entries =
|
|
320
|
+
var entries = nodeFs.readdirSync(rollbackRoot, { withFileTypes: true });
|
|
321
321
|
var deleted = [];
|
|
322
322
|
for (var i = 0; i < entries.length; i++) {
|
|
323
323
|
if (entries[i].isDirectory() && entries[i].name.indexOf("discarded-") === 0) {
|
|
324
|
-
var p =
|
|
325
|
-
try {
|
|
324
|
+
var p = nodePath.join(rollbackRoot, entries[i].name);
|
|
325
|
+
try { nodeFs.rmSync(p, { recursive: true, force: true }); deleted.push(p); }
|
|
326
326
|
catch (_e) { /* best-effort */ }
|
|
327
327
|
}
|
|
328
328
|
}
|
|
@@ -333,9 +333,9 @@ function purge(opts) {
|
|
|
333
333
|
for (var j = 0; j < toDelete.length; j++) {
|
|
334
334
|
var rbPath = toDelete[j].rollbackPath;
|
|
335
335
|
var mkPath = rbPath + ".marker.json";
|
|
336
|
-
try {
|
|
336
|
+
try { nodeFs.rmSync(rbPath, { recursive: true, force: true }); deleted.push(rbPath); }
|
|
337
337
|
catch (_e) { /* best-effort */ }
|
|
338
|
-
try { if (
|
|
338
|
+
try { if (nodeFs.existsSync(mkPath)) nodeFs.unlinkSync(mkPath); }
|
|
339
339
|
catch (_e) { /* best-effort */ }
|
|
340
340
|
}
|
|
341
341
|
return { kept: keep, deleted: deleted };
|
package/lib/restore.js
CHANGED
|
@@ -51,11 +51,11 @@
|
|
|
51
51
|
* manual recovery)
|
|
52
52
|
*/
|
|
53
53
|
|
|
54
|
-
var
|
|
54
|
+
var nodeFs = require("fs");
|
|
55
55
|
var os = require("os");
|
|
56
|
-
var
|
|
56
|
+
var nodePath = require("path");
|
|
57
57
|
var C = require("./constants");
|
|
58
|
-
var
|
|
58
|
+
var bCrypto = require("./crypto");
|
|
59
59
|
var numericChecks = require("./numeric-checks");
|
|
60
60
|
var restoreBundle = require("./restore-bundle");
|
|
61
61
|
var restoreRollback = require("./restore-rollback");
|
|
@@ -128,11 +128,11 @@ function create(opts) {
|
|
|
128
128
|
while (stack.length > 0) {
|
|
129
129
|
var current = stack.pop();
|
|
130
130
|
var entries;
|
|
131
|
-
try { entries =
|
|
131
|
+
try { entries = nodeFs.readdirSync(current, { withFileTypes: true }); }
|
|
132
132
|
catch (_e) { continue; }
|
|
133
133
|
for (var i = 0; i < entries.length; i++) {
|
|
134
134
|
var entry = entries[i];
|
|
135
|
-
var full =
|
|
135
|
+
var full = nodePath.join(current, entry.name);
|
|
136
136
|
if (entry.isDirectory()) {
|
|
137
137
|
stack.push(full);
|
|
138
138
|
} else if (entry.isFile()) {
|
|
@@ -141,7 +141,7 @@ function create(opts) {
|
|
|
141
141
|
return { tooManyFiles: true, fileCount: fileCount };
|
|
142
142
|
}
|
|
143
143
|
try {
|
|
144
|
-
totalBytes +=
|
|
144
|
+
totalBytes += nodeFs.statSync(full).size;
|
|
145
145
|
if (totalBytes > maxPulledBytes) {
|
|
146
146
|
return { tooManyBytes: true, totalBytes: totalBytes };
|
|
147
147
|
}
|
|
@@ -200,8 +200,8 @@ function create(opts) {
|
|
|
200
200
|
"inspect: bundle '" + bundleId + "' not in storage");
|
|
201
201
|
}
|
|
202
202
|
await _preflightBundleSize(bundleId);
|
|
203
|
-
var pullDir =
|
|
204
|
-
"blamejs-restore-inspect-" +
|
|
203
|
+
var pullDir = nodePath.join(os.tmpdir(),
|
|
204
|
+
"blamejs-restore-inspect-" + bCrypto.generateToken(4));
|
|
205
205
|
try {
|
|
206
206
|
await storage.readBundle(bundleId, pullDir);
|
|
207
207
|
var pulled = _walkPullDirFootprint(pullDir);
|
|
@@ -217,7 +217,7 @@ function create(opts) {
|
|
|
217
217
|
}
|
|
218
218
|
return restoreBundle.inspect({ bundleDir: pullDir });
|
|
219
219
|
} finally {
|
|
220
|
-
try {
|
|
220
|
+
try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
@@ -234,13 +234,13 @@ function create(opts) {
|
|
|
234
234
|
"run: bundle '" + bundleId + "' not in storage");
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
var pullId =
|
|
238
|
-
var pullDir =
|
|
239
|
-
var stagingDir =
|
|
237
|
+
var pullId = bCrypto.generateToken(4);
|
|
238
|
+
var pullDir = nodePath.join(os.tmpdir(), "blamejs-restore-pull-" + pullId);
|
|
239
|
+
var stagingDir = nodePath.join(os.tmpdir(), "blamejs-restore-staging-" + pullId);
|
|
240
240
|
|
|
241
241
|
function _cleanupTmp() {
|
|
242
|
-
try {
|
|
243
|
-
try {
|
|
242
|
+
try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
243
|
+
try { nodeFs.rmSync(stagingDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
244
244
|
}
|
|
245
245
|
|
|
246
246
|
// 1. Pull bundle out of storage
|
|
@@ -319,7 +319,7 @@ function create(opts) {
|
|
|
319
319
|
} catch (e) {
|
|
320
320
|
// Pull dir is safe to clean (the source bundle is in storage);
|
|
321
321
|
// staging stays for manual recovery.
|
|
322
|
-
try {
|
|
322
|
+
try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
323
323
|
_emitAudit("restore.failure",
|
|
324
324
|
{ bundleId: bundleId, reason: "swap: " + ((e && e.message) || String(e)) },
|
|
325
325
|
"failure");
|
|
@@ -331,7 +331,7 @@ function create(opts) {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
// 4. Clean up the pull dir (source bundle still in storage)
|
|
334
|
-
try {
|
|
334
|
+
try { nodeFs.rmSync(pullDir, { recursive: true, force: true }); } catch (_e) { /* best-effort tmpdir cleanup */ }
|
|
335
335
|
|
|
336
336
|
var summary = {
|
|
337
337
|
bundleId: bundleId,
|
package/lib/router.js
CHANGED
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
*/
|
|
35
35
|
var http = require("http");
|
|
36
36
|
var http2 = require("http2");
|
|
37
|
-
var
|
|
38
|
-
var
|
|
37
|
+
var nodeFs = require("fs");
|
|
38
|
+
var nodePath = require("path");
|
|
39
39
|
var C = require("./constants");
|
|
40
40
|
var requestHelpers = require("./request-helpers");
|
|
41
41
|
var lazyRequire = require("./lazy-require");
|
|
@@ -46,11 +46,11 @@ var websocket = require("./websocket");
|
|
|
46
46
|
var { boot } = require("./log");
|
|
47
47
|
var { RouterError } = require("./framework-error");
|
|
48
48
|
|
|
49
|
-
var
|
|
49
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
50
50
|
// compliance — lazy because router.js is required during boot before
|
|
51
51
|
// the operator's `b.compliance.set(...)` runs; the posture lookup only
|
|
52
52
|
// matters at listen() time, well after boot finishes.
|
|
53
|
-
var
|
|
53
|
+
var compliance = lazyRequire(function () { return require("./compliance"); });
|
|
54
54
|
|
|
55
55
|
var log = boot("router");
|
|
56
56
|
var HTTP_STATUS = requestHelpers.HTTP_STATUS;
|
|
@@ -296,8 +296,8 @@ class Router {
|
|
|
296
296
|
this.routes = [];
|
|
297
297
|
this.middleware = [];
|
|
298
298
|
// WebSocket routes are kept separate from HTTP routes — they're
|
|
299
|
-
// matched on the upgrade / Extended CONNECT
|
|
300
|
-
// verb. Map<
|
|
299
|
+
// matched on the upgrade / Extended CONNECT nodePath, not on a method
|
|
300
|
+
// verb. Map<nodePath, { handler, opts }>.
|
|
301
301
|
this._wsRoutes = new Map();
|
|
302
302
|
// Active WebSocket connections opened through router.ws(). Tracked
|
|
303
303
|
// so router.closeWebSockets() can do a clean rolling-shutdown.
|
|
@@ -407,7 +407,7 @@ class Router {
|
|
|
407
407
|
// process.exit(0);
|
|
408
408
|
//
|
|
409
409
|
// Tests use the same primitive in teardown — no parallel cleanup
|
|
410
|
-
//
|
|
410
|
+
// nodePath, no h1-upgrade detached-socket workaround.
|
|
411
411
|
async closeWebSockets(opts) {
|
|
412
412
|
opts = opts || {};
|
|
413
413
|
var timeoutMs = typeof opts.timeoutMs === "number" ? opts.timeoutMs : C.TIME.seconds(5);
|
|
@@ -603,7 +603,7 @@ class Router {
|
|
|
603
603
|
|
|
604
604
|
// ---- WebSocket route registration ----
|
|
605
605
|
//
|
|
606
|
-
// ws(
|
|
606
|
+
// ws(nodePath, handler, opts?)
|
|
607
607
|
// path — exact match. Path-param patterns aren't supported on
|
|
608
608
|
// upgrade requests; operators that need dynamic paths
|
|
609
609
|
// register one ws route per stable shape.
|
|
@@ -733,12 +733,12 @@ class Router {
|
|
|
733
733
|
if (declared !== "replay-cache") return declared;
|
|
734
734
|
var active = null;
|
|
735
735
|
try {
|
|
736
|
-
var
|
|
737
|
-
if (
|
|
736
|
+
var complianceInst = compliance();
|
|
737
|
+
if (complianceInst && typeof complianceInst.current === "function") active = complianceInst.current();
|
|
738
738
|
} catch (_e) { /* compliance not initialized */ }
|
|
739
739
|
if (active && TLS_0RTT_FAILCLOSED_POSTURES.indexOf(active) !== -1) {
|
|
740
740
|
try {
|
|
741
|
-
|
|
741
|
+
audit().safeEmit({
|
|
742
742
|
action: "tls.0rtt.refused",
|
|
743
743
|
outcome: "denied",
|
|
744
744
|
metadata: { reason: "posture-failclosed", posture: active, declared: declared },
|
|
@@ -759,7 +759,7 @@ class Router {
|
|
|
759
759
|
if (String(earlyDataHeader).trim() !== "1") return null; // RFC 8470: only "1" means early data
|
|
760
760
|
if (posture === "refuse") {
|
|
761
761
|
try {
|
|
762
|
-
|
|
762
|
+
audit().safeEmit({
|
|
763
763
|
action: "tls.0rtt.refused",
|
|
764
764
|
outcome: "denied",
|
|
765
765
|
metadata: { reason: "posture-refuse", method: req.method, url: req.url },
|
|
@@ -783,7 +783,7 @@ class Router {
|
|
|
783
783
|
var key = hash.digest("hex");
|
|
784
784
|
if (this._tls0RttReplayCache.has(key)) {
|
|
785
785
|
try {
|
|
786
|
-
|
|
786
|
+
audit().safeEmit({
|
|
787
787
|
action: "tls.0rtt.replayed",
|
|
788
788
|
outcome: "denied",
|
|
789
789
|
metadata: { reason: "cache-hit", method: req.method, url: req.url,
|
|
@@ -805,7 +805,7 @@ class Router {
|
|
|
805
805
|
}
|
|
806
806
|
this._tls0RttReplayCache.set(key, nowMs + TLS_0RTT_REPLAY_WINDOW_MS);
|
|
807
807
|
try {
|
|
808
|
-
|
|
808
|
+
audit().safeEmit({
|
|
809
809
|
action: "tls.0rtt.accepted",
|
|
810
810
|
outcome: "success",
|
|
811
811
|
metadata: { method: req.method, url: req.url, windowMs: TLS_0RTT_REPLAY_WINDOW_MS },
|
|
@@ -889,7 +889,7 @@ class Router {
|
|
|
889
889
|
});
|
|
890
890
|
} catch (parseErr) {
|
|
891
891
|
try {
|
|
892
|
-
|
|
892
|
+
audit().safeEmit({
|
|
893
893
|
action: "router.redirect.cross_origin.refused",
|
|
894
894
|
outcome: "denied",
|
|
895
895
|
metadata: {
|
|
@@ -913,7 +913,7 @@ class Router {
|
|
|
913
913
|
}
|
|
914
914
|
if (!match) {
|
|
915
915
|
try {
|
|
916
|
-
|
|
916
|
+
audit().safeEmit({
|
|
917
917
|
action: "router.redirect.cross_origin.refused",
|
|
918
918
|
outcome: "denied",
|
|
919
919
|
metadata: {
|
|
@@ -930,7 +930,7 @@ class Router {
|
|
|
930
930
|
);
|
|
931
931
|
}
|
|
932
932
|
try {
|
|
933
|
-
|
|
933
|
+
audit().safeEmit({
|
|
934
934
|
action: "router.redirect.cross_origin.allowed",
|
|
935
935
|
outcome: "success",
|
|
936
936
|
metadata: { target: url, origin: targetOrigin },
|
|
@@ -1064,7 +1064,7 @@ class Router {
|
|
|
1064
1064
|
// (a clean peer would not initiate after GOAWAY).
|
|
1065
1065
|
h2session.on("stream", function (stream) {
|
|
1066
1066
|
if (h2session._blamejsGoawaySent) {
|
|
1067
|
-
try {
|
|
1067
|
+
try { audit().safeEmit({
|
|
1068
1068
|
action: "http2.window_update.refused",
|
|
1069
1069
|
outcome: "denied",
|
|
1070
1070
|
metadata: { reason: "post-goaway-stream", streamId: stream.id || null,
|
|
@@ -1089,7 +1089,7 @@ class Router {
|
|
|
1089
1089
|
// server's emitter list clean for HTTP-only deployments.
|
|
1090
1090
|
if (self._wsRoutes.size > 0) {
|
|
1091
1091
|
// h1 upgrade event — fires for "Upgrade: websocket" from h1
|
|
1092
|
-
// clients. Routes by
|
|
1092
|
+
// clients. Routes by nodePath; refuses with 426 in h2-only mode.
|
|
1093
1093
|
server.on("upgrade", function (req, socket, head) {
|
|
1094
1094
|
var pathname = String(req.url || "/").split("?")[0];
|
|
1095
1095
|
var route = self._wsRoutes.get(pathname);
|
|
@@ -1201,18 +1201,18 @@ class Router {
|
|
|
1201
1201
|
*/
|
|
1202
1202
|
// Static file serving middleware
|
|
1203
1203
|
function serveStatic(dir) {
|
|
1204
|
-
var root =
|
|
1204
|
+
var root = nodePath.resolve(dir);
|
|
1205
1205
|
return (req, res, next) => {
|
|
1206
1206
|
if (req.method !== "GET") return next();
|
|
1207
1207
|
var rel = req.pathname;
|
|
1208
1208
|
if (rel.includes("\0")) return next();
|
|
1209
|
-
var filePath =
|
|
1209
|
+
var filePath = nodePath.resolve(nodePath.join(root, rel));
|
|
1210
1210
|
if (!filePath.startsWith(root)) return next();
|
|
1211
|
-
if (!
|
|
1211
|
+
if (!nodeFs.existsSync(filePath) || nodeFs.statSync(filePath).isDirectory()) return next();
|
|
1212
1212
|
|
|
1213
|
-
var ext =
|
|
1213
|
+
var ext = nodePath.extname(filePath).toLowerCase();
|
|
1214
1214
|
var mime = MIME_TYPES[ext] || "application/octet-stream";
|
|
1215
|
-
var stat =
|
|
1215
|
+
var stat = nodeFs.statSync(filePath);
|
|
1216
1216
|
var hasVersion = req.url && req.url.includes("?v=");
|
|
1217
1217
|
var cacheControl = hasVersion
|
|
1218
1218
|
? "public, max-age=31536000, immutable"
|
|
@@ -1222,7 +1222,7 @@ function serveStatic(dir) {
|
|
|
1222
1222
|
"Content-Length": stat.size,
|
|
1223
1223
|
"Cache-Control": cacheControl,
|
|
1224
1224
|
});
|
|
1225
|
-
|
|
1225
|
+
nodeFs.createReadStream(filePath).pipe(res);
|
|
1226
1226
|
};
|
|
1227
1227
|
}
|
|
1228
1228
|
|
package/lib/sandbox.js
CHANGED
|
@@ -78,11 +78,11 @@
|
|
|
78
78
|
* arbitrary source from the public internet.
|
|
79
79
|
*/
|
|
80
80
|
|
|
81
|
-
var
|
|
81
|
+
var nodePath = require("path");
|
|
82
82
|
var lazyRequire = require("./lazy-require");
|
|
83
83
|
var validateOpts = require("./validate-opts");
|
|
84
84
|
var numericBounds = require("./numeric-bounds");
|
|
85
|
-
var
|
|
85
|
+
var C = require("./constants");
|
|
86
86
|
var { SandboxError } = require("./framework-error");
|
|
87
87
|
|
|
88
88
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
@@ -110,14 +110,14 @@ var ALWAYS_AVAILABLE = Object.freeze([
|
|
|
110
110
|
"Promise", "Error", "TypeError", "RangeError", "RegExp",
|
|
111
111
|
]);
|
|
112
112
|
|
|
113
|
-
var WORKER_PATH =
|
|
113
|
+
var WORKER_PATH = nodePath.resolve(__dirname, "sandbox-worker.js");
|
|
114
114
|
|
|
115
115
|
// Default caps. Sourced from C.* helpers so the unit lives at the call site.
|
|
116
116
|
var DEFAULT_TIMEOUT_MS = 250;
|
|
117
|
-
var MAX_TIMEOUT_MS =
|
|
118
|
-
var DEFAULT_MAX_BYTES =
|
|
119
|
-
var MAX_MAX_BYTES =
|
|
120
|
-
var MIN_MAX_BYTES =
|
|
117
|
+
var MAX_TIMEOUT_MS = C.TIME.seconds(10);
|
|
118
|
+
var DEFAULT_MAX_BYTES = C.BYTES.mib(64);
|
|
119
|
+
var MAX_MAX_BYTES = C.BYTES.gib(1);
|
|
120
|
+
var MIN_MAX_BYTES = C.BYTES.mib(4);
|
|
121
121
|
|
|
122
122
|
function _validateAllowed(allowed) {
|
|
123
123
|
if (allowed === undefined || allowed === null) return [];
|
|
@@ -217,7 +217,7 @@ function run(opts) {
|
|
|
217
217
|
// floor so the worker can boot. Round each cap down to a MiB integer.
|
|
218
218
|
// Floors / caps are quanta of MiB chosen to fit a small embedded
|
|
219
219
|
// worker; passed straight to v8's resourceLimits.
|
|
220
|
-
var oneMib =
|
|
220
|
+
var oneMib = C.BYTES.mib(1);
|
|
221
221
|
// The MiB-unit caps below are integers passed directly to v8's
|
|
222
222
|
// resourceLimits (already typed in MiB by the v8 API), not byte
|
|
223
223
|
// counts - the constants helpers don't apply.
|
package/lib/sec-cyber.js
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
var audit = require("./audit");
|
|
70
70
|
var C = require("./constants");
|
|
71
71
|
var validateOpts = require("./validate-opts");
|
|
72
|
-
var
|
|
72
|
+
var numericBounds = require("./numeric-bounds");
|
|
73
73
|
var { defineClass } = require("./framework-error");
|
|
74
74
|
var SecCyberError = defineClass("SecCyberError", { alwaysPermanent: true });
|
|
75
75
|
|
|
@@ -104,9 +104,9 @@ function eightKArtifact(opts) {
|
|
|
104
104
|
"secCyber.eightKArtifact: registrant.name", SecCyberError, "BAD_REGISTRANT_NAME");
|
|
105
105
|
validateOpts.requireNonEmptyString(opts.registrant.cik,
|
|
106
106
|
"secCyber.eightKArtifact: registrant.cik", SecCyberError, "BAD_CIK");
|
|
107
|
-
|
|
107
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.detectedAt,
|
|
108
108
|
"secCyber.eightKArtifact: detectedAt", SecCyberError, "BAD_DETECTED_AT");
|
|
109
|
-
|
|
109
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.materialityDeterminedAt,
|
|
110
110
|
"secCyber.eightKArtifact: materialityDeterminedAt", SecCyberError, "BAD_MAT_AT");
|
|
111
111
|
|
|
112
112
|
if (FINDINGS.indexOf(opts.materialityFinding) === -1) {
|
package/lib/security-assert.js
CHANGED
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
* non-function extra entry, etc.) so the operator catches typos at
|
|
68
68
|
* boot, not at the moment they were trying to gate the boot.
|
|
69
69
|
*/
|
|
70
|
-
var
|
|
70
|
+
var nodeFs = require("fs");
|
|
71
71
|
var nodeTls = require("node:tls");
|
|
72
72
|
var lazyRequire = require("./lazy-require");
|
|
73
73
|
var safeEnv = require("./parsers/safe-env");
|
|
@@ -286,7 +286,7 @@ async function assertProduction(opts) {
|
|
|
286
286
|
if (typeof opts.dataDir === "string" && opts.dataDir.length > 0 && process.platform !== "win32") {
|
|
287
287
|
var maxMode = typeof opts.maxDataDirMode === "number" ? opts.maxDataDirMode : 0o750;
|
|
288
288
|
try {
|
|
289
|
-
var stat =
|
|
289
|
+
var stat = nodeFs.statSync(opts.dataDir);
|
|
290
290
|
var mode = stat.mode & 0o777;
|
|
291
291
|
if (mode > maxMode) {
|
|
292
292
|
failures.push({ ok: false, code: "security/datadir-permissions",
|
package/lib/seeders.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* module.exports = {
|
|
17
17
|
* description: "Create default admin user for local dev",
|
|
18
|
-
* // Optional — when omitted, the env is inferred from the
|
|
18
|
+
* // Optional — when omitted, the env is inferred from the nodePath.
|
|
19
19
|
* // When present, this seed only applies under one of these envs.
|
|
20
20
|
* envs: ["dev", "test"],
|
|
21
21
|
* // Default false — applied once and recorded in registry.
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
* applied state)
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
|
-
var
|
|
57
|
+
var nodePath = require("path");
|
|
58
58
|
var atomicFile = require("./atomic-file");
|
|
59
59
|
var C = require("./constants");
|
|
60
60
|
var dbSchema = require("./db-schema");
|
|
@@ -67,7 +67,7 @@ var { SeederError } = require("./framework-error");
|
|
|
67
67
|
|
|
68
68
|
var log = boot("seeders");
|
|
69
69
|
|
|
70
|
-
var
|
|
70
|
+
var db = lazyRequire(function () { return require("./db"); });
|
|
71
71
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
72
72
|
|
|
73
73
|
var _err = SeederError.factory;
|
|
@@ -151,7 +151,7 @@ function _validateCreateOpts(opts) {
|
|
|
151
151
|
|
|
152
152
|
function _resolveDb(opts) {
|
|
153
153
|
if (opts && opts.db && typeof opts.db.prepare === "function") return opts.db;
|
|
154
|
-
var d =
|
|
154
|
+
var d = db();
|
|
155
155
|
if (typeof d.prepare !== "function") {
|
|
156
156
|
throw _err("NO_DB", "seeders: no db handle: pass opts.db or initialize b.db before create()");
|
|
157
157
|
}
|
|
@@ -161,7 +161,7 @@ function _resolveDb(opts) {
|
|
|
161
161
|
// ---- Directory walking + seed loading ----
|
|
162
162
|
|
|
163
163
|
function _envDir(rootDir, env) {
|
|
164
|
-
return
|
|
164
|
+
return nodePath.join(rootDir, env);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
function _listSeedFiles(rootDir, env) {
|
|
@@ -171,7 +171,7 @@ function _listSeedFiles(rootDir, env) {
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
function _loadSeed(rootDir, env, file) {
|
|
174
|
-
var fullPath =
|
|
174
|
+
var fullPath = nodePath.join(_envDir(rootDir, env), file);
|
|
175
175
|
// Drop require cache for this path so a test rewriting a fixture
|
|
176
176
|
// between calls picks it up. Production restarts the process anyway.
|
|
177
177
|
try { delete require.cache[require.resolve(fullPath)]; } catch (_e) { /* not yet cached */ }
|