@blamejs/core 0.9.14 → 0.9.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/a2a-tasks.js +2 -2
- package/lib/a2a.js +11 -11
- package/lib/acme.js +5 -5
- package/lib/ai-input.js +2 -2
- package/lib/api-key.js +4 -4
- package/lib/api-snapshot.js +6 -6
- package/lib/app-shutdown.js +2 -2
- package/lib/app.js +5 -5
- package/lib/archive.js +8 -8
- package/lib/argon2-builtin.js +2 -2
- package/lib/atomic-file.js +53 -53
- package/lib/audit-sign.js +8 -8
- package/lib/audit-tools.js +22 -22
- package/lib/auth/dpop.js +3 -3
- package/lib/auth/elevation-grant.js +3 -3
- package/lib/auth/fido-mds3.js +6 -6
- package/lib/auth/jwt-external.js +2 -2
- package/lib/auth/sd-jwt-vc.js +2 -2
- package/lib/backup/bundle.js +17 -17
- package/lib/backup/index.js +36 -36
- package/lib/budr.js +3 -3
- package/lib/bundler.js +20 -20
- package/lib/circuit-breaker.js +4 -4
- package/lib/cli.js +25 -26
- package/lib/cluster.js +2 -2
- package/lib/compliance-sanctions.js +2 -2
- package/lib/compliance.js +6 -7
- package/lib/config-drift.js +15 -15
- package/lib/config.js +6 -6
- package/lib/content-credentials.js +4 -4
- package/lib/credential-hash.js +7 -7
- package/lib/crypto-field.js +9 -9
- package/lib/daemon.js +19 -19
- package/lib/db-file-lifecycle.js +24 -24
- package/lib/db-schema.js +2 -2
- package/lib/db.js +34 -34
- package/lib/dev.js +10 -10
- package/lib/dr-runbook.js +5 -5
- package/lib/dual-control.js +2 -2
- package/lib/external-db-migrate.js +17 -17
- package/lib/external-db.js +2 -2
- package/lib/fdx.js +2 -2
- package/lib/file-upload.js +30 -30
- package/lib/flag-evaluation-context.js +2 -2
- package/lib/flag-providers.js +4 -4
- package/lib/gate-contract.js +5 -5
- package/lib/graphql-federation.js +4 -7
- package/lib/honeytoken.js +6 -6
- package/lib/http-client-cookie-jar.js +6 -6
- package/lib/http-client.js +18 -18
- package/lib/i18n.js +5 -5
- package/lib/keychain.js +5 -5
- package/lib/legal-hold.js +2 -2
- package/lib/local-db-thin.js +9 -9
- package/lib/log-stream-local.js +17 -17
- package/lib/log-stream-syslog.js +2 -2
- package/lib/log-stream.js +3 -3
- package/lib/log.js +2 -2
- package/lib/mail-bounce.js +2 -2
- package/lib/mail-mdn.js +2 -2
- package/lib/mail-srs.js +2 -2
- package/lib/mail.js +7 -7
- package/lib/mcp-tool-registry.js +6 -6
- package/lib/mcp.js +2 -2
- package/lib/metrics.js +2 -2
- package/lib/middleware/api-encrypt.js +16 -16
- package/lib/middleware/body-parser.js +18 -18
- package/lib/middleware/compression.js +3 -3
- package/lib/middleware/csp-nonce.js +4 -4
- package/lib/middleware/health.js +7 -7
- package/lib/middleware/idempotency-key.js +163 -63
- package/lib/middleware/require-bound-key.js +4 -4
- package/lib/middleware/require-mtls.js +4 -4
- package/lib/migrations.js +5 -5
- package/lib/mtls-ca.js +26 -26
- package/lib/mtls-engine-default.js +5 -5
- package/lib/network-byte-quota.js +2 -2
- package/lib/network-dns.js +2 -2
- package/lib/network-nts.js +2 -2
- package/lib/network-proxy.js +3 -3
- package/lib/network-smtp-policy.js +2 -2
- package/lib/network-tls.js +17 -17
- package/lib/network.js +25 -25
- package/lib/notify.js +11 -11
- package/lib/object-store/gcs-bucket-ops.js +2 -2
- package/lib/object-store/gcs.js +5 -5
- package/lib/object-store/index.js +6 -6
- package/lib/object-store/local.js +19 -19
- package/lib/object-store/sigv4.js +3 -3
- package/lib/observability-tracer.js +4 -4
- package/lib/otel-export.js +3 -3
- package/lib/pagination.js +5 -5
- package/lib/parsers/safe-env.js +3 -3
- package/lib/parsers/safe-xml.js +3 -3
- package/lib/pqc-gate.js +5 -5
- package/lib/pubsub-redis.js +2 -2
- package/lib/queue-local.js +3 -3
- package/lib/queue.js +2 -2
- package/lib/redis-client.js +4 -4
- package/lib/restore-bundle.js +17 -17
- package/lib/restore-rollback.js +34 -34
- package/lib/restore.js +16 -16
- package/lib/router.js +25 -25
- package/lib/sandbox.js +8 -8
- package/lib/sec-cyber.js +3 -3
- package/lib/security-assert.js +2 -2
- package/lib/seeders.js +6 -6
- package/lib/self-update.js +18 -18
- package/lib/session-device-binding.js +2 -2
- package/lib/static.js +22 -22
- package/lib/template.js +19 -19
- package/lib/testing.js +9 -9
- package/lib/tls-exporter.js +5 -5
- package/lib/tracing.js +3 -3
- package/lib/vault/index.js +11 -11
- package/lib/vault/passphrase-ops.js +37 -37
- package/lib/vault/passphrase-source.js +2 -2
- package/lib/vault/rotate.js +64 -64
- package/lib/vault/seal-pem-file.js +26 -26
- package/lib/vault-aad.js +5 -5
- package/lib/watcher.js +22 -22
- package/lib/webhook.js +10 -10
- package/lib/worker-pool.js +6 -6
- package/lib/ws-client.js +6 -6
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/cluster.js
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
*/
|
|
46
46
|
var C = require("./constants");
|
|
47
47
|
var clusterProviderDb = require("./cluster-provider-db");
|
|
48
|
-
var
|
|
48
|
+
var bCrypto = require("./crypto");
|
|
49
49
|
var lazyRequire = require("./lazy-require");
|
|
50
50
|
var { boot } = require("./log");
|
|
51
51
|
var safeAsync = require("./safe-async");
|
|
@@ -428,7 +428,7 @@ function _vaultKeyFingerprint() {
|
|
|
428
428
|
if (!keys || !keys.publicKey || !keys.ecPublicKey) return null;
|
|
429
429
|
// Domain-separation prefix so this fingerprint can't be confused
|
|
430
430
|
// with a hash of the same bytes computed elsewhere in the framework.
|
|
431
|
-
return
|
|
431
|
+
return bCrypto.sha3Hash("blamejs/cluster-state/v1\n" +
|
|
432
432
|
keys.publicKey + "\n" +
|
|
433
433
|
keys.ecPublicKey);
|
|
434
434
|
}
|
|
@@ -483,9 +483,9 @@ function create(opts) {
|
|
|
483
483
|
// ignorable for the audit-trail use case (operators store the
|
|
484
484
|
// ruleVersion + entry count alongside).
|
|
485
485
|
function snapshot() {
|
|
486
|
-
var
|
|
486
|
+
var nodeCrypto = require("crypto");
|
|
487
487
|
var ids = index.map(function (e) { return e.id; }).sort();
|
|
488
|
-
var hash =
|
|
488
|
+
var hash = nodeCrypto.createHash("sha3-512");
|
|
489
489
|
for (var i = 0; i < ids.length; i++) hash.update(ids[i]);
|
|
490
490
|
return {
|
|
491
491
|
algorithm: algorithm,
|
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-drift.js
CHANGED
|
@@ -35,11 +35,11 @@
|
|
|
35
35
|
* @card
|
|
36
36
|
* Monitor + alert when runtime config diverges from a declared baseline.
|
|
37
37
|
*/
|
|
38
|
-
var
|
|
39
|
-
var
|
|
38
|
+
var nodeFs = require("node:fs");
|
|
39
|
+
var nodePath = require("node:path");
|
|
40
40
|
var auditSign = require("./audit-sign");
|
|
41
41
|
var canonicalJson = require("./canonical-json");
|
|
42
|
-
var
|
|
42
|
+
var bCrypto = require("./crypto");
|
|
43
43
|
var lazyRequire = require("./lazy-require");
|
|
44
44
|
var safeJson = require("./safe-json");
|
|
45
45
|
var validateOpts = require("./validate-opts");
|
|
@@ -57,12 +57,12 @@ var SIDECAR_VERSION = 1;
|
|
|
57
57
|
// Deterministic key order so the same snapshot always hashes to the same
|
|
58
58
|
// digest. Pre-v0.6.67 the in-line implementation silently lost Date /
|
|
59
59
|
// Map / Set / Buffer / BigInt content; the shared walker handles all
|
|
60
|
-
// of those + circular
|
|
60
|
+
// of those + circular renodeFs. Same bytes as audit-chain / audit-tools /
|
|
61
61
|
// pagination would produce for the same input.
|
|
62
62
|
function _stableStringify(value) { return canonicalJson.stringify(value); }
|
|
63
63
|
|
|
64
64
|
function _hashSnapshot(snapshot) {
|
|
65
|
-
return
|
|
65
|
+
return bCrypto.sha3Hash(_stableStringify(snapshot));
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
function _diffShallow(prev, next) {
|
|
@@ -155,7 +155,7 @@ function create(opts) {
|
|
|
155
155
|
// (e.g. operator-tracked metadata that legitimately changes per
|
|
156
156
|
// boot). Captured in the snapshot but never flagged.
|
|
157
157
|
var ignoreKeys = Array.isArray(opts.ignoreKeys) ? opts.ignoreKeys.slice() : [];
|
|
158
|
-
var sidecarPath =
|
|
158
|
+
var sidecarPath = nodePath.join(dataDir,
|
|
159
159
|
baselineName === "default" ? SIDECAR_NAME : ("config-baseline-" + baselineName + ".sig"));
|
|
160
160
|
|
|
161
161
|
function _emit(action, info, outcome) {
|
|
@@ -172,9 +172,9 @@ function create(opts) {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
function _readSidecar() {
|
|
175
|
-
if (!
|
|
175
|
+
if (!nodeFs.existsSync(sidecarPath)) return null;
|
|
176
176
|
var raw;
|
|
177
|
-
try { raw =
|
|
177
|
+
try { raw = nodeFs.readFileSync(sidecarPath, "utf8"); }
|
|
178
178
|
catch (_e) { return null; }
|
|
179
179
|
var parsed;
|
|
180
180
|
try { parsed = safeJson.parse(raw); }
|
|
@@ -200,8 +200,8 @@ function create(opts) {
|
|
|
200
200
|
snapshot: snapshot,
|
|
201
201
|
};
|
|
202
202
|
var tmp = sidecarPath + ".tmp";
|
|
203
|
-
|
|
204
|
-
|
|
203
|
+
nodeFs.writeFileSync(tmp, JSON.stringify(payload, null, 2));
|
|
204
|
+
nodeFs.renameSync(tmp, sidecarPath);
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
function _verifySidecar(parsed) {
|
|
@@ -366,10 +366,10 @@ function create(opts) {
|
|
|
366
366
|
*/
|
|
367
367
|
function verifyVendorIntegrity(opts) {
|
|
368
368
|
opts = opts || {};
|
|
369
|
-
var libVendorDir = opts.libVendorDir ||
|
|
370
|
-
var manifestPath = opts.manifestPath ||
|
|
369
|
+
var libVendorDir = opts.libVendorDir || nodePath.join(process.cwd(), "lib", "vendor");
|
|
370
|
+
var manifestPath = opts.manifestPath || nodePath.join(libVendorDir, "MANIFEST.json");
|
|
371
371
|
var raw;
|
|
372
|
-
try { raw =
|
|
372
|
+
try { raw = nodeFs.readFileSync(manifestPath, "utf8"); }
|
|
373
373
|
catch (_e) {
|
|
374
374
|
throw _err("VENDOR_MANIFEST_MISSING",
|
|
375
375
|
"vendor MANIFEST.json missing at " + manifestPath, true);
|
|
@@ -389,10 +389,10 @@ function verifyVendorIntegrity(opts) {
|
|
|
389
389
|
var rel = files[kind];
|
|
390
390
|
var expected = hashes[kind];
|
|
391
391
|
if (typeof rel !== "string" || typeof expected !== "string") return;
|
|
392
|
-
var abs =
|
|
392
|
+
var abs = nodePath.isAbsolute(rel) ? rel : nodePath.join(process.cwd(), rel);
|
|
393
393
|
var actual;
|
|
394
394
|
try {
|
|
395
|
-
var bytes =
|
|
395
|
+
var bytes = nodeFs.readFileSync(abs);
|
|
396
396
|
actual = "sha256:" + require("node:crypto")
|
|
397
397
|
.createHash("sha256").update(bytes).digest("hex");
|
|
398
398
|
} catch (_e) {
|
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
|
});
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* C2PA 2.1 content provenance — sign assets with a manifest declaring origin, edits, AI involvement.
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
-
var
|
|
31
|
+
var bCrypto = require("./crypto");
|
|
32
32
|
var canonicalJson = require("./canonical-json");
|
|
33
33
|
var validateOpts = require("./validate-opts");
|
|
34
34
|
var audit = require("./audit");
|
|
@@ -234,7 +234,7 @@ function sign(manifest, opts) {
|
|
|
234
234
|
validateOpts.requireNonEmptyString(opts.privateKeyPem,
|
|
235
235
|
"contentCredentials.sign: privateKeyPem", ContentCredentialsError, "BAD_KEY");
|
|
236
236
|
var canonical = canonicalJson.stringify(manifest);
|
|
237
|
-
var signature =
|
|
237
|
+
var signature = bCrypto.sign(Buffer.from(canonical, "utf8"), opts.privateKeyPem);
|
|
238
238
|
var auditOn = opts.audit !== false;
|
|
239
239
|
if (auditOn) {
|
|
240
240
|
audit.safeEmit({
|
|
@@ -299,7 +299,7 @@ function verify(envelope, publicKeyPem, opts) {
|
|
|
299
299
|
catch (_e) {
|
|
300
300
|
return { valid: false, claims: null, reason: "signature-base64-bad" };
|
|
301
301
|
}
|
|
302
|
-
var ok =
|
|
302
|
+
var ok = bCrypto.verify(Buffer.from(canonical, "utf8"), sigBuf, publicKeyPem);
|
|
303
303
|
if (!ok) {
|
|
304
304
|
return { valid: false, claims: null, reason: "signature-mismatch" };
|
|
305
305
|
}
|
|
@@ -519,7 +519,7 @@ function signCose(manifest, opts) {
|
|
|
519
519
|
var toBeSigned = Buffer.concat(sigStructureBufs);
|
|
520
520
|
|
|
521
521
|
// Sign with framework's b.crypto.sign — algorithm picked from the PEM.
|
|
522
|
-
var signature =
|
|
522
|
+
var signature = bCrypto.sign(toBeSigned, opts.privateKeyPem);
|
|
523
523
|
|
|
524
524
|
// COSE_Sign1 = tagged-18 array [protected, unprotected, payload, signature]
|
|
525
525
|
var coseSign1 = Buffer.concat([
|
package/lib/credential-hash.js
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
* Derive a deterministic, verifiable hash for credential lookup (API-key secret, shared bearer token, webhook signing key) without storing the credential itself.
|
|
42
42
|
*/
|
|
43
43
|
|
|
44
|
-
var
|
|
44
|
+
var bCrypto = require("./crypto");
|
|
45
45
|
var C = require("./constants");
|
|
46
46
|
var lazyRequire = require("./lazy-require");
|
|
47
47
|
var { FrameworkError } = require("./framework-error");
|
|
@@ -73,13 +73,13 @@ function _shake256(secret, length) {
|
|
|
73
73
|
// crypto.kdf wraps SHAKE256 with arbitrary output length. That's the
|
|
74
74
|
// exact primitive we need — the framework's KDF and credential-hash
|
|
75
75
|
// share one underlying XOF.
|
|
76
|
-
return
|
|
76
|
+
return bCrypto.kdf(secret, length);
|
|
77
77
|
}
|
|
78
78
|
|
|
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;
|
|
@@ -291,7 +291,7 @@ async function verify(secret, envelope) {
|
|
|
291
291
|
return false;
|
|
292
292
|
}
|
|
293
293
|
var expected = _shake256(secret, decoded.payload.length);
|
|
294
|
-
var ok =
|
|
294
|
+
var ok = bCrypto.timingSafeEqual(expected, decoded.payload);
|
|
295
295
|
_emitEvent("credentialHash.verify", 1,
|
|
296
296
|
{ outcome: ok ? "success" : "failure", algo: algoName });
|
|
297
297
|
return ok;
|
|
@@ -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/daemon.js
CHANGED
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
* Long-running process orchestration — supervisor wiring around `b.appShutdown`, foreground signal handling, detached-fork spawn via `b.processSpawn`, PID-file health probes, and a SIGTERM-then-SIGKILL restart policy on stop.
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
40
|
+
var nodeFs = require("fs");
|
|
41
|
+
var nodePath = require("path");
|
|
42
|
+
var numericBounds = require("./numeric-bounds");
|
|
43
43
|
var appShutdown = require("./app-shutdown");
|
|
44
44
|
var processSpawn = require("./process-spawn");
|
|
45
45
|
var lazyRequire = require("./lazy-require");
|
|
@@ -80,7 +80,7 @@ function _isLivePid(pid) {
|
|
|
80
80
|
|
|
81
81
|
function _readPidFile(pidFile) {
|
|
82
82
|
try {
|
|
83
|
-
var raw =
|
|
83
|
+
var raw = nodeFs.readFileSync(pidFile, "utf8");
|
|
84
84
|
var pid = parseInt(String(raw).trim(), 10);
|
|
85
85
|
return isFinite(pid) && pid > 0 ? pid : null;
|
|
86
86
|
} catch (_e) { return null; }
|
|
@@ -118,9 +118,9 @@ function _validateStopOpts(opts) {
|
|
|
118
118
|
"daemon.stop: opts.pidFile", DaemonError, "daemon/bad-pid-file");
|
|
119
119
|
validateOpts.optionalNonEmptyString(opts.signal,
|
|
120
120
|
"daemon.stop: opts.signal", DaemonError, "daemon/bad-signal");
|
|
121
|
-
|
|
121
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.timeoutMs,
|
|
122
122
|
"daemon.stop: opts.timeoutMs", DaemonError, "daemon/bad-timeout");
|
|
123
|
-
|
|
123
|
+
numericBounds.requirePositiveFiniteIntIfPresent(opts.pollMs,
|
|
124
124
|
"daemon.stop: opts.pollMs", DaemonError, "daemon/bad-poll");
|
|
125
125
|
}
|
|
126
126
|
|
|
@@ -133,7 +133,7 @@ function _maybeReapStale(pidFile) {
|
|
|
133
133
|
}
|
|
134
134
|
if (existing === process.pid) return false;
|
|
135
135
|
// Stale: PID is gone (or signal-0 returned ESRCH). Reap + audit.
|
|
136
|
-
try {
|
|
136
|
+
try { nodeFs.unlinkSync(pidFile); } catch (_e) { /* race: another reaper */ }
|
|
137
137
|
_safeAuditEmit("daemon.stale_pid_cleaned", "success", {
|
|
138
138
|
pidFile: pidFile,
|
|
139
139
|
stalePid: existing,
|
|
@@ -146,13 +146,13 @@ function _maybeReapStale(pidFile) {
|
|
|
146
146
|
// redirect of the current process' stdout/stderr.
|
|
147
147
|
function _openLogFd(logFile) {
|
|
148
148
|
if (typeof logFile !== "string" || logFile.length === 0) return null;
|
|
149
|
-
atomicFile.ensureDir(
|
|
150
|
-
var fd =
|
|
149
|
+
atomicFile.ensureDir(nodePath.dirname(logFile));
|
|
150
|
+
var fd = nodeFs.openSync(logFile, "a", DEFAULT_LOG_FILE_MODE);
|
|
151
151
|
return fd;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// Redirect the current process's stdout/stderr file descriptors at the
|
|
155
|
-
// given fd. Implemented via
|
|
155
|
+
// given fd. Implemented via nodeFs.writeSync streams: Node doesn't expose a
|
|
156
156
|
// portable dup2, so we replace process.stdout.write / process.stderr.write
|
|
157
157
|
// with a writer that pushes to the log fd. This is the standard
|
|
158
158
|
// pattern for foreground daemons that don't want to lose output when
|
|
@@ -163,7 +163,7 @@ function _redirectStdio(fd) {
|
|
|
163
163
|
var enc = typeof encOrCb === "string" ? encOrCb : "utf8";
|
|
164
164
|
var cb = typeof encOrCb === "function" ? encOrCb : maybeCb;
|
|
165
165
|
var buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk), enc);
|
|
166
|
-
try {
|
|
166
|
+
try { nodeFs.writeSync(fd, buf); }
|
|
167
167
|
catch (_e) { /* log fd closed underneath us — drop */ }
|
|
168
168
|
if (typeof cb === "function") cb();
|
|
169
169
|
return true;
|
|
@@ -246,21 +246,21 @@ function start(opts) {
|
|
|
246
246
|
cwd: typeof opts.cwd === "string" ? opts.cwd : undefined,
|
|
247
247
|
});
|
|
248
248
|
} catch (e) {
|
|
249
|
-
try { if (typeof logFd === "number")
|
|
249
|
+
try { if (typeof logFd === "number") nodeFs.closeSync(logFd); }
|
|
250
250
|
catch (_c) { /* best-effort */ }
|
|
251
251
|
throw new DaemonError("daemon/spawn-failed",
|
|
252
252
|
"daemon.start: spawn failed: " + ((e && e.message) || String(e)));
|
|
253
253
|
}
|
|
254
254
|
// Write the child's PID via atomic temp+rename so a concurrent
|
|
255
255
|
// observer never sees a half-written pidFile.
|
|
256
|
-
atomicFile.ensureDir(
|
|
256
|
+
atomicFile.ensureDir(nodePath.dirname(pidFile));
|
|
257
257
|
var pidStr = String(child.pid) + "\n";
|
|
258
258
|
atomicFile.writeSync(pidFile, pidStr, { fileMode: 0o600 });
|
|
259
259
|
// Detach so the child survives parent exit.
|
|
260
260
|
try { child.unref(); } catch (_u) { /* best-effort */ }
|
|
261
261
|
if (typeof logFd === "number") {
|
|
262
262
|
// Parent doesn't need its handle to the log; child inherited it.
|
|
263
|
-
try {
|
|
263
|
+
try { nodeFs.closeSync(logFd); } catch (_c) { /* best-effort */ }
|
|
264
264
|
}
|
|
265
265
|
_safeAuditEmit("daemon.started", "success", {
|
|
266
266
|
pidFile: pidFile,
|
|
@@ -307,7 +307,7 @@ function start(opts) {
|
|
|
307
307
|
run: function () {
|
|
308
308
|
try { lock.release(); } catch (_e) { /* best-effort */ }
|
|
309
309
|
if (logFdForeground !== null) {
|
|
310
|
-
try {
|
|
310
|
+
try { nodeFs.closeSync(logFdForeground); } catch (_c) { /* best-effort */ }
|
|
311
311
|
}
|
|
312
312
|
},
|
|
313
313
|
timeoutMs: C.TIME.seconds(2),
|
|
@@ -381,7 +381,7 @@ async function stop(opts) {
|
|
|
381
381
|
}
|
|
382
382
|
if (!_isLivePid(pid)) {
|
|
383
383
|
// Stale — clean up and report.
|
|
384
|
-
try {
|
|
384
|
+
try { nodeFs.unlinkSync(pidFile); } catch (_e) { /* best-effort */ }
|
|
385
385
|
_safeAuditEmit("daemon.stale_pid_cleaned", "success", { pidFile: pidFile, stalePid: pid });
|
|
386
386
|
return { stopped: false, pid: pid, reason: "stale" };
|
|
387
387
|
}
|
|
@@ -392,7 +392,7 @@ async function stop(opts) {
|
|
|
392
392
|
catch (e) {
|
|
393
393
|
if (e && e.code === "ESRCH") {
|
|
394
394
|
// Died between read and kill — cleanup + report.
|
|
395
|
-
try {
|
|
395
|
+
try { nodeFs.unlinkSync(pidFile); } catch (_u) { /* best-effort */ }
|
|
396
396
|
_safeAuditEmit("daemon.stopped", "success", {
|
|
397
397
|
pidFile: pidFile, signal: signal, waitMs: Date.now() - t0, escalated: false,
|
|
398
398
|
});
|
|
@@ -405,7 +405,7 @@ async function stop(opts) {
|
|
|
405
405
|
var deadline = t0 + timeoutMs;
|
|
406
406
|
while (Date.now() < deadline) {
|
|
407
407
|
if (!_isLivePid(pid)) {
|
|
408
|
-
try {
|
|
408
|
+
try { nodeFs.unlinkSync(pidFile); } catch (_u) { /* best-effort */ }
|
|
409
409
|
_safeAuditEmit("daemon.stopped", "success", {
|
|
410
410
|
pidFile: pidFile, signal: signal, waitMs: Date.now() - t0, escalated: false,
|
|
411
411
|
});
|
|
@@ -428,7 +428,7 @@ async function stop(opts) {
|
|
|
428
428
|
if (!_isLivePid(pid)) break;
|
|
429
429
|
await safeAsync.sleep(pollMs, { signal: opts.abortSignal });
|
|
430
430
|
}
|
|
431
|
-
try {
|
|
431
|
+
try { nodeFs.unlinkSync(pidFile); } catch (_u) { /* best-effort */ }
|
|
432
432
|
_safeAuditEmit("daemon.stopped", "success", {
|
|
433
433
|
pidFile: pidFile, signal: "SIGKILL", waitMs: Date.now() - t0, escalated: true,
|
|
434
434
|
});
|
package/lib/db-file-lifecycle.js
CHANGED
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
* bun:sqlite).
|
|
58
58
|
*/
|
|
59
59
|
|
|
60
|
-
var
|
|
60
|
+
var nodeFs = require("fs");
|
|
61
61
|
var os = require("os");
|
|
62
|
-
var
|
|
62
|
+
var nodePath = require("path");
|
|
63
63
|
var atomicFile = require("./atomic-file");
|
|
64
64
|
var C = require("./constants");
|
|
65
65
|
var { generateBytes, generateToken, encryptPacked, decryptPacked } = require("./crypto");
|
|
@@ -92,7 +92,7 @@ function _resolveTmpDir(operatorTmpDir, allowDiskFallback) {
|
|
|
92
92
|
// Linux: /dev/shm is the standard tmpfs mount.
|
|
93
93
|
if (process.platform === "linux") {
|
|
94
94
|
try {
|
|
95
|
-
var st =
|
|
95
|
+
var st = nodeFs.statSync("/dev/shm");
|
|
96
96
|
if (st && st.isDirectory()) return "/dev/shm";
|
|
97
97
|
} catch (_e) { /* fall through */ }
|
|
98
98
|
}
|
|
@@ -115,8 +115,8 @@ function _resolveTmpDir(operatorTmpDir, allowDiskFallback) {
|
|
|
115
115
|
* Returns an encrypted-DB-file lifecycle handle. Methods:
|
|
116
116
|
*
|
|
117
117
|
* - `decryptToTmp()` — decrypt the encrypted DB file to a fresh
|
|
118
|
-
* tmpfs path and return the
|
|
119
|
-
* return the existing
|
|
118
|
+
* tmpfs path and return the nodePath. Idempotent: subsequent calls
|
|
119
|
+
* return the existing nodePath.
|
|
120
120
|
* - `dbPath` — the resolved plaintext-tmpfs path (set after
|
|
121
121
|
* `decryptToTmp()` runs).
|
|
122
122
|
* - `startFlushTimer(db, opts?)` — start a periodic flush timer
|
|
@@ -164,15 +164,15 @@ function fileLifecycle(opts) {
|
|
|
164
164
|
var label = opts.label || "default";
|
|
165
165
|
var encName = opts.encryptedDbName || "db.enc";
|
|
166
166
|
var encPath = opts.encryptedDbPath
|
|
167
|
-
?
|
|
168
|
-
:
|
|
167
|
+
? nodePath.resolve(opts.encryptedDbPath)
|
|
168
|
+
: nodePath.join(opts.dataDir, encName);
|
|
169
169
|
var keyPath = opts.dbKeyPath
|
|
170
|
-
?
|
|
171
|
-
:
|
|
170
|
+
? nodePath.resolve(opts.dbKeyPath)
|
|
171
|
+
: nodePath.join(opts.dataDir, "db.key.enc");
|
|
172
172
|
var flushIntervalMs = opts.flushIntervalMs || DEFAULT_FLUSH_INTERVAL_MS;
|
|
173
173
|
var tmpDir = _resolveTmpDir(opts.tmpDir, opts.allowDiskFallback === true);
|
|
174
|
-
if (!
|
|
175
|
-
if (!
|
|
174
|
+
if (!nodeFs.existsSync(tmpDir)) nodeFs.mkdirSync(tmpDir, { recursive: true });
|
|
175
|
+
if (!nodeFs.existsSync(opts.dataDir)) nodeFs.mkdirSync(opts.dataDir, { recursive: true });
|
|
176
176
|
|
|
177
177
|
var dbPath = null;
|
|
178
178
|
var encKey = null;
|
|
@@ -181,8 +181,8 @@ function fileLifecycle(opts) {
|
|
|
181
181
|
|
|
182
182
|
function _loadOrGenerateKey() {
|
|
183
183
|
if (encKey) return encKey;
|
|
184
|
-
if (
|
|
185
|
-
var sealedKey =
|
|
184
|
+
if (nodeFs.existsSync(keyPath)) {
|
|
185
|
+
var sealedKey = nodeFs.readFileSync(keyPath, "utf8");
|
|
186
186
|
var keyB64;
|
|
187
187
|
try { keyB64 = opts.vault.unseal(sealedKey); }
|
|
188
188
|
catch (e) {
|
|
@@ -208,10 +208,10 @@ function fileLifecycle(opts) {
|
|
|
208
208
|
function decryptToTmp() {
|
|
209
209
|
if (decrypted) return dbPath;
|
|
210
210
|
_loadOrGenerateKey();
|
|
211
|
-
dbPath =
|
|
211
|
+
dbPath = nodePath.join(tmpDir, "blamejs-fl-" + label + "-" +
|
|
212
212
|
generateToken(TMP_NAME_BYTES) + ".db");
|
|
213
|
-
if (
|
|
214
|
-
var packed =
|
|
213
|
+
if (nodeFs.existsSync(encPath)) {
|
|
214
|
+
var packed = nodeFs.readFileSync(encPath);
|
|
215
215
|
if (packed.length < 26) { // allow:raw-byte-literal — minimum envelope length
|
|
216
216
|
throw new DbFileLifecycleError("db-file-lifecycle/short-envelope",
|
|
217
217
|
"fileLifecycle: " + encPath + " too short to be a valid envelope (" + packed.length + " bytes)");
|
|
@@ -231,7 +231,7 @@ function fileLifecycle(opts) {
|
|
|
231
231
|
label: label,
|
|
232
232
|
encPath: encPath,
|
|
233
233
|
dbPath: dbPath,
|
|
234
|
-
isEmpty: !
|
|
234
|
+
isEmpty: !nodeFs.existsSync(encPath),
|
|
235
235
|
});
|
|
236
236
|
_emitMetric("decrypted");
|
|
237
237
|
return dbPath;
|
|
@@ -246,8 +246,8 @@ function fileLifecycle(opts) {
|
|
|
246
246
|
try { db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").run(); }
|
|
247
247
|
catch (_e) { /* best-effort — operators on read-only handles or pre-init still flush */ }
|
|
248
248
|
}
|
|
249
|
-
if (!
|
|
250
|
-
var plain =
|
|
249
|
+
if (!nodeFs.existsSync(dbPath)) return null;
|
|
250
|
+
var plain = nodeFs.readFileSync(dbPath);
|
|
251
251
|
var packed = encryptPacked(plain, encKey, _aad(opts.dataDir, label));
|
|
252
252
|
atomicFile.writeSync(encPath, packed);
|
|
253
253
|
_emitAudit("flushed", "success", { label: label, bytes: plain.length });
|
|
@@ -264,11 +264,11 @@ function fileLifecycle(opts) {
|
|
|
264
264
|
try { db.prepare("PRAGMA wal_checkpoint(TRUNCATE)").run(); }
|
|
265
265
|
catch (_e) { /* best-effort */ }
|
|
266
266
|
}
|
|
267
|
-
if (!
|
|
267
|
+
if (!nodeFs.existsSync(dbPath)) {
|
|
268
268
|
throw new DbFileLifecycleError("db-file-lifecycle/no-source",
|
|
269
269
|
"fileLifecycle.snapshot: " + dbPath + " is missing");
|
|
270
270
|
}
|
|
271
|
-
var plain =
|
|
271
|
+
var plain = nodeFs.readFileSync(dbPath);
|
|
272
272
|
return encryptPacked(plain, encKey, _aad(opts.dataDir, label));
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -308,9 +308,9 @@ function fileLifecycle(opts) {
|
|
|
308
308
|
try { db.close(); } catch (_e) { /* best-effort */ }
|
|
309
309
|
}
|
|
310
310
|
if (fopts.removePlaintext === true && dbPath) {
|
|
311
|
-
try {
|
|
312
|
-
try {
|
|
313
|
-
try {
|
|
311
|
+
try { nodeFs.unlinkSync(dbPath); } catch (_e) { /* best-effort */ }
|
|
312
|
+
try { nodeFs.unlinkSync(dbPath + "-wal"); } catch (_e) { /* best-effort */ }
|
|
313
|
+
try { nodeFs.unlinkSync(dbPath + "-shm"); } catch (_e) { /* best-effort */ }
|
|
314
314
|
}
|
|
315
315
|
_emitAudit("shutdown", "success", { label: label });
|
|
316
316
|
}
|
package/lib/db-schema.js
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
* surfaces a rollback throw without
|
|
37
37
|
* swallowing the original error.
|
|
38
38
|
*/
|
|
39
|
-
var
|
|
39
|
+
var nodePath = require("path");
|
|
40
40
|
var atomicFile = require("./atomic-file");
|
|
41
41
|
var safeSql = require("./safe-sql");
|
|
42
42
|
|
|
@@ -280,7 +280,7 @@ function runMigrations(database, migrationDir) {
|
|
|
280
280
|
skipped.push(file);
|
|
281
281
|
continue;
|
|
282
282
|
}
|
|
283
|
-
var fullPath =
|
|
283
|
+
var fullPath = nodePath.join(migrationDir, file);
|
|
284
284
|
var mig;
|
|
285
285
|
try {
|
|
286
286
|
// Operator-supplied migration file — by definition not statically
|