@blamejs/core 0.18.3 → 0.18.5
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 +4 -0
- package/NOTICE +1 -1
- package/lib/session.js +21 -1
- package/lib/vendor/MANIFEST.json +11 -11
- package/lib/vendor/blamejs-pki.cjs +543 -24
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,10 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.18.x
|
|
10
10
|
|
|
11
|
+
- v0.18.5 (2026-08-01) — **Error- and edge-path tests for the agent orchestrator, mTLS request gate, and i18n primitives.** Error, edge, and adversarial branches of b.agent.orchestrator (registry hydrate/register validation, consumer spawn and start lifecycle, cluster election cache and leader probes, drain and quiesce budgets, tenant-scope authorization, per-row reseal), the b.middleware.requireMtls request gate (no-peer-cert and fingerprint-failure refusals, allow/deny-list gates, revocation-source and onAuthenticated error paths, audit outcomes), and b.i18n's translation-tree and file-load edges are now under test. Behaviour is unchanged -- these pin guarantees the primitives already met. **Changed:** *Error- and edge-path tests for the agent orchestrator, mTLS request gate, and i18n primitives* — b.agent.orchestrator (registry hydrate/register non-object refusals, consumer spawn missing-agent/missing-queue refusals and the shard-topic naming, consumer start double-start refusal and the direct / dotted-nested / unknown consume-handler method resolution, cluster onTransition election-cache clear, elect argument validation and the election cache-hit path, currentLeader/isLeader/fencingToken error handling, drain timeout and the quiesce poll budget, tenant-scope actor-missing / no-tenant authorization, and reseal store-validation with per-cell AAD-sealed filtering), the b.middleware.requireMtls gate (no-opts default, allow/deny-list normalization and gate outcomes, the connection fallback and null-peer-cert / no-raw / fingerprint-failed refusals, deny-match refusal, the revocationSource and onAuthenticated error paths including a non-Error rejection, and audit allowed/refused outcomes), and b.i18n's translation-tree validation and file-load edge branches now have explicit tests for their error and boundary paths. Behaviour is unchanged -- the additions pin guarantees the primitives already met.
|
|
12
|
+
|
|
13
|
+
- v0.18.4 (2026-08-01) — **`b.session.updateData({ merge: true })` now merges an inner object one level deep instead of discarding the existing nested keys, alongside error- and edge-path tests for the queue, encrypted-field, session, and CalDAV/CardDAV primitives.** b.session.updateData(token, data, { merge: true }) documents a one-level-deep merge, but an inner object value shallow-replaced the whole nested object -- so updating one nested key silently dropped the operator's other nested keys. It now merges an inner plain object into the existing one (its keys survive); an array or non-object value still replaces. Separately, error, edge, and adversarial branches of b.queue (init and backend resolution, consume success/retry/dead-letter, enqueueFlow validation and dependency ordering, dead-letter list/retry/size, shutdown drain, bootFromEnv), b.cryptoField (seal/unseal envelope-floor enforcement, derived-hash modes, per-row-key unseal rate cooldown, data residency, read-time hash upgrade, erase-row vacuum cascade, hash lookup), b.session (fingerprint-drift scoring and fail-closed binding, anonymous mint, rotate carry-forward and re-key, updateData merge guards, validFrom scheduling), and b.mail.dav (CalDAV/CardDAV request and response branches) are now under test. **Changed:** *Error- and edge-path tests for the queue, encrypted-field, session, and CalDAV/CardDAV primitives* — b.queue (init idempotency and the missing-backends / unknown-backend refusals, consume success/retry/dead-letter-write/rate-limit-wait/lease-extend/progress paths, enqueueFlow validation and cycle/dependency ordering, dead-letter list/retry/size with the SQS-unsupported refusals, shutdown pre-init no-op and in-flight drain, bootFromEnv config resolution), b.cryptoField (seal-envelope-floor enforcement, derived-hash keyed/legacy modes and truncation, per-row-key unseal rate cooldown and its audit, data residency, read-time derived-hash upgrade, the erase-row vacuum cascade, and hash lookup), b.session (fingerprint-drift scoring with clamp and fail-closed admission, strict-binding missing/unreadable refusal, anonymous mint and the anonymous-plus-userId refusal, rotate data-replace / carry-forward / bound re-key, updateData token and merge guards, and validFrom read-back with check admit/revoke), and b.mail.dav's request and response branches now have explicit tests for their error and boundary paths. Behaviour is unchanged for these -- the additions pin guarantees the primitives already met. · *Vendored @blamejs/pki refreshed to 0.3.28* — The bundled PKI toolkit (lib/vendor/blamejs-pki.cjs, the mTLS CA engine's certificate/CMS backend) is refreshed from 0.3.27 to 0.3.28 so the vendored copy matches the latest published release. It adds no runtime dependency and no public framework API; the upstream release adds HTTP Digest authentication (RFC 7616) to the EST client transport. **Fixed:** *b.session.updateData({ merge: true }) merges an inner object one level deep instead of discarding existing nested keys* — updateData(token, data, { merge: true }) is documented to merge one level deep -- inner objects merge, arrays replace. It merged only the top-level keys: an inner object value was assigned wholesale (next[k] = data[k]), so updateData(token, { prefs: { theme: "dark" } }, { merge: true }) against a stored { prefs: { locale: "en" } } dropped prefs.locale, leaving only { prefs: { theme: "dark" } }. An inner PLAIN-object value now merges into the existing plain object (its keys survive, then the incoming keys apply); an array, a non-plain object (a Date, Buffer, or class instance -- stored as its own JSON form rather than mangled into the retained old object or byte-index keys), or a non-object value replaces, and merging beyond one level replaces. Operators who relied on the previous top-level-only behaviour, or who intend to replace a whole nested object, can pass the full nested value they want stored. **Detectors:** *session.updateData({ merge: true }) must merge an inner object one level deep* — A source check asserts the merge path merges an inner value only when BOTH are PLAIN objects (a prototype-based _isPlainObject) as Object.assign({}, existing[k], data[k]), never a bare next[k] = data[k] shallow replace, so the nested-key data-loss regression -- and a Date/Buffer being merged instead of replaced -- cannot return unnoticed.
|
|
14
|
+
|
|
11
15
|
- v0.18.3 (2026-07-30) — **`b.mtlsCa` gains non-breaking CA algorithm-migration primitives -- rotate the CA, keep the superseded root trusted during re-enrollment, self-test that the runtime verifies the algorithm over TLS, and revoke by CA generation -- and `b.db.collection`'s `$like` operator now honours SQL `%` / `_` wildcards instead of matching them as literal characters.** b.mtlsCa can now migrate a certificate authority to a new signature algorithm without an outage. status() reports the stored CA's algorithm and key type; rotate({ generation, algorithm }) generates and atomically commits a new CA and returns the superseded certificate, without the algorithm-mismatch initCA() raises once a CA already exists; await commit({ retainPrevious: true }) (the public commit is the locked primitive — it resolves to a promise, takes the rotation lock so a direct caller cannot race a concurrent rotate/init, and refreshes the handle's algorithm pin to the committed CA like rotate() so a pinned handle that migrates algorithms via commit() stays usable) together with await loadTrustBundle() and dropRetained() keep the old root trusted through a re-enrollment grace window; canVerifyInTls() runs a loopback mutual-TLS self-test proving node:tls verifies the CA's algorithm on the running Node; and revokeGeneration(n) revokes every certificate the issuance ledger recorded under a CA generation below n. Separately, b.db.collection's $like operator emitted an escaped LIKE, so the caller's % and _ were matched as literal characters and $like collapsed to an exact match; it now emits a caller-controlled-wildcard LIKE, so find({ name: { $like: "Al%" } }) matches as SQL LIKE documents. The release also brings error and adversarial branches of the scheduler, session device-binding, body parser, notifier, and document collection under test. **Added:** *b.mtlsCa non-breaking CA algorithm-migration primitives* — A CA handle can now carry an operator through a signature-algorithm migration (for example a classical-to-post-quantum move) with no trust outage. status() reports the stored CA's algorithm and keyType (null before initCA(), and null for an EC CA whose curve is not the framework's P-384 OR whose signature digest is not SHA-384, so a custom P-256/P-521 or SHA-256/SHA-512-signed CA is not mislabeled ECDSA-P384-SHA384). An unpinned rotate({ generation }) preserves the stored CA's algorithm (it does not silently adopt the engine's post-quantum default), so advancing a generation on a classical ECDSA CA keeps issuing classical chains; changing algorithm stays explicit via rotate({ algorithm }). rotate({ generation, algorithm }) generates a new CA at a strictly higher integer generation and atomically commits it -- returning { caCertPem, previousCaCertPem, generation, algorithm } -- so an operator does not hit the algorithm-mismatch initCA() raises when a CA is already present, and the handle keeps issuing under the new algorithm afterward. A rotation also invalidates a persisted CRL (it was signed by the superseded CA), so regenerate it under the new CA with generateCrl(). The commit path gained retainPrevious (defaulted on for rotate): the outgoing certificate is snapshotted to ca.prev.crt, and await loadTrustBundle() returns [current, ...retained] so both roots are trusted while clients re-enroll (it resolves to a promise, taking the rotation lock so its snapshot is serialized with rotation and dropRetained()); dropRetained(), or rotate({ retainPrevious: false }), ends that window. There is one retained window at a time: a second retained rotation while a root is still retained is refused (mtls-ca/retained-root-exists) rather than silently overwriting the retained root and rejecting clients still enrolled under it -- end the window first, and a plain commit() that omits retainPrevious while a window is open is likewise refused (mtls-ca/retention-intent-required) rather than replacing the active CA while leaving the old root, which would drop trust for the just-superseded generation. A rotation also aborts if it cannot capture the existing key for its rollback journal, and loadTrustBundle() still returns a retained root a crashed rotation left only in that journal. canVerifyInTls(algorithm?) performs a loopback mutual-TLS handshake with a freshly issued leaf and resolves true only if node:tls actually authenticates the client against a CA on the running runtime -- pass the prospective algorithm to pre-flight a migration (canVerifyInTls("ML-DSA-87") probes the target chain, not the current CA) before rotating to an algorithm the deployed Node cannot verify; with no argument it probes the stored CA -- and requires an explicit algorithm when the stored CA's algorithm cannot be derived (a custom-engine CA this runtime does not classify, with no create-time pin), rather than probing an undeterminable one that the engine could resolve to a different algorithm. revokeGeneration(n) walks an issuance ledger (recorded automatically as certificates are issued, or supplied via the new issuanceStore option) and revokes every certificate issued under a CA generation below n -- with an undeterminable generation (a custom engine whose CA certificate this runtime cannot parse, such as an opaque post-quantum cert) recorded as null and skipped by that sweep, still revocable by serial or fingerprint, rather than misfiled as generation 0 (which would sweep current leaves and self-revoke future issuances); issuance fails closed if the ledger cannot be written, so no untracked credential escapes revocation, and importIssuance(entries) backfills leaf identities the ledger does not have (certificates from a pre-upgrade dataDir or issued out of band) so revokeGeneration can sweep them too. Rotation is crash-atomic: because the CA key, certificate, and retained root are separate files that cannot be swapped in a single rename, a rotation that is interrupted between them (a crash or power loss) is reconciled the next time the CA is opened -- the prior key AND the prior retained root are journaled durably before either is overwritten, so an interrupted rotation rolls back to the still-usable previous CA (and the trust bundle its retained root still serves) instead of stranding it as an unrecoverable new-key/old-certificate pair or dropping trust for clients enrolled under the formerly-retained generation. A rollback journal that is unreadable or is not a valid manifest fails the open closed (mtls-ca/rollback-journal-corrupt) so an unresolved rotation marker is never overwritten, rather than being silently discarded and losing the prior key. Establishing the requested retention is part of the commit: a rotation that cannot write the retained root (for example an unwritable ca.prev.crt directory) aborts and rolls back rather than publishing a new CA that silently omits the outgoing root, and a rotate({ retainPrevious: false }) that cannot remove the old root aborts rather than leaving a root the operator asked to hard-cut still trusted. The engine interface adds canVerifyInTls(algorithm) for pluggable engines. · *b.middleware.requireMtls enforces a live revocation source* — requireMtls({ revocationSource: caHandle }) consults a b.mtlsCa handle (or any { isRevoked(fingerprintHex) } object) on every request, refusing a peer whose fingerprint the CA reports revoked. This enforces revoke() and revokeGeneration() at the gate without mirroring the registry into denyList, and it is CA-generation-independent (fingerprint-keyed) -- so a revoked superseded-generation cert is refused even though a CRL signed by the new CA could not cover the old cohort. The lookup fails closed: a source that throws refuses the request. **Changed:** *b.mtlsCa revoke() and revokeGeneration() return a promise* — The revocation registry's read-modify-write now runs under a cross-process file lock so concurrent revocations against the same data directory cannot lose each other's entry. b.mtlsCa revoke() -- previously synchronous -- and revokeGeneration() therefore return a promise resolving to their result; input validation still throws synchronously. Callers must await them: await ca.revoke(serial) / const { revoked } = await ca.revokeGeneration(n). isRevoked() stays synchronous and now reads an in-memory index kept fresh against the store, so a revocationSource-wired require-mtls gate does O(1) per-request lookups. An issuance whose signing straddles a rotate()+revokeGeneration() for its own generation -- or a rotate({ retainPrevious: false }) / dropRetained() that removes the root it was signed under -- is now refused (mtls-ca/issuance-superseded) rather than returned as a credential the sweep missed or a leaf that chains to a root no longer trusted; a clustered revocation store (shared across hosts with per-host dataDirs) can share that supersede watermark by additionally exposing { readGenerationWatermark(), bumpGenerationWatermark(n) }. A clustered revocation store now also requires a shared issuanceStore -- constructing one with the default per-host issuance ledger is refused, since revokeGeneration() would otherwise miss certificates issued on another host and leave them accepted by the shared revocation gate. loadTrustBundle() reads a stable snapshot of the current certificate (re-reading and retrying if a retained rotation publishes mid-read), so it never returns a bundle that omits the newly active root, and it deduplicates an identical retained root. · *Error- and edge-path tests for the scheduler, session device-binding, body parser, notifier, and document collection* — The scheduler's register / schedule validation, start / stop / status lifecycle, and task fire-and-failure recording; b.session device-binding storage and its store-error path; the body parser across its supported content types; the notifier's transport surface; and b.db.collection's query operators now have explicit tests for their error and boundary branches. Behaviour is unchanged -- these pin guarantees the primitives already met. · *Vendored @blamejs/pki refreshed to 0.3.27* — The bundled PKI toolkit (lib/vendor/blamejs-pki.cjs, the mTLS CA engine's certificate/CMS backend) is refreshed from 0.3.25 to 0.3.27. It adds no runtime dependency and no public framework API; the upstream releases add CMP certificate-enrollment protocol support (pki.cmp.verify checks a PKIMessage's signature/PBMAC1 protection; pki.cmp.session drives a full RFC 9810 ir/cr/kur/p10cr enrollment exchange) and fixes across the X.509 / CMS / PKCS#12 surface (subjectAltName IP-address string GeneralNames, RSASSA-PKCS1-v1_5 WebCrypto algorithm-name casing, public-key-integrity PKCS#12, CMS countersignature verification, cross-origin SNI/pin retention in the EST/ACME clients). **Fixed:** *b.db.collection $like honours SQL % / _ wildcards instead of matching them literally* — find({ field: { $like: "Al%" } }) routed the pattern through the query builder's escaping LIKE comparator, which escapes % and _ so a bound value cannot smuggle wildcards. For $like -- whose contract is a SQL LIKE pattern -- that neutralised the caller's wildcards and collapsed the operator to an exact match ($like: "Al%" matched only the literal string "Al%", never "Alice"). $like now emits a caller-controlled-wildcard LIKE, so %, _, prefix, suffix, infix, and single-character patterns all resolve as SQL LIKE defines. Exact patterns with no wildcard are unchanged. Callers that relied on the previous literal-match behaviour should pass the field value directly ({ field: value }) or escape their own wildcards. · *b.mtlsCa fails closed on a schema-corrupt issuance ledger instead of silently emptying it* — A present issuance ledger that was valid JSON but carried no `issued` array (an accidental `{}`, or a truncated / externally-rewritten file) was treated as an empty ledger. The next issuance then rewrote the file with only its own entry, permanently dropping every prior certificate from the sole index revokeGeneration() consults -- so those certificates would survive a later generation revocation. A present ledger with a missing or non-array `issued` now fails closed (mtls-ca/issuance-corrupt), exactly as malformed JSON already did; the operator restores or removes the file. An absent ledger still reads as empty. **Detectors:** *b.db.collection $like must use the verbatim caller-wildcard LIKE* — A source check asserts the collection's $like operator routes through the caller-controlled-wildcard LIKE and never the value-escaping LIKE comparator, so the wildcard-escaping regression cannot return unnoticed. · *b.mtlsCa issuance fingerprint must hash the certificate DER* — A source check asserts the issued-certificate fingerprint is computed as the DER hash the require-mtls gate pins, not the PEM-text hash -- a mismatch would silently make revoke() / revokeGeneration() by fingerprint unenforceable at the gate. · *b.mtlsCa CA rotation must journal the prior key before overwriting it* — A source check asserts the commit path persists a durable rollback copy of the prior CA key before the rename that overwrites it, and that both the read and the rotate paths reconcile a leftover journal on open -- so an interrupted rotation can never regress to leaving the new key beside the old certificate with the prior key lost.
|
|
12
16
|
|
|
13
17
|
- v0.18.2 (2026-07-30) — **`b.sql` andWhere() now accepts the 2-argument andWhere(field, value) shorthand it previously rejected; the SQL builder, i18n, and static file server also gain error/adversarial-path tests.** b.sql query builders support two call shapes for their WHERE helpers -- where(field, value) and where(field, op, value), distinguished by argument count. orWhere honoured both, but andWhere re-passed three fixed positional arguments internally, so a two-argument andWhere(field, value) was misread as three: the value became the operator and the call threw sql-builder/bad-operator. andWhere now forwards its arguments like where/orWhere, so andWhere("col", value) appends `AND "col" = ?` as documented. The three- and object-argument forms are unchanged. Alongside the fix, the error, boundary, and adversarial branches of the SQL builder, the i18n primitive, and the static file server are brought under test; no other defects surfaced. **Changed:** *Error-path tests for the SQL builder, i18n, and the static file server* — The b.sql builder's dialect / cast / raw-SQL guards, its emitted-SQL size and encoding guards, and the full set of chained WHERE / HAVING / JOIN delegators now have explicit tests; b.i18n's option-validation and interpolation error contracts are pinned to their exact typed error codes; and b.staticServe's path-traversal, symlink-escape, reserved-name, content-disposition, conditional-request, and HEAD-sanitize branches are asserted through the wired middleware. Behaviour is unchanged -- these assert guarantees the primitives already met (a served-traversal or wrong content-type would be a real defect; none were found) -- so no migration is needed. **Fixed:** *b.sql andWhere() honours the 2-argument andWhere(field, value) shorthand* — A chained builder call such as b.sql.select("t").where("a", 1).andWhere("b", 2) previously threw sql-builder/bad-operator because andWhere forwarded three fixed positional parameters to where(), defeating where()'s argument-count check for the two-argument shorthand (the value was interpreted as the operator). andWhere now forwards its arguments verbatim, matching where() and orWhere(); andWhere("b", 2) emits `AND "b" = ?`. The three-argument andWhere(field, op, value) and object andWhere({ field: value }) forms are unaffected.
|
package/NOTICE
CHANGED
|
@@ -60,7 +60,7 @@ Used for: WebAuthn / passkey registration + authentication response
|
|
|
60
60
|
server.cjs).
|
|
61
61
|
--------------------------------------------------------------------------------
|
|
62
62
|
Component: @blamejs/pki
|
|
63
|
-
Version: 0.3.
|
|
63
|
+
Version: 0.3.28
|
|
64
64
|
Source: https://github.com/blamejs/pki
|
|
65
65
|
License: Apache-2.0
|
|
66
66
|
Copyright: Copyright (c) blamejs contributors
|
package/lib/session.js
CHANGED
|
@@ -63,6 +63,16 @@ var safeJson = require("./safe-json");
|
|
|
63
63
|
var sql = require("./sql");
|
|
64
64
|
var { SessionError } = require("./framework-error");
|
|
65
65
|
|
|
66
|
+
// A PLAIN object (created by {} or JSON.parse — its prototype is Object.prototype or null), NOT a Date /
|
|
67
|
+
// Buffer / Map / RegExp / class instance. updateData's one-level-deep merge merges two plain objects
|
|
68
|
+
// only; a non-plain value REPLACES so it reaches JSON.stringify as its own form (a Date's ISO string, a
|
|
69
|
+
// Buffer's byte array) instead of being mangled by Object.assign (which copies only enumerable own keys).
|
|
70
|
+
function _isPlainObject(v) {
|
|
71
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
|
|
72
|
+
var proto = Object.getPrototypeOf(v);
|
|
73
|
+
return proto === Object.prototype || proto === null;
|
|
74
|
+
}
|
|
75
|
+
|
|
66
76
|
// vault is initialized at boot before sessions; lazyRequire keeps the
|
|
67
77
|
// load order independent of module-import order. Used to seal/unseal
|
|
68
78
|
// the cookie-side sid so the wire token is ciphertext rather than
|
|
@@ -1273,7 +1283,17 @@ async function updateData(token, data, opts) {
|
|
|
1273
1283
|
if (data && typeof data === "object") {
|
|
1274
1284
|
Object.keys(data).forEach(function (k) {
|
|
1275
1285
|
if (k === "__bj_fingerprint") return; // reserved — only fingerprint binding writes this
|
|
1276
|
-
|
|
1286
|
+
var ev = existing[k], nv = data[k];
|
|
1287
|
+
// ONE LEVEL DEEP (per the doc): a PLAIN-object value merges into the existing plain object so its
|
|
1288
|
+
// keys survive; an array, a non-plain object (Date/Buffer/class instance), or a non-object value
|
|
1289
|
+
// REPLACES wholesale. Deeper than one level replaces — Object.assign copies own keys, not
|
|
1290
|
+
// recursively. Both must be PLAIN objects to merge, else a Date (no enumerable keys) would leave
|
|
1291
|
+
// the old object in place and a Buffer would mangle into byte-index keys.
|
|
1292
|
+
if (_isPlainObject(ev) && _isPlainObject(nv)) {
|
|
1293
|
+
next[k] = Object.assign({}, ev, nv);
|
|
1294
|
+
} else {
|
|
1295
|
+
next[k] = nv;
|
|
1296
|
+
}
|
|
1277
1297
|
});
|
|
1278
1298
|
}
|
|
1279
1299
|
} else {
|
package/lib/vendor/MANIFEST.json
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"hashes": {
|
|
19
19
|
"server": "sha256:2b30a26f728c5349f4c4b47834f862a4f77393b1224fc12b22abe3ce2cfab78f"
|
|
20
20
|
},
|
|
21
|
-
"refreshedAt": "2026-08-
|
|
21
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
22
22
|
},
|
|
23
23
|
"@noble/curves": {
|
|
24
24
|
"version": "2.2.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"hashes": {
|
|
41
41
|
"server": "sha256:2880c288b1285ef51d356d057bee6f0c8a00de36638cf47b47617e8c1faf10d5"
|
|
42
42
|
},
|
|
43
|
-
"refreshedAt": "2026-08-
|
|
43
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
44
44
|
},
|
|
45
45
|
"@noble/post-quantum": {
|
|
46
46
|
"version": "0.6.1",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"hashes": {
|
|
72
72
|
"server": "sha256:f9c94094b3c10fe73dac5343289da582454ea6053494fab2bf66099d9103d6c3"
|
|
73
73
|
},
|
|
74
|
-
"refreshedAt": "2026-08-
|
|
74
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
75
75
|
},
|
|
76
76
|
"@simplewebauthn/server": {
|
|
77
77
|
"version": "13.3.2",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"hashes": {
|
|
95
95
|
"server": "sha256:e83195dc9f189385da9c856ef38843f4466f93ea8f3d7fc2efcb1e1b18da6f20"
|
|
96
96
|
},
|
|
97
|
-
"refreshedAt": "2026-08-
|
|
97
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
98
98
|
},
|
|
99
99
|
"SecLists-common-passwords-top-10000": {
|
|
100
100
|
"version": "10k-most-common (master)",
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
},
|
|
115
115
|
"runtime_artifact": "lib/vendor/common-passwords-top-10000.data.js",
|
|
116
116
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
117
|
-
"refreshedAt": "2026-08-
|
|
117
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
118
118
|
},
|
|
119
119
|
"bimi-trust-anchors": {
|
|
120
120
|
"version": "operator-managed",
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
},
|
|
140
140
|
"runtime_artifact": "lib/vendor/bimi-trust-anchors.data.js",
|
|
141
141
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
142
|
-
"refreshedAt": "2026-08-
|
|
142
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
143
143
|
},
|
|
144
144
|
"publicsuffix-list": {
|
|
145
145
|
"version": "master",
|
|
@@ -159,10 +159,10 @@
|
|
|
159
159
|
},
|
|
160
160
|
"runtime_artifact": "lib/vendor/public-suffix-list.data.js",
|
|
161
161
|
"integrity_layers": "sha256 + sha3-512 + SLH-DSA-SHAKE-256f signature + in-payload canary (where applicable)",
|
|
162
|
-
"refreshedAt": "2026-08-
|
|
162
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
163
163
|
},
|
|
164
164
|
"@blamejs/pki": {
|
|
165
|
-
"version": "0.3.
|
|
165
|
+
"version": "0.3.28",
|
|
166
166
|
"license": "Apache-2.0",
|
|
167
167
|
"author": "blamejs",
|
|
168
168
|
"source": "https://github.com/blamejs/pki",
|
|
@@ -183,11 +183,11 @@
|
|
|
183
183
|
},
|
|
184
184
|
"bundler": "esbuild --format=cjs --platform=node --external:crypto --external:node:crypto",
|
|
185
185
|
"bundledAt": "2026-08-01T00:00:00Z",
|
|
186
|
-
"cpe": "cpe:2.3:a:blamejs:pki:0.3.
|
|
186
|
+
"cpe": "cpe:2.3:a:blamejs:pki:0.3.28:*:*:*:*:node.js:*:*",
|
|
187
187
|
"hashes": {
|
|
188
|
-
"server": "sha256:
|
|
188
|
+
"server": "sha256:5e6824b7a7b0a8f8cf191475c115aeba433b12dba790de6da1afb26ebf43a6ec"
|
|
189
189
|
},
|
|
190
|
-
"refreshedAt": "2026-08-
|
|
190
|
+
"refreshedAt": "2026-08-01T21:13:55.453Z"
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @blamejs/pki v0.3.
|
|
1
|
+
// @blamejs/pki v0.3.28 — vendored (Apache-2.0). Zero-dep pure CJS.
|
|
2
2
|
// https://github.com/blamejs/pki Exports: x509, crl, pkcs12, key, webcrypto, schema, csr, cms, ...
|
|
3
3
|
// Backs lib/mtls-engine-default.js (PQC-capable CA + PKCS#12 engine).
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -137,7 +137,7 @@ var require_package = __commonJS({
|
|
|
137
137
|
"node_modules/@blamejs/pki/package.json"(exports2, module2) {
|
|
138
138
|
module2.exports = {
|
|
139
139
|
name: "@blamejs/pki",
|
|
140
|
-
version: "0.3.
|
|
140
|
+
version: "0.3.28",
|
|
141
141
|
description: "Pure-JavaScript PKI toolkit that owns its stack \u2014 X.509, ASN.1/DER, CMS, PQC-first.",
|
|
142
142
|
license: "Apache-2.0",
|
|
143
143
|
author: "blamejs contributors",
|
|
@@ -314,6 +314,10 @@ var require_constants = __commonJS({
|
|
|
314
314
|
// document plus multipart/header overhead (16 MiB * 4/3 ~= 21.3 MiB); 24 MiB
|
|
315
315
|
// clears it. An operator may only tighten it DOWNWARD via opts.maxResponseBytes.
|
|
316
316
|
HTTP_MAX_RESPONSE_BYTES: BYTES.mib(24),
|
|
317
|
+
// A single untrusted WWW-Authenticate challenge header, bounded BEFORE the string
|
|
318
|
+
// is copied/tokenized so a hostile server cannot drive unbounded work parsing it
|
|
319
|
+
// (CWE-770). 8 KiB clears any realistic multi-scheme Digest challenge.
|
|
320
|
+
HTTP_AUTH_HEADER_MAX_BYTES: BYTES.kib(8),
|
|
317
321
|
// Deterministic-CBOR codec ceilings (RFC 8949), the DER neighbours' siblings:
|
|
318
322
|
// a whole-document cap refused before the walk, a nesting cap, and a per-value
|
|
319
323
|
// bignum ceiling the document cap can't provide. Unlike DER_MAX_INTEGER_BYTES,
|
|
@@ -5448,6 +5452,7 @@ var require_http_transport = __commonJS({
|
|
|
5448
5452
|
try {
|
|
5449
5453
|
req = nodeHttps.request(prep.options, function(res) {
|
|
5450
5454
|
var proto = res.socket && res.socket.getProtocol ? res.socket.getProtocol() : null;
|
|
5455
|
+
var cipher = res.socket && res.socket.getCipher ? res.socket.getCipher() : null;
|
|
5451
5456
|
var peer = res.socket && res.socket.getPeerCertificate ? res.socket.getPeerCertificate() : null;
|
|
5452
5457
|
var declared = parseInt((res.headers || {})["content-length"], 10);
|
|
5453
5458
|
if (Number.isFinite(declared) && declared > prep.maxBytes) {
|
|
@@ -5484,7 +5489,7 @@ var require_http_transport = __commonJS({
|
|
|
5484
5489
|
status: res.statusCode,
|
|
5485
5490
|
headers: lower,
|
|
5486
5491
|
body: Buffer.from(buf.subarray(0, len)),
|
|
5487
|
-
tls: { protocol: proto, peerCertificate: peer && peer.raw ? peer.raw : null }
|
|
5492
|
+
tls: { protocol: proto, cipher, peerCertificate: peer && peer.raw ? peer.raw : null }
|
|
5488
5493
|
});
|
|
5489
5494
|
});
|
|
5490
5495
|
res.on("error", function(e) {
|
|
@@ -25227,6 +25232,331 @@ var require_sigstore = __commonJS({
|
|
|
25227
25232
|
}
|
|
25228
25233
|
});
|
|
25229
25234
|
|
|
25235
|
+
// node_modules/@blamejs/pki/lib/http-digest.js
|
|
25236
|
+
var require_http_digest = __commonJS({
|
|
25237
|
+
"node_modules/@blamejs/pki/lib/http-digest.js"(exports2, module2) {
|
|
25238
|
+
"use strict";
|
|
25239
|
+
var crypto = require("crypto");
|
|
25240
|
+
var constants = require_constants();
|
|
25241
|
+
var ALGS = {
|
|
25242
|
+
"SHA-512-256": { hash: "sha512-256", rank: 3, sess: false },
|
|
25243
|
+
"SHA-512-256-SESS": { hash: "sha512-256", rank: 3, sess: true },
|
|
25244
|
+
"SHA-256": { hash: "sha256", rank: 2, sess: false },
|
|
25245
|
+
"SHA-256-SESS": { hash: "sha256", rank: 2, sess: true },
|
|
25246
|
+
"MD5": { hash: "md5", rank: 1, sess: false },
|
|
25247
|
+
"MD5-SESS": { hash: "md5", rank: 1, sess: true }
|
|
25248
|
+
};
|
|
25249
|
+
var DEFAULT_CODES = {
|
|
25250
|
+
unsupportedAlgorithm: "digest/unsupported-algorithm",
|
|
25251
|
+
weakAlgorithm: "digest/weak-algorithm",
|
|
25252
|
+
noQop: "digest/no-qop",
|
|
25253
|
+
badChallenge: "digest/bad-challenge"
|
|
25254
|
+
};
|
|
25255
|
+
function H(hash, s) {
|
|
25256
|
+
return crypto.createHash(hash).update(s, "latin1").digest("hex");
|
|
25257
|
+
}
|
|
25258
|
+
function KD(hash, secret, data) {
|
|
25259
|
+
return H(hash, secret + ":" + data);
|
|
25260
|
+
}
|
|
25261
|
+
function _qstr(v) {
|
|
25262
|
+
return '"' + String(v).replace(/(["\\])/g, "\\$1") + '"';
|
|
25263
|
+
}
|
|
25264
|
+
function _commaSplitOutsideQuotes(s) {
|
|
25265
|
+
var out = [], buf = "", inQ = false, esc = false;
|
|
25266
|
+
for (var i = 0; i < s.length; i++) {
|
|
25267
|
+
var c = s.charAt(i);
|
|
25268
|
+
if (esc) {
|
|
25269
|
+
buf += c;
|
|
25270
|
+
esc = false;
|
|
25271
|
+
continue;
|
|
25272
|
+
}
|
|
25273
|
+
if (inQ && c === "\\") {
|
|
25274
|
+
buf += c;
|
|
25275
|
+
esc = true;
|
|
25276
|
+
continue;
|
|
25277
|
+
}
|
|
25278
|
+
if (c === '"') {
|
|
25279
|
+
inQ = !inQ;
|
|
25280
|
+
buf += c;
|
|
25281
|
+
continue;
|
|
25282
|
+
}
|
|
25283
|
+
if (c === "," && !inQ) {
|
|
25284
|
+
out.push(buf);
|
|
25285
|
+
buf = "";
|
|
25286
|
+
continue;
|
|
25287
|
+
}
|
|
25288
|
+
buf += c;
|
|
25289
|
+
}
|
|
25290
|
+
out.push(buf);
|
|
25291
|
+
return out;
|
|
25292
|
+
}
|
|
25293
|
+
function _firstEqOutsideQuotes(s) {
|
|
25294
|
+
var inQ = false, esc = false;
|
|
25295
|
+
for (var i = 0; i < s.length; i++) {
|
|
25296
|
+
var c = s.charAt(i);
|
|
25297
|
+
if (esc) {
|
|
25298
|
+
esc = false;
|
|
25299
|
+
continue;
|
|
25300
|
+
}
|
|
25301
|
+
if (inQ && c === "\\") {
|
|
25302
|
+
esc = true;
|
|
25303
|
+
continue;
|
|
25304
|
+
}
|
|
25305
|
+
if (c === '"') {
|
|
25306
|
+
inQ = !inQ;
|
|
25307
|
+
continue;
|
|
25308
|
+
}
|
|
25309
|
+
if (c === "=" && !inQ) return i;
|
|
25310
|
+
}
|
|
25311
|
+
return -1;
|
|
25312
|
+
}
|
|
25313
|
+
function _unq(s) {
|
|
25314
|
+
return s.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
25315
|
+
}
|
|
25316
|
+
function _closedQuote(s) {
|
|
25317
|
+
if (s.length < 2 || s.charAt(0) !== '"') return false;
|
|
25318
|
+
var esc = false;
|
|
25319
|
+
for (var i = 1; i < s.length; i++) {
|
|
25320
|
+
var c = s.charAt(i);
|
|
25321
|
+
if (esc) {
|
|
25322
|
+
esc = false;
|
|
25323
|
+
continue;
|
|
25324
|
+
}
|
|
25325
|
+
if (c === "\\") {
|
|
25326
|
+
esc = true;
|
|
25327
|
+
continue;
|
|
25328
|
+
}
|
|
25329
|
+
if (c === '"') return i === s.length - 1;
|
|
25330
|
+
}
|
|
25331
|
+
return false;
|
|
25332
|
+
}
|
|
25333
|
+
function _hasCtl(s) {
|
|
25334
|
+
for (var i = 0; i < s.length; i++) {
|
|
25335
|
+
var c = s.charCodeAt(i);
|
|
25336
|
+
if (c < 32 && c !== 9 || c === 127) return true;
|
|
25337
|
+
}
|
|
25338
|
+
return false;
|
|
25339
|
+
}
|
|
25340
|
+
function _splitChallenges(s) {
|
|
25341
|
+
var segs = _commaSplitOutsideQuotes(s);
|
|
25342
|
+
var out = [], cur = null;
|
|
25343
|
+
for (var i = 0; i < segs.length; i++) {
|
|
25344
|
+
var seg = segs[i].replace(/^\s+|\s+$/g, "");
|
|
25345
|
+
if (seg === "") continue;
|
|
25346
|
+
var eq = _firstEqOutsideQuotes(seg);
|
|
25347
|
+
var pre = (eq < 0 ? seg : seg.slice(0, eq)).replace(/^\s+|\s+$/g, "");
|
|
25348
|
+
var ws = /^(\S+)\s+(\S[\s\S]*)$/.exec(pre);
|
|
25349
|
+
if (eq < 0) {
|
|
25350
|
+
cur = { scheme: seg, paramText: "" };
|
|
25351
|
+
out.push(cur);
|
|
25352
|
+
} else if (ws) {
|
|
25353
|
+
cur = { scheme: ws[1], paramText: seg.replace(/^\s*\S+\s+/, "") };
|
|
25354
|
+
out.push(cur);
|
|
25355
|
+
} else if (cur) {
|
|
25356
|
+
cur.paramText = cur.paramText ? cur.paramText + "," + seg : seg;
|
|
25357
|
+
}
|
|
25358
|
+
}
|
|
25359
|
+
return out;
|
|
25360
|
+
}
|
|
25361
|
+
function _parseParams(paramText, E, code) {
|
|
25362
|
+
var segs = _commaSplitOutsideQuotes(paramText), map = /* @__PURE__ */ Object.create(null);
|
|
25363
|
+
for (var i = 0; i < segs.length; i++) {
|
|
25364
|
+
var seg = segs[i].replace(/^\s+|\s+$/g, "");
|
|
25365
|
+
if (seg === "") continue;
|
|
25366
|
+
var eq = _firstEqOutsideQuotes(seg);
|
|
25367
|
+
if (eq < 0) throw E(code, "malformed Digest auth-param (no '='): " + JSON.stringify(seg));
|
|
25368
|
+
var key = seg.slice(0, eq).replace(/^\s+|\s+$/g, "").toLowerCase();
|
|
25369
|
+
var rawVal = seg.slice(eq + 1).replace(/^\s+|\s+$/g, "");
|
|
25370
|
+
var quoted = rawVal.charAt(0) === '"';
|
|
25371
|
+
if (quoted && !_closedQuote(rawVal)) throw E(code, "an unterminated or trailing-garbage Digest quoted-string (RFC 7230 sec. 3.2.6)");
|
|
25372
|
+
if (Object.prototype.hasOwnProperty.call(map, key)) throw E(code, "repeated Digest auth-param " + JSON.stringify(key));
|
|
25373
|
+
var value = quoted ? _unq(rawVal) : rawVal;
|
|
25374
|
+
if (_hasCtl(value)) throw E(code, "a Digest auth-param value contains a control character (RFC 7230 sec. 3.2.6)");
|
|
25375
|
+
map[key] = { value, quoted };
|
|
25376
|
+
}
|
|
25377
|
+
return map;
|
|
25378
|
+
}
|
|
25379
|
+
function _validateDigest(paramText, E, code) {
|
|
25380
|
+
var p = _parseParams(paramText, E, code);
|
|
25381
|
+
if (!p.realm || !p.realm.quoted || p.realm.value === "") throw E(code, "a Digest challenge requires a non-empty quoted realm (RFC 7616 sec. 3.3)");
|
|
25382
|
+
if (!p.nonce || !p.nonce.quoted || p.nonce.value === "") throw E(code, "a Digest challenge requires a non-empty quoted nonce (RFC 7616 sec. 3.3)");
|
|
25383
|
+
if (p.algorithm && p.algorithm.quoted) throw E(code, "the Digest algorithm must be a token, not a quoted-string (RFC 7616 sec. 3.3)");
|
|
25384
|
+
var qop = [];
|
|
25385
|
+
if (p.qop) {
|
|
25386
|
+
if (!p.qop.quoted) throw E(code, "the Digest qop must be a quoted list (RFC 7616 sec. 3.3)");
|
|
25387
|
+
qop = p.qop.value.split(",").map(function(x) {
|
|
25388
|
+
return x.replace(/^\s+|\s+$/g, "").toLowerCase();
|
|
25389
|
+
}).filter(Boolean);
|
|
25390
|
+
if (qop.length === 0) throw E(code, "a present Digest qop directive must list at least one value (RFC 7616 sec. 3.3)");
|
|
25391
|
+
}
|
|
25392
|
+
if (p.domain && !p.domain.quoted) throw E(code, "the Digest domain must be a quoted-string (RFC 7616 sec. 3.3)");
|
|
25393
|
+
var domain = p.domain && p.domain.value.replace(/^\s+|\s+$/g, "") !== "" ? p.domain.value.replace(/^\s+|\s+$/g, "").split(/\s+/) : null;
|
|
25394
|
+
if (p.charset && (p.charset.quoted || String(p.charset.value).toUpperCase() !== "UTF-8")) throw E(code, "the Digest charset must be the unquoted token UTF-8 (RFC 7616 sec. 3.3)");
|
|
25395
|
+
if (p.stale) {
|
|
25396
|
+
var sv = String(p.stale.value).toLowerCase();
|
|
25397
|
+
if (p.stale.quoted || sv !== "true" && sv !== "false") throw E(code, "the Digest stale directive must be the unquoted token true or false (RFC 7616 sec. 3.3)");
|
|
25398
|
+
}
|
|
25399
|
+
if (p.userhash) {
|
|
25400
|
+
var uhv = String(p.userhash.value).toLowerCase();
|
|
25401
|
+
if (p.userhash.quoted || uhv !== "true" && uhv !== "false") throw E(code, "the Digest userhash directive must be the unquoted token true or false (RFC 7616 sec. 3.3)");
|
|
25402
|
+
}
|
|
25403
|
+
if (p.opaque && !p.opaque.quoted) throw E(code, "the Digest opaque directive must be a quoted-string (RFC 7616 sec. 3.3)");
|
|
25404
|
+
return {
|
|
25405
|
+
scheme: "Digest",
|
|
25406
|
+
realm: p.realm.value,
|
|
25407
|
+
nonce: p.nonce.value,
|
|
25408
|
+
qop,
|
|
25409
|
+
domain,
|
|
25410
|
+
algorithm: p.algorithm ? p.algorithm.value.toUpperCase() : "MD5",
|
|
25411
|
+
opaque: p.opaque ? p.opaque.value : null,
|
|
25412
|
+
stale: !!(p.stale && String(p.stale.value).toLowerCase() === "true"),
|
|
25413
|
+
userhash: !!(p.userhash && String(p.userhash.value).toLowerCase() === "true"),
|
|
25414
|
+
charset: p.charset ? p.charset.value : null
|
|
25415
|
+
};
|
|
25416
|
+
}
|
|
25417
|
+
function parseChallenge(www, E, code, policy) {
|
|
25418
|
+
var pol = policy || {};
|
|
25419
|
+
var codes = pol.codes || DEFAULT_CODES;
|
|
25420
|
+
var preferStale = !!pol.preferStale;
|
|
25421
|
+
var raw = String(www == null ? "" : www);
|
|
25422
|
+
if (raw.length > constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES) throw E(code, "the WWW-Authenticate header exceeds the " + constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES + "-byte cap");
|
|
25423
|
+
var challenges = _splitChallenges(raw);
|
|
25424
|
+
var best = null, bestUsable = false, bestApplicable = false, bestStale = false, bestRank = -1, sawDigest = false;
|
|
25425
|
+
for (var i = 0; i < challenges.length; i++) {
|
|
25426
|
+
if (challenges[i].scheme.toLowerCase() !== "digest") continue;
|
|
25427
|
+
sawDigest = true;
|
|
25428
|
+
var parsed;
|
|
25429
|
+
try {
|
|
25430
|
+
parsed = _validateDigest(challenges[i].paramText, E, code);
|
|
25431
|
+
} catch (_e) {
|
|
25432
|
+
continue;
|
|
25433
|
+
}
|
|
25434
|
+
var alg = ALGS[parsed.algorithm];
|
|
25435
|
+
var rank = alg ? alg.rank : 0;
|
|
25436
|
+
var usable = _rejection(parsed, pol, codes) === null;
|
|
25437
|
+
var applicable = pol.requestTarget === void 0 ? true : inProtectionSpace(parsed, pol.requestOrigin, pol.requestTarget);
|
|
25438
|
+
var retryable = preferStale && !!parsed.stale && parsed.nonce !== pol.priorNonce && parsed.realm === pol.priorRealm;
|
|
25439
|
+
if (best === null || usable && !bestUsable || usable === bestUsable && applicable && !bestApplicable || usable === bestUsable && applicable === bestApplicable && retryable && !bestStale || usable === bestUsable && applicable === bestApplicable && retryable === bestStale && rank > bestRank) {
|
|
25440
|
+
best = parsed;
|
|
25441
|
+
bestUsable = usable;
|
|
25442
|
+
bestApplicable = applicable;
|
|
25443
|
+
bestStale = retryable;
|
|
25444
|
+
bestRank = rank;
|
|
25445
|
+
}
|
|
25446
|
+
}
|
|
25447
|
+
if (best) return best;
|
|
25448
|
+
if (sawDigest) throw E(code, "no valid Digest challenge: every Digest offer was malformed (missing realm / nonce or a bad directive, RFC 7616 sec. 3.3)");
|
|
25449
|
+
return null;
|
|
25450
|
+
}
|
|
25451
|
+
function _octets(s, charset) {
|
|
25452
|
+
var v = s == null ? "" : String(s);
|
|
25453
|
+
return charset && String(charset).toUpperCase() === "UTF-8" ? Buffer.from(v, "utf8").toString("latin1") : v;
|
|
25454
|
+
}
|
|
25455
|
+
function _hasNonAscii(s) {
|
|
25456
|
+
s = String(s == null ? "" : s);
|
|
25457
|
+
for (var i = 0; i < s.length; i++) {
|
|
25458
|
+
if (s.charCodeAt(i) > 127) return true;
|
|
25459
|
+
}
|
|
25460
|
+
return false;
|
|
25461
|
+
}
|
|
25462
|
+
function _pctEncodeUtf8(s) {
|
|
25463
|
+
var bytes = Buffer.from(String(s == null ? "" : s), "utf8");
|
|
25464
|
+
var out = "";
|
|
25465
|
+
for (var i = 0; i < bytes.length; i++) {
|
|
25466
|
+
var b = bytes[i];
|
|
25467
|
+
if (b >= 48 && b <= 57 || b >= 65 && b <= 90 || b >= 97 && b <= 122 || b === 33 || b === 35 || b === 36 || b === 38 || b === 43 || b === 45 || b === 46 || b === 94 || b === 95 || b === 96 || b === 124 || b === 126) {
|
|
25468
|
+
out += String.fromCharCode(b);
|
|
25469
|
+
} else {
|
|
25470
|
+
out += "%" + (b < 16 ? "0" : "") + b.toString(16).toUpperCase();
|
|
25471
|
+
}
|
|
25472
|
+
}
|
|
25473
|
+
return out;
|
|
25474
|
+
}
|
|
25475
|
+
function _rejection(challenge, pol, codes) {
|
|
25476
|
+
var alg = ALGS[challenge.algorithm];
|
|
25477
|
+
if (!alg) return { code: codes.unsupportedAlgorithm, msg: "unsupported Digest algorithm " + challenge.algorithm + " (supported: SHA-512-256, SHA-256, MD5)" };
|
|
25478
|
+
if (alg.hash === "md5" && !pol.allowMD5) return { code: codes.weakAlgorithm, msg: "MD5 Digest refused by default (RFC 7616 sec. 3.4 discourages it); set opts.auth.allowMD5 for legacy interop" };
|
|
25479
|
+
if (challenge.qop.length === 0) {
|
|
25480
|
+
if (!pol.allowLegacyQop) return { code: codes.noQop, msg: "a no-qop (RFC 2069) Digest challenge is refused by default; set opts.auth.allowLegacyQop for legacy interop" };
|
|
25481
|
+
} else if (challenge.qop.indexOf("auth") === -1 && challenge.qop.indexOf("auth-int") === -1) {
|
|
25482
|
+
return { code: codes.badChallenge, msg: "the Digest qop offered no member this client supports (auth / auth-int)" };
|
|
25483
|
+
}
|
|
25484
|
+
return null;
|
|
25485
|
+
}
|
|
25486
|
+
function answer(challenge, params, E) {
|
|
25487
|
+
var pol = params.policy || {};
|
|
25488
|
+
var codes = pol.codes || DEFAULT_CODES;
|
|
25489
|
+
var rej = _rejection(challenge, pol, codes);
|
|
25490
|
+
if (rej) throw E(rej.code, rej.msg);
|
|
25491
|
+
var alg = ALGS[challenge.algorithm];
|
|
25492
|
+
var useQop = challenge.qop.indexOf("auth") !== -1 ? "auth" : challenge.qop.indexOf("auth-int") !== -1 ? "auth-int" : null;
|
|
25493
|
+
var cnonce = String((params.rng || function() {
|
|
25494
|
+
return crypto.randomBytes(18).toString("base64");
|
|
25495
|
+
})());
|
|
25496
|
+
var realm = challenge.realm, nonce = challenge.nonce;
|
|
25497
|
+
var isUtf8 = String(challenge.charset || "").toUpperCase() === "UTF-8";
|
|
25498
|
+
var pUser = params.username == null ? "" : String(params.username);
|
|
25499
|
+
var pPass = params.password == null ? "" : String(params.password);
|
|
25500
|
+
if (isUtf8) {
|
|
25501
|
+
pUser = pUser.normalize("NFC");
|
|
25502
|
+
pPass = pPass.normalize("NFC");
|
|
25503
|
+
}
|
|
25504
|
+
var user = _octets(pUser, challenge.charset);
|
|
25505
|
+
var pass = _octets(pPass, challenge.charset);
|
|
25506
|
+
var HA1 = H(alg.hash, user + ":" + realm + ":" + pass);
|
|
25507
|
+
if (alg.sess) HA1 = H(alg.hash, HA1 + ":" + nonce + ":" + cnonce);
|
|
25508
|
+
var A2 = useQop === "auth-int" ? params.method + ":" + params.uri + ":" + H(alg.hash, params.body == null ? "" : params.body) : params.method + ":" + params.uri;
|
|
25509
|
+
var HA2 = H(alg.hash, A2);
|
|
25510
|
+
var ncNum = typeof params.nc === "number" && params.nc >= 1 ? Math.floor(params.nc) : 1;
|
|
25511
|
+
var nc = ("0000000" + ncNum.toString(16)).slice(-8);
|
|
25512
|
+
var response = useQop ? KD(alg.hash, HA1, nonce + ":" + nc + ":" + cnonce + ":" + useQop + ":" + HA2) : KD(alg.hash, HA1, nonce + ":" + HA2);
|
|
25513
|
+
var parts;
|
|
25514
|
+
if (!challenge.userhash && isUtf8 && _hasNonAscii(pUser)) {
|
|
25515
|
+
parts = ["username*=UTF-8''" + _pctEncodeUtf8(pUser)];
|
|
25516
|
+
} else {
|
|
25517
|
+
var sentUser = challenge.userhash ? H(alg.hash, _octets(pUser, challenge.charset) + ":" + realm) : _octets(pUser, challenge.charset);
|
|
25518
|
+
parts = ["username=" + _qstr(sentUser)];
|
|
25519
|
+
}
|
|
25520
|
+
parts.push("realm=" + _qstr(realm), "nonce=" + _qstr(nonce), "uri=" + _qstr(params.uri), "algorithm=" + challenge.algorithm);
|
|
25521
|
+
if (useQop) {
|
|
25522
|
+
parts.push("qop=" + useQop);
|
|
25523
|
+
parts.push("nc=" + nc);
|
|
25524
|
+
}
|
|
25525
|
+
if (useQop || alg.sess) {
|
|
25526
|
+
parts.push("cnonce=" + _qstr(cnonce));
|
|
25527
|
+
}
|
|
25528
|
+
parts.push("response=" + _qstr(response));
|
|
25529
|
+
if (challenge.opaque != null) parts.push("opaque=" + _qstr(challenge.opaque));
|
|
25530
|
+
if (challenge.userhash) parts.push("userhash=true");
|
|
25531
|
+
return "Digest " + parts.join(", ");
|
|
25532
|
+
}
|
|
25533
|
+
function _domainEntry(d) {
|
|
25534
|
+
d = String(d == null ? "" : d);
|
|
25535
|
+
if (d.charAt(0) === "/") return { origin: null, full: d };
|
|
25536
|
+
try {
|
|
25537
|
+
var u = new URL(d);
|
|
25538
|
+
return { origin: u.origin.toLowerCase(), full: (u.pathname || "/") + (u.search || "") };
|
|
25539
|
+
} catch (_e) {
|
|
25540
|
+
return null;
|
|
25541
|
+
}
|
|
25542
|
+
}
|
|
25543
|
+
function inProtectionSpace(challenge, requestOrigin, requestPathAndSearch) {
|
|
25544
|
+
var domain = challenge && challenge.domain;
|
|
25545
|
+
if (!domain || !domain.length) return true;
|
|
25546
|
+
var origin = String(requestOrigin == null ? "" : requestOrigin).toLowerCase();
|
|
25547
|
+
var full = String(requestPathAndSearch == null ? "" : requestPathAndSearch);
|
|
25548
|
+
for (var i = 0; i < domain.length; i++) {
|
|
25549
|
+
var e = _domainEntry(domain[i]);
|
|
25550
|
+
if (e === null) continue;
|
|
25551
|
+
if (e.origin !== null && e.origin !== origin) continue;
|
|
25552
|
+
if (full.indexOf(e.full) === 0) return true;
|
|
25553
|
+
}
|
|
25554
|
+
return false;
|
|
25555
|
+
}
|
|
25556
|
+
module2.exports = { parseChallenge, answer, inProtectionSpace };
|
|
25557
|
+
}
|
|
25558
|
+
});
|
|
25559
|
+
|
|
25230
25560
|
// node_modules/@blamejs/pki/lib/est.js
|
|
25231
25561
|
var require_est = __commonJS({
|
|
25232
25562
|
"node_modules/@blamejs/pki/lib/est.js"(exports2, module2) {
|
|
@@ -25237,10 +25567,13 @@ var require_est = __commonJS({
|
|
|
25237
25567
|
var cms = require_schema_cms();
|
|
25238
25568
|
var x509 = require_schema_x509();
|
|
25239
25569
|
var pkcs8 = require_schema_pkcs8();
|
|
25570
|
+
var key = require_key();
|
|
25240
25571
|
var csr = require_schema_csr();
|
|
25572
|
+
var csrattrsFmt = require_schema_csrattrs();
|
|
25241
25573
|
var frameworkError = require_framework_error();
|
|
25242
25574
|
var guard = require_guard_all();
|
|
25243
25575
|
var httpTransport = require_http_transport();
|
|
25576
|
+
var httpDigest = require_http_digest();
|
|
25244
25577
|
var retryAfter = require_http_retry_after();
|
|
25245
25578
|
var EstError = frameworkError.EstError;
|
|
25246
25579
|
function E(code, message, cause) {
|
|
@@ -25311,7 +25644,7 @@ var require_est = __commonJS({
|
|
|
25311
25644
|
var rawHeaders = seg.slice(0, sep);
|
|
25312
25645
|
var partBody = seg.slice(sep).replace(/^(\r?\n){2}/, "").replace(/\r?\n$/, "");
|
|
25313
25646
|
var headers = {};
|
|
25314
|
-
rawHeaders.split(/\r?\n/).forEach(function(line) {
|
|
25647
|
+
rawHeaders.replace(/\r?\n(?=[ \t])/g, "").split(/\r?\n/).forEach(function(line) {
|
|
25315
25648
|
var col = line.indexOf(":");
|
|
25316
25649
|
if (col > 0) headers[line.slice(0, col).trim().toLowerCase()] = line.slice(col + 1).trim();
|
|
25317
25650
|
});
|
|
@@ -25338,18 +25671,22 @@ var require_est = __commonJS({
|
|
|
25338
25671
|
}
|
|
25339
25672
|
return null;
|
|
25340
25673
|
}
|
|
25341
|
-
function _recipientKeyIds(recipientInfos) {
|
|
25674
|
+
function _recipientKeyIds(recipientInfos, kind) {
|
|
25342
25675
|
var ids = [];
|
|
25343
25676
|
function push(v) {
|
|
25344
25677
|
if (Buffer.isBuffer(v)) ids.push(v);
|
|
25345
25678
|
}
|
|
25346
25679
|
(recipientInfos || []).forEach(function(r) {
|
|
25347
|
-
if (
|
|
25348
|
-
|
|
25349
|
-
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
|
|
25680
|
+
if (kind !== "symmetric") {
|
|
25681
|
+
if (r.rid) push(r.rid.subjectKeyIdentifier);
|
|
25682
|
+
if (r.kemri && r.kemri.rid) push(r.kemri.rid.subjectKeyIdentifier);
|
|
25683
|
+
(r.recipientEncryptedKeys || []).forEach(function(rek) {
|
|
25684
|
+
if (rek.rid) push(rek.rid.subjectKeyIdentifier);
|
|
25685
|
+
});
|
|
25686
|
+
}
|
|
25687
|
+
if (kind !== "asymmetric") {
|
|
25688
|
+
if (r.kekid) push(r.kekid.keyIdentifier);
|
|
25689
|
+
}
|
|
25353
25690
|
});
|
|
25354
25691
|
return ids;
|
|
25355
25692
|
}
|
|
@@ -25368,13 +25705,17 @@ var require_est = __commonJS({
|
|
|
25368
25705
|
return out;
|
|
25369
25706
|
}
|
|
25370
25707
|
function _recipientMatches(recipientInfos, opts) {
|
|
25371
|
-
if (Buffer.isBuffer(opts.expectedRecipientKeyId) && _recipientKeyIds(recipientInfos).some(function(id) {
|
|
25708
|
+
if (Buffer.isBuffer(opts.expectedRecipientKeyId) && _recipientKeyIds(recipientInfos, opts.expectedRecipientKind).some(function(id) {
|
|
25372
25709
|
return id.equals(opts.expectedRecipientKeyId);
|
|
25373
25710
|
})) return true;
|
|
25374
25711
|
var ias = opts.expectedRecipientIssuerSerial;
|
|
25375
25712
|
if (ias && Buffer.isBuffer(ias.issuer) && ias.serialNumber != null) {
|
|
25376
|
-
var
|
|
25377
|
-
if (
|
|
25713
|
+
var sn = ias.serialNumber, want;
|
|
25714
|
+
if (typeof sn === "bigint" && sn >= 0n) want = sn;
|
|
25715
|
+
else if (typeof sn === "number" && Number.isSafeInteger(sn) && sn >= 0) want = BigInt(sn);
|
|
25716
|
+
else if (typeof sn === "string" && /^[0-9]+$/.test(sn)) want = BigInt(sn);
|
|
25717
|
+
else throw E("est/bad-input", "expectedRecipientIssuerSerial.serialNumber must be a NON-NEGATIVE bigint, a safe non-negative integer, or a decimal digit string (a certificate serial is non-negative, RFC 5280 sec. 4.1.2.2)");
|
|
25718
|
+
if (opts.expectedRecipientKind !== "symmetric" && _recipientIssuerSerials(recipientInfos).some(function(r) {
|
|
25378
25719
|
return r.issuer.equals(ias.issuer) && r.serialNumber === want;
|
|
25379
25720
|
})) return true;
|
|
25380
25721
|
}
|
|
@@ -25428,6 +25769,7 @@ var require_est = __commonJS({
|
|
|
25428
25769
|
}
|
|
25429
25770
|
if (!keyPart || !certPart) throw E("est/bad-multipart", "a serverkeygen response needs one key part and one certificate part");
|
|
25430
25771
|
if (opts.requestedEncryption && !encrypted) throw E("est/expected-encrypted-key", "encryption was requested but the private-key part is cleartext (RFC 7030 sec. 4.4.2)");
|
|
25772
|
+
if (opts.requestedEncryption === false && encrypted) throw E("est/unexpected-encrypted-key", "the server returned an encrypted key but the CSR advertised no DecryptKeyIdentifier / AsymmetricDecryptKeyIdentifier to decrypt it (RFC 7030 sec. 4.4.2)");
|
|
25431
25773
|
var out = { certificates: parseCertsOnly(transferDecode(certPart.body)).certificates };
|
|
25432
25774
|
if (encrypted) {
|
|
25433
25775
|
var parsedKey = cms.parse(transferDecode(keyPart.body));
|
|
@@ -25439,7 +25781,9 @@ var require_est = __commonJS({
|
|
|
25439
25781
|
}
|
|
25440
25782
|
out.encryptedKey = parsedKey;
|
|
25441
25783
|
} else {
|
|
25442
|
-
|
|
25784
|
+
var keyDer = transferDecode(keyPart.body);
|
|
25785
|
+
out.privateKey = pkcs8.parse(keyDer);
|
|
25786
|
+
out.privateKeyDer = keyDer;
|
|
25443
25787
|
}
|
|
25444
25788
|
return out;
|
|
25445
25789
|
}
|
|
@@ -25625,13 +25969,38 @@ var require_est = __commonJS({
|
|
|
25625
25969
|
}
|
|
25626
25970
|
return resolved;
|
|
25627
25971
|
}
|
|
25628
|
-
function
|
|
25972
|
+
function _offersScheme(www, scheme) {
|
|
25629
25973
|
var s = String(www || "").replace(/"(?:[^"\\]|\\.)*"/g, '""');
|
|
25630
|
-
return
|
|
25974
|
+
return new RegExp("(?:^|,)\\s*" + scheme + "(?=[\\s,]|$)", "i").test(s);
|
|
25975
|
+
}
|
|
25976
|
+
function _authScheme(opts) {
|
|
25977
|
+
if (opts.auth && opts.auth.scheme != null) {
|
|
25978
|
+
var sc = String(opts.auth.scheme).toLowerCase();
|
|
25979
|
+
if (sc !== "basic" && sc !== "digest") throw E("est/bad-input", 'opts.auth.scheme must be "basic" or "digest"');
|
|
25980
|
+
return sc;
|
|
25981
|
+
}
|
|
25982
|
+
if (opts.username !== void 0 || opts.password !== void 0) return "basic";
|
|
25983
|
+
return null;
|
|
25984
|
+
}
|
|
25985
|
+
function _authUser(opts) {
|
|
25986
|
+
return opts.auth && opts.auth.username !== void 0 ? opts.auth.username : opts.username;
|
|
25987
|
+
}
|
|
25988
|
+
function _authPass(opts) {
|
|
25989
|
+
return opts.auth && opts.auth.password !== void 0 ? opts.auth.password : opts.password;
|
|
25990
|
+
}
|
|
25991
|
+
function _hasCreds(opts) {
|
|
25992
|
+
return _authUser(opts) !== void 0 || _authPass(opts) !== void 0;
|
|
25993
|
+
}
|
|
25994
|
+
var DIGEST_CODES = { unsupportedAlgorithm: "est/digest-unsupported-algorithm", weakAlgorithm: "est/digest-weak-algorithm", noQop: "est/digest-no-qop", badChallenge: "est/digest-bad-challenge" };
|
|
25995
|
+
function _digestPolicy(opts) {
|
|
25996
|
+
return { allowMD5: !!(opts.auth && opts.auth.allowMD5), allowLegacyQop: !!(opts.auth && opts.auth.allowLegacyQop), codes: DIGEST_CODES };
|
|
25631
25997
|
}
|
|
25632
25998
|
function _drive(method, url, body, headers, opts, transport, budgets) {
|
|
25633
25999
|
var redirects = 0;
|
|
25634
26000
|
var authTried = false;
|
|
26001
|
+
var staleRetries = 0;
|
|
26002
|
+
var authSpaces = 0;
|
|
26003
|
+
var lastDigestNonce = null;
|
|
25635
26004
|
var initialOrigin = url.origin;
|
|
25636
26005
|
function _tlsFor(u) {
|
|
25637
26006
|
var t = budgets.tls;
|
|
@@ -25644,10 +26013,22 @@ var require_est = __commonJS({
|
|
|
25644
26013
|
return t;
|
|
25645
26014
|
}
|
|
25646
26015
|
var authValue = null;
|
|
26016
|
+
var digestChallenge = null;
|
|
26017
|
+
var digestNcByNonce = /* @__PURE__ */ Object.create(null);
|
|
26018
|
+
var digestSent = false;
|
|
25647
26019
|
function _headersFor(u) {
|
|
25648
|
-
|
|
26020
|
+
digestSent = false;
|
|
26021
|
+
if (u.origin !== initialOrigin || !authValue && !digestChallenge) return headers;
|
|
25649
26022
|
var hh = Object.assign({}, headers);
|
|
25650
|
-
|
|
26023
|
+
if (digestChallenge) {
|
|
26024
|
+
if (!httpDigest.inProtectionSpace(digestChallenge, u.origin, u.pathname + u.search)) return headers;
|
|
26025
|
+
var nkey = digestChallenge.nonce;
|
|
26026
|
+
digestNcByNonce[nkey] = (digestNcByNonce[nkey] || 0) + 1;
|
|
26027
|
+
hh.authorization = httpDigest.answer(digestChallenge, { method, uri: u.pathname + u.search, username: _authUser(opts), password: _authPass(opts), body, nc: digestNcByNonce[nkey], policy: _digestPolicy(opts) }, E);
|
|
26028
|
+
digestSent = true;
|
|
26029
|
+
} else {
|
|
26030
|
+
hh.authorization = authValue;
|
|
26031
|
+
}
|
|
25651
26032
|
return hh;
|
|
25652
26033
|
}
|
|
25653
26034
|
function step() {
|
|
@@ -25675,12 +26056,39 @@ var require_est = __commonJS({
|
|
|
25675
26056
|
return step();
|
|
25676
26057
|
}
|
|
25677
26058
|
if (status === 401) {
|
|
25678
|
-
if (authTried) throw E("est/auth-required", "the server rejected the credentialed request (RFC 7030 sec. 3.2.3)");
|
|
25679
26059
|
if (url.origin !== initialOrigin) throw E("est/auth-required", "refusing to send HTTP credentials to a redirected origin (RFC 7030 sec. 3.6)");
|
|
25680
26060
|
var www = String(h["www-authenticate"] || "");
|
|
25681
|
-
if (
|
|
25682
|
-
|
|
25683
|
-
|
|
26061
|
+
if (www.length > constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES) throw E("est/auth-required", "the WWW-Authenticate header exceeds the " + constants.LIMITS.HTTP_AUTH_HEADER_MAX_BYTES + "-byte cap (RFC 7030 sec. 3.2.3)");
|
|
26062
|
+
var scheme = _authScheme(opts);
|
|
26063
|
+
if (scheme === null) throw E("est/auth-required", "the server requires HTTP authentication but no credentials were supplied (RFC 7030 sec. 3.2.3)");
|
|
26064
|
+
if (scheme === "digest") {
|
|
26065
|
+
if (!_offersScheme(www, "Digest")) throw E("est/auth-required", 'opts.auth.scheme is "digest" but the server offered no Digest challenge: ' + www);
|
|
26066
|
+
if (!_hasCreds(opts)) throw E("est/auth-required", "Digest authentication requires opts.auth.username / password (RFC 7030 sec. 3.2.3)");
|
|
26067
|
+
var selPol = _digestPolicy(opts);
|
|
26068
|
+
selPol.preferStale = digestSent;
|
|
26069
|
+
selPol.priorNonce = lastDigestNonce;
|
|
26070
|
+
selPol.priorRealm = digestChallenge && digestChallenge.realm;
|
|
26071
|
+
selPol.requestOrigin = url.origin;
|
|
26072
|
+
selPol.requestTarget = url.pathname + url.search;
|
|
26073
|
+
var ch = httpDigest.parseChallenge(www, E, "est/digest-bad-challenge", selPol);
|
|
26074
|
+
if (!ch) throw E("est/auth-required", "the server offered no usable Digest challenge: " + www);
|
|
26075
|
+
if (digestSent && digestChallenge && ch.realm === digestChallenge.realm) {
|
|
26076
|
+
if (!(ch.stale && ch.nonce !== lastDigestNonce && staleRetries < budgets.maxStaleRetries)) throw E("est/auth-required", "the server rejected the credentialed Digest request (RFC 7030 sec. 3.2.3)");
|
|
26077
|
+
staleRetries += 1;
|
|
26078
|
+
} else {
|
|
26079
|
+
if (authTried && authSpaces >= budgets.maxRedirects) throw E("est/auth-required", "the server demanded Digest authentication for too many distinct protection spaces (RFC 7616 sec. 3.3)");
|
|
26080
|
+
if (authTried) authSpaces += 1;
|
|
26081
|
+
authTried = true;
|
|
26082
|
+
staleRetries = 0;
|
|
26083
|
+
}
|
|
26084
|
+
lastDigestNonce = ch.nonce;
|
|
26085
|
+
digestChallenge = ch;
|
|
26086
|
+
return step();
|
|
26087
|
+
}
|
|
26088
|
+
if (authTried) throw E("est/auth-required", "the server rejected the credentialed request (RFC 7030 sec. 3.2.3)");
|
|
26089
|
+
if (!_offersScheme(www, "Basic")) throw E("est/auth-required", "the server requires an unsupported HTTP authentication scheme (only Basic and Digest are supported): " + www);
|
|
26090
|
+
if (!_hasCreds(opts)) throw E("est/auth-required", "the server requires HTTP authentication but no credentials were supplied (RFC 7030 sec. 3.2.3)");
|
|
26091
|
+
authValue = "Basic " + Buffer.from((_authUser(opts) || "") + ":" + (_authPass(opts) || ""), "utf8").toString("base64");
|
|
25684
26092
|
authTried = true;
|
|
25685
26093
|
return step();
|
|
25686
26094
|
}
|
|
@@ -25709,8 +26117,10 @@ var require_est = __commonJS({
|
|
|
25709
26117
|
tls: _tlsForRequest(opts),
|
|
25710
26118
|
timeout: guard.limits.cap(opts.timeout, "timeout", DEFAULT_TIMEOUT, { E, code: "est/bad-input", min: 1, max: MAX_TIMEOUT }),
|
|
25711
26119
|
maxResponseBytes: guard.limits.cap(opts.maxResponseBytes, "maxResponseBytes", constants.LIMITS.HTTP_MAX_RESPONSE_BYTES, { E, code: "est/bad-input", min: 1, max: constants.LIMITS.HTTP_MAX_RESPONSE_BYTES }),
|
|
25712
|
-
maxRedirects: guard.limits.cap(opts.maxRedirects, "maxRedirects", 5, { E, code: "est/bad-input", min: 0, max: 32 })
|
|
26120
|
+
maxRedirects: guard.limits.cap(opts.maxRedirects, "maxRedirects", 5, { E, code: "est/bad-input", min: 0, max: 32 }),
|
|
26121
|
+
maxStaleRetries: guard.limits.cap(opts.auth && opts.auth.maxStaleRetries, "maxStaleRetries", 1, { E, code: "est/bad-input", min: 0, max: 8 })
|
|
25713
26122
|
};
|
|
26123
|
+
_authScheme(opts);
|
|
25714
26124
|
return _drive(method, url, body, Object.assign({}, headers), opts, transport, budgets);
|
|
25715
26125
|
}
|
|
25716
26126
|
function _certsResult(op, res, opts, csrSpki) {
|
|
@@ -25763,10 +26173,119 @@ var require_est = __commonJS({
|
|
|
25763
26173
|
return _enroll("simplereenroll", baseUrl, csrInput, opts);
|
|
25764
26174
|
});
|
|
25765
26175
|
}
|
|
26176
|
+
function _serverkeygenEncryptionFromCsr(csrDer) {
|
|
26177
|
+
var attrs = csr.parse(csrDer).attributes || [];
|
|
26178
|
+
var keyId = null, kind = null;
|
|
26179
|
+
for (var i = 0; i < attrs.length; i++) {
|
|
26180
|
+
if (attrs[i].type !== OID_DECRYPT_KEY_ID && attrs[i].type !== OID_ASYMM_DECRYPT_KEY_ID) continue;
|
|
26181
|
+
var thisKind = attrs[i].type === OID_ASYMM_DECRYPT_KEY_ID ? "asymmetric" : "symmetric";
|
|
26182
|
+
var vals = attrs[i].values || [];
|
|
26183
|
+
if (vals.length !== 1) throw E("est/bad-input", "a serverkeygen key-identifier attribute must carry exactly one value (RFC 7030 sec. 4.4.1)");
|
|
26184
|
+
var id;
|
|
26185
|
+
try {
|
|
26186
|
+
id = asn1.read.octetString(asn1.decode(vals[0]));
|
|
26187
|
+
} catch (e) {
|
|
26188
|
+
throw E("est/bad-input", "a serverkeygen key-identifier attribute value is not a valid OCTET STRING", e);
|
|
26189
|
+
}
|
|
26190
|
+
if (keyId !== null && !keyId.equals(id)) throw E("est/bad-input", "the CSR advertised two different serverkeygen key identifiers (RFC 7030 sec. 4.4.1)");
|
|
26191
|
+
if (kind !== null && kind !== thisKind) throw E("est/bad-input", "the CSR advertised both a symmetric (DecryptKeyIdentifier) and an asymmetric (AsymmetricDecryptKeyIdentifier) serverkeygen key -- the recipient mechanism is ambiguous (RFC 7030 sec. 4.4.1)");
|
|
26192
|
+
keyId = id;
|
|
26193
|
+
kind = thisKind;
|
|
26194
|
+
}
|
|
26195
|
+
return { requestedEncryption: keyId !== null, expectedRecipientKeyId: keyId, expectedRecipientKind: kind };
|
|
26196
|
+
}
|
|
26197
|
+
function _assertConfidentialCipher(res) {
|
|
26198
|
+
if (!res || !res.tls || !res.tls.cipher) return;
|
|
26199
|
+
var c = res.tls.cipher;
|
|
26200
|
+
var name = (String(c.name || "") + " " + String(c.standardName || "")).toUpperCase();
|
|
26201
|
+
if (/NULL|ANON|EXPORT|\bEXP[-_0-9]|\bA(EC)?DH\b/.test(name)) throw E("est/weak-cipher", "the serverkeygen channel negotiated a NULL / anonymous / EXPORT cipher (" + (c.standardName || c.name) + "), which cannot protect the delivered private key (RFC 7030 sec. 4.4)");
|
|
26202
|
+
}
|
|
26203
|
+
function _ciHeader(headers, name) {
|
|
26204
|
+
headers = headers || {};
|
|
26205
|
+
if (headers[name] !== void 0) return headers[name];
|
|
26206
|
+
var lname = name.toLowerCase(), keys = Object.keys(headers);
|
|
26207
|
+
for (var i = 0; i < keys.length; i++) {
|
|
26208
|
+
if (keys[i].toLowerCase() === lname) return headers[keys[i]];
|
|
26209
|
+
}
|
|
26210
|
+
return null;
|
|
26211
|
+
}
|
|
26212
|
+
async function _serverkeygenResult(res, opts, derived) {
|
|
26213
|
+
var verdict = classifyResponse(res.status, res.headers, res.body, { op: "serverkeygen", now: opts.now });
|
|
26214
|
+
if (verdict.status === "retry") return { retry: true, retryAfterSeconds: verdict.retryAfterSeconds, retryAfterDate: verdict.retryAfterDate };
|
|
26215
|
+
if (verdict.status !== "ok") throw E("est/http-error", "an EST serverkeygen response must be HTTP 200 or 202 (RFC 7030 sec. 4.4.2), got " + res.status);
|
|
26216
|
+
var bodyLen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
|
|
26217
|
+
if (bodyLen === 0) throw E("est/empty-body", "a 200 serverkeygen response carried an empty body (RFC 7030 sec. 4.4.2)");
|
|
26218
|
+
_assertConfidentialCipher(res);
|
|
26219
|
+
var out = parseServerKeygenResponse(res.body, _ciHeader(res.headers, "content-type"), {
|
|
26220
|
+
requestedEncryption: derived.requestedEncryption,
|
|
26221
|
+
expectedRecipientKeyId: derived.expectedRecipientKeyId,
|
|
26222
|
+
expectedRecipientKind: derived.expectedRecipientKind,
|
|
26223
|
+
expectedRecipientIssuerSerial: opts.expectedRecipientIssuerSerial
|
|
26224
|
+
});
|
|
26225
|
+
if (out.privateKeyDer) {
|
|
26226
|
+
var spki;
|
|
26227
|
+
try {
|
|
26228
|
+
spki = await key.publicFromPrivate(out.privateKeyDer);
|
|
26229
|
+
} catch (e) {
|
|
26230
|
+
throw E("est/key-cert-mismatch", "the cleartext server-generated private key's public half could not be derived to bind it to a returned certificate (RFC 7030 sec. 4.4.2)", e);
|
|
26231
|
+
}
|
|
26232
|
+
var bound = findIssuedCert(out.certificates, spki);
|
|
26233
|
+
if (!bound) throw E("est/key-cert-mismatch", "the cleartext server-generated private key matches no returned certificate's public key (RFC 7030 sec. 4.4.2)");
|
|
26234
|
+
if (findIssuedCert(out.certificates.filter(function(c) {
|
|
26235
|
+
return c !== bound;
|
|
26236
|
+
}), spki)) throw E("est/ambiguous-issued-cert", "more than one returned certificate carries the server-generated key; the issued certificate is ambiguous (RFC 7030 sec. 4.4.2)");
|
|
26237
|
+
delete out.privateKeyDer;
|
|
26238
|
+
}
|
|
26239
|
+
return out;
|
|
26240
|
+
}
|
|
26241
|
+
function serverkeygen(baseUrl, csrInput, opts) {
|
|
26242
|
+
opts = opts || {};
|
|
26243
|
+
return Promise.resolve().then(function() {
|
|
26244
|
+
var csrDer = _csrDer(csrInput);
|
|
26245
|
+
var derived = _serverkeygenEncryptionFromCsr(csrDer);
|
|
26246
|
+
if (opts.requestedEncryption !== void 0 && !!opts.requestedEncryption !== derived.requestedEncryption) throw E("est/bad-input", "opts.requestedEncryption (" + !!opts.requestedEncryption + ") contradicts the CSR's advertised key-encryption attribute (" + derived.requestedEncryption + ") (RFC 7030 sec. 4.4.1)");
|
|
26247
|
+
if (opts.expectedRecipientKeyId !== void 0) {
|
|
26248
|
+
if (!Buffer.isBuffer(opts.expectedRecipientKeyId)) throw E("est/bad-input", "opts.expectedRecipientKeyId must be a Buffer");
|
|
26249
|
+
if (derived.expectedRecipientKeyId && !opts.expectedRecipientKeyId.equals(derived.expectedRecipientKeyId)) throw E("est/bad-input", "opts.expectedRecipientKeyId contradicts the key identifier the CSR advertised (RFC 7030 sec. 4.4.1)");
|
|
26250
|
+
}
|
|
26251
|
+
if (opts.expectedRecipientIssuerSerial != null) {
|
|
26252
|
+
var eis = opts.expectedRecipientIssuerSerial;
|
|
26253
|
+
if (typeof eis !== "object" || Buffer.isBuffer(eis) || !Buffer.isBuffer(eis.issuer)) throw E("est/bad-input", "opts.expectedRecipientIssuerSerial must be { issuer: Buffer, serialNumber }");
|
|
26254
|
+
var s = eis.serialNumber;
|
|
26255
|
+
if (!(typeof s === "bigint" && s >= 0n || typeof s === "number" && Number.isSafeInteger(s) && s >= 0 || typeof s === "string" && /^[0-9]+$/.test(s))) throw E("est/bad-input", "opts.expectedRecipientIssuerSerial.serialNumber must be a NON-NEGATIVE bigint, a safe non-negative integer, or a decimal digit string (a certificate serial is non-negative, RFC 5280 sec. 4.1.2.2)");
|
|
26256
|
+
}
|
|
26257
|
+
if ((opts.expectedRecipientKeyId !== void 0 || opts.expectedRecipientIssuerSerial != null) && !derived.requestedEncryption) {
|
|
26258
|
+
throw E("est/bad-input", "a recipient expectation (expectedRecipientKeyId / expectedRecipientIssuerSerial) implies an encrypted key, but the CSR advertised no DecryptKeyIdentifier / AsymmetricDecryptKeyIdentifier attribute (RFC 7030 sec. 4.4.1)");
|
|
26259
|
+
}
|
|
26260
|
+
return _client("serverkeygen", "POST", baseUrl, transferEncode(csrDer), { accept: "multipart/mixed", "content-type": "application/pkcs10" }, opts).then(function(res) {
|
|
26261
|
+
return _serverkeygenResult(res, opts, derived);
|
|
26262
|
+
});
|
|
26263
|
+
});
|
|
26264
|
+
}
|
|
26265
|
+
function _csrattrsResult(res, opts) {
|
|
26266
|
+
var verdict = classifyResponse(res.status, res.headers, res.body, { op: "csrattrs", now: opts.now });
|
|
26267
|
+
if (verdict.status === "none-available") return { available: false, attrs: null };
|
|
26268
|
+
if (verdict.status === "retry") throw E("est/http-error", "a /csrattrs response must be HTTP 200, 204, or 404, not 202 (RFC 7030 sec. 4.5.2)");
|
|
26269
|
+
if (verdict.status !== "ok") throw E("est/http-error", "an EST csrattrs response must be HTTP 200 / 204 / 404 (RFC 7030 sec. 4.5.2), got " + res.status);
|
|
26270
|
+
var bodyLen = Buffer.isBuffer(res.body) ? res.body.length : Buffer.byteLength(String(res.body == null ? "" : res.body), "utf8");
|
|
26271
|
+
if (bodyLen === 0) throw E("est/empty-body", "a 200 csrattrs response carried an empty body (RFC 7030 sec. 4.5.2)");
|
|
26272
|
+
var attrs = csrattrsFmt.parse(transferDecode(res.body));
|
|
26273
|
+
return { available: true, attrs, plan: buildEnrollAttributes(attrs) };
|
|
26274
|
+
}
|
|
26275
|
+
function csrattrs(baseUrl, opts) {
|
|
26276
|
+
opts = opts || {};
|
|
26277
|
+
return Promise.resolve().then(function() {
|
|
26278
|
+
return _client("csrattrs", "GET", baseUrl, null, { accept: "application/csrattrs" }, opts);
|
|
26279
|
+
}).then(function(res) {
|
|
26280
|
+
return _csrattrsResult(res, opts);
|
|
26281
|
+
});
|
|
26282
|
+
}
|
|
25766
26283
|
module2.exports = {
|
|
25767
26284
|
cacerts,
|
|
25768
26285
|
simpleenroll,
|
|
25769
26286
|
simplereenroll,
|
|
26287
|
+
serverkeygen,
|
|
26288
|
+
csrattrs,
|
|
25770
26289
|
transferDecode,
|
|
25771
26290
|
transferEncode,
|
|
25772
26291
|
splitMultipartMixed,
|
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.5",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:c76abfd3-3a0a-4021-9a10-644df6ec17a3",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-08-
|
|
8
|
+
"timestamp": "2026-08-01T22:29:48.972Z",
|
|
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.18.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.18.5",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.18.
|
|
25
|
+
"version": "0.18.5",
|
|
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.18.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.18.5",
|
|
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.18.
|
|
57
|
+
"ref": "@blamejs/core@0.18.5",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|