@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/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
|
}
|
|
@@ -22,7 +22,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
22
22
|
var { defineClass } = require("./framework-error");
|
|
23
23
|
var FlagError = defineClass("FlagError", { alwaysPermanent: true });
|
|
24
24
|
|
|
25
|
-
var
|
|
25
|
+
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
26
26
|
|
|
27
27
|
function _normalize(input, label) {
|
|
28
28
|
if (input == null) return {};
|
|
@@ -96,7 +96,7 @@ function fromRequest(req, opts) {
|
|
|
96
96
|
headers["x-forwarded-for"].split(",")[0].trim()) ||
|
|
97
97
|
(req.connection && req.connection.remoteAddress) || "";
|
|
98
98
|
var ua = headers["user-agent"] || "";
|
|
99
|
-
tk = "anon:" +
|
|
99
|
+
tk = "anon:" + bCrypto().sha3Hash(ip + ":" + ua).slice(0, 16); // allow:raw-byte-literal — base16 prefix len
|
|
100
100
|
}
|
|
101
101
|
ctx.targetingKey = tk;
|
|
102
102
|
|
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) });
|
package/lib/http-client.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* Outbound HTTP client with SSRF gate, retry, circuit breaker, wall-clock + idle timeouts, AbortSignal propagation, connection pooling, streaming, and ALPN-negotiated HTTP/2.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
var
|
|
38
|
+
var nodeFs = require("fs");
|
|
39
39
|
var http = require("http");
|
|
40
40
|
var https = require("https");
|
|
41
41
|
var http2 = require("http2");
|
|
@@ -46,7 +46,7 @@ var streamPromises = require("node:stream/promises");
|
|
|
46
46
|
var { URL } = require("url");
|
|
47
47
|
var atomicFile = require("./atomic-file");
|
|
48
48
|
var C = require("./constants");
|
|
49
|
-
var
|
|
49
|
+
var bCrypto = require("./crypto");
|
|
50
50
|
var pqcAgent = require("./pqc-agent");
|
|
51
51
|
var safeAsync = require("./safe-async");
|
|
52
52
|
var safeBuffer = require("./safe-buffer");
|
|
@@ -461,7 +461,7 @@ function _attachJarCookie(headers, jar, url) {
|
|
|
461
461
|
// emits boundary headers + content + CRLF in order. Avoids the
|
|
462
462
|
// Buffer.concat() OOM class on large uploads. contentLength is
|
|
463
463
|
// a finite number when every source's size is statically
|
|
464
|
-
// resolvable (Buffer length,
|
|
464
|
+
// resolvable (Buffer length, nodeFs.statSync().size, opts.size on
|
|
465
465
|
// a stream entry); null otherwise — caller falls back to
|
|
466
466
|
// chunked transfer.
|
|
467
467
|
//
|
|
@@ -474,9 +474,9 @@ function _attachJarCookie(headers, jar, url) {
|
|
|
474
474
|
// `filename` and `contentType` apply to all three shapes; for
|
|
475
475
|
// `filePath` entries, `filename` defaults to path.basename(filePath).
|
|
476
476
|
function _buildMultipartBody(spec) {
|
|
477
|
-
var boundary = "----blamejs-mp-" +
|
|
477
|
+
var boundary = "----blamejs-mp-" + bCrypto.generateToken(C.BYTES.bytes(16));
|
|
478
478
|
var CRLF = "\r\n";
|
|
479
|
-
var
|
|
479
|
+
var nodeFs = require("fs"); // allow:inline-require — only on multipart paths that touch the filesystem
|
|
480
480
|
var path = require("path"); // allow:inline-require — same
|
|
481
481
|
var nodeStream = require("stream"); // allow:inline-require — Readable subclass only when streaming
|
|
482
482
|
|
|
@@ -558,7 +558,7 @@ function _buildMultipartBody(spec) {
|
|
|
558
558
|
} else if (hasFilePath) {
|
|
559
559
|
anyStreaming = true;
|
|
560
560
|
var st;
|
|
561
|
-
try { st =
|
|
561
|
+
try { st = nodeFs.statSync(file.filePath); }
|
|
562
562
|
catch (e) { throw new Error("multipart: file.filePath not readable: " + e.message); }
|
|
563
563
|
if (!st.isFile()) throw new Error("multipart: file.filePath is not a regular file");
|
|
564
564
|
_addEntry(head, { kind: "filePath", filePath: file.filePath, size: st.size });
|
|
@@ -613,7 +613,7 @@ function _buildMultipartBody(spec) {
|
|
|
613
613
|
if (entry.source.kind === "buffer") {
|
|
614
614
|
yield entry.source.buf;
|
|
615
615
|
} else if (entry.source.kind === "filePath") {
|
|
616
|
-
var rs =
|
|
616
|
+
var rs = nodeFs.createReadStream(entry.source.filePath);
|
|
617
617
|
try {
|
|
618
618
|
for await (var chunk of rs) yield chunk;
|
|
619
619
|
} finally {
|
|
@@ -1699,7 +1699,7 @@ function _requestH2(transport, u, opts) {
|
|
|
1699
1699
|
//
|
|
1700
1700
|
// uploadMultipartStream — POST a file body via multipart/form-data
|
|
1701
1701
|
// without buffering. Streams from disk through the request body using
|
|
1702
|
-
// `
|
|
1702
|
+
// `nodeFs.createReadStream` + `node:stream/promises` pipeline.
|
|
1703
1703
|
//
|
|
1704
1704
|
// Both compose through `request()` (responseMode: "stream") so safeUrl,
|
|
1705
1705
|
// ssrfGuard, allowedHosts, network-proxy, audit-on-host-deny, and the
|
|
@@ -1799,7 +1799,7 @@ async function downloadStream(opts) {
|
|
|
1799
1799
|
_validateDownloadOpts(opts);
|
|
1800
1800
|
var alg = opts.hash || DEFAULT_DOWNLOAD_HASH_ALG;
|
|
1801
1801
|
var dest = opts.dest;
|
|
1802
|
-
var tmpPath = dest + ".tmp-" +
|
|
1802
|
+
var tmpPath = dest + ".tmp-" + bCrypto.generateToken(C.BYTES.bytes(8));
|
|
1803
1803
|
var dir = nodePath.dirname(dest);
|
|
1804
1804
|
|
|
1805
1805
|
atomicFile.ensureDir(dir);
|
|
@@ -1857,13 +1857,13 @@ async function downloadStream(opts) {
|
|
|
1857
1857
|
});
|
|
1858
1858
|
counter.bytesWritten = 0;
|
|
1859
1859
|
|
|
1860
|
-
var fileStream =
|
|
1860
|
+
var fileStream = nodeFs.createWriteStream(tmpPath, { mode: DEFAULT_DOWNLOAD_FILE_MODE, flags: "w" });
|
|
1861
1861
|
|
|
1862
1862
|
try {
|
|
1863
1863
|
await streamPromises.pipeline(res.body, counter, fileStream);
|
|
1864
1864
|
} catch (e) {
|
|
1865
1865
|
// Pipeline failure → tmp may be partially written. Remove + audit.
|
|
1866
|
-
try {
|
|
1866
|
+
try { nodeFs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
|
|
1867
1867
|
_emitAudit(opts, "system.httpclient.download_stream.refused", "denied", {
|
|
1868
1868
|
reason: "pipeline-failed", message: e.message, code: e.code,
|
|
1869
1869
|
});
|
|
@@ -1876,15 +1876,15 @@ async function downloadStream(opts) {
|
|
|
1876
1876
|
// across platforms but matches the discipline of the rest of the
|
|
1877
1877
|
// framework's atomic-write paths.
|
|
1878
1878
|
try {
|
|
1879
|
-
var fd =
|
|
1880
|
-
try { atomicFile.fsync(fd); } finally { try {
|
|
1879
|
+
var fd = nodeFs.openSync(tmpPath, "r+");
|
|
1880
|
+
try { atomicFile.fsync(fd); } finally { try { nodeFs.closeSync(fd); } catch (_c) { /* best-effort fd close */ } }
|
|
1881
1881
|
} catch (_fe) { /* fsync best-effort */ }
|
|
1882
1882
|
|
|
1883
1883
|
var actualHex = hasher.digest("hex");
|
|
1884
1884
|
if (typeof opts.expected === "string" && opts.expected.length > 0) {
|
|
1885
1885
|
var expected = opts.expected.toLowerCase();
|
|
1886
1886
|
if (actualHex.toLowerCase() !== expected) {
|
|
1887
|
-
try {
|
|
1887
|
+
try { nodeFs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
|
|
1888
1888
|
_emitAudit(opts, "system.httpclient.download_stream.refused", "denied", {
|
|
1889
1889
|
reason: "hash-mismatch", alg: alg, expected: expected, actual: actualHex,
|
|
1890
1890
|
statusCode: res.statusCode, bytesWritten: counter.bytesWritten,
|
|
@@ -1897,10 +1897,10 @@ async function downloadStream(opts) {
|
|
|
1897
1897
|
|
|
1898
1898
|
// Atomic rename + dir fsync.
|
|
1899
1899
|
try {
|
|
1900
|
-
|
|
1900
|
+
nodeFs.renameSync(tmpPath, dest);
|
|
1901
1901
|
atomicFile.fsyncDir(dir);
|
|
1902
1902
|
} catch (e) {
|
|
1903
|
-
try {
|
|
1903
|
+
try { nodeFs.unlinkSync(tmpPath); } catch (_u) { /* best-effort cleanup */ }
|
|
1904
1904
|
_emitAudit(opts, "system.httpclient.download_stream.refused", "denied", {
|
|
1905
1905
|
reason: "rename-failed", message: e.message,
|
|
1906
1906
|
});
|
|
@@ -1959,7 +1959,7 @@ function _validateUploadOpts(opts) {
|
|
|
1959
1959
|
*
|
|
1960
1960
|
* POSTs a file body via `multipart/form-data` without buffering the
|
|
1961
1961
|
* file in memory. Streams from disk through the request body using
|
|
1962
|
-
* `
|
|
1962
|
+
* `nodeFs.createReadStream` + `node:stream/promises` pipeline. Throws
|
|
1963
1963
|
* `httpclient/missing-file` when `opts.file.path` doesn't exist or
|
|
1964
1964
|
* isn't a regular file. Composes through `request()` so SSRF gating,
|
|
1965
1965
|
* proxy routing, and the per-origin transport cache apply unchanged.
|
|
@@ -1989,7 +1989,7 @@ async function uploadMultipartStream(opts) {
|
|
|
1989
1989
|
|
|
1990
1990
|
var filePath = opts.file.path;
|
|
1991
1991
|
var st;
|
|
1992
|
-
try { st =
|
|
1992
|
+
try { st = nodeFs.statSync(filePath); }
|
|
1993
1993
|
catch (e) {
|
|
1994
1994
|
_emitAudit(opts, "system.httpclient.upload_stream.refused", "denied", {
|
|
1995
1995
|
reason: "missing-file", path: filePath, message: e.message,
|
package/lib/i18n.js
CHANGED
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
* ICU MessageFormat + CLDR Plural Rules + locale-aware Intl formatters with translation lookup.
|
|
58
58
|
*/
|
|
59
59
|
|
|
60
|
-
var
|
|
61
|
-
var
|
|
60
|
+
var nodeFs = require("node:fs");
|
|
61
|
+
var nodePath = require("node:path");
|
|
62
62
|
var lazyRequire = require("./lazy-require");
|
|
63
63
|
var requestHelpers = require("./request-helpers");
|
|
64
64
|
var safeJson = require("./safe-json");
|
|
@@ -227,13 +227,13 @@ function _loadFromDir(dir, locales) {
|
|
|
227
227
|
var out = {};
|
|
228
228
|
for (var i = 0; i < locales.length; i++) {
|
|
229
229
|
var locale = locales[i];
|
|
230
|
-
var filePath =
|
|
231
|
-
if (!
|
|
230
|
+
var filePath = nodePath.join(dir, locale + ".json");
|
|
231
|
+
if (!nodeFs.existsSync(filePath)) {
|
|
232
232
|
throw _err("LOAD_FAILED",
|
|
233
233
|
"i18n: translations file not found for locale '" + locale + "': " + filePath);
|
|
234
234
|
}
|
|
235
235
|
var raw;
|
|
236
|
-
try { raw =
|
|
236
|
+
try { raw = nodeFs.readFileSync(filePath, "utf8"); }
|
|
237
237
|
catch (e) {
|
|
238
238
|
throw _err("LOAD_FAILED",
|
|
239
239
|
"i18n: failed to read '" + filePath + "': " + ((e && e.message) || String(e)));
|
package/lib/keychain.js
CHANGED
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
* OS keychain abstraction with encrypted-file fallback — stores / retrieves / removes a `(service, account) -> password` binding via the host operating system's native credential store.
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
-
var
|
|
47
|
-
var
|
|
46
|
+
var nodeFs = require("fs");
|
|
47
|
+
var nodePath = require("path");
|
|
48
48
|
|
|
49
49
|
var atomicFile = require("./atomic-file");
|
|
50
50
|
var C = require("./constants");
|
|
@@ -91,7 +91,7 @@ function _detectBackend() {
|
|
|
91
91
|
|
|
92
92
|
function _existsExecutable(filepath) {
|
|
93
93
|
try {
|
|
94
|
-
var st =
|
|
94
|
+
var st = nodeFs.statSync(filepath);
|
|
95
95
|
return st.isFile();
|
|
96
96
|
} catch (_e) { return false; }
|
|
97
97
|
}
|
|
@@ -124,7 +124,7 @@ function _resolveOnPath(binName) {
|
|
|
124
124
|
for (var i = 0; i < parts.length; i += 1) {
|
|
125
125
|
var dir = parts[i];
|
|
126
126
|
if (typeof dir !== "string" || dir.length === 0) continue;
|
|
127
|
-
var candidate =
|
|
127
|
+
var candidate = nodePath.join(dir, binName);
|
|
128
128
|
if (_existsExecutable(candidate)) return candidate;
|
|
129
129
|
}
|
|
130
130
|
return null;
|
|
@@ -159,7 +159,7 @@ function _validateCommonOpts(opts, primitive) {
|
|
|
159
159
|
function _validateFallbackFile(filepath, primitive) {
|
|
160
160
|
validateOpts.requireNonEmptyString(filepath, "fallbackFile",
|
|
161
161
|
KeychainError, "keychain/bad-fallback-file");
|
|
162
|
-
if (!
|
|
162
|
+
if (!nodePath.isAbsolute(filepath)) {
|
|
163
163
|
throw new KeychainError("keychain/relative-fallback-file",
|
|
164
164
|
primitive + ": fallbackFile must be an absolute path; got " + filepath);
|
|
165
165
|
}
|
package/lib/legal-hold.js
CHANGED
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
* missing/garbage subjectId at the API; emit + return shaped error
|
|
60
60
|
* on policy denials (already-held / not-held / invalid-citation).
|
|
61
61
|
*/
|
|
62
|
-
var
|
|
62
|
+
var bCrypto = require("./crypto");
|
|
63
63
|
var lazyRequire = require("./lazy-require");
|
|
64
64
|
var safeJson = require("./safe-json");
|
|
65
65
|
var validateOpts = require("./validate-opts");
|
|
@@ -96,7 +96,7 @@ function _subjectIdString(subjectId) {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
function _hashSubject(subjectId) {
|
|
99
|
-
return
|
|
99
|
+
return bCrypto.sha3Hash("bj-legal-hold:" + subjectId);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function create(opts) {
|