@blamejs/core 0.9.12 → 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 +3 -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 +10 -7
- 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/audit.js +29 -17
- 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 +24 -9
- 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/crypto.js +145 -0
- 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/dsr.js +22 -15
- 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/inbox.js +21 -15
- 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 +249 -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 +250 -0
- 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-agent.js +116 -26
- 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/retry.js +50 -0
- 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-standalone-verifier.js +280 -0
- package/lib/self-update.js +32 -26
- 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 +70 -66
- 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/network.js
CHANGED
|
@@ -34,8 +34,8 @@ var byteQuota = require("./network-byte-quota");
|
|
|
34
34
|
var ntpCheck = require("./ntp-check");
|
|
35
35
|
var nts = require("./network-nts");
|
|
36
36
|
var dns = require("./network-dns");
|
|
37
|
-
var
|
|
38
|
-
var
|
|
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");
|
|
@@ -223,19 +223,19 @@ function bootFromEnv(opts) {
|
|
|
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
|
|
|
@@ -287,10 +287,10 @@ function snapshot() {
|
|
|
287
287
|
thresholds: ntpCheck.getThresholds(),
|
|
288
288
|
},
|
|
289
289
|
dns: dns._stateForTest(),
|
|
290
|
-
proxy:
|
|
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(),
|
|
@@ -306,8 +306,8 @@ function _resetForTest() {
|
|
|
306
306
|
ntpFacade._defaultTimeoutMs = null;
|
|
307
307
|
if (typeof ntpCheck._resetThresholdsForTest === "function") ntpCheck._resetThresholdsForTest();
|
|
308
308
|
dns._resetForTest();
|
|
309
|
-
|
|
310
|
-
|
|
309
|
+
networkProxy._resetForTest();
|
|
310
|
+
networkTls._resetForTest();
|
|
311
311
|
heartbeat._resetForTest();
|
|
312
312
|
SOCKET_DEFAULTS.noDelay = true;
|
|
313
313
|
SOCKET_DEFAULTS.keepAlive = true;
|
|
@@ -317,8 +317,8 @@ function _resetForTest() {
|
|
|
317
317
|
module.exports = {
|
|
318
318
|
ntp: ntpFacade,
|
|
319
319
|
dns: dns,
|
|
320
|
-
proxy:
|
|
321
|
-
tls:
|
|
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");
|
|
@@ -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",
|
|
@@ -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;
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
*/
|
|
30
30
|
var localProto = require("./local");
|
|
31
31
|
var httpPutProto = require("./http-put");
|
|
32
|
-
var
|
|
32
|
+
var sigv4 = require("./sigv4");
|
|
33
33
|
var sigv4BucketOps = require("./sigv4-bucket-ops");
|
|
34
|
-
var
|
|
34
|
+
var gcs = require("./gcs");
|
|
35
35
|
var gcsBucketOps = require("./gcs-bucket-ops");
|
|
36
|
-
var
|
|
36
|
+
var azureBlob = require("./azure-blob");
|
|
37
37
|
var azureBlobBucketOps = require("./azure-blob-bucket-ops");
|
|
38
38
|
var retryHelper = require("../retry");
|
|
39
39
|
var protocolDispatcher = require("../protocol-dispatcher");
|
|
@@ -47,9 +47,9 @@ var dispatcher = protocolDispatcher.create({
|
|
|
47
47
|
protocols: {
|
|
48
48
|
"local": localProto,
|
|
49
49
|
"http-put": httpPutProto,
|
|
50
|
-
"sigv4":
|
|
51
|
-
"gcs":
|
|
52
|
-
"azure-blob":
|
|
50
|
+
"sigv4": sigv4,
|
|
51
|
+
"gcs": gcs,
|
|
52
|
+
"azure-blob": azureBlob,
|
|
53
53
|
},
|
|
54
54
|
deferred: {},
|
|
55
55
|
fallbackProtocol: "local",
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Implements the uniform protocol surface (put / get / getStream / delete /
|
|
6
6
|
* head / list) against a directory tree. Streaming is via Node's native
|
|
7
|
-
*
|
|
7
|
+
* nodeFs.createReadStream / createWriteStream — no in-memory buffering of
|
|
8
8
|
* full files.
|
|
9
9
|
*
|
|
10
10
|
* Path safety: every key resolves under the configured rootDir, with an
|
|
11
11
|
* alphanumeric + `_-./` charset whitelist and explicit rejection of any
|
|
12
12
|
* path that escapes rootDir after resolution.
|
|
13
13
|
*/
|
|
14
|
-
var
|
|
15
|
-
var
|
|
14
|
+
var nodeFs = require("fs");
|
|
15
|
+
var nodePath = require("path");
|
|
16
16
|
var atomicFile = require("../atomic-file");
|
|
17
17
|
var cluster = require("../cluster");
|
|
18
18
|
var { ObjectStoreError } = require("../framework-error");
|
|
@@ -24,10 +24,10 @@ function _resolveSafe(rootDir, key) {
|
|
|
24
24
|
throw _err("INVALID_KEY", "key must be a non-empty string", true);
|
|
25
25
|
}
|
|
26
26
|
if (key.includes("\0")) throw _err("INVALID_KEY", "null byte in key", true);
|
|
27
|
-
if (
|
|
27
|
+
if (nodePath.isAbsolute(key)) throw _err("INVALID_KEY", "absolute key not allowed", true);
|
|
28
28
|
if (!SAFE_KEY.test(key)) throw _err("INVALID_KEY", "invalid characters in key", true);
|
|
29
|
-
var full =
|
|
30
|
-
var withSep = rootDir.endsWith(
|
|
29
|
+
var full = nodePath.resolve(rootDir, key);
|
|
30
|
+
var withSep = rootDir.endsWith(nodePath.sep) ? rootDir : rootDir + nodePath.sep;
|
|
31
31
|
if (full !== rootDir && !full.startsWith(withSep)) {
|
|
32
32
|
throw _err("INVALID_KEY", "key escapes rootDir", true);
|
|
33
33
|
}
|
|
@@ -40,14 +40,14 @@ function create(config) {
|
|
|
40
40
|
if (!config || !config.rootDir) {
|
|
41
41
|
throw new Error("local protocol requires { rootDir }");
|
|
42
42
|
}
|
|
43
|
-
var rootDir =
|
|
44
|
-
if (!
|
|
43
|
+
var rootDir = nodePath.resolve(config.rootDir);
|
|
44
|
+
if (!nodeFs.existsSync(rootDir)) nodeFs.mkdirSync(rootDir, { recursive: true });
|
|
45
45
|
|
|
46
46
|
function put(key, body, _opts) {
|
|
47
47
|
cluster.requireLeader();
|
|
48
48
|
var full = _resolveSafe(rootDir, key);
|
|
49
|
-
var dir =
|
|
50
|
-
if (!
|
|
49
|
+
var dir = nodePath.dirname(full);
|
|
50
|
+
if (!nodeFs.existsSync(dir)) nodeFs.mkdirSync(dir, { recursive: true });
|
|
51
51
|
|
|
52
52
|
if (Buffer.isBuffer(body)) {
|
|
53
53
|
atomicFile.writeSync(full, body);
|
|
@@ -56,7 +56,7 @@ function create(config) {
|
|
|
56
56
|
if (body && typeof body.pipe === "function") {
|
|
57
57
|
// Streaming put — pipe directly to disk
|
|
58
58
|
return new Promise(function (resolve, reject) {
|
|
59
|
-
var ws =
|
|
59
|
+
var ws = nodeFs.createWriteStream(full);
|
|
60
60
|
var bytes = 0;
|
|
61
61
|
body.on("data", function (chunk) { bytes += chunk.length; });
|
|
62
62
|
body.pipe(ws);
|
|
@@ -75,26 +75,26 @@ function create(config) {
|
|
|
75
75
|
|
|
76
76
|
function get(key) {
|
|
77
77
|
var full = _resolveSafe(rootDir, key);
|
|
78
|
-
if (!
|
|
78
|
+
if (!nodeFs.existsSync(full)) {
|
|
79
79
|
return Promise.reject(_err("NOT_FOUND", "key not found: " + key, true));
|
|
80
80
|
}
|
|
81
|
-
return Promise.resolve(
|
|
81
|
+
return Promise.resolve(nodeFs.readFileSync(full));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
function getStream(key) {
|
|
85
85
|
var full = _resolveSafe(rootDir, key);
|
|
86
|
-
if (!
|
|
86
|
+
if (!nodeFs.existsSync(full)) {
|
|
87
87
|
throw _err("NOT_FOUND", "key not found: " + key, true);
|
|
88
88
|
}
|
|
89
|
-
return
|
|
89
|
+
return nodeFs.createReadStream(full);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
function head(key) {
|
|
93
93
|
var full = _resolveSafe(rootDir, key);
|
|
94
|
-
if (!
|
|
94
|
+
if (!nodeFs.existsSync(full)) {
|
|
95
95
|
return Promise.reject(_err("NOT_FOUND", "key not found: " + key, true));
|
|
96
96
|
}
|
|
97
|
-
var stat =
|
|
97
|
+
var stat = nodeFs.statSync(full);
|
|
98
98
|
return Promise.resolve({
|
|
99
99
|
size: stat.size,
|
|
100
100
|
lastModified: stat.mtimeMs,
|
|
@@ -104,8 +104,8 @@ function create(config) {
|
|
|
104
104
|
function deleteKey(key) {
|
|
105
105
|
cluster.requireLeader();
|
|
106
106
|
var full = _resolveSafe(rootDir, key);
|
|
107
|
-
if (!
|
|
108
|
-
|
|
107
|
+
if (!nodeFs.existsSync(full)) return Promise.resolve(false);
|
|
108
|
+
nodeFs.unlinkSync(full);
|
|
109
109
|
return Promise.resolve(true);
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -29,7 +29,7 @@ var { Readable } = require("stream");
|
|
|
29
29
|
var safeXml = require("../parsers/safe-xml");
|
|
30
30
|
var sharedRequest = require("./http-request");
|
|
31
31
|
var C = require("../constants");
|
|
32
|
-
var
|
|
32
|
+
var numericBounds = require("../numeric-bounds");
|
|
33
33
|
var requestHelpers = require("../request-helpers");
|
|
34
34
|
var { ObjectStoreError } = require("../framework-error");
|
|
35
35
|
var safeUrl = require("../safe-url");
|
|
@@ -821,10 +821,10 @@ function create(config) {
|
|
|
821
821
|
"POST-form policy enforces body size via the content-length-range condition; " +
|
|
822
822
|
"use presignedUploadUrl if size enforcement is not needed", true);
|
|
823
823
|
}
|
|
824
|
-
if (opts.minBytes !== undefined && !
|
|
824
|
+
if (opts.minBytes !== undefined && !numericBounds.isNonNegativeFiniteInt(opts.minBytes)) {
|
|
825
825
|
throw _err("INVALID_MIN_BYTES",
|
|
826
826
|
"presignedUploadPolicy: minBytes must be a non-negative finite integer; got " +
|
|
827
|
-
|
|
827
|
+
numericBounds.shape(opts.minBytes), true);
|
|
828
828
|
}
|
|
829
829
|
var minBytes = opts.minBytes !== undefined ? opts.minBytes : 0;
|
|
830
830
|
var expiresIn = opts.expiresIn != null ? opts.expiresIn : PRESIGN_DEFAULT_EXPIRES_SECONDS;
|
|
@@ -73,14 +73,14 @@
|
|
|
73
73
|
*/
|
|
74
74
|
|
|
75
75
|
var bCrypto = require("./crypto");
|
|
76
|
-
var
|
|
76
|
+
var C = require("./constants");
|
|
77
77
|
var lazyRequire = require("./lazy-require");
|
|
78
78
|
var safeBuffer = require("./safe-buffer");
|
|
79
79
|
var validateOpts = require("./validate-opts");
|
|
80
80
|
var { defineClass } = require("./framework-error");
|
|
81
81
|
|
|
82
|
-
var TRACE_ID_BYTES =
|
|
83
|
-
var SPAN_ID_BYTES =
|
|
82
|
+
var TRACE_ID_BYTES = C.BYTES.bytes(16); // W3C §3.2.2.3 — 128-bit trace-id
|
|
83
|
+
var SPAN_ID_BYTES = C.BYTES.bytes(8); // W3C §3.2.2.4 — 64-bit span-id
|
|
84
84
|
|
|
85
85
|
var TracerError = defineClass("TracerError", { alwaysPermanent: true });
|
|
86
86
|
|
|
@@ -168,7 +168,7 @@ function create(opts) {
|
|
|
168
168
|
var resource = Object.assign({
|
|
169
169
|
"service.name": opts.service,
|
|
170
170
|
}, opts.resource || {});
|
|
171
|
-
var scope = opts.scope || { name: "blamejs", version:
|
|
171
|
+
var scope = opts.scope || { name: "blamejs", version: C.version || null };
|
|
172
172
|
var maxAttributes = opts.maxAttributes || DEFAULT_MAX_ATTRIBUTES;
|
|
173
173
|
var maxEvents = opts.maxEvents || DEFAULT_MAX_EVENTS;
|
|
174
174
|
var maxAttrValLen = opts.maxAttributeValueLength || DEFAULT_MAX_ATTR_VALUE_LEN;
|
package/lib/otel-export.js
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
*/
|
|
42
42
|
var C = require("./constants");
|
|
43
43
|
var canonicalJson = require("./canonical-json");
|
|
44
|
-
var
|
|
44
|
+
var httpClient = require("./http-client");
|
|
45
45
|
var safeAsync = require("./safe-async");
|
|
46
46
|
var validateOpts = require("./validate-opts");
|
|
47
47
|
var { defineClass } = require("./framework-error");
|
|
@@ -110,7 +110,7 @@ function create(opts) {
|
|
|
110
110
|
throw new OtelExportError("otel-export/bad-interval",
|
|
111
111
|
"create: intervalMs must be a non-negative finite number");
|
|
112
112
|
}
|
|
113
|
-
var
|
|
113
|
+
var effectiveHttpClient = opts.httpClient || httpClient;
|
|
114
114
|
var scopeName = (opts.scope && opts.scope.name) || "blamejs";
|
|
115
115
|
var scopeVersion = (opts.scope && opts.scope.version) || "0.5.x";
|
|
116
116
|
var resourceAttrs = Object.assign({ "service.name": serviceName },
|
|
@@ -220,7 +220,7 @@ function create(opts) {
|
|
|
220
220
|
if (!payload) return { sent: false, reason: "no-data" };
|
|
221
221
|
var body = JSON.stringify(payload);
|
|
222
222
|
try {
|
|
223
|
-
var res = await
|
|
223
|
+
var res = await effectiveHttpClient.request({
|
|
224
224
|
method: "POST",
|
|
225
225
|
url: endpoint,
|
|
226
226
|
headers: Object.assign({ "Content-Type": "application/json" }, headers),
|
package/lib/pagination.js
CHANGED
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
var nodeCrypto = require("node:crypto");
|
|
50
50
|
var C = require("./constants");
|
|
51
51
|
var canonicalJson = require("./canonical-json");
|
|
52
|
-
var
|
|
53
|
-
var
|
|
52
|
+
var bCrypto = require("./crypto");
|
|
53
|
+
var numericBounds = require("./numeric-bounds");
|
|
54
54
|
var safeJson = require("./safe-json");
|
|
55
55
|
var safeSql = require("./safe-sql");
|
|
56
56
|
var { defineClass } = require("./framework-error");
|
|
@@ -185,7 +185,7 @@ function decodeCursor(token, secret) {
|
|
|
185
185
|
throw new PaginationError("pagination/bad-cursor", "cursor base64 decode failed");
|
|
186
186
|
}
|
|
187
187
|
var expected = _tag(sb, json);
|
|
188
|
-
if (!
|
|
188
|
+
if (!bCrypto.timingSafeEqual(tag, expected)) {
|
|
189
189
|
throw new PaginationError("pagination/cursor-tag-mismatch",
|
|
190
190
|
"cursor HMAC verification failed (tampered or wrong secret)");
|
|
191
191
|
}
|
|
@@ -205,9 +205,9 @@ function decodeCursor(token, secret) {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
function _resolveLimit(opts) {
|
|
208
|
-
|
|
208
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.max,
|
|
209
209
|
"max", PaginationError, "pagination/bad-opt");
|
|
210
|
-
|
|
210
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.default,
|
|
211
211
|
"default", PaginationError, "pagination/bad-opt");
|
|
212
212
|
var max = opts.max || DEFAULT_MAX_LIMIT;
|
|
213
213
|
var def = opts.default || DEFAULT_LIMIT;
|
package/lib/parsers/safe-xml.js
CHANGED
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
42
|
var C = require("../constants");
|
|
43
|
-
var
|
|
43
|
+
var numericBounds = require("../numeric-bounds");
|
|
44
44
|
var safeBuffer = require("../safe-buffer");
|
|
45
45
|
var { FrameworkError } = require("../framework-error");
|
|
46
46
|
|
|
@@ -77,10 +77,10 @@ var BUILT_IN_ENTITIES = { lt: "<", gt: ">", amp: "&", quot: "\"", apos: "'" };
|
|
|
77
77
|
|
|
78
78
|
function _validateAndCap(name, value, defaultValue, ceiling) {
|
|
79
79
|
if (value === undefined) return defaultValue;
|
|
80
|
-
if (!
|
|
80
|
+
if (!numericBounds.isPositiveFiniteInt(value)) {
|
|
81
81
|
throw new SafeXmlError("xml/bad-opt",
|
|
82
82
|
"xml.parse: " + name + " must be a positive finite integer; got " +
|
|
83
|
-
|
|
83
|
+
numericBounds.shape(value));
|
|
84
84
|
}
|
|
85
85
|
return Math.min(value, ceiling);
|
|
86
86
|
}
|
package/lib/pqc-agent.js
CHANGED
|
@@ -1,31 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @module b.pqcAgent
|
|
4
|
+
* @nav Production
|
|
5
|
+
* @title PQC Agent
|
|
6
|
+
* @order 630
|
|
4
7
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
8
|
+
* @intro
|
|
9
|
+
* Outbound HTTPS agent locked to the framework's PQC group preference.
|
|
10
|
+
* The framework's posture is "all outbound TLS is PQC-only"; this
|
|
11
|
+
* primitive defines what that means at the agent level — TLSv1.3
|
|
12
|
+
* minimum, `ecdhCurve` set to the framework's PQC hybrid preference
|
|
13
|
+
* (`constants.TLS_GROUP_CURVE_STR`), keep-alive on.
|
|
9
14
|
*
|
|
10
|
-
*
|
|
15
|
+
* `b.pqcAgent.agent` is a process-wide default agent, lazy-built on
|
|
16
|
+
* first access; `b.pqcAgent.create(opts)` builds a fresh agent with
|
|
17
|
+
* custom pool / timeout opts (ecdhCurve and minVersion cannot be
|
|
18
|
+
* weakened); `b.pqcAgent.reload()` tears down the default agent so
|
|
19
|
+
* the next access rebuilds against current TLS posture.
|
|
11
20
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
21
|
+
* `lib/http-client.js`'s transport cache uses `pqcAgent.create()` under
|
|
22
|
+
* the hood, so the framework's bundled HTTP client and any operator-
|
|
23
|
+
* direct `https.request` calls converge on the same agent posture.
|
|
15
24
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* 2. b.pqcAgent.create(opts) — build a fresh agent with custom
|
|
19
|
-
* pool / timeout opts. ecdhCurve and minVersion CANNOT be
|
|
20
|
-
* weakened via opts; operator-supplied values for those are
|
|
21
|
-
* ignored and the framework's defaults win. Operators who need
|
|
22
|
-
* a non-PQC agent for a deliberate one-off integration with a
|
|
23
|
-
* non-PQC server construct their own new https.Agent() directly,
|
|
24
|
-
* outside this primitive.
|
|
25
|
-
*
|
|
26
|
-
* lib/http-client.js's transport cache uses pqcAgent.create() under
|
|
27
|
-
* the hood, so the framework's bundled HTTP client and any operator-
|
|
28
|
-
* direct https.request calls converge on the same agent posture.
|
|
25
|
+
* @card
|
|
26
|
+
* Outbound HTTPS agent locked to TLSv1.3 + framework PQC hybrid group preference.
|
|
29
27
|
*/
|
|
30
28
|
|
|
31
29
|
var https = require("node:https");
|
|
@@ -152,14 +150,63 @@ function _buildAgentOpts(opts) {
|
|
|
152
150
|
return merged;
|
|
153
151
|
}
|
|
154
152
|
|
|
153
|
+
/**
|
|
154
|
+
* @primitive b.pqcAgent.create
|
|
155
|
+
* @signature b.pqcAgent.create(opts?)
|
|
156
|
+
* @since 0.5.0
|
|
157
|
+
* @status stable
|
|
158
|
+
* @related b.pqcAgent.reload
|
|
159
|
+
*
|
|
160
|
+
* Build a fresh https.Agent locked to the framework PQC hybrid group
|
|
161
|
+
* preference (TLSv1.3 minimum, ecdhCurve set to
|
|
162
|
+
* `C.TLS_GROUP_CURVE_STR`). Operator-supplied values for ecdhCurve
|
|
163
|
+
* may NARROW the framework default (drop a group) but cannot widen it
|
|
164
|
+
* unless `opts.allowOperatorGroups: true` is set; minVersion is fixed
|
|
165
|
+
* at TLSv1.3 and cannot be weakened.
|
|
166
|
+
*
|
|
167
|
+
* @opts
|
|
168
|
+
* keepAlive?: boolean,
|
|
169
|
+
* keepAliveMsecs?: number,
|
|
170
|
+
* maxSockets?: number,
|
|
171
|
+
* maxFreeSockets?: number,
|
|
172
|
+
* scheduling?: string,
|
|
173
|
+
* ecdhCurve?: string, // colon-separated group names; must subset C.TLS_GROUP_PREFERENCE
|
|
174
|
+
* allowOperatorGroups?: boolean, // default false; opt in to operator-supplied groups outside the framework PQC preference
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* var agent = b.pqcAgent.create({ maxSockets: 200 });
|
|
178
|
+
* var req = https.request("https://api.example.com/v1/x", { agent: agent });
|
|
179
|
+
* req.end();
|
|
180
|
+
*/
|
|
155
181
|
function create(opts) {
|
|
156
182
|
return new https.Agent(_buildAgentOpts(opts));
|
|
157
183
|
}
|
|
158
184
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
185
|
+
/**
|
|
186
|
+
* @primitive b.pqcAgent.createHttp
|
|
187
|
+
* @signature b.pqcAgent.createHttp(opts?)
|
|
188
|
+
* @since 0.5.0
|
|
189
|
+
* @status stable
|
|
190
|
+
* @related b.pqcAgent.create
|
|
191
|
+
*
|
|
192
|
+
* Build a cleartext `http.Agent` with the same pool defaults as
|
|
193
|
+
* `b.pqcAgent.create` — no TLS posture to enforce. Exists so the
|
|
194
|
+
* framework's HTTP client's h1 transport for cleartext origins (h2c
|
|
195
|
+
* fixtures, internal services on a private network) shares the same
|
|
196
|
+
* pool tuning as the encrypted path.
|
|
197
|
+
*
|
|
198
|
+
* @opts
|
|
199
|
+
* keepAlive?: boolean,
|
|
200
|
+
* keepAliveMsecs?: number,
|
|
201
|
+
* maxSockets?: number,
|
|
202
|
+
* maxFreeSockets?: number,
|
|
203
|
+
* scheduling?: string,
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* var agent = b.pqcAgent.createHttp({ maxSockets: 100 });
|
|
207
|
+
* var req = http.request("http://internal.svc/health", { agent: agent });
|
|
208
|
+
* req.end();
|
|
209
|
+
*/
|
|
163
210
|
function createHttp(opts) {
|
|
164
211
|
return new http.Agent(Object.assign({}, DEFAULT_OPTS, opts || {}));
|
|
165
212
|
}
|
|
@@ -173,11 +220,54 @@ function _getDefaultAgent() {
|
|
|
173
220
|
return _defaultAgent;
|
|
174
221
|
}
|
|
175
222
|
|
|
223
|
+
/**
|
|
224
|
+
* @primitive b.pqcAgent.reload
|
|
225
|
+
* @signature b.pqcAgent.reload()
|
|
226
|
+
* @since 0.9.14
|
|
227
|
+
* @status stable
|
|
228
|
+
* @related b.pqcAgent.create
|
|
229
|
+
*
|
|
230
|
+
* Tear down the lazily-built default agent and reset to null so the
|
|
231
|
+
* next `b.pqcAgent.agent` access rebuilds against current TLS posture
|
|
232
|
+
* + network-tls applyToContext output.
|
|
233
|
+
*
|
|
234
|
+
* Long-running daemons that rotate the framework's TLS posture (via
|
|
235
|
+
* `b.network.tls` config refresh, certificate-pinset reload, or a
|
|
236
|
+
* `C.TLS_GROUP_PREFERENCE` update behind a feature flag) need a way
|
|
237
|
+
* to re-source the outbound https.Agent without forking a new
|
|
238
|
+
* process. `reload()` calls `.destroy()` on the existing default
|
|
239
|
+
* agent — Node closes idle keep-alive sockets and lets in-flight
|
|
240
|
+
* sockets complete naturally — then nulls the cache so the next
|
|
241
|
+
* `agent` access builds fresh. Agents handed out via explicit
|
|
242
|
+
* `b.pqcAgent.create()` are unaffected; only the framework's lazy
|
|
243
|
+
* default is recycled.
|
|
244
|
+
*
|
|
245
|
+
* Returns `{ destroyed: boolean }` — `destroyed: true` when an agent
|
|
246
|
+
* was actually torn down, `false` when no default had been built
|
|
247
|
+
* (no callers yet asked for it).
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* // operator's daemon picked up a refreshed TLS-pinset config:
|
|
251
|
+
* b.network.tls.reload();
|
|
252
|
+
* var res = b.pqcAgent.reload();
|
|
253
|
+
* logger.info("pqc-agent reloaded", res);
|
|
254
|
+
*/
|
|
255
|
+
function reload() {
|
|
256
|
+
var hadAgent = _defaultAgent !== null;
|
|
257
|
+
if (hadAgent) {
|
|
258
|
+
try { _defaultAgent.destroy(); }
|
|
259
|
+
catch (_e) { /* destroy is best-effort */ }
|
|
260
|
+
_defaultAgent = null;
|
|
261
|
+
}
|
|
262
|
+
return { destroyed: hadAgent };
|
|
263
|
+
}
|
|
264
|
+
|
|
176
265
|
module.exports = {
|
|
177
266
|
// Read property — getter so the agent is built on first access.
|
|
178
267
|
get agent() { return _getDefaultAgent(); },
|
|
179
268
|
create: create,
|
|
180
269
|
createHttp: createHttp,
|
|
270
|
+
reload: reload,
|
|
181
271
|
DEFAULT_OPTS: DEFAULT_OPTS,
|
|
182
272
|
KNOWN_TLS_GROUPS: KNOWN_TLS_GROUPS,
|
|
183
273
|
enforced: true,
|
package/lib/pqc-gate.js
CHANGED
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
var net = require("node:net");
|
|
40
40
|
var C = require("./constants");
|
|
41
41
|
var { PQC_GROUPS } = require("./constants");
|
|
42
|
-
var
|
|
42
|
+
var numericBounds = require("./numeric-bounds");
|
|
43
43
|
var validateOpts = require("./validate-opts");
|
|
44
44
|
var { boot } = require("./log");
|
|
45
45
|
|
|
@@ -155,14 +155,14 @@ function create(opts) {
|
|
|
155
155
|
}
|
|
156
156
|
var internalHost = typeof opts.internalHost === "string" ? opts.internalHost : "127.0.0.1";
|
|
157
157
|
var bypass = Array.isArray(opts.bypass) ? opts.bypass.slice() : DEFAULT_BYPASS.slice();
|
|
158
|
-
if (opts.clientHelloTimeoutMs !== undefined && !
|
|
158
|
+
if (opts.clientHelloTimeoutMs !== undefined && !numericBounds.isPositiveFiniteInt(opts.clientHelloTimeoutMs)) {
|
|
159
159
|
throw new Error("pqc-gate: clientHelloTimeoutMs must be a positive finite integer; got " +
|
|
160
|
-
|
|
160
|
+
numericBounds.shape(opts.clientHelloTimeoutMs));
|
|
161
161
|
}
|
|
162
162
|
var clientHelloTimeoutMs = opts.clientHelloTimeoutMs || DEFAULT_CLIENTHELLO_TIMEOUT_MS;
|
|
163
|
-
if (opts.maxClientHelloBytes !== undefined && !
|
|
163
|
+
if (opts.maxClientHelloBytes !== undefined && !numericBounds.isPositiveFiniteInt(opts.maxClientHelloBytes)) {
|
|
164
164
|
throw new Error("pqc-gate: maxClientHelloBytes must be a positive finite integer; got " +
|
|
165
|
-
|
|
165
|
+
numericBounds.shape(opts.maxClientHelloBytes));
|
|
166
166
|
}
|
|
167
167
|
var maxClientHelloBytes = opts.maxClientHelloBytes || DEFAULT_MAX_CLIENTHELLO_BYTES;
|
|
168
168
|
var log = opts.log || null;
|
package/lib/pubsub-redis.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* conventions of its own.
|
|
23
23
|
*/
|
|
24
24
|
var C = require("./constants");
|
|
25
|
-
var
|
|
25
|
+
var bCrypto = require("./crypto");
|
|
26
26
|
var lazyRequire = require("./lazy-require");
|
|
27
27
|
var redisClient = require("./redis-client");
|
|
28
28
|
var safeJson = require("./safe-json");
|
|
@@ -39,7 +39,7 @@ function create(opts) {
|
|
|
39
39
|
// the local dispatch synchronously before awaiting the remote
|
|
40
40
|
// write — without this filter every same-instance publish would
|
|
41
41
|
// double-fire local handlers).
|
|
42
|
-
var instanceNonce =
|
|
42
|
+
var instanceNonce = bCrypto.generateToken(C.BYTES.bytes(8));
|
|
43
43
|
|
|
44
44
|
var clientOpts = redisClient.pickClientOpts(opts, "redis");
|
|
45
45
|
|