@blamejs/core 0.8.43 → 0.8.49
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 +92 -0
- package/README.md +10 -10
- package/index.js +52 -0
- package/lib/a2a.js +159 -34
- package/lib/acme.js +762 -0
- package/lib/ai-pref.js +166 -43
- package/lib/api-key.js +108 -47
- package/lib/api-snapshot.js +157 -40
- package/lib/app-shutdown.js +113 -77
- package/lib/archive.js +337 -40
- package/lib/arg-parser.js +697 -0
- package/lib/asyncapi.js +99 -55
- package/lib/atomic-file.js +465 -104
- package/lib/audit-chain.js +123 -34
- package/lib/audit-daily-review.js +389 -0
- package/lib/audit-sign.js +302 -56
- package/lib/audit-tools.js +412 -63
- package/lib/audit.js +656 -35
- package/lib/auth/jwt-external.js +17 -0
- package/lib/auth/oauth.js +7 -0
- package/lib/auth-bot-challenge.js +505 -0
- package/lib/auth-header.js +92 -25
- package/lib/backup/bundle.js +26 -0
- package/lib/backup/index.js +512 -89
- package/lib/backup/manifest.js +168 -7
- package/lib/break-glass.js +415 -39
- package/lib/budr.js +103 -30
- package/lib/bundler.js +86 -66
- package/lib/cache.js +192 -72
- package/lib/chain-writer.js +65 -40
- package/lib/circuit-breaker.js +56 -33
- package/lib/cli-helpers.js +106 -75
- package/lib/cli.js +6 -30
- package/lib/cloud-events.js +99 -32
- package/lib/cluster-storage.js +162 -37
- package/lib/cluster.js +340 -49
- package/lib/codepoint-class.js +66 -0
- package/lib/compliance.js +424 -24
- package/lib/config-drift.js +111 -46
- package/lib/config.js +94 -40
- package/lib/consent.js +165 -18
- package/lib/constants.js +1 -0
- package/lib/content-credentials.js +153 -48
- package/lib/cookies.js +154 -62
- package/lib/credential-hash.js +133 -61
- package/lib/crypto-field.js +702 -18
- package/lib/crypto-hpke.js +256 -0
- package/lib/crypto.js +744 -22
- package/lib/csv.js +178 -35
- package/lib/daemon.js +456 -0
- package/lib/dark-patterns.js +186 -55
- package/lib/db-query.js +79 -2
- package/lib/db.js +1431 -60
- package/lib/ddl-change-control.js +523 -0
- package/lib/deprecate.js +195 -40
- package/lib/dev.js +82 -39
- package/lib/dora.js +67 -48
- package/lib/dr-runbook.js +368 -0
- package/lib/dsr.js +142 -11
- package/lib/dual-control.js +91 -56
- package/lib/events.js +120 -41
- package/lib/external-db-migrate.js +192 -2
- package/lib/external-db.js +795 -50
- package/lib/fapi2.js +122 -1
- package/lib/fda-21cfr11.js +395 -0
- package/lib/fdx.js +132 -2
- package/lib/file-type.js +87 -0
- package/lib/file-upload.js +93 -0
- package/lib/flag.js +82 -20
- package/lib/forms.js +132 -29
- package/lib/framework-error.js +169 -0
- package/lib/framework-schema.js +163 -35
- package/lib/gate-contract.js +849 -175
- package/lib/graphql-federation.js +68 -7
- package/lib/guard-all.js +172 -55
- package/lib/guard-archive.js +286 -124
- package/lib/guard-auth.js +194 -21
- package/lib/guard-cidr.js +190 -28
- package/lib/guard-csv.js +397 -51
- package/lib/guard-domain.js +213 -91
- package/lib/guard-email.js +236 -29
- package/lib/guard-filename.js +307 -75
- package/lib/guard-graphql.js +263 -30
- package/lib/guard-html.js +310 -116
- package/lib/guard-image.js +243 -30
- package/lib/guard-json.js +260 -54
- package/lib/guard-jsonpath.js +235 -23
- package/lib/guard-jwt.js +284 -30
- package/lib/guard-markdown.js +204 -22
- package/lib/guard-mime.js +190 -26
- package/lib/guard-oauth.js +277 -28
- package/lib/guard-pdf.js +251 -27
- package/lib/guard-regex.js +226 -18
- package/lib/guard-shell.js +229 -26
- package/lib/guard-svg.js +177 -10
- package/lib/guard-template.js +232 -21
- package/lib/guard-time.js +195 -29
- package/lib/guard-uuid.js +189 -30
- package/lib/guard-xml.js +259 -36
- package/lib/guard-yaml.js +241 -44
- package/lib/honeytoken.js +63 -27
- package/lib/html-balance.js +83 -0
- package/lib/http-client.js +486 -59
- package/lib/http-message-signature.js +582 -0
- package/lib/i18n.js +102 -49
- package/lib/iab-mspa.js +112 -32
- package/lib/iab-tcf.js +107 -2
- package/lib/inbox.js +90 -52
- package/lib/keychain.js +865 -0
- package/lib/legal-hold.js +374 -0
- package/lib/local-db-thin.js +320 -0
- package/lib/log-stream.js +281 -51
- package/lib/log.js +184 -86
- package/lib/mail-bounce.js +107 -62
- package/lib/mail.js +295 -58
- package/lib/mcp.js +108 -27
- package/lib/metrics.js +98 -89
- package/lib/middleware/age-gate.js +36 -0
- package/lib/middleware/ai-act-disclosure.js +37 -0
- package/lib/middleware/api-encrypt.js +45 -0
- package/lib/middleware/assetlinks.js +40 -0
- package/lib/middleware/asyncapi-serve.js +35 -0
- package/lib/middleware/attach-user.js +40 -0
- package/lib/middleware/bearer-auth.js +40 -0
- package/lib/middleware/body-parser.js +230 -0
- package/lib/middleware/bot-disclose.js +34 -0
- package/lib/middleware/bot-guard.js +39 -0
- package/lib/middleware/compression.js +37 -0
- package/lib/middleware/cookies.js +32 -0
- package/lib/middleware/cors.js +40 -0
- package/lib/middleware/csp-nonce.js +40 -0
- package/lib/middleware/csp-report.js +34 -0
- package/lib/middleware/csrf-protect.js +43 -0
- package/lib/middleware/daily-byte-quota.js +53 -85
- package/lib/middleware/db-role-for.js +40 -0
- package/lib/middleware/dpop.js +40 -0
- package/lib/middleware/error-handler.js +37 -14
- package/lib/middleware/fetch-metadata.js +39 -0
- package/lib/middleware/flag-context.js +34 -0
- package/lib/middleware/gpc.js +33 -0
- package/lib/middleware/headers.js +35 -0
- package/lib/middleware/health.js +46 -0
- package/lib/middleware/host-allowlist.js +30 -0
- package/lib/middleware/network-allowlist.js +38 -0
- package/lib/middleware/openapi-serve.js +34 -0
- package/lib/middleware/rate-limit.js +160 -18
- package/lib/middleware/request-id.js +36 -18
- package/lib/middleware/request-log.js +37 -0
- package/lib/middleware/require-aal.js +29 -0
- package/lib/middleware/require-auth.js +32 -0
- package/lib/middleware/require-bound-key.js +41 -0
- package/lib/middleware/require-content-type.js +32 -0
- package/lib/middleware/require-methods.js +27 -0
- package/lib/middleware/require-mtls.js +33 -0
- package/lib/middleware/require-step-up.js +37 -0
- package/lib/middleware/security-headers.js +44 -0
- package/lib/middleware/security-txt.js +38 -0
- package/lib/middleware/span-http-server.js +37 -0
- package/lib/middleware/sse.js +36 -0
- package/lib/middleware/trace-log-correlation.js +33 -0
- package/lib/middleware/trace-propagate.js +32 -0
- package/lib/middleware/tus-upload.js +90 -0
- package/lib/middleware/web-app-manifest.js +53 -0
- package/lib/mtls-ca.js +100 -70
- package/lib/network-byte-quota.js +308 -0
- package/lib/network-heartbeat.js +135 -0
- package/lib/network-tls.js +534 -4
- package/lib/network.js +103 -0
- package/lib/notify.js +114 -43
- package/lib/ntp-check.js +192 -51
- package/lib/observability.js +145 -47
- package/lib/openapi.js +90 -44
- package/lib/outbox.js +99 -1
- package/lib/pagination.js +168 -86
- package/lib/parsers/index.js +16 -5
- package/lib/permissions.js +93 -40
- package/lib/pqc-agent.js +84 -8
- package/lib/pqc-software.js +94 -60
- package/lib/process-spawn.js +95 -21
- package/lib/pubsub.js +96 -66
- package/lib/queue.js +375 -54
- package/lib/redact.js +793 -21
- package/lib/render.js +139 -47
- package/lib/request-helpers.js +485 -121
- package/lib/restore-bundle.js +142 -39
- package/lib/restore-rollback.js +136 -45
- package/lib/retention.js +178 -50
- package/lib/retry.js +116 -33
- package/lib/router.js +475 -23
- package/lib/safe-async.js +543 -94
- package/lib/safe-buffer.js +337 -41
- package/lib/safe-json.js +467 -62
- package/lib/safe-jsonpath.js +285 -0
- package/lib/safe-schema.js +631 -87
- package/lib/safe-sql.js +221 -59
- package/lib/safe-url.js +278 -46
- package/lib/sandbox-worker.js +135 -0
- package/lib/sandbox.js +358 -0
- package/lib/scheduler.js +135 -70
- package/lib/self-update.js +647 -0
- package/lib/session-device-binding.js +431 -0
- package/lib/session.js +259 -49
- package/lib/slug.js +138 -26
- package/lib/ssrf-guard.js +316 -56
- package/lib/storage.js +433 -70
- package/lib/subject.js +405 -23
- package/lib/template.js +148 -8
- package/lib/tenant-quota.js +545 -0
- package/lib/testing.js +440 -53
- package/lib/time.js +291 -23
- package/lib/tls-exporter.js +239 -0
- package/lib/tracing.js +90 -74
- package/lib/uuid.js +97 -22
- package/lib/vault/index.js +284 -22
- package/lib/vault/seal-pem-file.js +66 -0
- package/lib/watcher.js +368 -0
- package/lib/webhook.js +196 -63
- package/lib/websocket.js +393 -68
- package/lib/wiki-concepts.js +338 -0
- package/lib/worker-pool.js +464 -0
- package/package.json +3 -3
- package/sbom.cyclonedx.json +7 -7
package/lib/pqc-software.js
CHANGED
|
@@ -1,59 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* b.pqcSoftware
|
|
4
|
-
*
|
|
3
|
+
* @module b.pqcSoftware
|
|
4
|
+
* @nav Crypto
|
|
5
|
+
* @title PQC Software
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Pure-JS post-quantum cryptography wrapper around the vendored
|
|
9
|
+
* `@noble/post-quantum` bundle (`lib/vendor/noble-post-quantum.cjs`).
|
|
10
|
+
* Ships the FIPS-203 ML-KEM family, FIPS-204 ML-DSA family, and
|
|
11
|
+
* FIPS-205 SLH-DSA family (both SHAKE and SHA-2 hash variants) as
|
|
12
|
+
* first-class accessors on `b.pqcSoftware.*`.
|
|
10
13
|
*
|
|
11
|
-
*
|
|
14
|
+
* Defaults pin to the highest category-5 parameter set per family:
|
|
15
|
+
* `DEFAULT_KEM` = ML-KEM-1024, `DEFAULT_LATTICE_SIG` = ML-DSA-87,
|
|
16
|
+
* `DEFAULT_HASH_SIG` = SLH-DSA-SHAKE-256f. Ciphertexts are FIPS-203
|
|
17
|
+
* conformant in both directions — output produced by Node's
|
|
18
|
+
* WebCrypto ML-KEM-1024 (used by `b.crypto.encrypt` and
|
|
19
|
+
* `b.middleware.apiEncrypt`) decapsulates here, and vice versa,
|
|
20
|
+
* making this the reference-implementation path for interop tests
|
|
21
|
+
* against Node WebCrypto or a hardware HSM.
|
|
12
22
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
23
|
+
* Each KEM exposes `keygen()` / `encapsulate()` / `decapsulate()`;
|
|
24
|
+
* each signature object exposes `keygen()` / `sign()` / `verify()`
|
|
25
|
+
* — both shapes match the upstream `@noble/post-quantum` API
|
|
26
|
+
* directly, so the module is also re-bundlable into a browser
|
|
27
|
+
* build that ships `b.middleware.apiEncrypt.client`.
|
|
17
28
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
29
|
+
* The vendored bundle is a build artifact. In deployments that
|
|
30
|
+
* stripped `lib/vendor/`, `isAvailable()` returns `false` and every
|
|
31
|
+
* accessor returns a stub that throws `PqcError` on call —
|
|
32
|
+
* operators in that posture fall back to Node WebCrypto via
|
|
33
|
+
* `b.crypto.encrypt` / `b.crypto.decrypt`.
|
|
20
34
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* - DEFAULT_KEM = ML-KEM-1024 (FIPS 203)
|
|
24
|
-
* - DEFAULT_LATTICE_SIG = ML-DSA-87 (FIPS 204)
|
|
25
|
-
* - DEFAULT_HASH_SIG = SLH-DSA-SHAKE-256f (FIPS 205)
|
|
26
|
-
*
|
|
27
|
-
* Public surface (b.pqcSoftware.*):
|
|
28
|
-
*
|
|
29
|
-
* .ml_kem_1024 / .ml_kem_768 / .ml_kem_512 — FIPS 203 KEM objects
|
|
30
|
-
* .ml_dsa_87 / .ml_dsa_65 / .ml_dsa_44 — FIPS 204 lattice sig
|
|
31
|
-
* .slh_dsa_shake_256f / 192f / 128f — FIPS 205 (SHAKE)
|
|
32
|
-
* .slh_dsa_sha2_256f / 192f / 128f — FIPS 205 (SHA-2)
|
|
33
|
-
*
|
|
34
|
-
* .DEFAULT_KEM — alias to ml_kem_1024
|
|
35
|
-
* .DEFAULT_LATTICE_SIG — alias to ml_dsa_87
|
|
36
|
-
* .DEFAULT_HASH_SIG — alias to slh_dsa_shake_256f
|
|
37
|
-
*
|
|
38
|
-
* .isAvailable() — boolean: is the vendored bundle loadable?
|
|
39
|
-
* .listAlgorithms() — string[] of algorithm names
|
|
40
|
-
*
|
|
41
|
-
* Each KEM / signature object exposes `keygen()` / `encapsulate()` /
|
|
42
|
-
* `decapsulate()` (KEMs) or `keygen()` / `sign()` / `verify()`
|
|
43
|
-
* (signatures), matching the @noble/post-quantum API directly.
|
|
44
|
-
*
|
|
45
|
-
* Operators chaining this into other primitives:
|
|
46
|
-
*
|
|
47
|
-
* var pqc = b.pqcSoftware;
|
|
48
|
-
* var kp = pqc.DEFAULT_KEM.keygen();
|
|
49
|
-
* var enc = pqc.DEFAULT_KEM.encapsulate(kp.publicKey);
|
|
50
|
-
* // enc.cipherText / enc.sharedSecret
|
|
51
|
-
*
|
|
52
|
-
* Note on availability: the bundle is a build artifact in
|
|
53
|
-
* lib/vendor/noble-post-quantum.cjs. In tightly-locked deployments
|
|
54
|
-
* where operators stripped the vendor directory, .isAvailable()
|
|
55
|
-
* returns false and the module exposes a stub that throws on every
|
|
56
|
-
* primitive call.
|
|
35
|
+
* @card
|
|
36
|
+
* Pure-JS post-quantum cryptography wrapper around the vendored `@noble/post-quantum` bundle (`lib/vendor/noble-post-quantum.cjs`).
|
|
57
37
|
*/
|
|
58
38
|
|
|
59
39
|
var { defineClass } = require("./framework-error");
|
|
@@ -107,10 +87,50 @@ function _accessor(name) {
|
|
|
107
87
|
return algo;
|
|
108
88
|
}
|
|
109
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @primitive b.pqcSoftware.isAvailable
|
|
92
|
+
* @signature b.pqcSoftware.isAvailable()
|
|
93
|
+
* @since 0.7.28
|
|
94
|
+
* @status stable
|
|
95
|
+
* @related b.pqcSoftware.listAlgorithms, b.pqcSoftware.runKnownAnswerTest
|
|
96
|
+
*
|
|
97
|
+
* Returns `true` when the vendored `@noble/post-quantum` bundle loaded
|
|
98
|
+
* successfully and its KEM / signature objects are wired into the
|
|
99
|
+
* accessors. Returns `false` when `lib/vendor/noble-post-quantum.cjs`
|
|
100
|
+
* is missing or threw at require time — every accessor in that
|
|
101
|
+
* posture returns a stub whose primitive calls throw `PqcError`.
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* var b = require("blamejs").create();
|
|
105
|
+
* if (b.pqcSoftware.isAvailable()) {
|
|
106
|
+
* var ss = b.pqcSoftware.DEFAULT_KEM.keygen();
|
|
107
|
+
* ss.publicKey.length;
|
|
108
|
+
* // → 1568 (ML-KEM-1024 public key, FIPS 203 §8 |pk| = 1568)
|
|
109
|
+
* }
|
|
110
|
+
*/
|
|
110
111
|
function isAvailable() {
|
|
111
112
|
return _load() !== null;
|
|
112
113
|
}
|
|
113
114
|
|
|
115
|
+
/**
|
|
116
|
+
* @primitive b.pqcSoftware.listAlgorithms
|
|
117
|
+
* @signature b.pqcSoftware.listAlgorithms()
|
|
118
|
+
* @since 0.7.28
|
|
119
|
+
* @status stable
|
|
120
|
+
* @related b.pqcSoftware.isAvailable, b.pqcSoftware.runKnownAnswerTest
|
|
121
|
+
*
|
|
122
|
+
* Returns the names of every PQC algorithm exposed on the
|
|
123
|
+
* `b.pqcSoftware` surface — the three ML-KEM parameter sets, the
|
|
124
|
+
* three ML-DSA parameter sets, and six SLH-DSA parameter sets (three
|
|
125
|
+
* SHAKE + three SHA-2). Returns an empty array when the vendored
|
|
126
|
+
* bundle is unavailable.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* var b = require("blamejs").create();
|
|
130
|
+
* var names = b.pqcSoftware.listAlgorithms();
|
|
131
|
+
* names.indexOf("ml_kem_1024") >= 0;
|
|
132
|
+
* // → true (when the vendored bundle is present)
|
|
133
|
+
*/
|
|
114
134
|
function listAlgorithms() {
|
|
115
135
|
if (!isAvailable()) return [];
|
|
116
136
|
return [
|
|
@@ -193,17 +213,31 @@ Object.defineProperty(pqc, "DEFAULT_HASH_SIG", {
|
|
|
193
213
|
get: function () { return _accessor("slh_dsa_shake_256f"); },
|
|
194
214
|
});
|
|
195
215
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
216
|
+
/**
|
|
217
|
+
* @primitive b.pqcSoftware.runKnownAnswerTest
|
|
218
|
+
* @signature b.pqcSoftware.runKnownAnswerTest()
|
|
219
|
+
* @since 0.7.28
|
|
220
|
+
* @status stable
|
|
221
|
+
* @related b.pqcSoftware.isAvailable, b.pqcSoftware.listAlgorithms
|
|
222
|
+
*
|
|
223
|
+
* Round-trips ML-KEM-1024 against itself with a self-generated
|
|
224
|
+
* keypair: `keygen` → `encapsulate` → `decapsulate`, then a
|
|
225
|
+
* constant-time compare of the two shared secrets. This is a self-
|
|
226
|
+
* consistency gate, not the FIPS 203 Appendix A KAT vectors (those
|
|
227
|
+
* ~800 KB of test data are intentionally not vendored). The check
|
|
228
|
+
* fails fast at boot if the vendored bundle is broken, rather than
|
|
229
|
+
* mid-request when an envelope decrypt aborts.
|
|
230
|
+
*
|
|
231
|
+
* Returns `{ ok, reason?, sharedSecretLength? }`. `ok: true` means
|
|
232
|
+
* keygen / encapsulate / decapsulate cycled cleanly and the two
|
|
233
|
+
* shared secrets are byte-identical (32 bytes per FIPS 203 §1).
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* var b = require("blamejs").create();
|
|
237
|
+
* var result = b.pqcSoftware.runKnownAnswerTest();
|
|
238
|
+
* result.ok;
|
|
239
|
+
* // → true (or { ok: false, reason: "<diagnostic>" } when broken)
|
|
240
|
+
*/
|
|
207
241
|
function runKnownAnswerTest() {
|
|
208
242
|
if (!isAvailable()) {
|
|
209
243
|
return { ok: false, reason: "vendored @noble/post-quantum bundle not loadable" };
|
package/lib/process-spawn.js
CHANGED
|
@@ -1,29 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* b.processSpawn
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* which means a child (jq, postgres CLI, an unzipper) sees
|
|
7
|
-
* `DATABASE_URL`, `PG*`, `REDIS_URL`, `S3_*`, `AWS_*`. OWASP-1 closes
|
|
8
|
-
* that class: every spawn through this primitive uses a filtered env
|
|
9
|
-
* by default; operators opt in to specific secret env vars when the
|
|
10
|
-
* child genuinely needs them.
|
|
3
|
+
* @module b.processSpawn
|
|
4
|
+
* @nav Production
|
|
5
|
+
* @title Process Spawn
|
|
11
6
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Secret-safe `child_process.spawn` wrapper — argv allowlist via
|
|
9
|
+
* the operator's caller, no `shell:true` reliance, env scrubbing of
|
|
10
|
+
* connection strings and credential variables, and output redaction
|
|
11
|
+
* in the audit metadata (env-var NAMES are recorded, never values).
|
|
12
|
+
*
|
|
13
|
+
* Operators reaching for `child_process.spawn` directly inherit
|
|
14
|
+
* `process.env` by default — which means a child (`jq`, the
|
|
15
|
+
* postgres CLI, an unzipper) sees `DATABASE_URL`, `PG*`, `REDIS_URL`,
|
|
16
|
+
* `S3_*`, `AWS_*`. OWASP-1 closes that class: every spawn through
|
|
17
|
+
* `b.processSpawn` uses a filtered env by default; operators opt in
|
|
18
|
+
* to specific secret env vars via `opts.allowEnv` when the child
|
|
19
|
+
* genuinely needs them.
|
|
17
20
|
*
|
|
18
|
-
*
|
|
19
|
-
* DATABASE_URL
|
|
20
|
-
*
|
|
21
|
-
* S3_
|
|
22
|
-
*
|
|
21
|
+
* Filter patterns (case-insensitive — matches Windows env-var
|
|
22
|
+
* capitalization too): `DATABASE_URL`, `PG*`, `POSTGRES*`, `MYSQL*`,
|
|
23
|
+
* `REDIS_URL`, `MONGO*`, `AWS_(ACCESS_KEY_ID|SECRET_ACCESS_KEY|
|
|
24
|
+
* SESSION_TOKEN)`, `S3_*`, `AZURE_*`, `GCP_*`,
|
|
25
|
+
* `GOOGLE_APPLICATION_CREDENTIALS`, suffixes `*_TOKEN`, `*_SECRET`,
|
|
26
|
+
* `*_PASSWORD`, `*_API_KEY`, `*_PRIVATE_KEY`, `*_PASSPHRASE`. The
|
|
27
|
+
* frozen pattern list is exposed as
|
|
28
|
+
* `b.processSpawn.FILTER_PATTERNS` for operator inspection.
|
|
23
29
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
30
|
+
* Audit: `process.spawn` (success) — metadata carries command, arg
|
|
31
|
+
* count, and the redacted list of env-var names that were stripped.
|
|
32
|
+
*
|
|
33
|
+
* @card
|
|
34
|
+
* Secret-safe `child_process.spawn` wrapper — argv allowlist via the operator's caller, no `shell:true` reliance, env scrubbing of connection strings and credential variables, and output redaction in the audit metadata (env-var NAMES are recorded, never values).
|
|
27
35
|
*/
|
|
28
36
|
|
|
29
37
|
var lazyRequire = require("./lazy-require");
|
|
@@ -62,6 +70,37 @@ function _shouldFilter(name) {
|
|
|
62
70
|
return false;
|
|
63
71
|
}
|
|
64
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @primitive b.processSpawn.filteredEnv
|
|
75
|
+
* @signature b.processSpawn.filteredEnv(source, allowEnv)
|
|
76
|
+
* @since 0.8.42
|
|
77
|
+
* @status stable
|
|
78
|
+
* @related b.processSpawn.spawn
|
|
79
|
+
*
|
|
80
|
+
* Pure helper that returns `{ env, filtered }` — `env` is `source`
|
|
81
|
+
* with every variable matching `FILTER_PATTERNS` removed, except for
|
|
82
|
+
* names listed in `allowEnv` (explicit pass-through). `filtered` is
|
|
83
|
+
* the array of stripped variable names; values are never returned or
|
|
84
|
+
* logged.
|
|
85
|
+
*
|
|
86
|
+
* `source` defaults to `process.env` when omitted. Useful for
|
|
87
|
+
* pre-flight inspection (which secrets would the spawn drop?) without
|
|
88
|
+
* actually launching a child.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* var report = b.processSpawn.filteredEnv({
|
|
92
|
+
* PATH: "/usr/bin",
|
|
93
|
+
* AWS_ACCESS_KEY_ID: "AKIA...",
|
|
94
|
+
* AWS_SECRET_ACCESS_KEY: "wJalr...",
|
|
95
|
+
* AWS_REGION: "us-east-1",
|
|
96
|
+
* DATABASE_URL: "postgres://...",
|
|
97
|
+
* }, ["AWS_REGION"]);
|
|
98
|
+
* report.env.PATH; // → "/usr/bin"
|
|
99
|
+
* report.env.AWS_REGION; // → "us-east-1"
|
|
100
|
+
* report.env.DATABASE_URL; // → undefined
|
|
101
|
+
* report.filtered.indexOf("AWS_ACCESS_KEY_ID") !== -1; // → true
|
|
102
|
+
* report.filtered.indexOf("DATABASE_URL") !== -1; // → true
|
|
103
|
+
*/
|
|
65
104
|
function filteredEnv(source, allowEnv) {
|
|
66
105
|
var src = source || process.env;
|
|
67
106
|
var allowSet = {};
|
|
@@ -81,6 +120,41 @@ function filteredEnv(source, allowEnv) {
|
|
|
81
120
|
return { env: out, filtered: filtered };
|
|
82
121
|
}
|
|
83
122
|
|
|
123
|
+
/**
|
|
124
|
+
* @primitive b.processSpawn.spawn
|
|
125
|
+
* @signature b.processSpawn.spawn(command, args, opts)
|
|
126
|
+
* @since 0.8.42
|
|
127
|
+
* @status stable
|
|
128
|
+
* @related b.processSpawn.filteredEnv, b.daemon.start, b.audit.safeEmit
|
|
129
|
+
*
|
|
130
|
+
* Spawn a child process with the connection-string filter applied to
|
|
131
|
+
* `process.env` before exec. Returns the underlying
|
|
132
|
+
* `child_process.ChildProcess` so operators can attach the usual
|
|
133
|
+
* `stdout` / `stderr` / `close` listeners. Emits one `process.spawn`
|
|
134
|
+
* audit row carrying the command, arg count, and the names (never
|
|
135
|
+
* values) of the env vars that were stripped.
|
|
136
|
+
*
|
|
137
|
+
* Throws `ProcessSpawnError("process-spawn/bad-command")` when
|
|
138
|
+
* `command` is not a non-empty string. `opts.env`, when supplied, is
|
|
139
|
+
* trusted verbatim — operators that pass an explicit env take full
|
|
140
|
+
* responsibility for what reaches the child.
|
|
141
|
+
*
|
|
142
|
+
* @opts
|
|
143
|
+
* stdio: string | Array, // forwarded to child_process.spawn
|
|
144
|
+
* cwd: string, // forwarded to child_process.spawn
|
|
145
|
+
* detached: boolean, // forwarded to child_process.spawn
|
|
146
|
+
* env: object, // explicit override; bypasses filter
|
|
147
|
+
* allowEnv: string[], // pass-through whitelist applied to process.env
|
|
148
|
+
* ... // every other Node spawn opt is forwarded
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* var child = b.processSpawn.spawn(process.execPath, ["-e", "process.exit(0)"], {
|
|
152
|
+
* stdio: "ignore",
|
|
153
|
+
* allowEnv: ["AWS_REGION"],
|
|
154
|
+
* });
|
|
155
|
+
* typeof child.pid; // → "number"
|
|
156
|
+
* child.kill();
|
|
157
|
+
*/
|
|
84
158
|
function spawn(command, args, opts) {
|
|
85
159
|
if (typeof command !== "string" || command.length === 0) {
|
|
86
160
|
throw new ProcessSpawnError("process-spawn/bad-command",
|
package/lib/pubsub.js
CHANGED
|
@@ -1,76 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @module b.pubsub
|
|
4
|
+
* @nav Communication
|
|
5
|
+
* @title Pubsub
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Cluster-aware pub/sub channel for in-process and cross-replica
|
|
9
|
+
* messaging. Three backends share one operator API:
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
11
|
+
* local — in-process `Map<channel, Set<handler>>`; publish
|
|
12
|
+
* dispatches synchronously before returning. Single-node
|
|
13
|
+
* deploys pay zero coordination overhead.
|
|
14
|
+
* cluster — shared `_blamejs_pubsub_messages` table polled at
|
|
15
|
+
* `pollIntervalMs`; publish writes a row + dispatches
|
|
16
|
+
* locally; other nodes pick up rows via
|
|
17
|
+
* `id > lastSeenId AND publishedBy <> selfNodeId`. The
|
|
18
|
+
* default for any `b.cluster`-aware deploy.
|
|
19
|
+
* redis — Redis PUB/SUB on `lib/redis-client.js`. One connection
|
|
20
|
+
* enters subscribe mode (demultiplexed via
|
|
21
|
+
* `setOnPushMessage`); publish goes through a separate
|
|
22
|
+
* command-mode connection.
|
|
21
23
|
*
|
|
22
|
-
*
|
|
24
|
+
* Pattern subscribe accepts glob-style topic matchers (`*` matches one
|
|
25
|
+
* `.`-delimited segment, `**` matches any suffix). Channel names cap
|
|
26
|
+
* at 1 KiB to defeat pathological matcher inputs. Subscribe /
|
|
27
|
+
* unsubscribe is ref-counted across local handlers so the remote
|
|
28
|
+
* backend only carries one subscription per scoped channel.
|
|
23
29
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* pruneEveryMs: C.TIME.ms? — default 5min
|
|
31
|
-
* // redis opts
|
|
32
|
-
* redisUrl: string — required for redis backend
|
|
33
|
-
* redisPassword: string?
|
|
34
|
-
* redisUsername: string?
|
|
35
|
-
* redisTls: boolean?
|
|
36
|
-
* redisCa: string|Buffer?
|
|
37
|
-
* redisServername: string?
|
|
38
|
-
* // common
|
|
39
|
-
* topicPrefix: string? — every publish/subscribe channel
|
|
40
|
-
* scoped to `<topicPrefix>:<channel>`
|
|
41
|
-
* so independent pubsub instances
|
|
42
|
-
* sharing a backend don't collide.
|
|
43
|
-
* audit: boolean? — default false. When true emits
|
|
44
|
-
* `system.pubsub.publish` per call.
|
|
45
|
-
* });
|
|
46
|
-
*
|
|
47
|
-
* var token = ps.subscribe(channel, function (payload, ev) {
|
|
48
|
-
* // payload is whatever publish() received (objects survive JSON
|
|
49
|
-
* // round-trip on remote backends; on the local backend the
|
|
50
|
-
* // reference is passed through). ev = { channel, source: 'local'
|
|
51
|
-
* // | 'remote', publishedBy?, publishedAt? }.
|
|
52
|
-
* });
|
|
53
|
-
* ps.unsubscribe(token);
|
|
54
|
-
*
|
|
55
|
-
* await ps.publish(channel, payload); // returns { local, remote? }
|
|
56
|
-
*
|
|
57
|
-
* await ps.close(); // tears down backend
|
|
30
|
+
* Local dispatch always happens BEFORE `publish()` resolves regardless
|
|
31
|
+
* of backend — same-node subscribers see the payload with near-zero
|
|
32
|
+
* latency. Handler errors are caught and logged via the boot logger;
|
|
33
|
+
* they never abort dispatch to siblings. Bad-shape remote payloads
|
|
34
|
+
* drop silently after a logged warning so one malformed cross-node
|
|
35
|
+
* message can't poison the local handler chain.
|
|
58
36
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* near-zero latency. The remote write is awaited so the caller knows
|
|
62
|
-
* the cross-node fan-out completed.
|
|
63
|
-
*
|
|
64
|
-
* Subscription handler errors are caught and logged via the framework's
|
|
65
|
-
* boot logger; they never abort dispatch to other handlers on the same
|
|
66
|
-
* channel.
|
|
67
|
-
*
|
|
68
|
-
* Channel naming is operator-defined — pubsub treats names as opaque
|
|
69
|
-
* strings (with the optional topicPrefix prepended). Pattern subscribe
|
|
70
|
-
* (Redis-style `news.*`) is exposed via `subscribePattern(pattern,
|
|
71
|
-
* handler)`; not every backend supports it (cluster-table backend
|
|
72
|
-
* matches client-side, redis backend uses PSUBSCRIBE, local backend
|
|
73
|
-
* matches client-side too).
|
|
37
|
+
* @card
|
|
38
|
+
* Cluster-aware pub/sub channel for in-process and cross-replica messaging.
|
|
74
39
|
*/
|
|
75
40
|
var C = require("./constants");
|
|
76
41
|
var lazyRequire = require("./lazy-require");
|
|
@@ -196,6 +161,71 @@ function _matchPattern(pattern, channel) {
|
|
|
196
161
|
return false;
|
|
197
162
|
}
|
|
198
163
|
|
|
164
|
+
/**
|
|
165
|
+
* @primitive b.pubsub.create
|
|
166
|
+
* @signature b.pubsub.create(opts)
|
|
167
|
+
* @since 0.6.34
|
|
168
|
+
* @status stable
|
|
169
|
+
* @related b.cluster, b.websocketChannels
|
|
170
|
+
*
|
|
171
|
+
* Build a pub/sub instance bound to one of the supported backends.
|
|
172
|
+
* Returned object exposes `subscribe(channel, handler)` /
|
|
173
|
+
* `subscribePattern(pattern, handler)` for receive,
|
|
174
|
+
* `unsubscribe(token)` for cleanup, `publish(channel, payload)` for
|
|
175
|
+
* fan-out, and `close()` for teardown. Tokens returned by subscribe
|
|
176
|
+
* are opaque records; pass them back to `unsubscribe` verbatim.
|
|
177
|
+
*
|
|
178
|
+
* Throws `PubsubError("UNKNOWN_BACKEND")` when `opts.backend` is not
|
|
179
|
+
* one of `"local"`, `"cluster"`, `"redis"`, or a custom backend object
|
|
180
|
+
* implementing `{ publishRemote, start, stop }`. Throws
|
|
181
|
+
* `PubsubError("BAD_BACKEND")` when a custom backend object is missing
|
|
182
|
+
* those methods.
|
|
183
|
+
*
|
|
184
|
+
* @opts
|
|
185
|
+
* backend: "local" | "cluster" | "redis" | object // default "local"
|
|
186
|
+
* cluster: object, // required for backend "cluster"
|
|
187
|
+
* pollIntervalMs: number, // cluster poll cadence; default 100ms
|
|
188
|
+
* retentionMs: number, // cluster row retention; default 60_000
|
|
189
|
+
* pruneEveryMs: number, // cluster prune cadence; default 300_000
|
|
190
|
+
* redisUrl: string, // required for backend "redis"
|
|
191
|
+
* redisPassword: string,
|
|
192
|
+
* redisUsername: string,
|
|
193
|
+
* redisTls: boolean,
|
|
194
|
+
* redisCa: string | Buffer,
|
|
195
|
+
* redisServername: string,
|
|
196
|
+
* topicPrefix: string, // scopes every channel as `<prefix>:<channel>`
|
|
197
|
+
* audit: boolean, // default false; emit system.pubsub.publish
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* var ps = b.pubsub.create({ backend: "local" });
|
|
201
|
+
*
|
|
202
|
+
* var token = ps.subscribe("user.created", function (payload, ev) {
|
|
203
|
+
* console.log(ev.channel, ev.source, payload.id);
|
|
204
|
+
* // → user.created local 42
|
|
205
|
+
* });
|
|
206
|
+
*
|
|
207
|
+
* await ps.publish("user.created", { id: 42 });
|
|
208
|
+
* ps.unsubscribe(token);
|
|
209
|
+
* await ps.close();
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* // Glob-style topic matchers: '*' matches one segment, '**' any suffix.
|
|
213
|
+
* var ps = b.pubsub.create({ backend: "local" });
|
|
214
|
+
*
|
|
215
|
+
* ps.subscribePattern("orders.*.created", function (payload, ev) {
|
|
216
|
+
* console.log(ev.channel);
|
|
217
|
+
* // → orders.eu.created
|
|
218
|
+
* });
|
|
219
|
+
*
|
|
220
|
+
* ps.subscribePattern("audit.**", function (payload, ev) {
|
|
221
|
+
* console.log(ev.channel);
|
|
222
|
+
* // → audit.security.login.failed
|
|
223
|
+
* });
|
|
224
|
+
*
|
|
225
|
+
* await ps.publish("orders.eu.created", { orderId: "ord_1" });
|
|
226
|
+
* await ps.publish("audit.security.login.failed", { userId: "u_7" });
|
|
227
|
+
* await ps.close();
|
|
228
|
+
*/
|
|
199
229
|
function create(opts) {
|
|
200
230
|
opts = opts || {};
|
|
201
231
|
validateOpts(opts, [
|