@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.
- package/CHANGELOG.md +1 -0
- 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/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/config-drift.js +15 -15
- package/lib/content-credentials.js +4 -4
- package/lib/credential-hash.js +3 -3
- 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 +35 -35
- 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 +2 -2
- package/lib/external-db.js +2 -2
- package/lib/fdx.js +2 -2
- package/lib/file-upload.js +30 -30
- 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 +9 -9
- 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/mail-bounce.js +2 -2
- package/lib/mail-mdn.js +2 -2
- package/lib/mail-srs.js +2 -2
- package/lib/mail.js +4 -4
- 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 +16 -16
- 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/migrations.js +3 -3
- package/lib/mtls-ca.js +26 -26
- package/lib/mtls-engine-default.js +5 -5
- 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 +13 -13
- package/lib/notify.js +3 -3
- 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-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 +18 -18
- package/lib/restore-rollback.js +34 -34
- package/lib/restore.js +16 -16
- package/lib/router.js +13 -13
- package/lib/sandbox.js +8 -8
- package/lib/sec-cyber.js +3 -3
- package/lib/security-assert.js +2 -2
- package/lib/seeders.js +4 -4
- 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 +7 -7
- 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/watcher.js +23 -23
- package/lib/webhook.js +10 -10
- package/lib/worker-pool.js +6 -6
- package/lib/ws-client.js +4 -4
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/dev.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* spawned app's logs unchanged.
|
|
12
12
|
*
|
|
13
13
|
* The hot-reload loop spawns the app as a child process, watches the
|
|
14
|
-
* source directories with `
|
|
14
|
+
* source directories with `nodeFs.watch({ recursive: true })`, and
|
|
15
15
|
* restarts the child when an unignored file changes. On-disk state
|
|
16
16
|
* (vault keys, encrypted DB, sealed cookies) survives the restart
|
|
17
17
|
* because the child re-opens the files; only in-process state is
|
|
@@ -41,18 +41,18 @@
|
|
|
41
41
|
*
|
|
42
42
|
* Test seams: `opts._spawn(cmd, args, sopts)` and
|
|
43
43
|
* `opts._watch(dir, wopts, listener)` default to `child_process.spawn`
|
|
44
|
-
* and `
|
|
44
|
+
* and `nodeFs.watch`; unit tests pass fakes to drive the engine without
|
|
45
45
|
* real subprocesses.
|
|
46
46
|
*
|
|
47
47
|
* @card
|
|
48
48
|
* Dev-mode helpers — hot-reload signal (file watch + child-process restart), route-list dump exposed via `dev.stats()`, and a request inspector courtesy of `stdio: 'inherit'` so the operator sees the spawned app's logs unchanged.
|
|
49
49
|
*/
|
|
50
50
|
|
|
51
|
-
var
|
|
52
|
-
var
|
|
51
|
+
var nodePath = require("path");
|
|
52
|
+
var nodeFs = require("fs");
|
|
53
53
|
var lazyRequire = require("./lazy-require");
|
|
54
54
|
var logModule = require("./log");
|
|
55
|
-
var
|
|
55
|
+
var numericBounds = require("./numeric-bounds");
|
|
56
56
|
var safeEnv = require("./parsers/safe-env");
|
|
57
57
|
var validateOpts = require("./validate-opts");
|
|
58
58
|
var { FrameworkError } = require("./framework-error");
|
|
@@ -166,11 +166,11 @@ function create(opts) {
|
|
|
166
166
|
var ignore = Array.isArray(opts.ignore)
|
|
167
167
|
? DEFAULT_IGNORE.concat(opts.ignore)
|
|
168
168
|
: DEFAULT_IGNORE.slice();
|
|
169
|
-
|
|
169
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.graceMs,
|
|
170
170
|
"dev.create: opts.graceMs", DevError, "dev/bad-grace-ms");
|
|
171
171
|
var graceMs = opts.graceMs !== undefined ? opts.graceMs : DEFAULT_GRACE_MS;
|
|
172
172
|
var killSignal = typeof opts.killSignal === "string" ? opts.killSignal : DEFAULT_KILL_SIGNAL;
|
|
173
|
-
|
|
173
|
+
numericBounds.requireNonNegativeFiniteIntIfPresent(opts.killTimeoutMs,
|
|
174
174
|
"dev.create: opts.killTimeoutMs", DevError, "dev/bad-kill-timeout-ms");
|
|
175
175
|
var killTimeoutMs = opts.killTimeoutMs !== undefined ? opts.killTimeoutMs : DEFAULT_KILL_TIMEOUT_MS;
|
|
176
176
|
var log = opts.log || null;
|
|
@@ -198,7 +198,7 @@ function create(opts) {
|
|
|
198
198
|
return childProcess().spawn(cmd, sargs, sopts);
|
|
199
199
|
};
|
|
200
200
|
var watchFn = opts._watch || function (dir, wopts, listener) {
|
|
201
|
-
return
|
|
201
|
+
return nodeFs.watch(dir, wopts, listener);
|
|
202
202
|
};
|
|
203
203
|
var setTimeoutFn = opts._setTimeout || setTimeout;
|
|
204
204
|
var clearTimeoutFn = opts._clearTimeout || clearTimeout;
|
|
@@ -313,7 +313,7 @@ function create(opts) {
|
|
|
313
313
|
function _onWatchEvent(dir, eventType, filename) {
|
|
314
314
|
if (!filename) return;
|
|
315
315
|
var rel = String(filename);
|
|
316
|
-
var full =
|
|
316
|
+
var full = nodePath.join(dir, rel);
|
|
317
317
|
if (_matchesAny(ignore, rel) || _matchesAny(ignore, full)) return;
|
|
318
318
|
_scheduleRestart(eventType + ":" + rel);
|
|
319
319
|
}
|
|
@@ -321,7 +321,7 @@ function create(opts) {
|
|
|
321
321
|
function _armWatchers() {
|
|
322
322
|
for (var i = 0; i < watch.length; i++) {
|
|
323
323
|
(function (dir) {
|
|
324
|
-
var resolved =
|
|
324
|
+
var resolved = nodePath.isAbsolute(dir) ? dir : nodePath.resolve(cwd, dir);
|
|
325
325
|
var w;
|
|
326
326
|
try {
|
|
327
327
|
w = watchFn(resolved, { recursive: true, persistent: false }, function (eventType, filename) {
|
package/lib/dr-runbook.js
CHANGED
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
* Disaster-recovery runbook executor — composes pre-recorded regulatory steps, operator confirmation gates, and the framework's audit chain into a posture-appropriate Markdown runbook a regulator can read alongside `b.audit`.
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
var
|
|
46
|
-
var
|
|
45
|
+
var nodeFs = require("fs");
|
|
46
|
+
var nodePath = require("path");
|
|
47
47
|
var C = require("./constants");
|
|
48
48
|
var atomicFile = require("./atomic-file");
|
|
49
49
|
var lazyRequire = require("./lazy-require");
|
|
@@ -333,11 +333,11 @@ async function emit(opts) {
|
|
|
333
333
|
var body = sections.join("\n");
|
|
334
334
|
|
|
335
335
|
// Ensure outDir exists.
|
|
336
|
-
if (!
|
|
337
|
-
|
|
336
|
+
if (!nodeFs.existsSync(opts.outDir)) {
|
|
337
|
+
nodeFs.mkdirSync(opts.outDir, { recursive: true });
|
|
338
338
|
}
|
|
339
339
|
var filename = opts.filename || ("runbook-" + opts.posture + ".md");
|
|
340
|
-
var outPath =
|
|
340
|
+
var outPath = nodePath.join(opts.outDir, filename);
|
|
341
341
|
atomicFile.writeSync(outPath, body, { fileMode: 0o644 });
|
|
342
342
|
|
|
343
343
|
if (auditOn) {
|
package/lib/dual-control.js
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* M-of-N approval workflow for destructive operations (eraseHard, key rotation, etc.).
|
|
32
32
|
*/
|
|
33
33
|
var lazyRequire = require("./lazy-require");
|
|
34
|
-
var
|
|
34
|
+
var bCrypto = require("./crypto");
|
|
35
35
|
var requestHelpers = require("./request-helpers");
|
|
36
36
|
var validateOpts = require("./validate-opts");
|
|
37
37
|
var C = require("./constants");
|
|
@@ -261,7 +261,7 @@ function create(opts) {
|
|
|
261
261
|
if (reasonProblem) {
|
|
262
262
|
return Object.assign({ grantId: null }, reasonProblem);
|
|
263
263
|
}
|
|
264
|
-
var grantId = "dc-" +
|
|
264
|
+
var grantId = "dc-" + bCrypto.generateToken(C.BYTES.bytes(8));
|
|
265
265
|
var nowMs = Date.now();
|
|
266
266
|
var record = {
|
|
267
267
|
grantId: grantId,
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
* - externaldb.migrate.lock.acquired { holder }
|
|
49
49
|
* - externaldb.migrate.lock.released { holder }
|
|
50
50
|
*/
|
|
51
|
-
var
|
|
51
|
+
var nodePath = require("path");
|
|
52
52
|
var atomicFile = require("./atomic-file");
|
|
53
53
|
var canonicalJson = require("./canonical-json");
|
|
54
54
|
var { sha3Hash } = require("./crypto");
|
|
@@ -285,7 +285,7 @@ function _list(dir) {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
function _loadMigration(file, dir) {
|
|
288
|
-
var fullPath =
|
|
288
|
+
var fullPath = nodePath.join(dir, file);
|
|
289
289
|
// Drop the require cache so a test/dev that edits a file picks up the
|
|
290
290
|
// new content. Matches lib/migrations.js semantics.
|
|
291
291
|
try { delete require.cache[require.resolve(fullPath)]; } catch (_e) { /* not yet cached */ }
|
package/lib/external-db.js
CHANGED
|
@@ -754,8 +754,8 @@ async function transaction(fn, opts) {
|
|
|
754
754
|
if (isTransient && attempt <= maxRetries) {
|
|
755
755
|
_emitMetric("externaldb.transaction.retry", 1,
|
|
756
756
|
{ backend: b.name, code: txErr.code, attempt: String(attempt) });
|
|
757
|
-
var
|
|
758
|
-
var jitter =
|
|
757
|
+
var nodeCrypto = require("node:crypto");
|
|
758
|
+
var jitter = nodeCrypto.randomInt(0, 6); // allow:raw-byte-literal — 0-5ms jitter
|
|
759
759
|
await safeAsync.sleep(attempt * 5 + jitter); // allow:raw-time-literal — sub-second backoff
|
|
760
760
|
continue;
|
|
761
761
|
}
|
package/lib/fdx.js
CHANGED
|
@@ -85,7 +85,7 @@ var fapi2 = require("./fapi2");
|
|
|
85
85
|
var C = require("./constants");
|
|
86
86
|
var audit = require("./audit");
|
|
87
87
|
var validateOpts = require("./validate-opts");
|
|
88
|
-
var
|
|
88
|
+
var numericBounds = require("./numeric-bounds");
|
|
89
89
|
var { defineClass } = require("./framework-error");
|
|
90
90
|
var FdxError = defineClass("FdxError", { alwaysPermanent: true });
|
|
91
91
|
|
|
@@ -327,7 +327,7 @@ function consentReceipt(opts) {
|
|
|
327
327
|
throw FdxError.factory("BAD_SCOPES",
|
|
328
328
|
"fdx.consentReceipt: scopes must be a non-empty array");
|
|
329
329
|
}
|
|
330
|
-
|
|
330
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.durationMs,
|
|
331
331
|
"fdx.consentReceipt: durationMs", FdxError, "BAD_DURATION");
|
|
332
332
|
|
|
333
333
|
var issuedAt = Date.now();
|
package/lib/file-upload.js
CHANGED
|
@@ -184,12 +184,12 @@
|
|
|
184
184
|
* point. Operator wires their scanner of choice.
|
|
185
185
|
*/
|
|
186
186
|
|
|
187
|
-
var
|
|
188
|
-
var
|
|
189
|
-
var
|
|
187
|
+
var nodeFs = require("node:fs");
|
|
188
|
+
var nodePath = require("node:path");
|
|
189
|
+
var nodeStream = require("node:stream");
|
|
190
190
|
var atomicFile = require("./atomic-file");
|
|
191
191
|
var C = require("./constants");
|
|
192
|
-
var
|
|
192
|
+
var bCrypto = require("./crypto");
|
|
193
193
|
var gateContract = require("./gate-contract");
|
|
194
194
|
var lazyRequire = require("./lazy-require");
|
|
195
195
|
var numericBounds = require("./numeric-bounds");
|
|
@@ -250,7 +250,7 @@ function _validateUploadId(id) {
|
|
|
250
250
|
function _validateCreateOpts(opts) {
|
|
251
251
|
validateOpts.requireObject(opts, "fileUpload.create", FileUploadError);
|
|
252
252
|
validateOpts.requireNonEmptyString(opts.stagingDir, "fileUpload.create: stagingDir", FileUploadError);
|
|
253
|
-
if (!
|
|
253
|
+
if (!nodePath.isAbsolute(opts.stagingDir)) {
|
|
254
254
|
throw _err("BAD_OPT", "fileUpload.create: stagingDir must be an absolute path, got " +
|
|
255
255
|
JSON.stringify(opts.stagingDir));
|
|
256
256
|
}
|
|
@@ -472,10 +472,10 @@ function create(opts) {
|
|
|
472
472
|
// staging files.
|
|
473
473
|
atomicFile.ensureDir(stagingDir, 0o700);
|
|
474
474
|
|
|
475
|
-
function _uploadDir(uploadId) { return
|
|
476
|
-
function _chunkPath(uploadId, index) { return
|
|
477
|
-
function _receivedPath(uploadId) { return
|
|
478
|
-
function _metaPath(uploadId) { return
|
|
475
|
+
function _uploadDir(uploadId) { return nodePath.join(stagingDir, uploadId); }
|
|
476
|
+
function _chunkPath(uploadId, index) { return nodePath.join(_uploadDir(uploadId), String(index)); }
|
|
477
|
+
function _receivedPath(uploadId) { return nodePath.join(_uploadDir(uploadId), "_received.json"); }
|
|
478
|
+
function _metaPath(uploadId) { return nodePath.join(_uploadDir(uploadId), "_meta.json"); }
|
|
479
479
|
|
|
480
480
|
function _checkPermission(action, actor) {
|
|
481
481
|
if (!permissions) return;
|
|
@@ -491,7 +491,7 @@ function create(opts) {
|
|
|
491
491
|
|
|
492
492
|
function _readReceivedIndices(uploadId) {
|
|
493
493
|
var p = _receivedPath(uploadId);
|
|
494
|
-
if (!
|
|
494
|
+
if (!nodeFs.existsSync(p)) return [];
|
|
495
495
|
try {
|
|
496
496
|
var raw = atomicFile.readSync(p, { maxBytes: SIDECAR_MAX_BYTES });
|
|
497
497
|
var parsed = safeJson.parse(raw.toString("utf8"));
|
|
@@ -504,7 +504,7 @@ function create(opts) {
|
|
|
504
504
|
|
|
505
505
|
function _readMeta(uploadId) {
|
|
506
506
|
var p = _metaPath(uploadId);
|
|
507
|
-
if (!
|
|
507
|
+
if (!nodeFs.existsSync(p)) return null;
|
|
508
508
|
try {
|
|
509
509
|
var raw = atomicFile.readSync(p, { maxBytes: SIDECAR_MAX_BYTES });
|
|
510
510
|
return safeJson.parse(raw.toString("utf8"));
|
|
@@ -521,7 +521,7 @@ function create(opts) {
|
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
function _enumerateUploads() {
|
|
524
|
-
if (!
|
|
524
|
+
if (!nodeFs.existsSync(stagingDir)) return [];
|
|
525
525
|
var entries;
|
|
526
526
|
try { entries = atomicFile.listDir(stagingDir, { includeStat: true }); }
|
|
527
527
|
catch (_e) { return []; }
|
|
@@ -578,7 +578,7 @@ function create(opts) {
|
|
|
578
578
|
}
|
|
579
579
|
|
|
580
580
|
// Refuse re-init of an existing upload (caller-side bug).
|
|
581
|
-
if (
|
|
581
|
+
if (nodeFs.existsSync(_uploadDir(uploadId))) {
|
|
582
582
|
throw _err("UPLOAD_EXISTS",
|
|
583
583
|
"fileUpload.init: upload '" + uploadId + "' already exists; cancel or finalize first");
|
|
584
584
|
}
|
|
@@ -675,8 +675,8 @@ function create(opts) {
|
|
|
675
675
|
}
|
|
676
676
|
|
|
677
677
|
// Verify chunk hash matches the supplied header.
|
|
678
|
-
var actualHex =
|
|
679
|
-
if (!
|
|
678
|
+
var actualHex = bCrypto.sha3Hash(body);
|
|
679
|
+
if (!bCrypto.timingSafeEqual(actualHex, sha3Hex)) {
|
|
680
680
|
_emitObs("fileUpload.chunk_hash_mismatch", 1);
|
|
681
681
|
_emitAudit("fileUpload.chunk_received", {
|
|
682
682
|
actor: requestHelpers.extractActorContext(actor),
|
|
@@ -718,9 +718,9 @@ function create(opts) {
|
|
|
718
718
|
// Idempotent re-PUT: if this index is already received with a
|
|
719
719
|
// matching body, no-op. Different body = caller bug.
|
|
720
720
|
var p = _chunkPath(uploadId, index);
|
|
721
|
-
if (
|
|
721
|
+
if (nodeFs.existsSync(p)) {
|
|
722
722
|
var existing = atomicFile.readSync(p, { maxBytes: maxChunkBytes });
|
|
723
|
-
if (
|
|
723
|
+
if (bCrypto.timingSafeEqual(bCrypto.sha3Hash(existing), sha3Hex)) {
|
|
724
724
|
return {
|
|
725
725
|
received: _readReceivedIndices(uploadId).length,
|
|
726
726
|
totalBytesAccepted: meta.totalBytesAccepted,
|
|
@@ -744,8 +744,8 @@ function create(opts) {
|
|
|
744
744
|
meta.lastChunkAt = clock();
|
|
745
745
|
meta.totalBytesAccepted = (meta.totalBytesAccepted || 0) + body.length;
|
|
746
746
|
if (meta.totalBytesAccepted > maxFileBytes) {
|
|
747
|
-
// Reclaim staging — the upload exceeded the cap mid-
|
|
748
|
-
try {
|
|
747
|
+
// Reclaim staging — the upload exceeded the cap mid-nodeStream.
|
|
748
|
+
try { nodeFs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
|
|
749
749
|
catch (_e) { /* purgeIncomplete will reclaim */ }
|
|
750
750
|
_emitObs("fileUpload.file_too_large", 1);
|
|
751
751
|
throw _err("FILE_TOO_LARGE",
|
|
@@ -824,13 +824,13 @@ function create(opts) {
|
|
|
824
824
|
SHA3_512_HEX_LENGTH + " chars)");
|
|
825
825
|
}
|
|
826
826
|
var chunkPath = _chunkPath(uploadId, ck.index);
|
|
827
|
-
if (!
|
|
827
|
+
if (!nodeFs.existsSync(chunkPath)) {
|
|
828
828
|
throw _err("MISSING_CHUNK",
|
|
829
829
|
"fileUpload.finalize: chunk " + ck.index + " missing from staging");
|
|
830
830
|
}
|
|
831
831
|
var chunkBody = atomicFile.readSync(chunkPath, { maxBytes: maxChunkBytes });
|
|
832
|
-
var actualChunkHex =
|
|
833
|
-
if (!
|
|
832
|
+
var actualChunkHex = bCrypto.sha3Hash(chunkBody);
|
|
833
|
+
if (!bCrypto.timingSafeEqual(actualChunkHex, ck.sha3)) {
|
|
834
834
|
throw _err("CHUNK_HASH_MISMATCH",
|
|
835
835
|
"fileUpload.finalize: chunk " + ck.index +
|
|
836
836
|
" on-disk SHA3-512 doesn't match manifest");
|
|
@@ -849,7 +849,7 @@ function create(opts) {
|
|
|
849
849
|
" bytes, manifest declares " + manifest.totalBytes);
|
|
850
850
|
}
|
|
851
851
|
var totalHashHex = hasher.digest("hex");
|
|
852
|
-
if (!
|
|
852
|
+
if (!bCrypto.timingSafeEqual(totalHashHex, manifest.sha3)) {
|
|
853
853
|
throw _err("MANIFEST_HASH_MISMATCH",
|
|
854
854
|
"fileUpload.finalize: reassembled SHA3-512 doesn't match manifest.sha3");
|
|
855
855
|
}
|
|
@@ -911,13 +911,13 @@ function create(opts) {
|
|
|
911
911
|
// onFinalize reads through to wherever they're piping.
|
|
912
912
|
async function* generate() {
|
|
913
913
|
for (var i = 0; i < paths.length; i += 1) {
|
|
914
|
-
var fh =
|
|
914
|
+
var fh = nodeFs.createReadStream(paths[i]);
|
|
915
915
|
for await (var chunk of fh) {
|
|
916
916
|
yield chunk;
|
|
917
917
|
}
|
|
918
918
|
}
|
|
919
919
|
}
|
|
920
|
-
return
|
|
920
|
+
return nodeStream.Readable.from(generate(), { objectMode: false });
|
|
921
921
|
}
|
|
922
922
|
|
|
923
923
|
async function finalize(callerOpts) {
|
|
@@ -1034,7 +1034,7 @@ function create(opts) {
|
|
|
1034
1034
|
}
|
|
1035
1035
|
}
|
|
1036
1036
|
if (contentSafety) {
|
|
1037
|
-
var safetyExt =
|
|
1037
|
+
var safetyExt = nodePath.extname(filename).toLowerCase();
|
|
1038
1038
|
var safetyGate = contentSafety[safetyExt];
|
|
1039
1039
|
if (safetyGate && typeof safetyGate.check === "function" && bodyBuffer) {
|
|
1040
1040
|
var safetyDecision;
|
|
@@ -1109,7 +1109,7 @@ function create(opts) {
|
|
|
1109
1109
|
}
|
|
1110
1110
|
|
|
1111
1111
|
// Cleanup staging on success.
|
|
1112
|
-
try {
|
|
1112
|
+
try { nodeFs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
|
|
1113
1113
|
catch (_e) { /* best-effort */ }
|
|
1114
1114
|
|
|
1115
1115
|
_emitObs("fileUpload.finalize_success", 1);
|
|
@@ -1175,7 +1175,7 @@ function create(opts) {
|
|
|
1175
1175
|
_checkPermission("cancel", callerOpts.actor);
|
|
1176
1176
|
var meta = _readMeta(uploadId);
|
|
1177
1177
|
if (!meta) return { ok: false, uploadId: uploadId, reason: "not-found" };
|
|
1178
|
-
try {
|
|
1178
|
+
try { nodeFs.rmSync(_uploadDir(uploadId), { recursive: true, force: true }); }
|
|
1179
1179
|
catch (_e) { /* best-effort */ }
|
|
1180
1180
|
_emitObs("fileUpload.cancelled", 1);
|
|
1181
1181
|
_emitAudit("fileUpload.cancelled", {
|
|
@@ -1190,7 +1190,7 @@ function create(opts) {
|
|
|
1190
1190
|
// ---- purgeIncomplete ----
|
|
1191
1191
|
|
|
1192
1192
|
function purgeIncomplete() {
|
|
1193
|
-
if (!
|
|
1193
|
+
if (!nodeFs.existsSync(stagingDir)) return { purged: 0, ids: [] };
|
|
1194
1194
|
var now = clock();
|
|
1195
1195
|
var entries;
|
|
1196
1196
|
try { entries = atomicFile.listDir(stagingDir, { includeStat: true }); }
|
|
@@ -1211,7 +1211,7 @@ function create(opts) {
|
|
|
1211
1211
|
}
|
|
1212
1212
|
if (!purgeReason) continue;
|
|
1213
1213
|
try {
|
|
1214
|
-
|
|
1214
|
+
nodeFs.rmSync(e.fullPath, { recursive: true, force: true });
|
|
1215
1215
|
purged.push({ id: e.name, reason: purgeReason });
|
|
1216
1216
|
} catch (_e2) { /* best-effort; will retry */ }
|
|
1217
1217
|
}
|
package/lib/flag-providers.js
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* provider.kind -> "local-file" | "memory" | "environment" | <operator-defined>
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
-
var
|
|
39
|
+
var nodeFs = require("fs");
|
|
40
40
|
var validateOpts = require("./validate-opts");
|
|
41
41
|
var lazyRequire = require("./lazy-require");
|
|
42
42
|
var safeJson = require("./safe-json");
|
|
@@ -128,7 +128,7 @@ function localFile(opts) {
|
|
|
128
128
|
validateOpts.requireNonEmptyString(opts.path,
|
|
129
129
|
"providers.localFile: path", FlagError, "flag/bad-provider");
|
|
130
130
|
var raw;
|
|
131
|
-
try { raw =
|
|
131
|
+
try { raw = nodeFs.readFileSync(opts.path, "utf8"); }
|
|
132
132
|
catch (e) {
|
|
133
133
|
throw new FlagError("flag/bad-provider",
|
|
134
134
|
"providers.localFile: cannot read file " + JSON.stringify(opts.path) +
|
|
@@ -152,9 +152,9 @@ function localFile(opts) {
|
|
|
152
152
|
provider._path = opts.path;
|
|
153
153
|
if (opts.watch === true) {
|
|
154
154
|
try {
|
|
155
|
-
|
|
155
|
+
nodeFs.watch(opts.path, { persistent: false }, function () {
|
|
156
156
|
try {
|
|
157
|
-
var nextRaw =
|
|
157
|
+
var nextRaw = nodeFs.readFileSync(opts.path, "utf8");
|
|
158
158
|
var nextParsed = safeJson.parse(nextRaw, { maxBytes: C.BYTES.mib(1) });
|
|
159
159
|
if (nextParsed && nextParsed.flags) {
|
|
160
160
|
for (var k in nextParsed.flags) {
|
package/lib/gate-contract.js
CHANGED
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
48
|
var C = require("./constants");
|
|
49
|
-
var
|
|
49
|
+
var bCrypto = require("./crypto");
|
|
50
50
|
var lazyRequire = require("./lazy-require");
|
|
51
51
|
var safeAsync = require("./safe-async");
|
|
52
52
|
var validateOpts = require("./validate-opts");
|
|
@@ -341,13 +341,13 @@ function defineGate(opts) {
|
|
|
341
341
|
async function check(ctx) {
|
|
342
342
|
var startedAt = Date.now();
|
|
343
343
|
ctx = ctx || {};
|
|
344
|
-
if (!ctx.forensicId) ctx.forensicId =
|
|
344
|
+
if (!ctx.forensicId) ctx.forensicId = bCrypto.generateToken(FORENSIC_ID_BYTES);
|
|
345
345
|
|
|
346
346
|
// Decision cache lookup (memoize per-forensicHash).
|
|
347
347
|
var bytes = ctx.bytes;
|
|
348
348
|
var forensicHash = bytes && Buffer.isBuffer(bytes)
|
|
349
|
-
?
|
|
350
|
-
: (typeof bytes === "string" ?
|
|
349
|
+
? bCrypto.sha3Hash(bytes, "hex")
|
|
350
|
+
: (typeof bytes === "string" ? bCrypto.sha3Hash(Buffer.from(bytes, "utf8"), "hex") : null);
|
|
351
351
|
var cacheKey = forensicHash ? (opts.name + ":" + ruleHash + ":" + forensicHash) : null;
|
|
352
352
|
if (decisionCache && cacheKey) {
|
|
353
353
|
try {
|
|
@@ -555,7 +555,7 @@ function _build(partial) {
|
|
|
555
555
|
}
|
|
556
556
|
|
|
557
557
|
function _hashFingerprint(obj) {
|
|
558
|
-
return
|
|
558
|
+
return bCrypto.sha3Hash(JSON.stringify(obj), "hex").slice(0, FINGERPRINT_HEX_LENGTH);
|
|
559
559
|
}
|
|
560
560
|
|
|
561
561
|
// ---- Host-side helpers ----
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
* GraphQL federation gateway with SDL trust boundary, sub-graph health, subgraph SDL signing, query plan caps.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
var
|
|
24
|
+
var bCrypto = require("./crypto");
|
|
25
25
|
var C = require("./constants");
|
|
26
|
-
var
|
|
26
|
+
var numericBounds = require("./numeric-bounds");
|
|
27
27
|
var safeJson = require("./safe-json");
|
|
28
28
|
var safeBuffer = require("./safe-buffer");
|
|
29
29
|
var requestHelpers = require("./request-helpers");
|
|
@@ -73,10 +73,7 @@ function _readBearer(req) {
|
|
|
73
73
|
|
|
74
74
|
function _timingSafeEqual(a, b) {
|
|
75
75
|
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
76
|
-
|
|
77
|
-
var bb = Buffer.from(b, "utf8");
|
|
78
|
-
if (ab.length !== bb.length) return false;
|
|
79
|
-
return crypto.timingSafeEqual(ab, bb);
|
|
76
|
+
return bCrypto.timingSafeEqual(a, b);
|
|
80
77
|
}
|
|
81
78
|
|
|
82
79
|
function _readBody(req, errorClass) {
|
|
@@ -141,7 +138,7 @@ function guardSdl(opts) {
|
|
|
141
138
|
}
|
|
142
139
|
var nonceStore = opts.nonceStore && typeof opts.nonceStore.has === "function" &&
|
|
143
140
|
typeof opts.nonceStore.remember === "function" ? opts.nonceStore : null;
|
|
144
|
-
|
|
141
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.nonceTtlMs, "graphqlFederation.guardSdl: opts.nonceTtlMs", errorClass, "BAD_TTL");
|
|
145
142
|
var nonceTtlMs = opts.nonceTtlMs || C.TIME.minutes(5);
|
|
146
143
|
var auditOn = opts.audit !== false;
|
|
147
144
|
|
package/lib/honeytoken.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* Framework-seeded canary records that trigger an audit alert on read; integrates with sealed columns.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
var
|
|
33
|
+
var bCrypto = require("./crypto");
|
|
34
34
|
var lazyRequire = require("./lazy-require");
|
|
35
35
|
var validateOpts = require("./validate-opts");
|
|
36
36
|
var { defineClass } = require("./framework-error");
|
|
@@ -40,10 +40,10 @@ var audit = lazyRequire(function () { return require("./audit"); });
|
|
|
40
40
|
var HoneytokenError = defineClass("HoneytokenError", { alwaysPermanent: true });
|
|
41
41
|
|
|
42
42
|
var KINDS = Object.freeze({
|
|
43
|
-
apiKey: function () { return "bk_canary_" +
|
|
44
|
-
session: function () { return "bks_canary_" +
|
|
45
|
-
url: function () { return "/admin/canary-" +
|
|
46
|
-
rowId: function () { return "ht_canary_" +
|
|
43
|
+
apiKey: function () { return "bk_canary_" + bCrypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte (128-bit) canary entropy
|
|
44
|
+
session: function () { return "bks_canary_" + bCrypto.generateToken(24); }, // allow:raw-byte-literal — 24-byte (192-bit) canary entropy
|
|
45
|
+
url: function () { return "/admin/canary-" + bCrypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte canary entropy
|
|
46
|
+
rowId: function () { return "ht_canary_" + bCrypto.generateToken(16); }, // allow:raw-byte-literal — 16-byte canary entropy
|
|
47
47
|
});
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -102,7 +102,7 @@ function create(opts) {
|
|
|
102
102
|
"(supported: " + Object.keys(KINDS).join(", ") + ")");
|
|
103
103
|
}
|
|
104
104
|
var value = KINDS[kind]();
|
|
105
|
-
var id = "ht_" +
|
|
105
|
+
var id = "ht_" + bCrypto.generateToken(8); // allow:raw-byte-literal — 8-byte registry id
|
|
106
106
|
var record = Object.freeze({
|
|
107
107
|
id: id,
|
|
108
108
|
kind: kind,
|
|
@@ -60,8 +60,8 @@
|
|
|
60
60
|
* }
|
|
61
61
|
*/
|
|
62
62
|
|
|
63
|
-
var
|
|
64
|
-
var
|
|
63
|
+
var nodeFs = require("node:fs");
|
|
64
|
+
var nodePath = require("node:path");
|
|
65
65
|
var C = require("./constants");
|
|
66
66
|
var numericBounds = require("./numeric-bounds");
|
|
67
67
|
var safeAsync = require("./safe-async");
|
|
@@ -181,7 +181,7 @@ function create(opts) {
|
|
|
181
181
|
filePath = opts.file;
|
|
182
182
|
// Refuse relative paths so a process running in a different cwd
|
|
183
183
|
// doesn't accidentally serialize to a sibling directory.
|
|
184
|
-
if (!
|
|
184
|
+
if (!nodePath.isAbsolute(filePath)) {
|
|
185
185
|
throw _err("BAD_OPT",
|
|
186
186
|
"cookieJar.create: opts.file must be an absolute path, got " + JSON.stringify(filePath));
|
|
187
187
|
}
|
|
@@ -441,7 +441,7 @@ function create(opts) {
|
|
|
441
441
|
var rows = getAll();
|
|
442
442
|
var serialized = JSON.stringify(rows);
|
|
443
443
|
var blob = vault ? vault.seal(serialized) : serialized;
|
|
444
|
-
|
|
444
|
+
nodeFs.writeFileSync(filePath, blob);
|
|
445
445
|
}
|
|
446
446
|
var flushScheduler = safeAsync.makeScheduledFlush(flushDebounceMs, function () {
|
|
447
447
|
if (!filePath) return;
|
|
@@ -477,9 +477,9 @@ function create(opts) {
|
|
|
477
477
|
// Persist file may be operator-tampered (or vault-sealed) — route through
|
|
478
478
|
// safeJson with an explicit byte cap so a maliciously-large file can't
|
|
479
479
|
// OOM the process before the parse fails.
|
|
480
|
-
if (filePath &&
|
|
480
|
+
if (filePath && nodeFs.existsSync(filePath)) {
|
|
481
481
|
try {
|
|
482
|
-
var raw =
|
|
482
|
+
var raw = nodeFs.readFileSync(filePath, "utf8");
|
|
483
483
|
var serialized = vault ? vault.unseal(raw) : raw;
|
|
484
484
|
if (serialized && serialized.length > 0) {
|
|
485
485
|
var rows = safeJson.parse(serialized, { maxBytes: C.BYTES.mib(16) });
|