@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/mtls-ca.js
CHANGED
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
* Mutual TLS Certificate Authority — internal CA cert issuance, mTLS gate setup, fingerprint pinning.
|
|
53
53
|
*/
|
|
54
54
|
|
|
55
|
-
var
|
|
56
|
-
var
|
|
55
|
+
var nodeFs = require("fs");
|
|
56
|
+
var nodePath = require("path");
|
|
57
57
|
var nodeCrypto = require("node:crypto");
|
|
58
58
|
var atomicFile = require("./atomic-file");
|
|
59
59
|
var C = require("./constants");
|
|
@@ -101,10 +101,10 @@ var VALID_SEAL_MODES = { required: 1, disabled: 1 };
|
|
|
101
101
|
// through unchanged. The pre-v0.8.58 shape always joined under
|
|
102
102
|
// dataDir, which silently overrode an operator-supplied absolute
|
|
103
103
|
// path (e.g. `MTLS_CA_KEY=/etc/ssl/ca.key` → `<dataDir>/etc/ssl/ca.key`).
|
|
104
|
-
// Standard Node `
|
|
104
|
+
// Standard Node `nodePath.join` semantics already preserve absolute
|
|
105
105
|
// arguments — the always-join was an oversight, not by design.
|
|
106
106
|
function _absoluteOrUnderDataDir(dataDir, p) {
|
|
107
|
-
return
|
|
107
|
+
return nodePath.isAbsolute(p) ? p : nodePath.join(dataDir, p);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
function _resolvePaths(dataDir, paths) {
|
|
@@ -202,8 +202,8 @@ function create(opts) {
|
|
|
202
202
|
// the first initCA() / generateClientCert() call fails with ENOENT
|
|
203
203
|
// on `ca.key.tmp` because the atomic-file write expects the parent
|
|
204
204
|
// dir to exist.
|
|
205
|
-
if (!
|
|
206
|
-
|
|
205
|
+
if (!nodeFs.existsSync(opts.dataDir)) {
|
|
206
|
+
nodeFs.mkdirSync(opts.dataDir, { recursive: true, mode: 0o700 });
|
|
207
207
|
}
|
|
208
208
|
var paths = _resolvePaths(opts.dataDir, opts.paths);
|
|
209
209
|
var vault = opts.vault || null;
|
|
@@ -228,10 +228,10 @@ function create(opts) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
function keyExists() {
|
|
231
|
-
return
|
|
231
|
+
return nodeFs.existsSync(paths.caKey) || nodeFs.existsSync(paths.caKeySealed);
|
|
232
232
|
}
|
|
233
233
|
function exists() {
|
|
234
|
-
return keyExists() &&
|
|
234
|
+
return keyExists() && nodeFs.existsSync(paths.caCert);
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
function status() {
|
|
@@ -243,7 +243,7 @@ function create(opts) {
|
|
|
243
243
|
current: generation,
|
|
244
244
|
};
|
|
245
245
|
}
|
|
246
|
-
var pem =
|
|
246
|
+
var pem = nodeFs.readFileSync(paths.caCert);
|
|
247
247
|
var gen = parseGeneration(pem);
|
|
248
248
|
return {
|
|
249
249
|
exists: true,
|
|
@@ -257,8 +257,8 @@ function create(opts) {
|
|
|
257
257
|
// caKeySealedMode dispatch. Returns Buffer of PEM bytes, or throws
|
|
258
258
|
// with a precise reason when the mode rejects the on-disk form.
|
|
259
259
|
function loadKey() {
|
|
260
|
-
var hasPlain =
|
|
261
|
-
var hasSealed =
|
|
260
|
+
var hasPlain = nodeFs.existsSync(paths.caKey);
|
|
261
|
+
var hasSealed = nodeFs.existsSync(paths.caKeySealed);
|
|
262
262
|
if (!hasPlain && !hasSealed) {
|
|
263
263
|
throw new MtlsCaError("mtls-ca/missing-key",
|
|
264
264
|
"no CA key on disk at " + paths.caKey + " or " + paths.caKeySealed);
|
|
@@ -269,7 +269,7 @@ function create(opts) {
|
|
|
269
269
|
"CA_KEY_SEALED='required' but " + paths.caKeySealed + " does not exist");
|
|
270
270
|
}
|
|
271
271
|
_requireVault("sealed CA key load");
|
|
272
|
-
var sealedBytes =
|
|
272
|
+
var sealedBytes = nodeFs.readFileSync(paths.caKeySealed, "utf8").trim();
|
|
273
273
|
var pem = vault.unseal(sealedBytes);
|
|
274
274
|
if (!pem) {
|
|
275
275
|
throw new MtlsCaError("mtls-ca/unseal-failed",
|
|
@@ -282,15 +282,15 @@ function create(opts) {
|
|
|
282
282
|
throw new MtlsCaError("mtls-ca/plain-required",
|
|
283
283
|
"caKeySealedMode='disabled' but " + paths.caKey + " does not exist");
|
|
284
284
|
}
|
|
285
|
-
return
|
|
285
|
+
return nodeFs.readFileSync(paths.caKey);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
function loadCert() {
|
|
289
|
-
if (!
|
|
289
|
+
if (!nodeFs.existsSync(paths.caCert)) {
|
|
290
290
|
throw new MtlsCaError("mtls-ca/missing-cert",
|
|
291
291
|
"no CA cert on disk at " + paths.caCert);
|
|
292
292
|
}
|
|
293
|
-
return
|
|
293
|
+
return nodeFs.readFileSync(paths.caCert);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
// Atomic commit: write .tmp + atomic rename for both key and cert.
|
|
@@ -311,22 +311,22 @@ function create(opts) {
|
|
|
311
311
|
try {
|
|
312
312
|
if (sealed) {
|
|
313
313
|
_requireVault("sealed CA key commit");
|
|
314
|
-
|
|
314
|
+
nodeFs.writeFileSync(keyTmp, vault.seal(opts2.caKeyPem), { mode: 0o600 });
|
|
315
315
|
} else {
|
|
316
|
-
|
|
316
|
+
nodeFs.writeFileSync(keyTmp, opts2.caKeyPem, { mode: 0o600 });
|
|
317
317
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
nodeFs.writeFileSync(certTmp, opts2.caCertPem, { mode: 0o644 });
|
|
319
|
+
nodeFs.renameSync(keyTmp, keyDest);
|
|
320
|
+
nodeFs.renameSync(certTmp, paths.caCert);
|
|
321
321
|
} catch (e) {
|
|
322
322
|
// Best-effort cleanup of half-written tmp files; the original
|
|
323
323
|
// commit error is what we re-raise. Log cleanup failures at debug
|
|
324
324
|
// so a genuinely-broken filesystem state surfaces in operator logs
|
|
325
325
|
// rather than getting silently swallowed.
|
|
326
|
-
try { if (
|
|
327
|
-
catch (cleanupErr) { caLog.debug("cleanup-failed", { op: "
|
|
328
|
-
try { if (
|
|
329
|
-
catch (cleanupErr) { caLog.debug("cleanup-failed", { op: "
|
|
326
|
+
try { if (nodeFs.existsSync(keyTmp)) nodeFs.unlinkSync(keyTmp); }
|
|
327
|
+
catch (cleanupErr) { caLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: keyTmp, error: cleanupErr.message }); }
|
|
328
|
+
try { if (nodeFs.existsSync(certTmp)) nodeFs.unlinkSync(certTmp); }
|
|
329
|
+
catch (cleanupErr) { caLog.debug("cleanup-failed", { op: "nodeFs.unlinkSync", path: certTmp, error: cleanupErr.message }); }
|
|
330
330
|
throw new MtlsCaError("mtls-ca/commit-failed",
|
|
331
331
|
"atomic CA commit failed: " + ((e && e.message) || String(e)));
|
|
332
332
|
}
|
|
@@ -381,13 +381,13 @@ function create(opts) {
|
|
|
381
381
|
// ---- Revocation registry + CRL ----
|
|
382
382
|
|
|
383
383
|
function _loadRevocations() {
|
|
384
|
-
if (!
|
|
384
|
+
if (!nodeFs.existsSync(paths.revocations)) return { revocations: [] };
|
|
385
385
|
try {
|
|
386
386
|
// safeJson.parse caps depth + size + protects against
|
|
387
387
|
// proto-pollution; the revocation file is under the operator's
|
|
388
388
|
// dataDir but a tampered or truncated file shouldn't be able to
|
|
389
389
|
// corrupt the rotator process.
|
|
390
|
-
var json = safeJson.parse(
|
|
390
|
+
var json = safeJson.parse(nodeFs.readFileSync(paths.revocations, "utf8"),
|
|
391
391
|
{ maxBytes: C.BYTES.mib(16) });
|
|
392
392
|
if (!json || !Array.isArray(json.revocations)) return { revocations: [] };
|
|
393
393
|
return json;
|
|
@@ -32,8 +32,8 @@ var nodeCrypto = require("node:crypto");
|
|
|
32
32
|
var pki = require("./vendor/pki.cjs");
|
|
33
33
|
|
|
34
34
|
var C = require("./constants");
|
|
35
|
-
var
|
|
36
|
-
var
|
|
35
|
+
var bCrypto = require("./crypto");
|
|
36
|
+
var numericBounds = require("./numeric-bounds");
|
|
37
37
|
var { FrameworkError } = require("./framework-error");
|
|
38
38
|
|
|
39
39
|
var x509 = pki.x509;
|
|
@@ -231,7 +231,7 @@ async function generateCa(opts) {
|
|
|
231
231
|
var keys = await webcrypto.subtle.generateKey(CA_KEY_ALG, true, CA_KEY_USAGES);
|
|
232
232
|
var now = new Date();
|
|
233
233
|
var ca = await x509.X509CertificateGenerator.createSelfSigned({
|
|
234
|
-
serialNumber:
|
|
234
|
+
serialNumber: bCrypto.generateToken(C.BYTES.bytes(16)),
|
|
235
235
|
name: "CN=" + caName + ",OU=CAv" + generation,
|
|
236
236
|
notBefore: now,
|
|
237
237
|
notAfter: new Date(now.getTime() + C.TIME.days(CA_VALIDITY_DAYS)),
|
|
@@ -255,7 +255,7 @@ async function signClientCert(opts) {
|
|
|
255
255
|
throw new MtlsEngineError("mtls-engine/missing-arg",
|
|
256
256
|
"signClientCert requires { cn, caCertPem, caKeyPem }");
|
|
257
257
|
}
|
|
258
|
-
|
|
258
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.validityDays,
|
|
259
259
|
"signClientCert: validityDays", MtlsEngineError, "mtls-engine/bad-validity-days");
|
|
260
260
|
var validityDays = opts.validityDays !== undefined
|
|
261
261
|
? opts.validityDays : LEAF_DEFAULT_DAYS;
|
|
@@ -314,7 +314,7 @@ async function signClientCert(opts) {
|
|
|
314
314
|
if (sanExt) extensions.push(sanExt);
|
|
315
315
|
|
|
316
316
|
var clientCert = await x509.X509CertificateGenerator.create({
|
|
317
|
-
serialNumber:
|
|
317
|
+
serialNumber: bCrypto.generateToken(C.BYTES.bytes(16)),
|
|
318
318
|
subject: "CN=" + cn,
|
|
319
319
|
issuer: caCert.subject,
|
|
320
320
|
notBefore: now,
|
|
@@ -43,7 +43,7 @@ var defineClass = require("./framework-error").defineClass;
|
|
|
43
43
|
var lazyRequire = require("./lazy-require");
|
|
44
44
|
var validateOpts = require("./validate-opts");
|
|
45
45
|
|
|
46
|
-
var
|
|
46
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
47
47
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
48
48
|
|
|
49
49
|
var ByteQuotaError = defineClass("ByteQuotaError", { alwaysPermanent: true });
|
|
@@ -181,7 +181,7 @@ function create(opts) {
|
|
|
181
181
|
function _emitAudit(action, outcome, metadata) {
|
|
182
182
|
if (!auditOn) return;
|
|
183
183
|
try {
|
|
184
|
-
|
|
184
|
+
audit().safeEmit({
|
|
185
185
|
action: "network.byte_quota." + action,
|
|
186
186
|
outcome: outcome,
|
|
187
187
|
metadata: metadata || {},
|
package/lib/network-dns.js
CHANGED
|
@@ -4,7 +4,7 @@ var dns = require("node:dns");
|
|
|
4
4
|
var net = require("node:net");
|
|
5
5
|
var nodeCrypto = require("node:crypto");
|
|
6
6
|
var https = require("node:https");
|
|
7
|
-
var
|
|
7
|
+
var nodeTls = require("node:tls");
|
|
8
8
|
var dnsPromises = dns.promises;
|
|
9
9
|
|
|
10
10
|
var C = require("./constants");
|
|
@@ -561,7 +561,7 @@ function _dotConnect() {
|
|
|
561
561
|
ecdhCurve: C.TLS_GROUP_CURVE_STR,
|
|
562
562
|
};
|
|
563
563
|
if (STATE.dot.ca) connectOpts.ca = STATE.dot.ca;
|
|
564
|
-
var sock =
|
|
564
|
+
var sock = nodeTls.connect(connectOpts);
|
|
565
565
|
// The pool entry is ref()'d while a query is in flight and unref()'d
|
|
566
566
|
// when idle — _dotLookup toggles this around its query. Calling
|
|
567
567
|
// unref() unconditionally here let node exit during a normal lookup
|
package/lib/network-nts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var nodeTls = require("node:tls");
|
|
4
4
|
var dgram = require("node:dgram");
|
|
5
5
|
var nodeCrypto = require("node:crypto");
|
|
6
6
|
|
|
@@ -258,7 +258,7 @@ function performKeHandshake(opts) {
|
|
|
258
258
|
ecdhCurve: C.TLS_GROUP_CURVE_STR,
|
|
259
259
|
};
|
|
260
260
|
if (opts.ca) connectOpts.ca = opts.ca;
|
|
261
|
-
var sock =
|
|
261
|
+
var sock = nodeTls.connect(connectOpts);
|
|
262
262
|
var timer = setTimeout(function () {
|
|
263
263
|
try { sock.destroy(); } catch (_e) { /* best-effort socket teardown */ }
|
|
264
264
|
done(new NtsError("nts/ke-timeout", "NTS-KE handshake timed out after " + timeoutMs + "ms"));
|
package/lib/network-proxy.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var http = require("node:http");
|
|
4
4
|
var https = require("node:https");
|
|
5
5
|
var net = require("node:net");
|
|
6
|
-
var
|
|
6
|
+
var nodeTls = require("node:tls");
|
|
7
7
|
|
|
8
8
|
var C = require("./constants");
|
|
9
9
|
var lazyRequire = require("./lazy-require");
|
|
@@ -155,7 +155,7 @@ function _proxyAuthHeader(proxyUrl) {
|
|
|
155
155
|
function _connectThroughTunnel(proxyUrl, targetHost, targetPort, callback) {
|
|
156
156
|
var proxyPort = proxyUrl.port || (proxyUrl.protocol === "https:" ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT);
|
|
157
157
|
var proxySocket = proxyUrl.protocol === "https:"
|
|
158
|
-
?
|
|
158
|
+
? nodeTls.connect({
|
|
159
159
|
host: proxyUrl.hostname,
|
|
160
160
|
port: proxyPort,
|
|
161
161
|
servername: proxyUrl.hostname,
|
|
@@ -212,7 +212,7 @@ function agentFor(targetUrl) {
|
|
|
212
212
|
agent.createConnection = function (options, cb) {
|
|
213
213
|
_connectThroughTunnel(proxy, options.host, options.port, function (err, tunnel) {
|
|
214
214
|
if (err) return cb(err);
|
|
215
|
-
var secure =
|
|
215
|
+
var secure = nodeTls.connect({
|
|
216
216
|
socket: tunnel,
|
|
217
217
|
servername: options.servername || options.host,
|
|
218
218
|
minVersion: "TLSv1.3",
|
|
@@ -58,7 +58,7 @@ var zlib = require("node:zlib");
|
|
|
58
58
|
var asn1 = require("./asn1-der");
|
|
59
59
|
var lazyRequire = require("./lazy-require");
|
|
60
60
|
var validateOpts = require("./validate-opts");
|
|
61
|
-
var
|
|
61
|
+
var bCrypto = require("./crypto");
|
|
62
62
|
var safeUrl = require("./safe-url");
|
|
63
63
|
var safeJson = require("./safe-json");
|
|
64
64
|
var C = require("./constants");
|
|
@@ -561,7 +561,7 @@ function tlsRptRecordShape(opts) {
|
|
|
561
561
|
|
|
562
562
|
function _genReportId() {
|
|
563
563
|
// RFC 8460 §4.4 requires uniqueness — use timestamp + random token.
|
|
564
|
-
return Date.now() + "-" +
|
|
564
|
+
return Date.now() + "-" + bCrypto.generateToken(C.BYTES.bytes(8));
|
|
565
565
|
}
|
|
566
566
|
|
|
567
567
|
// ---- TLS-RPT policy fetch (RFC 8460 §3) ----
|
package/lib/network-tls.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
3
|
+
var nodeTls = require("node:tls");
|
|
4
|
+
var nodeFs = require("node:fs");
|
|
5
|
+
var nodePath = require("node:path");
|
|
6
6
|
var net = require("node:net");
|
|
7
7
|
var nodeCrypto = require("node:crypto");
|
|
8
8
|
|
|
9
|
-
var
|
|
9
|
+
var bCrypto = require("./crypto");
|
|
10
10
|
var C = require("./constants");
|
|
11
11
|
var safeBuffer = require("./safe-buffer");
|
|
12
12
|
var validateOpts = require("./validate-opts");
|
|
@@ -85,14 +85,14 @@ function _isPathLike(s) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function _readPath(p) {
|
|
88
|
-
var stat =
|
|
88
|
+
var stat = nodeFs.statSync(p);
|
|
89
89
|
if (stat.isDirectory()) {
|
|
90
|
-
var files =
|
|
90
|
+
var files = nodeFs.readdirSync(p)
|
|
91
91
|
.filter(function (f) { return /\.(pem|crt|cer)$/i.test(f); })
|
|
92
92
|
.sort();
|
|
93
|
-
return files.map(function (f) { return
|
|
93
|
+
return files.map(function (f) { return nodeFs.readFileSync(nodePath.join(p, f), "utf8"); }).join("\n");
|
|
94
94
|
}
|
|
95
|
-
return
|
|
95
|
+
return nodeFs.readFileSync(p, "utf8");
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
function addCa(pemOrPath, opts) {
|
|
@@ -101,7 +101,7 @@ function addCa(pemOrPath, opts) {
|
|
|
101
101
|
var raw = pemOrPath;
|
|
102
102
|
if (typeof pemOrPath === "string" && _isPathLike(pemOrPath)) {
|
|
103
103
|
var stat;
|
|
104
|
-
try { stat =
|
|
104
|
+
try { stat = nodeFs.statSync(pemOrPath); } catch (_e) {
|
|
105
105
|
throw new TlsTrustError("tls/empty-pem", "tls.addCa: input has no PEM marker and is not a readable path: " +
|
|
106
106
|
pemOrPath);
|
|
107
107
|
}
|
|
@@ -408,7 +408,7 @@ function applyToContext(opts) {
|
|
|
408
408
|
var base = Object.assign({}, opts.base || {});
|
|
409
409
|
var caStrings = STATE.cas.map(function (e) { return e.pem; });
|
|
410
410
|
if (STATE.systemTrust) {
|
|
411
|
-
var rootCAs =
|
|
411
|
+
var rootCAs = nodeTls.rootCertificates;
|
|
412
412
|
if (Array.isArray(rootCAs)) {
|
|
413
413
|
caStrings = caStrings.concat(rootCAs);
|
|
414
414
|
}
|
|
@@ -719,7 +719,7 @@ function _connectAndCheckOcsp(opts, requireStapled) {
|
|
|
719
719
|
var connectOpts = Object.assign({}, opts, { requestOCSP: true });
|
|
720
720
|
var sock;
|
|
721
721
|
try {
|
|
722
|
-
sock =
|
|
722
|
+
sock = nodeTls.connect(connectOpts);
|
|
723
723
|
} catch (e) {
|
|
724
724
|
reject(new TlsTrustError("tls/connect-failed",
|
|
725
725
|
"tls.connect threw: " + ((e && e.message) || String(e))));
|
|
@@ -1110,7 +1110,7 @@ function evaluateOcspResponse(ocspDer, opts) {
|
|
|
1110
1110
|
// critical here (the OCSP response is CA-signed and signature
|
|
1111
1111
|
// already verified) but matches the project discipline.
|
|
1112
1112
|
// (Audit 2026-05-11.)
|
|
1113
|
-
if (!
|
|
1113
|
+
if (!bCrypto.timingSafeEqual(parsed.basic.nonce, opts.expectedNonce)) {
|
|
1114
1114
|
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1115
1115
|
errors: ["OCSP nonce mismatch — possible replay or wrong responder"] };
|
|
1116
1116
|
}
|
|
@@ -2182,7 +2182,7 @@ var ct = Object.freeze({
|
|
|
2182
2182
|
if (!Buffer.isBuffer(sthRoot) || sthRoot.length !== 32) { // allow:raw-byte-literal — RFC 9162 SHA-256 digest length
|
|
2183
2183
|
return { valid: false, reason: "bad-sth-root" };
|
|
2184
2184
|
}
|
|
2185
|
-
if (!
|
|
2185
|
+
if (!bCrypto.timingSafeEqual(computedRoot, sthRoot)) {
|
|
2186
2186
|
return { valid: false, reason: "root-mismatch",
|
|
2187
2187
|
computedRoot: computedRoot.toString("hex") };
|
|
2188
2188
|
}
|
|
@@ -2201,7 +2201,7 @@ var ct = Object.freeze({
|
|
|
2201
2201
|
var computedSecond = _ctVerifyConsistencyPath(
|
|
2202
2202
|
opts.consistency.firstSize, opts.sthFromLog.treeSize,
|
|
2203
2203
|
opts.consistency.proof || [], firstRoot);
|
|
2204
|
-
var ok =
|
|
2204
|
+
var ok = bCrypto.timingSafeEqual(computedSecond, sthRoot);
|
|
2205
2205
|
consistencyResult = {
|
|
2206
2206
|
ok: ok,
|
|
2207
2207
|
computedSecondRoot: computedSecond.toString("hex"),
|
|
@@ -2256,7 +2256,7 @@ var ct = Object.freeze({
|
|
|
2256
2256
|
return { valid: false, reason: "consistency-walk-failed",
|
|
2257
2257
|
error: (e && e.message) || String(e) };
|
|
2258
2258
|
}
|
|
2259
|
-
if (!
|
|
2259
|
+
if (!bCrypto.timingSafeEqual(computed, secondRoot)) {
|
|
2260
2260
|
return { valid: false, reason: "root-mismatch",
|
|
2261
2261
|
computedRoot: computed.toString("hex") };
|
|
2262
2262
|
}
|
|
@@ -2500,7 +2500,7 @@ function _isEchSupported() {
|
|
|
2500
2500
|
// immediately-destroyed socket. Any non-throwing path = supported.
|
|
2501
2501
|
var supported = false;
|
|
2502
2502
|
try {
|
|
2503
|
-
var probe =
|
|
2503
|
+
var probe = nodeTls.connect({
|
|
2504
2504
|
host: "127.0.0.1",
|
|
2505
2505
|
port: 1,
|
|
2506
2506
|
ech: Buffer.alloc(0),
|
|
@@ -2651,7 +2651,7 @@ function connectWithEch(opts) {
|
|
|
2651
2651
|
}
|
|
2652
2652
|
|
|
2653
2653
|
var sock;
|
|
2654
|
-
try { sock =
|
|
2654
|
+
try { sock = nodeTls.connect(connectOpts); }
|
|
2655
2655
|
catch (e) {
|
|
2656
2656
|
reject(new NetworkTlsError("tls/ech-connect-failed",
|
|
2657
2657
|
"connectWithEch: tls.connect threw: " + ((e && e.message) || String(e))));
|
package/lib/network.js
CHANGED
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
var byteQuota = require("./network-byte-quota");
|
|
34
34
|
var ntpCheck = require("./ntp-check");
|
|
35
35
|
var nts = require("./network-nts");
|
|
36
|
-
var
|
|
37
|
-
var
|
|
38
|
-
var
|
|
36
|
+
var networkDns = require("./network-dns");
|
|
37
|
+
var networkProxy = require("./network-proxy");
|
|
38
|
+
var networkTls = require("./network-tls");
|
|
39
39
|
var heartbeat = require("./network-heartbeat");
|
|
40
40
|
var smtpPolicy = require("./network-smtp-policy");
|
|
41
41
|
var ssrfGuard = require("./ssrf-guard");
|
|
@@ -211,31 +211,31 @@ function bootFromEnv(opts) {
|
|
|
211
211
|
var dnsServers = env.BLAMEJS_DNS_SERVERS;
|
|
212
212
|
if (dnsServers) {
|
|
213
213
|
var dl = String(dnsServers).split(",").map(function (s) { return s.trim(); }).filter(Boolean);
|
|
214
|
-
if (dl.length > 0) {
|
|
214
|
+
if (dl.length > 0) { networkDns.setServers(dl); applied.dns.servers = dl.length; }
|
|
215
215
|
}
|
|
216
|
-
if (env.BLAMEJS_DNS_RESULT_ORDER) {
|
|
217
|
-
if (env.BLAMEJS_DNS_FAMILY) {
|
|
218
|
-
if (env.BLAMEJS_DNS_LOOKUP_TIMEOUT_MS) {
|
|
219
|
-
if (env.BLAMEJS_DNS_CACHE_TTL_MS) {
|
|
220
|
-
if (env.BLAMEJS_DOH_URL) {
|
|
221
|
-
else if (env.BLAMEJS_DOH_PROVIDER) {
|
|
222
|
-
if (env.BLAMEJS_DOT_HOST) {
|
|
216
|
+
if (env.BLAMEJS_DNS_RESULT_ORDER) { networkDns.setResultOrder(env.BLAMEJS_DNS_RESULT_ORDER); applied.dns.resultOrder = env.BLAMEJS_DNS_RESULT_ORDER; }
|
|
217
|
+
if (env.BLAMEJS_DNS_FAMILY) { networkDns.setFamily(parseInt(env.BLAMEJS_DNS_FAMILY, 10)); applied.dns.family = parseInt(env.BLAMEJS_DNS_FAMILY, 10); }
|
|
218
|
+
if (env.BLAMEJS_DNS_LOOKUP_TIMEOUT_MS) { networkDns.setLookupTimeoutMs(parseInt(env.BLAMEJS_DNS_LOOKUP_TIMEOUT_MS, 10)); applied.dns.lookupTimeoutMs = parseInt(env.BLAMEJS_DNS_LOOKUP_TIMEOUT_MS, 10); }
|
|
219
|
+
if (env.BLAMEJS_DNS_CACHE_TTL_MS) { networkDns.setCacheTtlMs(parseInt(env.BLAMEJS_DNS_CACHE_TTL_MS, 10)); applied.dns.cacheTtlMs = parseInt(env.BLAMEJS_DNS_CACHE_TTL_MS, 10); }
|
|
220
|
+
if (env.BLAMEJS_DOH_URL) { networkDns.useDnsOverHttps({ url: env.BLAMEJS_DOH_URL }); applied.dns.doh = env.BLAMEJS_DOH_URL; }
|
|
221
|
+
else if (env.BLAMEJS_DOH_PROVIDER) { networkDns.useDnsOverHttps({ provider: env.BLAMEJS_DOH_PROVIDER }); applied.dns.dohProvider = env.BLAMEJS_DOH_PROVIDER; }
|
|
222
|
+
if (env.BLAMEJS_DOT_HOST) { networkDns.useDnsOverTls({ host: env.BLAMEJS_DOT_HOST, port: env.BLAMEJS_DOT_PORT ? parseInt(env.BLAMEJS_DOT_PORT, 10) : 853 }); applied.dns.dot = env.BLAMEJS_DOT_HOST; }
|
|
223
223
|
|
|
224
224
|
if (env.HTTP_PROXY || env.http_proxy || env.HTTPS_PROXY || env.https_proxy ||
|
|
225
225
|
env.NO_PROXY || env.no_proxy || env.ALL_PROXY || env.all_proxy) {
|
|
226
|
-
applied.proxy =
|
|
226
|
+
applied.proxy = networkProxy.fromEnv(env);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
if (env.BLAMEJS_EXTRA_CA_CERTS) {
|
|
230
|
-
|
|
230
|
+
networkTls.addCa(env.BLAMEJS_EXTRA_CA_CERTS, { label: "BLAMEJS_EXTRA_CA_CERTS" });
|
|
231
231
|
applied.tls.fileLoaded = env.BLAMEJS_EXTRA_CA_CERTS;
|
|
232
232
|
}
|
|
233
233
|
if (env.BLAMEJS_EXTRA_CA_CERTS_DIR) {
|
|
234
|
-
|
|
234
|
+
networkTls.addCaBundle(env.BLAMEJS_EXTRA_CA_CERTS_DIR, { label: "BLAMEJS_EXTRA_CA_CERTS_DIR" });
|
|
235
235
|
applied.tls.dirLoaded = env.BLAMEJS_EXTRA_CA_CERTS_DIR;
|
|
236
236
|
}
|
|
237
237
|
if (env.BLAMEJS_USE_SYSTEM_TRUST === "1" || env.BLAMEJS_USE_SYSTEM_TRUST === "true") {
|
|
238
|
-
|
|
238
|
+
networkTls.useSystemTrust(true);
|
|
239
239
|
applied.tls.systemTrust = true;
|
|
240
240
|
}
|
|
241
241
|
|
|
@@ -286,11 +286,11 @@ function snapshot() {
|
|
|
286
286
|
servers: ntpFacade.getServers(),
|
|
287
287
|
thresholds: ntpCheck.getThresholds(),
|
|
288
288
|
},
|
|
289
|
-
dns:
|
|
290
|
-
proxy:
|
|
289
|
+
dns: networkDns._stateForTest(),
|
|
290
|
+
proxy: networkProxy.snapshot(),
|
|
291
291
|
tls: {
|
|
292
|
-
systemTrust:
|
|
293
|
-
caCount:
|
|
292
|
+
systemTrust: networkTls.isSystemTrustEnabled(),
|
|
293
|
+
caCount: networkTls.getTrustStore().length,
|
|
294
294
|
},
|
|
295
295
|
heartbeat: heartbeat.statuses(),
|
|
296
296
|
socket: _socketDefaults(),
|
|
@@ -305,9 +305,9 @@ function _resetForTest() {
|
|
|
305
305
|
ntpFacade._defaultServers = null;
|
|
306
306
|
ntpFacade._defaultTimeoutMs = null;
|
|
307
307
|
if (typeof ntpCheck._resetThresholdsForTest === "function") ntpCheck._resetThresholdsForTest();
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
308
|
+
networkDns._resetForTest();
|
|
309
|
+
networkProxy._resetForTest();
|
|
310
|
+
networkTls._resetForTest();
|
|
311
311
|
heartbeat._resetForTest();
|
|
312
312
|
SOCKET_DEFAULTS.noDelay = true;
|
|
313
313
|
SOCKET_DEFAULTS.keepAlive = true;
|
|
@@ -316,9 +316,9 @@ function _resetForTest() {
|
|
|
316
316
|
|
|
317
317
|
module.exports = {
|
|
318
318
|
ntp: ntpFacade,
|
|
319
|
-
dns:
|
|
320
|
-
proxy:
|
|
321
|
-
tls:
|
|
319
|
+
dns: networkDns,
|
|
320
|
+
proxy: networkProxy,
|
|
321
|
+
tls: networkTls,
|
|
322
322
|
heartbeat: heartbeat,
|
|
323
323
|
smtp: {
|
|
324
324
|
policy: smtpPolicy,
|
package/lib/notify.js
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
*/
|
|
42
42
|
|
|
43
43
|
var lazyRequire = require("./lazy-require");
|
|
44
|
-
var
|
|
44
|
+
var logModule = require("./log");
|
|
45
45
|
var numericChecks = require("./numeric-checks");
|
|
46
46
|
var requestHelpers = require("./request-helpers");
|
|
47
47
|
var safeAsync = require("./safe-async");
|
|
@@ -53,10 +53,10 @@ var { NotifyError } = require("./framework-error");
|
|
|
53
53
|
// Lazy-required modules to avoid load-order cycles. retry / observability /
|
|
54
54
|
// redact / httpClient don't currently import notify, but treating them
|
|
55
55
|
// the same way every primitive does keeps the load-order story uniform.
|
|
56
|
-
var
|
|
56
|
+
var retryHelper = lazyRequire(function () { return require("./retry"); });
|
|
57
57
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
58
|
-
var
|
|
59
|
-
var
|
|
58
|
+
var redact = lazyRequire(function () { return require("./redact"); });
|
|
59
|
+
var httpClient = lazyRequire(function () { return require("./http-client"); });
|
|
60
60
|
|
|
61
61
|
var _err = NotifyError.factory;
|
|
62
62
|
|
|
@@ -222,7 +222,7 @@ function httpJson(opts) {
|
|
|
222
222
|
return {
|
|
223
223
|
name: name,
|
|
224
224
|
send: async function (message, sendOpts) {
|
|
225
|
-
var client = customClient ||
|
|
225
|
+
var client = customClient || httpClient();
|
|
226
226
|
var body;
|
|
227
227
|
var contentType;
|
|
228
228
|
if (bodyFormat === "form") {
|
|
@@ -282,14 +282,14 @@ function httpJson(opts) {
|
|
|
282
282
|
// log — fire-and-forget developer logger. Never throws; audit + obs still emit.
|
|
283
283
|
function logTransport(opts) {
|
|
284
284
|
opts = opts || {};
|
|
285
|
-
//
|
|
285
|
+
// logModule.boot() returns a callable with .info / .warn / .error
|
|
286
286
|
// attached; that shape satisfies the operator-supplied opts.logger
|
|
287
287
|
// contract directly. No fallback wrapper needed.
|
|
288
288
|
var logger;
|
|
289
289
|
if (opts.logger && typeof opts.logger.info === "function") {
|
|
290
290
|
logger = opts.logger;
|
|
291
291
|
} else {
|
|
292
|
-
logger =
|
|
292
|
+
logger = logModule.boot("notify.log");
|
|
293
293
|
}
|
|
294
294
|
return {
|
|
295
295
|
name: opts.name || "log",
|
|
@@ -380,7 +380,7 @@ function create(opts) {
|
|
|
380
380
|
var redactFn = (typeof opts.redact === "function")
|
|
381
381
|
? opts.redact
|
|
382
382
|
// Default: b.redact.redact — the framework's PII detector chain.
|
|
383
|
-
: function (m) { return
|
|
383
|
+
: function (m) { return redact().redact(m); };
|
|
384
384
|
var defaultTimeoutMs = cfg.defaultTimeoutMs;
|
|
385
385
|
var defaultRetry = opts.defaultRetry || null;
|
|
386
386
|
var defaultBreaker = opts.defaultBreaker || null;
|
|
@@ -402,7 +402,7 @@ function create(opts) {
|
|
|
402
402
|
};
|
|
403
403
|
var breakerOpts = entry.breaker || defaultBreaker;
|
|
404
404
|
if (breakerOpts) {
|
|
405
|
-
registry.breaker = new (
|
|
405
|
+
registry.breaker = new (retryHelper().CircuitBreaker)(n, breakerOpts);
|
|
406
406
|
}
|
|
407
407
|
if (entry.serialize) registry.mutex = new safeAsync.Mutex();
|
|
408
408
|
channels[n] = registry;
|
|
@@ -519,7 +519,7 @@ function create(opts) {
|
|
|
519
519
|
try {
|
|
520
520
|
// b.retry.withRetry IS the retry loop. Notify never hand-rolls
|
|
521
521
|
// backoff/jitter/classification — the framework owns it.
|
|
522
|
-
var result = await
|
|
522
|
+
var result = await retryHelper().withRetry(function (attempt) {
|
|
523
523
|
return _attemptSerialized(attempt);
|
|
524
524
|
}, perCallRetry);
|
|
525
525
|
|
|
@@ -647,7 +647,7 @@ function create(opts) {
|
|
|
647
647
|
mutex: null,
|
|
648
648
|
};
|
|
649
649
|
var breakerOpts = entry.breaker || defaultBreaker;
|
|
650
|
-
if (breakerOpts) registry.breaker = new (
|
|
650
|
+
if (breakerOpts) registry.breaker = new (retryHelper().CircuitBreaker)(name, breakerOpts);
|
|
651
651
|
if (entry.serialize) registry.mutex = new safeAsync.Mutex();
|
|
652
652
|
channels[name] = registry;
|
|
653
653
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* Auth: same service-account JSON / RSA-SHA256-signed JWT exchanged
|
|
24
24
|
* for an OAuth2 access token as `lib/object-store/gcs.js`.
|
|
25
25
|
*/
|
|
26
|
-
var
|
|
26
|
+
var nodeFs = require("node:fs");
|
|
27
27
|
var gcs = require("./gcs");
|
|
28
28
|
var authHeader = require("../auth-header");
|
|
29
29
|
var httpClient = require("../http-client");
|
|
@@ -139,7 +139,7 @@ function create(config) {
|
|
|
139
139
|
var serviceAccount = config.serviceAccount;
|
|
140
140
|
if (!serviceAccount && config.serviceAccountFile) {
|
|
141
141
|
try {
|
|
142
|
-
serviceAccount = safeJson.parse(
|
|
142
|
+
serviceAccount = safeJson.parse(nodeFs.readFileSync(config.serviceAccountFile));
|
|
143
143
|
} catch (e) {
|
|
144
144
|
throw _err("BAD_OPT", "gcs bucketOps: failed to read serviceAccountFile '" +
|
|
145
145
|
config.serviceAccountFile + "': " + ((e && e.message) || String(e)), true);
|
package/lib/object-store/gcs.js
CHANGED
|
@@ -22,12 +22,12 @@
|
|
|
22
22
|
* https://cloud.google.com/storage/docs/json_api/v1
|
|
23
23
|
* https://developers.google.com/identity/protocols/oauth2/service-account
|
|
24
24
|
*/
|
|
25
|
-
var
|
|
25
|
+
var nodeFs = require("fs");
|
|
26
26
|
var nodeCrypto = require("crypto");
|
|
27
27
|
var { Readable } = require("stream");
|
|
28
28
|
var safeJson = require("../safe-json");
|
|
29
29
|
var C = require("../constants");
|
|
30
|
-
var
|
|
30
|
+
var numericBounds = require("../numeric-bounds");
|
|
31
31
|
var requestHelpers = require("../request-helpers");
|
|
32
32
|
var { ObjectStoreError } = require("../framework-error");
|
|
33
33
|
var safeUrl = require("../safe-url");
|
|
@@ -125,7 +125,7 @@ function create(config) {
|
|
|
125
125
|
var serviceAccount = config.serviceAccount;
|
|
126
126
|
if (!serviceAccount && config.serviceAccountFile) {
|
|
127
127
|
try {
|
|
128
|
-
serviceAccount = safeJson.parse(
|
|
128
|
+
serviceAccount = safeJson.parse(nodeFs.readFileSync(config.serviceAccountFile), { schema: SERVICE_ACCOUNT_SCHEMA });
|
|
129
129
|
} catch (e) {
|
|
130
130
|
throw new Error("gcs: failed to read serviceAccountFile '" + config.serviceAccountFile + "': " + e.message);
|
|
131
131
|
}
|
|
@@ -421,10 +421,10 @@ function create(config) {
|
|
|
421
421
|
"POST-form policy enforces body size via the content-length-range condition; " +
|
|
422
422
|
"use presignedUploadUrl if size enforcement is not needed", true);
|
|
423
423
|
}
|
|
424
|
-
if (opts.minBytes !== undefined && !
|
|
424
|
+
if (opts.minBytes !== undefined && !numericBounds.isNonNegativeFiniteInt(opts.minBytes)) {
|
|
425
425
|
throw _err("INVALID_MIN_BYTES",
|
|
426
426
|
"presignedUploadPolicy: minBytes must be a non-negative finite integer; got " +
|
|
427
|
-
|
|
427
|
+
numericBounds.shape(opts.minBytes), true);
|
|
428
428
|
}
|
|
429
429
|
var minBytes = opts.minBytes !== undefined ? opts.minBytes : 0;
|
|
430
430
|
var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
|