@blamejs/core 0.9.15 → 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 +1 -0
- package/lib/a2a-tasks.js +2 -2
- 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/compliance.js +6 -7
- package/lib/config.js +6 -6
- package/lib/credential-hash.js +4 -4
- package/lib/crypto-field.js +9 -9
- package/lib/db.js +1 -1
- package/lib/external-db-migrate.js +15 -15
- package/lib/flag-evaluation-context.js +2 -2
- package/lib/keychain.js +4 -4
- package/lib/log.js +2 -2
- package/lib/mail.js +3 -3
- package/lib/mcp-tool-registry.js +6 -6
- package/lib/middleware/body-parser.js +2 -2
- package/lib/middleware/require-bound-key.js +4 -4
- package/lib/middleware/require-mtls.js +4 -4
- package/lib/migrations.js +2 -2
- package/lib/network-byte-quota.js +2 -2
- package/lib/network.js +12 -12
- package/lib/notify.js +8 -8
- package/lib/parsers/safe-env.js +3 -3
- package/lib/restore-bundle.js +1 -1
- package/lib/router.js +12 -12
- package/lib/seeders.js +2 -2
- package/lib/testing.js +2 -2
- package/lib/vault-aad.js +5 -5
- package/lib/watcher.js +1 -1
- package/lib/ws-client.js +2 -2
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,7 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.9.x
|
|
10
10
|
|
|
11
|
+
- v0.9.16 (2026-05-14) — **Operator-facing prose cleanup + `require-binding-name` detector now covers `lazyRequire` wrappers + dbStore seal round-trip test added.** Post-v0.9.15 audit surfaced three classes of follow-up. (1) **Operator-facing prose leaks (7 sites)** — the v0.9.15 mechanical rename pattern `<OLD>.` → `<NEW>.` also caught occurrences inside JSDoc `@opts` comments and error-message string literals, so operators reading `b.keychain.create(opts)` saw `// absolute nodePath; required if file fallback may engage` instead of `// absolute path`. Fixed: `lib/db.js` (stream-limit error), `lib/keychain.js` (fallback-file error + 3 JSDoc lines), `lib/restore-bundle.js` (staging-dir error), `lib/watcher.js` (fs.watch failure error). Operators see plain English; internal binding names stay internal. (2) **`require-binding-name` detector extended to cover `lazyRequire`** — the v0.9.15 detector only matched plain `var X = require("M")` and missed the framework's `var X = lazyRequire(function () { return require("M"); })` pattern (used to break load cycles). 34 additional inconsistencies surfaced (`auditFwk` / `auditMod` / `auditModule` / `lazyAudit` → `audit`, `crypto` / `fwCrypto` → `bCrypto`, `dbMod` / `dbModule` → `db`, etc.) — every minority site renamed per the same canonical-name map. (3) **dbStore seal round-trip test** — the v0.9.15 test suite covered seal-falls-back-when-vault-not-ready and cross-process-sealed-row-preserved, but did NOT exercise the actual default-ON seal/unseal path because the test environment didn't `b.vault.init(...)`. New `testDbStoreSealRoundTripWithVault` bootstraps a plaintext vault, builds a dbStore with `seal: true`, writes a record + reads it back, and asserts (a) `headers` + `body` columns carry the `vault:` envelope on disk, (b) the round-trip restores the original values, and (c) `status_code` stays plaintext so forensic SELECTs still work without unsealing.
|
|
11
12
|
- v0.9.15 (2026-05-13) — **`b.middleware.idempotencyKey.dbStore` hardening + framework-wide `require()` binding-name consistency.** Two operator-surfaced gaps closed: (1) **dbStore now hashes keys + seals body/headers by default.** Operator-supplied idempotency keys sometimes carry PII (order numbers, emails, vendor prefixes); the `k` column previously stored them raw, leaving every DB dump as a PII surface. v0.9.15 sha3-512 namespace-hashes the key via `b.crypto.namespaceHash("idempotency-key", key)` before insert/lookup — round-trips are transparent (operators still pass raw keys), but the DB never sees the original. The schema also splits the previous single-`v` JSON-envelope column into discrete `fingerprint` / `status_code` / `headers` / `body` / `expires_at` columns; `headers` + `body` are sealed via `b.cryptoField.sealRow` (vault-managed AEAD envelope) when vault is initialized, so a DB dump leaks neither cached response bodies nor headers. Non-sealed columns (`status_code`, `fingerprint`, `expires_at`) stay forensic-queryable. Both defaults are operator-opt-out via `opts.hashKeys: false` and `opts.seal: false`; the seal path silently falls back to plaintext + emits an `idempotency.seal_skipped_no_vault` audit warning on first use when vault isn't ready, so test fixtures and boot scripts still work. **Schema migration**: v0.9.15's split columns are incompatible with v0.9.14's single-`v` column — operators with a v0.9.14 idempotency table `DROP TABLE <tableName>;` (or pick a fresh `tableName`) before upgrading. Pre-v1 framework breaks across patch versions for security correctness. (2) **New `require-binding-name` codebase-patterns detector enforces consistent `var X = require("M")` names framework-wide.** Inconsistent names (`fs` vs `nodeFs`, `crypto` vs `nodeCrypto`, `path` vs `nodePath`, `nb` vs `numericBounds`) made `grep` across the lib unreliable and let reviewers miss shadowing bugs (`var crypto = require("crypto")` collides with the framework's own `b.crypto`). The detector carries a `CANONICAL_REQUIRE_BINDINGS` map with safety-first defaults: Node built-ins get a `node<X>` prefix (`nodeFs` / `nodePath` / `nodeCrypto` / `nodeStream` / `nodeTls` / `nodeUrl`) so a local var named `fs` / `path` / `crypto` can never shadow them; the framework's own `lib/crypto.js` binds as `bCrypto` (matches the `b.crypto` public-namespace shape and doesn't shadow node:crypto). Modules without a declared canonical fall back to majority-wins (most-sites name wins, alphabetical tiebreak). Fix is rename, not allowlist — every minority site was updated. **Sweep**: 184 require-binding renames across 108 framework files in this release; primitives + tests unchanged in surface. (3) **`b.graphqlFederation` internal `_timingSafeEqual` now routes through `b.crypto.timingSafeEqual`** (was re-implementing the length-tolerant wrapper inline; the new require-binding-name detector surfaced this; same-patch fix per the audit-existing-code rule).
|
|
12
13
|
- v0.9.14 (2026-05-13) — **Audit-existing-code sweep: `safeSql.quoteIdentifier` adopted across every framework primitive that interpolates SQL identifiers; new `raw-sql-identifier-interpolation` detector seals the bug class.** Codex P1 on PR #44 flagged that `dbStore.get`'s expired-row cleanup was an unconditional `DELETE WHERE k = ?` between SELECT and DELETE — in a multi-process deployment another process could upsert the same key in the race window, and the unconditional delete would erase the fresh row. Fix: scope the delete by the observed `expires_at`. While reviewing, a wider gap surfaced — every `db.prepare("CREATE TABLE " + tableName + ...)` site in the framework had been concatenating identifiers raw, sometimes with inline shape-regex validation that varied per module. The framework already shipped `b.safeSql.quoteIdentifier(name, dialect?)` for exactly this; per the audit-existing-code rule the same patch (a) routes every site through the helper (`lib/audit.js` segregation-of-duties trigger DDL, `lib/dsr.js` ticket store, `lib/inbox.js` message-receive table, `lib/middleware/idempotency-key.js` dbStore, `lib/vault/rotate.js` column-rotation DDL) and (b) adds a `raw-sql-identifier-interpolation` codebase-patterns detector so the bug class can't return. The detector skips variables whose names signal already-quoted identifiers (`q<X>` / `Q_<X>` / `quoted<X>` prefix), so future primitives that use the helper read naturally. **Plus: bounded JSON parse for two file-/DB-backed primitives.** `b.metrics.snapshot.read` and `b.middleware.idempotencyKey.dbStore.get` previously used bare `JSON.parse` with `allow:bare-json-parse` markers; both are read by processes separate from where they were written (CLI/sidecar reads daemon-written snapshot; multi-process fleet shares DB) where a hostile or misbehaving writer could plant a multi-GB value and OOM the reader. Both now route through `b.safeJson.parse(raw, { maxBytes: 4 MiB })`. **Three new primitives — `b.crypto.hashFilesParallel`, `b.pqcAgent.reload`, `b.middleware.idempotencyKey.dbStore` — plus republish of v0.9.13's surface**. v0.9.13's npm-publish workflow failed at the wiki-e2e gate (the Codex P1 fix removed the `opts` parameter from `b.selfUpdate.standaloneVerifier.verify` but the `@signature` JSDoc still declared four arguments — three vs. four arity drift). The v0.9.13 git tag + GH release reached operators but the npm tarball did not; the registry stayed at v0.9.12. v0.9.14 carries v0.9.13's entire shipped surface (circuitBreaker.create({...}) opts-object fix + `b.selfUpdate.standaloneVerifier` + `b.metrics.snapshot` + `b.retry.withBreaker`) plus three additional Tier 2 primitives surfaced by the same downstream consumer that flagged the v0.9.13 batch. (1) **`b.crypto.hashFilesParallel(filePaths, opts?)`** — parallel multi-digest hashing for many files in a single read pass per file. Worker-pool concurrency cap (default `min(8, paths.length)`, 1..256), operator-tunable `algorithms` list (default `["sha256", "sha3-512"]`), optional `onProgress(completed, total)` callback (throws swallowed). Returns rows in the same order as input. The common consumer-side reason to reach for this is SBOM regeneration / vendor-data integrity sweeps / release-asset bundling — situations where N files each need both SHA-256 (legacy compat) and SHA-3-512 (PQC-first) digests and rolling a worker pool by hand has cost a downstream consumer the same two-loop, capture-N-promises, settle-Q boilerplate every release. (2) **`b.pqcAgent.reload()`** — tear down the lazily-built default agent and reset to null so the next `b.pqcAgent.agent` access rebuilds against current TLS posture + `b.network.tls.applyToContext` output. Long-running daemons that rotate the framework's TLS posture (TLS-pinset reload, certificate-pinset refresh, `C.TLS_GROUP_PREFERENCE` update behind a feature flag) need a way to re-source the outbound `https.Agent` without forking a new process. `reload()` calls `.destroy()` on the existing default agent (Node closes idle keep-alive sockets, lets in-flight sockets complete) then nulls the cache. Agents handed out via explicit `b.pqcAgent.create()` are unaffected. Returns `{ destroyed: boolean }`. (3) **`b.middleware.idempotencyKey.dbStore({ db, tableName?, init? })`** — persistent-backed store for the `idempotencyKey` middleware. Same three-method interface as `memoryStore` (`get` / `set` / `delete`) but stores records in any sqlite-shaped database (`{ prepare(sql) → { run, get, all } }`) — the framework's internal `b.db`, an operator-supplied better-sqlite3 instance, or a custom adapter. Use this instead of `memoryStore` when (a) multiple processes share the request-handling fleet so retries can land on a different process than the original, (b) the daemon may restart between the original request and the retry (graceful rolling deploy, OOM kill), or (c) audit / compliance review needs to walk historic idempotency-cache decisions queryable via `SELECT * FROM <tableName>`. TTL is lazily enforced at read time; `set()` upserts on conflict so concurrent retries on different processes don't error. The `tableName` is validated via `b.safeSql.validateIdentifier` (ASCII identifier shape, 63-char cap, no reserved words). (4) **Internal fix**: `b.apiSnapshot.read` now passes `maxBytes: 64 MiB` to `safeJson.parse` — the framework-generated snapshot file outgrew safeJson's 1 MiB default after v0.9.13. Operators consuming `@blamejs/core` via npm jump from v0.9.12 directly to v0.9.14; v0.9.13's content is included.
|
|
13
14
|
- v0.9.13 (2026-05-13) — **Two `b.circuitBreaker.create` / `b.retry` bug fixes plus three new operator-facing primitives: `b.selfUpdate.standaloneVerifier`, `b.metrics.snapshot`, `b.retry.withBreaker`**. (1) `b.circuitBreaker.create({...})` was rejecting the documented opts-object call shape with `name must be a non-empty string, got object` — every consumer following the docstring hit a hard error. The factory now reads `opts.name` (defaulting to empty string) and forwards to the internal `CircuitBreaker(name, opts)` constructor. (2) The breaker's open-circuit error code was documented as `retry/circuit-open` but the runtime threw `CIRCUIT_OPEN`. The docstring is corrected to match the runtime; an alias rename will follow with a deprecation cycle in a future minor. (3) **`b.selfUpdate.standaloneVerifier`** — zero-dep verifier for install-pipeline contexts that run BEFORE the framework is installed (Dockerfile build stages, `install.sh`, `update.sh`, SEA-bundle verification at deploy time). Surface: `verify(assetPath, sigPath, pubkeyPem, opts?)` returns `{ ok, sha3_512, sha256, alg }`. Streams the asset in 64 KiB chunks through SHA-256 + SHA-3-512 + the signature verifier in parallel — multi-GB SEA bundles don't OOM the install runner. Supports ECDSA P-384 (both IEEE-P1363 96-byte and DER encodings), Ed25519, and ML-DSA-87. Detects signature format from length so `verifier.verify(...)` runs exactly once (calling it twice returns stale state and silently passes tampered assets). Module is hermetic: `node:crypto` + `node:fs` only, no framework imports. Operators copy the file via `cp "$(node -p "require('@blamejs/core').selfUpdate.standaloneVerifier.path")" install/standalone-verifier.js` into version control on their side. (4) **`b.metrics.snapshot`** — out-of-process metrics export for long-running daemons. `startWriter({ path, intervalMs, fields })` atomically flushes a JSON snapshot (first flush synchronous so the file exists by return-time; subsequent flushes on the interval with `.unref()`; `stop()` clears + final-flushes). `read(path)` parses with shape validation (writtenAt + fields). `render(snap, { format, prefix })` produces operator-readable text or Prometheus 0.0.4 exposition (gauge metrics, prefixed; only finite numeric scalars with prom-compatible names emit; invalid-name / non-numeric / non-finite fields skipped silently). Lets a CLI process scrape a daemon's live metrics without opening an HTTP port. (5) **`b.retry.withBreaker(fn, { retry, breaker })`** — composition primitive collapsing the two-line wrapper every consumer rolls: `breaker.wrap(() => retry.withRetry(fn, opts.retry))`. One breaker call per retry loop (the retry budget is INSIDE the breaker's accounting, so a single transient burst doesn't open the breaker spuriously). Throws on non-function `fn` or breaker without `.wrap`.
|
package/lib/a2a-tasks.js
CHANGED
|
@@ -92,14 +92,14 @@ function _emitAudit(action, metadata, outcome) {
|
|
|
92
92
|
} catch (_e) { /* best-effort */ }
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
var
|
|
95
|
+
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
96
96
|
|
|
97
97
|
// _newTaskId is reserved for the operator-handler path that mints
|
|
98
98
|
// peer-assigned task IDs server-side. The underscore prefix already
|
|
99
99
|
// satisfies the framework's unused-var policy so the helper stays
|
|
100
100
|
// available without an explicit disable directive.
|
|
101
101
|
function _newTaskId() {
|
|
102
|
-
return
|
|
102
|
+
return bCrypto().generateToken(12); // allow:raw-byte-literal — 96-bit task id, not byte arithmetic on payload
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function _validateTaskShape(task, where) {
|
|
@@ -36,7 +36,7 @@ var C = require("../constants");
|
|
|
36
36
|
var { AuthError } = require("../framework-error");
|
|
37
37
|
|
|
38
38
|
var audit = lazyRequire(function () { return require("../audit"); });
|
|
39
|
-
var
|
|
39
|
+
var bCrypto = lazyRequire(function () { return require("../crypto"); });
|
|
40
40
|
|
|
41
41
|
var DEFAULT_TTL_SEC = C.TIME.minutes(15) / C.TIME.seconds(1);
|
|
42
42
|
var MAX_TTL_SEC = C.TIME.hours(1) / C.TIME.seconds(1);
|
|
@@ -82,7 +82,7 @@ function _macFor(payloadB64) {
|
|
|
82
82
|
|
|
83
83
|
function _timingSafeEqualBuf(a, b) {
|
|
84
84
|
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) return false;
|
|
85
|
-
return
|
|
85
|
+
return bCrypto().timingSafeEqual(a, b);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
function create(opts) {
|
|
@@ -128,7 +128,7 @@ function create(opts) {
|
|
|
128
128
|
}
|
|
129
129
|
var nowSec = (typeof opts.now === "number" && isFinite(opts.now))
|
|
130
130
|
? opts.now : Math.floor(Date.now() / C.TIME.seconds(1));
|
|
131
|
-
var jti =
|
|
131
|
+
var jti = bCrypto().generateBytes(C.BYTES.bytes(16)).toString("base64url");
|
|
132
132
|
var payload = {
|
|
133
133
|
sub: opts.subject,
|
|
134
134
|
scope: opts.scope,
|
package/lib/auth/fido-mds3.js
CHANGED
|
@@ -47,8 +47,8 @@ var _wa = require("../vendor/simplewebauthn-server.cjs");
|
|
|
47
47
|
var { FidoMds3Error } = require("../framework-error");
|
|
48
48
|
|
|
49
49
|
var httpClient = lazyRequire(function () { return require("../http-client"); });
|
|
50
|
-
var
|
|
51
|
-
var
|
|
50
|
+
var cache = lazyRequire(function () { return require("../cache"); });
|
|
51
|
+
var audit = lazyRequire(function () { return require("../audit"); });
|
|
52
52
|
|
|
53
53
|
var DEFAULT_URL = "https://mds3.fidoalliance.org/";
|
|
54
54
|
var DEFAULT_TIMEOUT_MS = C.TIME.seconds(30);
|
|
@@ -289,7 +289,7 @@ function _verifyJws(jws, leafCert) {
|
|
|
289
289
|
var _sharedCache = null;
|
|
290
290
|
function _getCache() {
|
|
291
291
|
if (_sharedCache) return _sharedCache;
|
|
292
|
-
_sharedCache =
|
|
292
|
+
_sharedCache = cache().create({
|
|
293
293
|
namespace: "auth-fido-mds3.blob",
|
|
294
294
|
ttlMs: MAX_CACHE_TTL_MS,
|
|
295
295
|
maxEntries: 8, // allow:raw-byte-literal — operator-pinned URL set
|
|
@@ -440,7 +440,7 @@ async function fetch(opts) { // allow:raw-outbound-http — function name is f
|
|
|
440
440
|
},
|
|
441
441
|
});
|
|
442
442
|
} catch (e) {
|
|
443
|
-
try {
|
|
443
|
+
try { audit().safeEmit({
|
|
444
444
|
action: "auth.fido_mds3.fetch.network",
|
|
445
445
|
outcome: "failure",
|
|
446
446
|
metadata: { url: url, reason: (e && e.message) || String(e) },
|
|
@@ -482,7 +482,7 @@ async function fetch(opts) { // allow:raw-outbound-http — function name is f
|
|
|
482
482
|
// wrap-call's safe-minimum seed).
|
|
483
483
|
try { await c.set(cacheKey, record, _ttlFromNextUpdate(nextUpdate)); }
|
|
484
484
|
catch (_e) { /* cache.set best-effort */ }
|
|
485
|
-
try {
|
|
485
|
+
try { audit().safeEmit({
|
|
486
486
|
action: "auth.fido_mds3.fetch",
|
|
487
487
|
outcome: "success",
|
|
488
488
|
metadata: { url: url, no: payload.no, entries: payload.entries.length,
|
|
@@ -633,7 +633,7 @@ function verifyAuthenticator(blob, registrationInfo, vopts) {
|
|
|
633
633
|
}
|
|
634
634
|
var certifiedLevel = _certifiedLevel(statusReports);
|
|
635
635
|
if (refusedStatus) {
|
|
636
|
-
try {
|
|
636
|
+
try { audit().safeEmit({
|
|
637
637
|
action: "auth.fido_mds3.verify.refused",
|
|
638
638
|
outcome: "denied",
|
|
639
639
|
metadata: { aaguid: registrationInfo.aaguid, status: refusedStatus },
|
package/lib/auth/jwt-external.js
CHANGED
|
@@ -59,7 +59,7 @@ var { AuthError } = require("../framework-error");
|
|
|
59
59
|
|
|
60
60
|
var httpClient = lazyRequire(function () { return require("../http-client"); });
|
|
61
61
|
var cache = lazyRequire(function () { return require("../cache"); });
|
|
62
|
-
var
|
|
62
|
+
var audit = lazyRequire(function () { return require("../audit"); });
|
|
63
63
|
|
|
64
64
|
// ---- constants ----
|
|
65
65
|
|
|
@@ -271,7 +271,7 @@ async function verifyExternal(token, opts) {
|
|
|
271
271
|
// outright. Operators with JWE need a separate handler wired to
|
|
272
272
|
// their KMS — never a defaulted JWE path on the JWS verifier.
|
|
273
273
|
if (parts.length === 5) {
|
|
274
|
-
try {
|
|
274
|
+
try { audit().safeEmit({
|
|
275
275
|
action: "jwt.jwe.refused",
|
|
276
276
|
outcome: "denied",
|
|
277
277
|
metadata: { reason: "jwe-on-jws-verifier" },
|
package/lib/compliance.js
CHANGED
|
@@ -46,11 +46,10 @@ var sanctions = require("./compliance-sanctions");
|
|
|
46
46
|
var aiAct = require("./compliance-ai-act");
|
|
47
47
|
var { ComplianceError } = require("./framework-error");
|
|
48
48
|
|
|
49
|
-
var audit
|
|
49
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
50
50
|
var retentionMod = lazyRequire(function () { return require("./retention"); });
|
|
51
|
-
var
|
|
52
|
-
var
|
|
53
|
-
var cryptoFieldMod = lazyRequire(function () { return require("./crypto-field"); });
|
|
51
|
+
var db = lazyRequire(function () { return require("./db"); });
|
|
52
|
+
var cryptoField = lazyRequire(function () { return require("./crypto-field"); });
|
|
54
53
|
|
|
55
54
|
// Recognised posture names. Aligns with the compliance-posture
|
|
56
55
|
// vocabulary every guard / retention floor / etc. accepts. Operators
|
|
@@ -371,9 +370,9 @@ function set(posture) {
|
|
|
371
370
|
function _applyPostureCascade(posture) {
|
|
372
371
|
var steps = [
|
|
373
372
|
{ primitive: "retention", resolver: function () { return retentionMod(); } },
|
|
374
|
-
{ primitive: "audit", resolver: function () { return
|
|
375
|
-
{ primitive: "db", resolver: function () { return
|
|
376
|
-
{ primitive: "cryptoField", resolver: function () { return
|
|
373
|
+
{ primitive: "audit", resolver: function () { return audit(); } },
|
|
374
|
+
{ primitive: "db", resolver: function () { return db(); } },
|
|
375
|
+
{ primitive: "cryptoField", resolver: function () { return cryptoField(); } },
|
|
377
376
|
];
|
|
378
377
|
for (var i = 0; i < steps.length; i += 1) {
|
|
379
378
|
var step = steps[i];
|
package/lib/config.js
CHANGED
|
@@ -29,7 +29,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
29
29
|
var safeAsync = require("./safe-async");
|
|
30
30
|
var { defineClass } = require("./framework-error");
|
|
31
31
|
|
|
32
|
-
var
|
|
32
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
33
33
|
|
|
34
34
|
var REDACT_MASK = "[REDACTED]";
|
|
35
35
|
|
|
@@ -349,7 +349,7 @@ function loadDbBacked(opts) {
|
|
|
349
349
|
try { rows = await opts.fetchRows(); }
|
|
350
350
|
catch (e) {
|
|
351
351
|
try {
|
|
352
|
-
|
|
352
|
+
audit().safeEmit({
|
|
353
353
|
action: "config.reload.failed", outcome: "failure",
|
|
354
354
|
metadata: { phase: "fetch", reason: e && e.message },
|
|
355
355
|
});
|
|
@@ -367,7 +367,7 @@ function loadDbBacked(opts) {
|
|
|
367
367
|
value = await transformValue(row);
|
|
368
368
|
} catch (e) {
|
|
369
369
|
try {
|
|
370
|
-
|
|
370
|
+
audit().safeEmit({
|
|
371
371
|
action: "config.reload.failed", outcome: "failure",
|
|
372
372
|
metadata: { phase: "transform", key: row.key, reason: e && e.message },
|
|
373
373
|
});
|
|
@@ -376,7 +376,7 @@ function loadDbBacked(opts) {
|
|
|
376
376
|
}
|
|
377
377
|
if (typeof value !== "string") {
|
|
378
378
|
try {
|
|
379
|
-
|
|
379
|
+
audit().safeEmit({
|
|
380
380
|
action: "config.reload.failed", outcome: "failure",
|
|
381
381
|
metadata: { phase: "transform", key: row.key, reason: "transformValue did not return a string" },
|
|
382
382
|
});
|
|
@@ -390,7 +390,7 @@ function loadDbBacked(opts) {
|
|
|
390
390
|
// applied its newer fetch — my overlay would clobber fresher data.
|
|
391
391
|
if (mySeq <= ticksAppliedMax) {
|
|
392
392
|
try {
|
|
393
|
-
|
|
393
|
+
audit().safeEmit({
|
|
394
394
|
action: "config.reload.skipped", outcome: "success",
|
|
395
395
|
metadata: { phase: "stale-tick", mySeq: mySeq, appliedMax: ticksAppliedMax },
|
|
396
396
|
});
|
|
@@ -408,7 +408,7 @@ function loadDbBacked(opts) {
|
|
|
408
408
|
}
|
|
409
409
|
catch (e) {
|
|
410
410
|
try {
|
|
411
|
-
|
|
411
|
+
audit().safeEmit({
|
|
412
412
|
action: "config.reload.failed", outcome: "failure",
|
|
413
413
|
metadata: { phase: "validate", reason: e && e.message },
|
|
414
414
|
});
|
package/lib/credential-hash.js
CHANGED
|
@@ -79,7 +79,7 @@ function _shake256(secret, length) {
|
|
|
79
79
|
|
|
80
80
|
// auth/password is required lazily because it imports the (large)
|
|
81
81
|
// argon2 vendor; loading it for SHA3-only callers is wasted work.
|
|
82
|
-
var
|
|
82
|
+
var passwordModule = lazyRequire(function () { return require("./auth/password"); });
|
|
83
83
|
|
|
84
84
|
class CredentialHashError extends FrameworkError {
|
|
85
85
|
constructor(message, code) {
|
|
@@ -219,7 +219,7 @@ async function hash(secret, opts) {
|
|
|
219
219
|
}
|
|
220
220
|
if (algoId === C.CRED_HASH_IDS.ARGON2ID) {
|
|
221
221
|
var plain = Buffer.isBuffer(secret) ? secret.toString("utf8") : secret;
|
|
222
|
-
var phc = await
|
|
222
|
+
var phc = await passwordModule().hash(plain, opts && opts.params);
|
|
223
223
|
var argonEnv = _envelope(algoId, Buffer.from(phc, "utf8"));
|
|
224
224
|
_emitEvent("credentialHash.hash", 1, { algo: algoName });
|
|
225
225
|
return argonEnv;
|
|
@@ -300,7 +300,7 @@ async function verify(secret, envelope) {
|
|
|
300
300
|
var phc = decoded.payload.toString("utf8");
|
|
301
301
|
var plain = Buffer.isBuffer(secret) ? secret.toString("utf8") : secret;
|
|
302
302
|
var argOk = false;
|
|
303
|
-
try { argOk = await
|
|
303
|
+
try { argOk = await passwordModule().verify(phc, plain); }
|
|
304
304
|
catch (_e) { argOk = false; }
|
|
305
305
|
_emitEvent("credentialHash.verify", 1,
|
|
306
306
|
{ outcome: argOk ? "success" : "failure", algo: algoName });
|
|
@@ -377,7 +377,7 @@ function needsRehash(envelope, opts) {
|
|
|
377
377
|
// Defer the parameter-lag check to the password primitive's
|
|
378
378
|
// own needsRehash so the threshold stays in one place.
|
|
379
379
|
var phc = decoded.payload.toString("utf8");
|
|
380
|
-
try { return
|
|
380
|
+
try { return passwordModule().needsRehash(phc, opts && opts.params); }
|
|
381
381
|
catch (_e) { return true; }
|
|
382
382
|
}
|
|
383
383
|
return false;
|
package/lib/crypto-field.js
CHANGED
|
@@ -47,9 +47,9 @@ var vault = require("./vault");
|
|
|
47
47
|
var { sha3Hash, kdf } = require("./crypto");
|
|
48
48
|
var { HASH_PREFIX, VAULT_PREFIX, TIME } = require("./constants");
|
|
49
49
|
|
|
50
|
-
var
|
|
51
|
-
var
|
|
52
|
-
var
|
|
50
|
+
var compliance = lazyRequire(function () { return require("./compliance"); });
|
|
51
|
+
var db = lazyRequire(function () { return require("./db"); });
|
|
52
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
53
53
|
|
|
54
54
|
// F-POSTURE-1 cascade hook + F-RTBF-2 integration. Recording the
|
|
55
55
|
// posture lets eraseRow call b.db.vacuumAfterErase({ mode: "full" })
|
|
@@ -88,7 +88,7 @@ function applyPosture(posture) {
|
|
|
88
88
|
_activePosture = posture;
|
|
89
89
|
var requireVacuum = false;
|
|
90
90
|
try {
|
|
91
|
-
requireVacuum =
|
|
91
|
+
requireVacuum = compliance().postureDefault(posture, "requireVacuumAfterErase") === true;
|
|
92
92
|
} catch (_e) { /* compliance not loaded — record posture only */ }
|
|
93
93
|
return { posture: posture, requireVacuumAfterErase: requireVacuum };
|
|
94
94
|
}
|
|
@@ -495,14 +495,14 @@ function eraseRow(table, row) {
|
|
|
495
495
|
if (_activePosture) {
|
|
496
496
|
var requireVacuum = false;
|
|
497
497
|
try {
|
|
498
|
-
requireVacuum =
|
|
498
|
+
requireVacuum = compliance().postureDefault(
|
|
499
499
|
_activePosture, "requireVacuumAfterErase") === true;
|
|
500
500
|
} catch (_e) { /* compliance lookup best-effort */ }
|
|
501
501
|
if (requireVacuum) {
|
|
502
502
|
try {
|
|
503
|
-
var
|
|
504
|
-
if (
|
|
505
|
-
|
|
503
|
+
var dbInst = db();
|
|
504
|
+
if (dbInst && typeof dbInst.vacuumAfterErase === "function") {
|
|
505
|
+
dbInst.vacuumAfterErase({ mode: "full" });
|
|
506
506
|
}
|
|
507
507
|
} catch (_vacErr) {
|
|
508
508
|
// VACUUM is best-effort at the eraseRow seam — DB might not be
|
|
@@ -510,7 +510,7 @@ function eraseRow(table, row) {
|
|
|
510
510
|
// captures the skip; operators on regulated postures wire the
|
|
511
511
|
// sweep through b.retention which gates erasure on db.init().
|
|
512
512
|
try {
|
|
513
|
-
|
|
513
|
+
audit().safeEmit({
|
|
514
514
|
action: "cryptofield.vacuum.skipped",
|
|
515
515
|
outcome: "failure",
|
|
516
516
|
metadata: {
|
package/lib/db.js
CHANGED
|
@@ -1479,7 +1479,7 @@ function stream(sql) {
|
|
|
1479
1479
|
this.destroy(new DbError("db/stream-limit-exceeded",
|
|
1480
1480
|
"db.stream: emitted " + emitted + " rows, exceeding streamLimit " +
|
|
1481
1481
|
perCallLimit + ". Pass opts.streamLimit higher OR raise via " +
|
|
1482
|
-
"db.init({ streamLimit }) after auditing the export
|
|
1482
|
+
"db.init({ streamLimit }) after auditing the export path."));
|
|
1483
1483
|
return;
|
|
1484
1484
|
}
|
|
1485
1485
|
var step = iter.next();
|
|
@@ -64,7 +64,7 @@ var ExternalDbMigrateError = defineClass("ExternalDbMigrateError", { alwaysPerma
|
|
|
64
64
|
|
|
65
65
|
// Lazy require — external-db imports back into this module via its
|
|
66
66
|
// public `migrate` namespace; load-order would cycle without lazy.
|
|
67
|
-
var
|
|
67
|
+
var externalDb = lazyRequire(function () { return require("./external-db"); });
|
|
68
68
|
|
|
69
69
|
var TRACKING_TABLE = "_blamejs_externaldb_migrations";
|
|
70
70
|
var LOCK_TABLE = "_blamejs_externaldb_migrations_lock";
|
|
@@ -327,7 +327,7 @@ function _resolveBackendName(opts) {
|
|
|
327
327
|
}
|
|
328
328
|
// Default to the externalDb's defaultBackend; throw clear if not initialized.
|
|
329
329
|
var listed;
|
|
330
|
-
try { listed =
|
|
330
|
+
try { listed = externalDb().listBackends(); }
|
|
331
331
|
catch (_e) {
|
|
332
332
|
throw _err("externaldb-migrate/not-initialized",
|
|
333
333
|
"externalDb is not initialized — call b.externalDb.init({ backends }) first");
|
|
@@ -363,14 +363,14 @@ function create(opts) {
|
|
|
363
363
|
|
|
364
364
|
function _ctx(backendName) {
|
|
365
365
|
return {
|
|
366
|
-
externalDb:
|
|
366
|
+
externalDb: externalDb(),
|
|
367
367
|
backendName: backendName,
|
|
368
368
|
};
|
|
369
369
|
}
|
|
370
370
|
|
|
371
371
|
async function status() {
|
|
372
372
|
var backendName = _resolveBackendName(opts);
|
|
373
|
-
return await
|
|
373
|
+
return await externalDb().transaction(async function (xdb) {
|
|
374
374
|
await _ensureTrackingTable(xdb);
|
|
375
375
|
var res = await xdb.query(
|
|
376
376
|
"SELECT name, description, appliedAt FROM " + Q_TRACKING +
|
|
@@ -394,7 +394,7 @@ function create(opts) {
|
|
|
394
394
|
var backendName = _resolveBackendName(opts);
|
|
395
395
|
var ctx = _ctx(backendName);
|
|
396
396
|
|
|
397
|
-
return await
|
|
397
|
+
return await externalDb().transaction(async function (xdb) {
|
|
398
398
|
await _ensureTrackingTable(xdb);
|
|
399
399
|
await _ensureLockTable(xdb);
|
|
400
400
|
await _ensureHistoryTable(xdb);
|
|
@@ -404,7 +404,7 @@ function create(opts) {
|
|
|
404
404
|
// pool acquisition for the lock connection — the migrate runner
|
|
405
405
|
// serializes apply order, so this single-connection lock is
|
|
406
406
|
// sufficient.
|
|
407
|
-
var lockResult = await
|
|
407
|
+
var lockResult = await externalDb().transaction(async function (xdb) {
|
|
408
408
|
return await _acquireLock(xdb, opts);
|
|
409
409
|
}, { backend: backendName });
|
|
410
410
|
var lockHolder = lockResult.holder;
|
|
@@ -421,7 +421,7 @@ function create(opts) {
|
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
try {
|
|
424
|
-
var appliedRes = await
|
|
424
|
+
var appliedRes = await externalDb().query(
|
|
425
425
|
"SELECT name FROM " + Q_TRACKING, [], { backend: backendName }
|
|
426
426
|
);
|
|
427
427
|
var appliedSet = new Set(((appliedRes && appliedRes.rows) || []).map(function (r) { return r.name; }));
|
|
@@ -435,7 +435,7 @@ function create(opts) {
|
|
|
435
435
|
var mod = _loadMigration(file, dir);
|
|
436
436
|
var t0 = Date.now();
|
|
437
437
|
try {
|
|
438
|
-
await
|
|
438
|
+
await externalDb().transaction(async function (xdb) {
|
|
439
439
|
await mod.up(xdb, ctx);
|
|
440
440
|
var ranAt = new Date().toISOString();
|
|
441
441
|
await xdb.query(
|
|
@@ -489,7 +489,7 @@ function create(opts) {
|
|
|
489
489
|
return { applied: applied, skipped: skipped, backend: backendName };
|
|
490
490
|
} finally {
|
|
491
491
|
try {
|
|
492
|
-
await
|
|
492
|
+
await externalDb().transaction(async function (xdb) {
|
|
493
493
|
await _releaseLock(xdb, lockHolder);
|
|
494
494
|
}, { backend: backendName });
|
|
495
495
|
_emit(audit, "externaldb.migrate.lock.released", "success",
|
|
@@ -509,12 +509,12 @@ function create(opts) {
|
|
|
509
509
|
var backendName = _resolveBackendName(opts);
|
|
510
510
|
var ctx = _ctx(backendName);
|
|
511
511
|
|
|
512
|
-
await
|
|
512
|
+
await externalDb().transaction(async function (xdb) {
|
|
513
513
|
await _ensureTrackingTable(xdb);
|
|
514
514
|
await _ensureLockTable(xdb);
|
|
515
515
|
}, { backend: backendName });
|
|
516
516
|
|
|
517
|
-
var lockResultDown = await
|
|
517
|
+
var lockResultDown = await externalDb().transaction(async function (xdb) {
|
|
518
518
|
return await _acquireLock(xdb, opts);
|
|
519
519
|
}, { backend: backendName });
|
|
520
520
|
var lockHolder = lockResultDown.holder;
|
|
@@ -528,7 +528,7 @@ function create(opts) {
|
|
|
528
528
|
}
|
|
529
529
|
|
|
530
530
|
try {
|
|
531
|
-
var appliedRes = await
|
|
531
|
+
var appliedRes = await externalDb().query(
|
|
532
532
|
"SELECT name FROM " + Q_TRACKING + " ORDER BY appliedAt DESC, name DESC LIMIT $1",
|
|
533
533
|
[steps], { backend: backendName }
|
|
534
534
|
);
|
|
@@ -543,7 +543,7 @@ function create(opts) {
|
|
|
543
543
|
}
|
|
544
544
|
var t0 = Date.now();
|
|
545
545
|
try {
|
|
546
|
-
await
|
|
546
|
+
await externalDb().transaction(async function (xdb) {
|
|
547
547
|
await mod.down(xdb, ctx);
|
|
548
548
|
await xdb.query(
|
|
549
549
|
"DELETE FROM " + Q_TRACKING + " WHERE name = $1",
|
|
@@ -564,7 +564,7 @@ function create(opts) {
|
|
|
564
564
|
return { reverted: reverted, backend: backendName };
|
|
565
565
|
} finally {
|
|
566
566
|
try {
|
|
567
|
-
await
|
|
567
|
+
await externalDb().transaction(async function (xdb) {
|
|
568
568
|
await _releaseLock(xdb, lockHolder);
|
|
569
569
|
}, { backend: backendName });
|
|
570
570
|
_emit(audit, "externaldb.migrate.lock.released", "success",
|
|
@@ -588,7 +588,7 @@ function create(opts) {
|
|
|
588
588
|
async function history(historyOpts) {
|
|
589
589
|
historyOpts = historyOpts || {};
|
|
590
590
|
var backendName = _resolveBackendName(opts);
|
|
591
|
-
return await
|
|
591
|
+
return await externalDb().transaction(async function (xdb) {
|
|
592
592
|
await _ensureHistoryTable(xdb);
|
|
593
593
|
var res = await xdb.query(
|
|
594
594
|
"SELECT version, ranAt, ranBy, schemaIntrospectionHash, signature, publicKeyFingerprint " +
|
|
@@ -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/keychain.js
CHANGED
|
@@ -161,7 +161,7 @@ function _validateFallbackFile(filepath, primitive) {
|
|
|
161
161
|
KeychainError, "keychain/bad-fallback-file");
|
|
162
162
|
if (!nodePath.isAbsolute(filepath)) {
|
|
163
163
|
throw new KeychainError("keychain/relative-fallback-file",
|
|
164
|
-
primitive + ": fallbackFile must be an absolute
|
|
164
|
+
primitive + ": fallbackFile must be an absolute path; got " + filepath);
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
|
|
@@ -613,7 +613,7 @@ function _isFallbackError(e) {
|
|
|
613
613
|
* service: string, // required, no NUL/CR/LF bytes
|
|
614
614
|
* account: string, // required, no NUL/CR/LF bytes
|
|
615
615
|
* password: string, // required, non-empty
|
|
616
|
-
* fallbackFile?: string, // absolute
|
|
616
|
+
* fallbackFile?: string, // absolute path; required if file fallback may engage
|
|
617
617
|
* passphrase?: string, // required when fallbackFile engages (Argon2id-derived KEK)
|
|
618
618
|
* preferFile?: boolean, // default: false
|
|
619
619
|
* audit?: boolean, // default: true (emits keychain.stored)
|
|
@@ -685,7 +685,7 @@ async function store(opts) {
|
|
|
685
685
|
* {
|
|
686
686
|
* service: string, // required
|
|
687
687
|
* account: string, // required
|
|
688
|
-
* fallbackFile?: string, // absolute
|
|
688
|
+
* fallbackFile?: string, // absolute path; required for file-backend lookup
|
|
689
689
|
* passphrase?: string, // required when fallbackFile engages
|
|
690
690
|
* preferFile?: boolean, // default: false
|
|
691
691
|
* audit?: boolean, // default: true (emits keychain.retrieved)
|
|
@@ -781,7 +781,7 @@ async function retrieve(opts) {
|
|
|
781
781
|
* {
|
|
782
782
|
* service: string, // required
|
|
783
783
|
* account: string, // required
|
|
784
|
-
* fallbackFile?: string, // absolute
|
|
784
|
+
* fallbackFile?: string, // absolute path; required for file-backend cleanup
|
|
785
785
|
* passphrase?: string, // required when fallbackFile engages
|
|
786
786
|
* preferFile?: boolean, // default: false
|
|
787
787
|
* audit?: boolean, // default: true (emits keychain.removed)
|
package/lib/log.js
CHANGED
|
@@ -72,7 +72,7 @@ var { FrameworkError } = require("./framework-error");
|
|
|
72
72
|
// pulling the whole crypto bundle into the framework's earliest
|
|
73
73
|
// boot path (request-id middleware needs only generateToken).
|
|
74
74
|
var safeEnv = lazyRequire(function () { return require("./parsers/safe-env"); });
|
|
75
|
-
var
|
|
75
|
+
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
76
76
|
|
|
77
77
|
// Request-id correlation token — 8 bytes hex-encoded (16 chars). Short
|
|
78
78
|
// enough to read in a log line, long enough to keep collisions far below
|
|
@@ -384,7 +384,7 @@ function create(opts) {
|
|
|
384
384
|
// 16 random hex chars — short, sufficient correlation entropy.
|
|
385
385
|
// Routes through the framework token primitive so the entropy
|
|
386
386
|
// source matches the rest of the codebase.
|
|
387
|
-
return
|
|
387
|
+
return bCrypto().generateToken(REQUEST_ID_BYTES);
|
|
388
388
|
};
|
|
389
389
|
return function logRequestIdMiddleware(req, res, next) {
|
|
390
390
|
var inbound = req.headers && req.headers[headerName];
|
package/lib/mail.js
CHANGED
|
@@ -72,7 +72,7 @@ var net = lazyRequire(function () { return require("net"); });
|
|
|
72
72
|
var networkDns = lazyRequire(function () { return require("./network-dns"); });
|
|
73
73
|
var nodeUrl = require("url");
|
|
74
74
|
var numericBounds = require("./numeric-bounds");
|
|
75
|
-
var
|
|
75
|
+
var nodeTls = lazyRequire(function () { return require("tls"); });
|
|
76
76
|
var safeJson = require("./safe-json");
|
|
77
77
|
var safeSchema = require("./safe-schema");
|
|
78
78
|
var validateOpts = require("./validate-opts");
|
|
@@ -1142,7 +1142,7 @@ function _smtpSend(message, cfg) {
|
|
|
1142
1142
|
tlsConnectOpts.host = cfg.host;
|
|
1143
1143
|
tlsConnectOpts.port = cfg.port;
|
|
1144
1144
|
if (family === 4 || family === 6) tlsConnectOpts.family = family;
|
|
1145
|
-
attachSocket(
|
|
1145
|
+
attachSocket(nodeTls().connect(tlsConnectOpts));
|
|
1146
1146
|
} else {
|
|
1147
1147
|
var netOpts = { host: cfg.host, port: cfg.port };
|
|
1148
1148
|
if (family === 4 || family === 6) netOpts.family = family;
|
|
@@ -1211,7 +1211,7 @@ function _smtpSend(message, cfg) {
|
|
|
1211
1211
|
if (code !== 220) { fail("starttls-rejected (code " + code + ")"); return; }
|
|
1212
1212
|
var tlsConnectOpts = Object.assign({ socket: socket }, cfg.tlsOpts);
|
|
1213
1213
|
if (cfg.servername) tlsConnectOpts.servername = cfg.servername;
|
|
1214
|
-
var tlsSocket =
|
|
1214
|
+
var tlsSocket = nodeTls().connect(tlsConnectOpts, function () {
|
|
1215
1215
|
upgradedToTLS = true;
|
|
1216
1216
|
try { socket.removeAllListeners("data"); } catch (_e) { /* listeners migrate to upgraded socket */ }
|
|
1217
1217
|
attachSocket(tlsSocket);
|
package/lib/mcp-tool-registry.js
CHANGED
|
@@ -64,7 +64,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
64
64
|
var validateOpts = require("./validate-opts");
|
|
65
65
|
var { McpError } = require("./framework-error");
|
|
66
66
|
|
|
67
|
-
var
|
|
67
|
+
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
68
68
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
69
69
|
var C = require("./constants");
|
|
70
70
|
|
|
@@ -202,7 +202,7 @@ function create(opts) {
|
|
|
202
202
|
outputSchema: tool.outputSchema || null,
|
|
203
203
|
alg: alg,
|
|
204
204
|
});
|
|
205
|
-
var sig =
|
|
205
|
+
var sig = bCrypto().sign(Buffer.from(payload, "utf8"), signingKey);
|
|
206
206
|
return {
|
|
207
207
|
tool: tool.name,
|
|
208
208
|
description: tool.description || "",
|
|
@@ -254,7 +254,7 @@ function create(opts) {
|
|
|
254
254
|
}),
|
|
255
255
|
issuedAt: new Date().toISOString(),
|
|
256
256
|
});
|
|
257
|
-
var sig =
|
|
257
|
+
var sig = bCrypto().sign(Buffer.from(manifestBody, "utf8"), signingKey);
|
|
258
258
|
return {
|
|
259
259
|
body: manifestBody,
|
|
260
260
|
signature: sig.toString("base64"),
|
|
@@ -309,7 +309,7 @@ function create(opts) {
|
|
|
309
309
|
}
|
|
310
310
|
var nonce = typeof callOpts.nonce === "string" && callOpts.nonce.length > 0
|
|
311
311
|
? callOpts.nonce
|
|
312
|
-
:
|
|
312
|
+
: bCrypto().generateToken(16); // allow:raw-byte-literal — 128-bit nonce, not byte arithmetic on a payload
|
|
313
313
|
var iat = new Date();
|
|
314
314
|
var exp = new Date(iat.getTime() + ttlMs);
|
|
315
315
|
var envelope = {
|
|
@@ -320,7 +320,7 @@ function create(opts) {
|
|
|
320
320
|
exp: exp.toISOString(),
|
|
321
321
|
};
|
|
322
322
|
var payload = Buffer.from(canonicalJson.stringify(envelope), "utf8");
|
|
323
|
-
var sig =
|
|
323
|
+
var sig = bCrypto().sign(payload, signingKey);
|
|
324
324
|
_emitAudit("mcp.tool_registry.call_signed",
|
|
325
325
|
{ tool: envelope.tool, nonce: nonce, alg: alg });
|
|
326
326
|
return {
|
|
@@ -438,7 +438,7 @@ function create(opts) {
|
|
|
438
438
|
"verifyCall: signature not valid base64");
|
|
439
439
|
}
|
|
440
440
|
var ok;
|
|
441
|
-
try { ok =
|
|
441
|
+
try { ok = bCrypto().verify(payload, sigBuf, verifyingKey); }
|
|
442
442
|
catch (verifyErr) {
|
|
443
443
|
_emitAudit("mcp.tool_registry.call_verify_error",
|
|
444
444
|
{ tool: env.tool, nonce: env.nonce, error: String(verifyErr.message || verifyErr) }, "denied");
|
|
@@ -123,7 +123,7 @@ var validateOpts = require("../validate-opts");
|
|
|
123
123
|
var C = require("../constants");
|
|
124
124
|
var { defineClass } = require("../framework-error");
|
|
125
125
|
|
|
126
|
-
var
|
|
126
|
+
var audit = lazyRequire(function () { return require("../audit"); });
|
|
127
127
|
|
|
128
128
|
// Node's HTTP parser surfaces malformed chunked-transfer-encoding via a
|
|
129
129
|
// stable family of HPE_* codes. RFC 9112 §7.1 — when a server rejects a
|
|
@@ -1294,7 +1294,7 @@ function create(opts) {
|
|
|
1294
1294
|
? "http.chunked.extension.refused"
|
|
1295
1295
|
: "http.chunked.malformed.refused";
|
|
1296
1296
|
try {
|
|
1297
|
-
|
|
1297
|
+
audit().safeEmit({
|
|
1298
1298
|
action: chunkAction,
|
|
1299
1299
|
outcome: "denied",
|
|
1300
1300
|
metadata: {
|
|
@@ -52,7 +52,7 @@ var defineClass = require("../framework-error").defineClass;
|
|
|
52
52
|
var lazyRequire = require("../lazy-require");
|
|
53
53
|
var validateOpts = require("../validate-opts");
|
|
54
54
|
|
|
55
|
-
var
|
|
55
|
+
var bCrypto = lazyRequire(function () { return require("../crypto"); });
|
|
56
56
|
var audit = lazyRequire(function () { return require("../audit"); });
|
|
57
57
|
|
|
58
58
|
var RequireBoundKeyError = defineClass("RequireBoundKeyError", { alwaysPermanent: true });
|
|
@@ -67,7 +67,7 @@ function _parseBearer(req) {
|
|
|
67
67
|
function _timingSafeStringEqual(a, b) {
|
|
68
68
|
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
69
69
|
if (a.length !== b.length) return false;
|
|
70
|
-
return
|
|
70
|
+
return bCrypto().timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
/**
|
|
@@ -243,7 +243,7 @@ function create(opts) {
|
|
|
243
243
|
var fpColon = req.peerFingerprint && req.peerFingerprint.colon;
|
|
244
244
|
if (!fpHex && req.peerCert && req.peerCert.raw) {
|
|
245
245
|
try {
|
|
246
|
-
var fp =
|
|
246
|
+
var fp = bCrypto().hashCertFingerprint(req.peerCert.raw);
|
|
247
247
|
fpHex = fp.hex; fpColon = fp.colon;
|
|
248
248
|
} catch (_e) { /* fall through to refused below */ }
|
|
249
249
|
}
|
|
@@ -256,7 +256,7 @@ function create(opts) {
|
|
|
256
256
|
keyId: record.id || null,
|
|
257
257
|
});
|
|
258
258
|
}
|
|
259
|
-
} else if (!
|
|
259
|
+
} else if (!bCrypto().isCertRevoked(req.peerCert.raw, pinned)) {
|
|
260
260
|
// isCertRevoked returns true on MATCH against the deny-list
|
|
261
261
|
// shape; we use it here as a fingerprint-set membership test
|
|
262
262
|
// because it does the same constant-time hex/colon comparison
|
|
@@ -49,7 +49,7 @@ var defineClass = require("../framework-error").defineClass;
|
|
|
49
49
|
var lazyRequire = require("../lazy-require");
|
|
50
50
|
var validateOpts = require("../validate-opts");
|
|
51
51
|
|
|
52
|
-
var
|
|
52
|
+
var bCrypto = lazyRequire(function () { return require("../crypto"); });
|
|
53
53
|
var audit = lazyRequire(function () { return require("../audit"); });
|
|
54
54
|
|
|
55
55
|
var RequireMtlsError = defineClass("RequireMtlsError", { alwaysPermanent: true });
|
|
@@ -169,18 +169,18 @@ function create(opts) {
|
|
|
169
169
|
// allow/deny matching.
|
|
170
170
|
var fp;
|
|
171
171
|
try {
|
|
172
|
-
fp =
|
|
172
|
+
fp = bCrypto().hashCertFingerprint(peerCert.raw);
|
|
173
173
|
} catch (e) {
|
|
174
174
|
return _refuse(res, "fingerprint-failed", { error: (e && e.message) || String(e) });
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
if (denyList.length > 0 &&
|
|
177
|
+
if (denyList.length > 0 && bCrypto().isCertRevoked(peerCert.raw, denyList)) {
|
|
178
178
|
return _refuse(res, "fingerprint-on-deny-list", {
|
|
179
179
|
fingerprint: fp.colon,
|
|
180
180
|
subject: (peerCert.subject && peerCert.subject.CN) || null,
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
|
-
if (allowList && allowList.length > 0 && !
|
|
183
|
+
if (allowList && allowList.length > 0 && !bCrypto().isCertRevoked(peerCert.raw, allowList)) {
|
|
184
184
|
return _refuse(res, "fingerprint-not-allowed", {
|
|
185
185
|
fingerprint: fp.colon,
|
|
186
186
|
subject: (peerCert.subject && peerCert.subject.CN) || null,
|
package/lib/migrations.js
CHANGED
|
@@ -45,7 +45,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
45
45
|
var { boot } = require("./log");
|
|
46
46
|
var migrationFiles = require("./migration-files");
|
|
47
47
|
var numericBounds = require("./numeric-bounds");
|
|
48
|
-
var
|
|
48
|
+
var db = lazyRequire(function () { return require("./db"); });
|
|
49
49
|
var validateOpts = require("./validate-opts");
|
|
50
50
|
var { FrameworkError } = require("./framework-error");
|
|
51
51
|
|
|
@@ -224,7 +224,7 @@ function _resolveDb(opts) {
|
|
|
224
224
|
if (opts && opts.db && typeof opts.db.prepare === "function") return opts.db;
|
|
225
225
|
// Fall back to the framework's singleton db when one isn't passed —
|
|
226
226
|
// operator-side wiring usually does `b.migrations.create({ dir })`.
|
|
227
|
-
var d =
|
|
227
|
+
var d = db();
|
|
228
228
|
if (typeof d.prepare !== "function") {
|
|
229
229
|
throw new MigrationError("migrations/no-db",
|
|
230
230
|
"no db handle: pass opts.db or initialize b.db before create()",
|
|
@@ -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.js
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
var byteQuota = require("./network-byte-quota");
|
|
34
34
|
var ntpCheck = require("./ntp-check");
|
|
35
35
|
var nts = require("./network-nts");
|
|
36
|
-
var
|
|
36
|
+
var networkDns = require("./network-dns");
|
|
37
37
|
var networkProxy = require("./network-proxy");
|
|
38
38
|
var networkTls = require("./network-tls");
|
|
39
39
|
var heartbeat = require("./network-heartbeat");
|
|
@@ -211,15 +211,15 @@ 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) {
|
|
@@ -286,7 +286,7 @@ function snapshot() {
|
|
|
286
286
|
servers: ntpFacade.getServers(),
|
|
287
287
|
thresholds: ntpCheck.getThresholds(),
|
|
288
288
|
},
|
|
289
|
-
dns:
|
|
289
|
+
dns: networkDns._stateForTest(),
|
|
290
290
|
proxy: networkProxy.snapshot(),
|
|
291
291
|
tls: {
|
|
292
292
|
systemTrust: networkTls.isSystemTrustEnabled(),
|
|
@@ -305,7 +305,7 @@ function _resetForTest() {
|
|
|
305
305
|
ntpFacade._defaultServers = null;
|
|
306
306
|
ntpFacade._defaultTimeoutMs = null;
|
|
307
307
|
if (typeof ntpCheck._resetThresholdsForTest === "function") ntpCheck._resetThresholdsForTest();
|
|
308
|
-
|
|
308
|
+
networkDns._resetForTest();
|
|
309
309
|
networkProxy._resetForTest();
|
|
310
310
|
networkTls._resetForTest();
|
|
311
311
|
heartbeat._resetForTest();
|
|
@@ -316,7 +316,7 @@ function _resetForTest() {
|
|
|
316
316
|
|
|
317
317
|
module.exports = {
|
|
318
318
|
ntp: ntpFacade,
|
|
319
|
-
dns:
|
|
319
|
+
dns: networkDns,
|
|
320
320
|
proxy: networkProxy,
|
|
321
321
|
tls: networkTls,
|
|
322
322
|
heartbeat: heartbeat,
|
package/lib/notify.js
CHANGED
|
@@ -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") {
|
|
@@ -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
|
}
|
package/lib/parsers/safe-env.js
CHANGED
|
@@ -68,7 +68,7 @@ var { boot } = require("../log");
|
|
|
68
68
|
// (transitively) vault, which leaves safe-env's module.exports
|
|
69
69
|
// half-built when vault first reaches readVar. Defer audit resolution
|
|
70
70
|
// until the first emit-driven call.
|
|
71
|
-
var
|
|
71
|
+
var audit = lazyRequire(function () { return require("../audit"); });
|
|
72
72
|
|
|
73
73
|
var log = boot("env");
|
|
74
74
|
|
|
@@ -519,11 +519,11 @@ function _writeAuditRows(filepath, diff) {
|
|
|
519
519
|
// we're a follower, audit.record will throw NotLeaderError. Catch
|
|
520
520
|
// explicitly: a follower's local config-load shouldn't crash because
|
|
521
521
|
// the cluster's audit chain belongs to the leader.
|
|
522
|
-
var
|
|
522
|
+
var auditInst = audit(); // resolve the lazy-required audit module
|
|
523
523
|
|
|
524
524
|
function _safeRecord(action, metadata) {
|
|
525
525
|
try {
|
|
526
|
-
|
|
526
|
+
auditInst.emit({
|
|
527
527
|
actor: { kind: "system", id: "config-loader" },
|
|
528
528
|
action: action,
|
|
529
529
|
outcome: "success",
|
package/lib/restore-bundle.js
CHANGED
|
@@ -131,7 +131,7 @@ async function extract(opts) {
|
|
|
131
131
|
if (nodeFs.existsSync(opts.stagingDir)) {
|
|
132
132
|
throw new RestoreBundleError("restore-bundle/staging-exists",
|
|
133
133
|
"extract: stagingDir already exists: " + opts.stagingDir +
|
|
134
|
-
" (refusing to merge into existing directory — pick a fresh
|
|
134
|
+
" (refusing to merge into existing directory — pick a fresh path)");
|
|
135
135
|
}
|
|
136
136
|
if (!Buffer.isBuffer(opts.passphrase) && typeof opts.passphrase !== "string") {
|
|
137
137
|
throw new RestoreBundleError("restore-bundle/no-passphrase",
|
package/lib/router.js
CHANGED
|
@@ -46,11 +46,11 @@ var websocket = require("./websocket");
|
|
|
46
46
|
var { boot } = require("./log");
|
|
47
47
|
var { RouterError } = require("./framework-error");
|
|
48
48
|
|
|
49
|
-
var
|
|
49
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
50
50
|
// compliance — lazy because router.js is required during boot before
|
|
51
51
|
// the operator's `b.compliance.set(...)` runs; the posture lookup only
|
|
52
52
|
// matters at listen() time, well after boot finishes.
|
|
53
|
-
var
|
|
53
|
+
var compliance = lazyRequire(function () { return require("./compliance"); });
|
|
54
54
|
|
|
55
55
|
var log = boot("router");
|
|
56
56
|
var HTTP_STATUS = requestHelpers.HTTP_STATUS;
|
|
@@ -733,12 +733,12 @@ class Router {
|
|
|
733
733
|
if (declared !== "replay-cache") return declared;
|
|
734
734
|
var active = null;
|
|
735
735
|
try {
|
|
736
|
-
var
|
|
737
|
-
if (
|
|
736
|
+
var complianceInst = compliance();
|
|
737
|
+
if (complianceInst && typeof complianceInst.current === "function") active = complianceInst.current();
|
|
738
738
|
} catch (_e) { /* compliance not initialized */ }
|
|
739
739
|
if (active && TLS_0RTT_FAILCLOSED_POSTURES.indexOf(active) !== -1) {
|
|
740
740
|
try {
|
|
741
|
-
|
|
741
|
+
audit().safeEmit({
|
|
742
742
|
action: "tls.0rtt.refused",
|
|
743
743
|
outcome: "denied",
|
|
744
744
|
metadata: { reason: "posture-failclosed", posture: active, declared: declared },
|
|
@@ -759,7 +759,7 @@ class Router {
|
|
|
759
759
|
if (String(earlyDataHeader).trim() !== "1") return null; // RFC 8470: only "1" means early data
|
|
760
760
|
if (posture === "refuse") {
|
|
761
761
|
try {
|
|
762
|
-
|
|
762
|
+
audit().safeEmit({
|
|
763
763
|
action: "tls.0rtt.refused",
|
|
764
764
|
outcome: "denied",
|
|
765
765
|
metadata: { reason: "posture-refuse", method: req.method, url: req.url },
|
|
@@ -783,7 +783,7 @@ class Router {
|
|
|
783
783
|
var key = hash.digest("hex");
|
|
784
784
|
if (this._tls0RttReplayCache.has(key)) {
|
|
785
785
|
try {
|
|
786
|
-
|
|
786
|
+
audit().safeEmit({
|
|
787
787
|
action: "tls.0rtt.replayed",
|
|
788
788
|
outcome: "denied",
|
|
789
789
|
metadata: { reason: "cache-hit", method: req.method, url: req.url,
|
|
@@ -805,7 +805,7 @@ class Router {
|
|
|
805
805
|
}
|
|
806
806
|
this._tls0RttReplayCache.set(key, nowMs + TLS_0RTT_REPLAY_WINDOW_MS);
|
|
807
807
|
try {
|
|
808
|
-
|
|
808
|
+
audit().safeEmit({
|
|
809
809
|
action: "tls.0rtt.accepted",
|
|
810
810
|
outcome: "success",
|
|
811
811
|
metadata: { method: req.method, url: req.url, windowMs: TLS_0RTT_REPLAY_WINDOW_MS },
|
|
@@ -889,7 +889,7 @@ class Router {
|
|
|
889
889
|
});
|
|
890
890
|
} catch (parseErr) {
|
|
891
891
|
try {
|
|
892
|
-
|
|
892
|
+
audit().safeEmit({
|
|
893
893
|
action: "router.redirect.cross_origin.refused",
|
|
894
894
|
outcome: "denied",
|
|
895
895
|
metadata: {
|
|
@@ -913,7 +913,7 @@ class Router {
|
|
|
913
913
|
}
|
|
914
914
|
if (!match) {
|
|
915
915
|
try {
|
|
916
|
-
|
|
916
|
+
audit().safeEmit({
|
|
917
917
|
action: "router.redirect.cross_origin.refused",
|
|
918
918
|
outcome: "denied",
|
|
919
919
|
metadata: {
|
|
@@ -930,7 +930,7 @@ class Router {
|
|
|
930
930
|
);
|
|
931
931
|
}
|
|
932
932
|
try {
|
|
933
|
-
|
|
933
|
+
audit().safeEmit({
|
|
934
934
|
action: "router.redirect.cross_origin.allowed",
|
|
935
935
|
outcome: "success",
|
|
936
936
|
metadata: { target: url, origin: targetOrigin },
|
|
@@ -1064,7 +1064,7 @@ class Router {
|
|
|
1064
1064
|
// (a clean peer would not initiate after GOAWAY).
|
|
1065
1065
|
h2session.on("stream", function (stream) {
|
|
1066
1066
|
if (h2session._blamejsGoawaySent) {
|
|
1067
|
-
try {
|
|
1067
|
+
try { audit().safeEmit({
|
|
1068
1068
|
action: "http2.window_update.refused",
|
|
1069
1069
|
outcome: "denied",
|
|
1070
1070
|
metadata: { reason: "post-goaway-stream", streamId: stream.id || null,
|
package/lib/seeders.js
CHANGED
|
@@ -67,7 +67,7 @@ var { SeederError } = require("./framework-error");
|
|
|
67
67
|
|
|
68
68
|
var log = boot("seeders");
|
|
69
69
|
|
|
70
|
-
var
|
|
70
|
+
var db = lazyRequire(function () { return require("./db"); });
|
|
71
71
|
var observability = lazyRequire(function () { return require("./observability"); });
|
|
72
72
|
|
|
73
73
|
var _err = SeederError.factory;
|
|
@@ -151,7 +151,7 @@ function _validateCreateOpts(opts) {
|
|
|
151
151
|
|
|
152
152
|
function _resolveDb(opts) {
|
|
153
153
|
if (opts && opts.db && typeof opts.db.prepare === "function") return opts.db;
|
|
154
|
-
var d =
|
|
154
|
+
var d = db();
|
|
155
155
|
if (typeof d.prepare !== "function") {
|
|
156
156
|
throw _err("NO_DB", "seeders: no db handle: pass opts.db or initialize b.db before create()");
|
|
157
157
|
}
|
package/lib/testing.js
CHANGED
|
@@ -62,7 +62,7 @@ var { TestingError } = require("./framework-error");
|
|
|
62
62
|
// metrics is the only place that exposes the global `tap` slot the
|
|
63
63
|
// captureMetricsTap helper swaps; pulling it lazily keeps testing.js
|
|
64
64
|
// safe to require at any framework load order.
|
|
65
|
-
var
|
|
65
|
+
var metrics = lazyRequire(function () { return require("./metrics"); });
|
|
66
66
|
|
|
67
67
|
var _err = TestingError.factory;
|
|
68
68
|
|
|
@@ -563,7 +563,7 @@ function captureObservability() {
|
|
|
563
563
|
* }
|
|
564
564
|
*/
|
|
565
565
|
function captureMetricsTap() {
|
|
566
|
-
var m =
|
|
566
|
+
var m = metrics();
|
|
567
567
|
var original = m.tap;
|
|
568
568
|
var captured = [];
|
|
569
569
|
m.tap = function (name, value, labels) {
|
package/lib/vault-aad.js
CHANGED
|
@@ -52,7 +52,7 @@ var C = require("./constants");
|
|
|
52
52
|
var { defineClass } = require("./framework-error");
|
|
53
53
|
var VaultAadError = defineClass("VaultAadError", { alwaysPermanent: true });
|
|
54
54
|
|
|
55
|
-
var
|
|
55
|
+
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
56
56
|
var vault = lazyRequire(function () { return require("./vault"); });
|
|
57
57
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
58
58
|
|
|
@@ -154,11 +154,11 @@ function _deriveKey(aadBytes) {
|
|
|
154
154
|
// this is a deterministic derivation; rotating vault keys produces
|
|
155
155
|
// a different root and breaks all prior AAD-sealed values (operator
|
|
156
156
|
// intent: rotation = re-seal).
|
|
157
|
-
var rootHash =
|
|
157
|
+
var rootHash = bCrypto().sha3Hash(keysJson);
|
|
158
158
|
var prefix = Buffer.from("vault.aad/v1/", "utf8");
|
|
159
159
|
var rootBuf = Buffer.from(rootHash, "hex");
|
|
160
160
|
var input = Buffer.concat([prefix, rootBuf, aadBytes]);
|
|
161
|
-
return
|
|
161
|
+
return bCrypto().kdf(input, C.BYTES.bytes(32));
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
function seal(plaintext, aadParts) {
|
|
@@ -178,7 +178,7 @@ function seal(plaintext, aadParts) {
|
|
|
178
178
|
var aadBytes = _canonicalize(aadParts);
|
|
179
179
|
var key = _deriveKey(aadBytes);
|
|
180
180
|
var ptBuf = Buffer.from(plaintext, "utf8");
|
|
181
|
-
var packed =
|
|
181
|
+
var packed = bCrypto().encryptPacked(ptBuf, key, aadBytes);
|
|
182
182
|
|
|
183
183
|
try {
|
|
184
184
|
audit().safeEmit({
|
|
@@ -213,7 +213,7 @@ function unseal(value, aadParts) {
|
|
|
213
213
|
"unseal: base64 decode failed - " + e.message);
|
|
214
214
|
}
|
|
215
215
|
var pt;
|
|
216
|
-
try { pt =
|
|
216
|
+
try { pt = bCrypto().decryptPacked(packed, key, aadBytes); }
|
|
217
217
|
catch (e) {
|
|
218
218
|
try {
|
|
219
219
|
audit().safeEmit({
|
package/lib/watcher.js
CHANGED
|
@@ -434,7 +434,7 @@ function create(opts) {
|
|
|
434
434
|
((e && e.message) || String(e)) + " — pass mode: \"poll\" to fall back to interval polling");
|
|
435
435
|
}
|
|
436
436
|
throw new WatcherError("watcher/start-failed",
|
|
437
|
-
"watcher.create:
|
|
437
|
+
"watcher.create: fs.watch failed: " + ((e && e.message) || String(e)));
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
|
package/lib/ws-client.js
CHANGED
|
@@ -54,7 +54,7 @@ var lazyRequire = require("./lazy-require");
|
|
|
54
54
|
var validateOpts = require("./validate-opts");
|
|
55
55
|
var safeAsync = require("./safe-async");
|
|
56
56
|
var safeBuffer = require("./safe-buffer");
|
|
57
|
-
var
|
|
57
|
+
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
58
58
|
var websocket = lazyRequire(function () { return require("./websocket"); });
|
|
59
59
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
60
60
|
var networkTls = lazyRequire(function () { return require("./network-tls"); });
|
|
@@ -137,7 +137,7 @@ function _inflateRawCappedSync(zlib, compressed, maxBytes, windowBits) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
function _generateKey() {
|
|
140
|
-
return
|
|
140
|
+
return bCrypto().generateBytes(C.BYTES.bytes(16)).toString("base64");
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
function _expectedAccept(secKey, handshakeGuid) {
|
package/package.json
CHANGED
package/sbom.cdx.json
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.6",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:8e662af8-d254-46ec-8625-aa19174598d2",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-05-
|
|
8
|
+
"timestamp": "2026-05-14T04:47:34.904Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/core@0.9.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.9.16",
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.9.
|
|
25
|
+
"version": "0.9.16",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "The Node framework that owns its stack.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/core@0.9.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.9.16",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/core@0.9.
|
|
57
|
+
"ref": "@blamejs/core@0.9.16",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|