@blamejs/core 0.15.58 → 0.15.60
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/lib/middleware/require-step-up.js +40 -1
- package/lib/network-tls.js +97 -5
- 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.15.x
|
|
10
10
|
|
|
11
|
+
- v0.15.60 (2026-06-29) — **`requireStepUp` binds the elevation grant to the authenticated principal, refusing a grant minted for a different user (cross-user step-up replay).** The b.middleware.requireStepUp gate accepts an operator-issued step-up elevation grant from the X-Step-Up-Grant header and verifies it with b.auth.stepUp.grant.verify. An elevation grant carries the subject it was minted for (payload.sub), but the middleware verified only the grant's signature, expiry, and scope — never that the grant's subject matched the request's authenticated principal. A grant minted for one user (and then leaked through a shared cache, a log line, a referrer, or a shared device) therefore satisfied the step-up requirement for ANY other authenticated user who presented it, elevating their session to the granted assurance level without ever completing a step-up ceremony. requireStepUp now passes the resolved principal as the grant's required subject, so the grant satisfies step-up only for the user it was issued to. The principal is resolved from whichever shape the authenticator populated — a session's req.user.id / req.user.userId, or the JWT subject (req.user.claims.sub / req.user.sub) set by bearerAuth with an external verifier — so a grant legitimately minted for a JWT subject still binds. A request with no resolvable principal cannot bind the grant and falls through to the claims-based challenge. **Security:** *Step-up elevation grants are bound to the authenticated principal* — requireStepUp's grant path called b.auth.stepUp.grant.verify with only the grant scope, not the subject, so any holder of a valid, unexpired, scope-matching elevation grant passed the step-up gate regardless of which user the request was authenticated as — a leaked or shared grant elevated a different user's session (cross-user step-up replay). The grant already binds a subject at mint time and the verifier supports a subject check; the middleware now supplies the request's principal as the required subject, refusing a grant whose subject does not match. The principal is read from whichever field the authenticator set — a session's id/userId or the JWT subject (claims.sub / sub) from bearerAuth's external verifier — so a grant minted for any of those binds correctly. A request with no authenticated principal cannot bind a grant and is handled by the normal claims-based step-up challenge. The grant verifier's signature/expiry/scope/jti-revocation checks are unchanged.
|
|
12
|
+
|
|
13
|
+
- v0.15.59 (2026-06-29) — **OCSP response validation binds the response to the certificate's issuer (issuerNameHash + issuerKeyHash), not the serial number alone, refusing a wrong-issuer "good".** An OCSP SingleResponse identifies the certificate it covers by a CertID of (hashAlgorithm, issuerNameHash, issuerKeyHash, serialNumber) — RFC 6960 §4.1.1. b.network.tls.ocsp.evaluate matched a response to the certificate under validation by the serial number alone and never compared the CertID's issuer hashes. Because a serial number is unique only within one issuer, a responder key that serves more than one issuer identity — a delegated OCSP responder, or a CA key spanning issuers — could have a signed "good" response for serial-S under issuer-Y accepted as proof for the serial-S certificate under issuer-X. The evaluator now recomputes the expected issuerNameHash and issuerKeyHash from the issuer certificate and refuses a response whose CertID issuer hashes do not match. b.network.tls.ocsp.fetch supplies the issuer certificate automatically (its issuerPem is the leaf's issuer); ocsp.requireGood and direct ocsp.evaluate callers pass the issuer cert explicitly as the new issuerCertDer (requireGood's issuerPem may be a delegated OCSP responder rather than the issuer, so it is not used as the issuer), and a response with no issuer certificate available stays bound on serial plus signature as before. **Security:** *OCSP evaluate binds the response CertID to the issuer, not the serial alone (RFC 6960 §4.1.1)* — b.network.tls.ocsp.evaluate selected the matching SingleResponse by normalized serial number only, ignoring the CertID's issuerNameHash and issuerKeyHash. Since serials are unique only per issuer, a "good" response signed by a key that also serves a different issuer (a delegated id-kp-OCSPSigning responder, or a shared CA key) could be replayed as proof for a same-serial certificate under another issuer. evaluate now recomputes the expected issuerNameHash = Hash(issuer DN) and issuerKeyHash = Hash(issuer public key) under the CertID's own hash algorithm and refuses the response if either differs ("wrong-issuer response"). b.network.tls.ocsp.fetch forwards the issuer certificate automatically (its issuerPem is the leaf's issuer); ocsp.requireGood and a direct ocsp.evaluate caller enable the binding by passing issuerCertDer (the issuer cert DER) — requireGood's issuerPem may be a delegated OCSP responder, so the issuer cert is taken explicitly rather than derived from it — and a call without an issuer certificate retains the prior serial-plus-signature binding.
|
|
14
|
+
|
|
11
15
|
- v0.15.58 (2026-06-29) — **File-upload content-safety scanning now also runs the gate for a file's magic-byte-detected type, so a mislabeled file can't dodge the scanner for its real type by choosing the extension.** b.fileUpload selected the per-extension content-safety gate purely from the upload's filename extension, which the uploader controls. A file whose magic bytes identify one type but whose name carries another extension (e.g. a PDF named photo.png) was therefore scanned by the gate for the named extension — or, when no gate was registered for that extension, not scanned at all — so an uploader could dodge the scanner configured for the file's real type by renaming it. When a fileType detector is wired, finalize now also runs the content-safety gate for the type the magic bytes identify, in addition to the filename-extension gate, so the scanner for the real type runs even under a mismatched name. Magic-byte-less text formats (HTML, SVG, CSV) cannot be classified this way; that residual is covered by serving uploads with an explicit Content-Type plus X-Content-Type-Options: nosniff and by registering a content-safety gate for every accepted extension. **Security:** *Content-safety gate selection follows the sniffed type, not just the filename extension* — finalize chose the content-safety gate from nodePath.extname(filename), so a file's real type could be hidden behind a chosen extension: a PDF named photo.png ran the .png gate (or, with no .png gate, skipped scanning) and never reached the .pdf scanner. When opts.fileType is wired, finalize now detects the magic-byte type and, if it differs from the filename extension and a gate is registered for it, runs that gate too (alongside the filename-extension gate), refusing or sanitizing per the gate's decision. Existing behavior is unchanged when no fileType detector is wired or the detected type matches the extension. Formats without magic bytes (HTML/SVG/CSV/plain text) remain undetectable by content sniffing — defend those by serving stored files with a fixed Content-Type and X-Content-Type-Options: nosniff, and by registering a content-safety gate for each accepted extension.
|
|
12
16
|
|
|
13
17
|
- v0.15.57 (2026-06-29) — **The RFC 9421 HTTP-message-signature verifier now enforces a required-coverage floor by default, refusing a signature whose covered set omits `@method` / `@target-uri` (and `content-digest` for bodied requests).** b.crypto.httpSig.verify built the signature base entirely from the covered-component list carried in the message's own Signature-Input header and never checked that the signature actually covered the security-relevant parts of the request (RFC 9421 §3.2). A signature covering only @authority therefore verified even after the method, target URI, or body were changed — so a captured signature could be replayed across a different method and path, or a request body swapped, under an otherwise-valid signature. verify now refuses a signature whose covered set omits a required component, returning { valid: false, reason: "missing-required-component", missing: [...] } before the cryptographic check. By default (security-on, not opt-in) it requires @method and @target-uri on every request, plus content-digest when the request carries a body; an operator can pass requiredComponents to assert an exact set, or requiredComponents: [] to waive the floor (the signature itself is still verified). **Security:** *httpSig.verify enforces a required-coverage floor (RFC 9421 §3.2)* — The verifier trusted the covered-component list from the message's Signature-Input header without requiring that any particular component be covered, so an under-covered signature (e.g. covering only @authority) verified while the method, target URI, and body were free to change — a captured signature replayed across method+path, or a swapped body, passed verification. verify now refuses a signature missing a required component with reason "missing-required-component" (and a missing[] list) before the crypto check. The default floor is @method + @target-uri on every request plus content-digest for bodied requests; requiredComponents overrides it explicitly, and requiredComponents: [] waives the floor for callers that intentionally sign a narrower set (the signature itself is still verified). **Migration:** *Signers must cover @method + @target-uri (and content-digest for bodied requests)* — If you verify HTTP message signatures with b.crypto.httpSig.verify, signatures whose covered set omits @method or @target-uri (or content-digest on a request with a body) now fail with reason "missing-required-component". Broaden the signer's covered set to include them (recommended), pass requiredComponents to assert the exact components your application requires, or pass requiredComponents: [] to keep the prior behavior of accepting whatever the signer covered. Verifying a fully-covered signature is unchanged.
|
|
@@ -64,6 +64,30 @@ function _defaultGetClaims(req) {
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
// Resolve the authenticated principal an elevation grant must be bound to.
|
|
68
|
+
// The principal subject lives under different keys depending on the
|
|
69
|
+
// authenticator: a session populates req.user.id / req.user.userId, while
|
|
70
|
+
// bearerAuth with an external JWT verifier (auth.jwt.verifyExternal)
|
|
71
|
+
// populates req.user.claims.sub (or a flattened req.user.sub) — the same
|
|
72
|
+
// shapes _defaultGetClaims reads. Resolving all of them keeps a grant
|
|
73
|
+
// legitimately minted for a claims.sub principal from being silently
|
|
74
|
+
// dropped to a 401. Returns undefined when no principal is resolvable, in
|
|
75
|
+
// which case the caller refuses the grant path (fail-closed: an unbound or
|
|
76
|
+
// mismatched subject can only narrow what a grant satisfies, never widen).
|
|
77
|
+
function _resolveStepUpPrincipal(req) {
|
|
78
|
+
if (!req || typeof req !== "object" || !req.user || typeof req.user !== "object") {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
var u = req.user;
|
|
82
|
+
if (u.id != null) return u.id;
|
|
83
|
+
if (u.userId != null) return u.userId;
|
|
84
|
+
if (u.claims && typeof u.claims === "object" && u.claims.sub != null) {
|
|
85
|
+
return u.claims.sub;
|
|
86
|
+
}
|
|
87
|
+
if (u.sub != null) return u.sub;
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
67
91
|
function _writeChallenge(res, challenge, body, statusCode) {
|
|
68
92
|
if (res.headersSent) return;
|
|
69
93
|
var json = JSON.stringify(body);
|
|
@@ -159,7 +183,22 @@ function create(opts) {
|
|
|
159
183
|
if (typeof grantToken === "string" && grantToken.length > 0) {
|
|
160
184
|
var verifyOpts = {};
|
|
161
185
|
if (grantScope) verifyOpts.scope = grantScope;
|
|
162
|
-
|
|
186
|
+
// Bind the grant to the authenticated principal: an elevation grant
|
|
187
|
+
// carries its subject (payload.sub) and must only satisfy step-up for
|
|
188
|
+
// THAT subject. Without this, a grant minted for one user (leaked via a
|
|
189
|
+
// shared cache / log / kiosk) elevates ANY other authenticated user's
|
|
190
|
+
// session — a cross-user step-up replay. When no principal is resolvable
|
|
191
|
+
// the grant cannot be bound, so the grant path is refused (the request
|
|
192
|
+
// falls through to the claims-based path / 401 challenge).
|
|
193
|
+
var stepUpPrincipal = _resolveStepUpPrincipal(req);
|
|
194
|
+
var grantResult;
|
|
195
|
+
if (stepUpPrincipal == null) {
|
|
196
|
+
grantResult = { ok: false, error: "no_principal",
|
|
197
|
+
reason: "step-up grant requires an authenticated principal to bind to" };
|
|
198
|
+
} else {
|
|
199
|
+
verifyOpts.subject = stepUpPrincipal;
|
|
200
|
+
grantResult = elevation().verify(grantToken, verifyOpts);
|
|
201
|
+
}
|
|
163
202
|
if (grantResult.ok) {
|
|
164
203
|
if (auditOn) {
|
|
165
204
|
try {
|
package/lib/network-tls.js
CHANGED
|
@@ -857,6 +857,16 @@ var OID_BASIC_OCSP_RESPONSE = "1.3.6.1.5.5.7.48.1.1";
|
|
|
857
857
|
// OCSP nonce extension — id-pkix-ocsp-nonce.
|
|
858
858
|
var OID_OCSP_NONCE = "1.3.6.1.5.5.7.48.1.2";
|
|
859
859
|
var OID_SHA1 = "1.3.14.3.2.26"; // SHA-1 algorithm OID arc
|
|
860
|
+
// RFC 6960 §4.1.1 CertID hashAlgorithm OIDs → node hash name. SHA-1 is the
|
|
861
|
+
// universally-supported default (buildOcspRequest emits it); the SHA-2 family is
|
|
862
|
+
// §4.3-optional but accepted when a responder uses it. An unknown OID is refused
|
|
863
|
+
// (fail closed) rather than skipping the issuer binding.
|
|
864
|
+
var OCSP_CERTID_HASH_OID_TO_NODE = {
|
|
865
|
+
"1.3.14.3.2.26": "sha1", // id-sha1
|
|
866
|
+
"2.16.840.1.101.3.4.2.1": "sha256", // id-sha256
|
|
867
|
+
"2.16.840.1.101.3.4.2.2": "sha384", // id-sha384
|
|
868
|
+
"2.16.840.1.101.3.4.2.3": "sha512", // id-sha512
|
|
869
|
+
};
|
|
860
870
|
var OID_RSA_SHA256 = "1.2.840.113549.1.1.11";
|
|
861
871
|
var OID_RSA_SHA384 = "1.2.840.113549.1.1.12";
|
|
862
872
|
var OID_RSA_SHA512 = "1.2.840.113549.1.1.13";
|
|
@@ -1032,7 +1042,20 @@ function parseOcspResponse(der) {
|
|
|
1032
1042
|
if (sr.length < 3) continue; // minimum SingleResponse fields
|
|
1033
1043
|
// sr[0] = certID SEQUENCE, sr[1] = certStatus CHOICE, sr[2] = thisUpdate.
|
|
1034
1044
|
var certIdChildren = asn1.readSequence(sr[0].value);
|
|
1035
|
-
// certID = SEQUENCE { hashAlgorithm, issuerNameHash
|
|
1045
|
+
// certID = SEQUENCE { hashAlgorithm AlgorithmIdentifier, issuerNameHash
|
|
1046
|
+
// OCTET STRING, issuerKeyHash OCTET STRING,
|
|
1047
|
+
// serialNumber INTEGER } (RFC 6960 §4.1.1)
|
|
1048
|
+
// Capture ALL FOUR fields. A response binds to the cert under validation by
|
|
1049
|
+
// (issuerNameHash, issuerKeyHash, serialNumber): a serial is unique only
|
|
1050
|
+
// PER ISSUER, so a "good" for serial-S under a different issuer (a delegated
|
|
1051
|
+
// responder, or a CA key spanning issuer identities) must not be accepted as
|
|
1052
|
+
// proof for serial-S under the issuer we actually asked about.
|
|
1053
|
+
var certIdHashAlgOid = certIdChildren.length >= 1
|
|
1054
|
+
? asn1.readOid(asn1.readSequence(certIdChildren[0].value)[0]) : null;
|
|
1055
|
+
var issuerNameHash = certIdChildren.length >= 2
|
|
1056
|
+
? asn1.readOctetString(certIdChildren[1]) : null;
|
|
1057
|
+
var issuerKeyHash = certIdChildren.length >= 3
|
|
1058
|
+
? asn1.readOctetString(certIdChildren[2]) : null;
|
|
1036
1059
|
var serialHex = certIdChildren.length >= 4
|
|
1037
1060
|
? certIdChildren[3].value.toString("hex")
|
|
1038
1061
|
: null;
|
|
@@ -1053,10 +1076,13 @@ function parseOcspResponse(der) {
|
|
|
1053
1076
|
nextUpdate = _parseTime(asn1.readNode(sr[3].value, 0));
|
|
1054
1077
|
}
|
|
1055
1078
|
responses.push({
|
|
1056
|
-
certIdSerialHex:
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1079
|
+
certIdSerialHex: serialHex,
|
|
1080
|
+
certIdHashAlgOid: certIdHashAlgOid,
|
|
1081
|
+
issuerNameHash: issuerNameHash,
|
|
1082
|
+
issuerKeyHash: issuerKeyHash,
|
|
1083
|
+
certStatus: certStatus,
|
|
1084
|
+
thisUpdate: thisUpdate,
|
|
1085
|
+
nextUpdate: nextUpdate,
|
|
1060
1086
|
});
|
|
1061
1087
|
}
|
|
1062
1088
|
|
|
@@ -1169,6 +1195,40 @@ function evaluateOcspResponse(ocspDer, opts) {
|
|
|
1169
1195
|
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1170
1196
|
errors: ["OCSP response has no entry for the requested cert serial"] };
|
|
1171
1197
|
}
|
|
1198
|
+
// Bind the matched SingleResponse to the ISSUER of the cert under validation,
|
|
1199
|
+
// not the serial alone. RFC 6960 §4.1.1: CertID is (hashAlgorithm,
|
|
1200
|
+
// issuerNameHash, issuerKeyHash, serialNumber); a serial is unique only per
|
|
1201
|
+
// issuer. Without this, a "good" for serial-S signed by a key that also serves
|
|
1202
|
+
// a DIFFERENT issuer (delegated responder, or a CA key spanning issuer
|
|
1203
|
+
// identities) is accepted as proof for serial-S under the issuer we asked
|
|
1204
|
+
// about. opts.issuerCertDer is the issuer cert (DER) whose DN + SPKI we hash;
|
|
1205
|
+
// both built-in consumer paths forward it from the issuer cert they hold.
|
|
1206
|
+
if (opts.issuerCertDer) {
|
|
1207
|
+
if (!Buffer.isBuffer(opts.issuerCertDer)) {
|
|
1208
|
+
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1209
|
+
errors: ["evaluateOcspResponse: opts.issuerCertDer must be a Buffer (issuer cert DER)"] };
|
|
1210
|
+
}
|
|
1211
|
+
if (!match.issuerNameHash || !match.issuerKeyHash) {
|
|
1212
|
+
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1213
|
+
errors: ["OCSP CertID is missing issuerNameHash/issuerKeyHash — cannot bind the response to the issuer"] };
|
|
1214
|
+
}
|
|
1215
|
+
var expected;
|
|
1216
|
+
try { expected = _expectedOcspCertIdHashes(opts.issuerCertDer, match.certIdHashAlgOid); }
|
|
1217
|
+
catch (e) {
|
|
1218
|
+
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1219
|
+
errors: [(e && e.message) || String(e)] };
|
|
1220
|
+
}
|
|
1221
|
+
if (expected.nameHash.length !== match.issuerNameHash.length ||
|
|
1222
|
+
!bCrypto.timingSafeEqual(expected.nameHash, match.issuerNameHash)) {
|
|
1223
|
+
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1224
|
+
errors: ["OCSP CertID issuerNameHash does not match the issuer of the cert under validation (RFC 6960 §4.1.1 — wrong-issuer response)"] };
|
|
1225
|
+
}
|
|
1226
|
+
if (expected.keyHash.length !== match.issuerKeyHash.length ||
|
|
1227
|
+
!bCrypto.timingSafeEqual(expected.keyHash, match.issuerKeyHash)) {
|
|
1228
|
+
return { ok: false, status: parsed.status, signatureValid: true,
|
|
1229
|
+
errors: ["OCSP CertID issuerKeyHash does not match the issuer of the cert under validation (RFC 6960 §4.1.1 — wrong-issuer response)"] };
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1172
1232
|
// Optional nonce echo verification (RFC 8954 / RFC 6960 §4.4.1).
|
|
1173
1233
|
// When opts.expectedNonce is supplied, the response MUST carry an
|
|
1174
1234
|
// OCSP nonce extension equal to the expected bytes — defends against
|
|
@@ -1321,6 +1381,27 @@ function _extractIssuerNameDerAndKeyBitString(certDer) {
|
|
|
1321
1381
|
};
|
|
1322
1382
|
}
|
|
1323
1383
|
|
|
1384
|
+
// Compute the EXPECTED CertID issuerNameHash + issuerKeyHash for an issuer cert
|
|
1385
|
+
// (DER), under the hash algorithm the responder used. RFC 6960 §4.1.1:
|
|
1386
|
+
// issuerNameHash = Hash(issuer DN, DER); issuerKeyHash = Hash(issuer
|
|
1387
|
+
// subjectPublicKey BIT STRING contents — the key bytes, not the SPKI SEQUENCE).
|
|
1388
|
+
// Reuses _extractIssuerNameDerAndKeyBitString (the same inputs buildOcspRequest
|
|
1389
|
+
// hashes) so the request-build and the response-bind hash IDENTICAL bytes.
|
|
1390
|
+
function _expectedOcspCertIdHashes(issuerCertDer, certIdHashAlgOid) {
|
|
1391
|
+
var nodeHash = OCSP_CERTID_HASH_OID_TO_NODE[certIdHashAlgOid];
|
|
1392
|
+
if (!nodeHash) {
|
|
1393
|
+
throw new TlsTrustError("tls/ocsp-bad-certid-hash-alg",
|
|
1394
|
+
"OCSP CertID hashAlgorithm OID '" + certIdHashAlgOid +
|
|
1395
|
+
"' is not a recognized hash (RFC 6960 §4.1.1)");
|
|
1396
|
+
}
|
|
1397
|
+
var iss = _extractIssuerNameDerAndKeyBitString(issuerCertDer);
|
|
1398
|
+
// lgtm[js/weak-cryptographic-algorithm] — RFC 6960 §4.1.1 CertID lookup hash over the PUBLIC issuer name; a name/key lookup, not an integrity or secrecy operation.
|
|
1399
|
+
var nameHash = nodeCrypto.createHash(nodeHash).update(iss.issuerNameDer).digest(); // lgtm[js/weak-cryptographic-algorithm]
|
|
1400
|
+
// lgtm[js/weak-cryptographic-algorithm] — RFC 6960 §4.1.1 CertID lookup hash over the PUBLIC issuer key; a name/key lookup, not an integrity or secrecy operation.
|
|
1401
|
+
var keyHash = nodeCrypto.createHash(nodeHash).update(iss.issuerKey).digest(); // lgtm[js/weak-cryptographic-algorithm]
|
|
1402
|
+
return { nameHash: nameHash, keyHash: keyHash };
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1324
1405
|
function _extractLeafSerial(leafCertDer) {
|
|
1325
1406
|
var top = asn1.readNode(leafCertDer);
|
|
1326
1407
|
if (top.tag !== asn1.TAG.SEQUENCE) {
|
|
@@ -1470,6 +1551,9 @@ async function fetchOcspResponse(opts) {
|
|
|
1470
1551
|
}
|
|
1471
1552
|
var evald = evaluateOcspResponse(res.body, {
|
|
1472
1553
|
issuerPem: opts.issuerPem,
|
|
1554
|
+
// Bind the response's CertID to the actual issuer (RFC 6960 §4.1.1), not
|
|
1555
|
+
// the serial alone — issuerX is the issuer cert we already parsed.
|
|
1556
|
+
issuerCertDer: issuerX.raw,
|
|
1473
1557
|
// Bind to the leaf being checked (its serial), not whatever the response
|
|
1474
1558
|
// happens to carry — defaults to leafX.serialNumber when not overridden.
|
|
1475
1559
|
serialHex: opts.serialHex || leafX.serialNumber,
|
|
@@ -1510,8 +1594,16 @@ var ocsp = Object.freeze({
|
|
|
1510
1594
|
throw new TlsTrustError("tls/ocsp-empty",
|
|
1511
1595
|
"OCSP response was empty");
|
|
1512
1596
|
}
|
|
1597
|
+
// opts.issuerPem is the OCSP-SIGNING cert, which may be a DELEGATED responder
|
|
1598
|
+
// (not the leaf's issuing CA). The CertID issuer hashes are computed over the
|
|
1599
|
+
// leaf's issuer, so the RFC 6960 §4.1.1 binding needs the actual issuer cert,
|
|
1600
|
+
// supplied explicitly as opts.issuerCertDer. Deriving it from issuerPem would
|
|
1601
|
+
// hash the responder and wrongly reject valid delegated-responder staples, so
|
|
1602
|
+
// when issuerCertDer is absent the bind stays on serial + signature only.
|
|
1603
|
+
var rgIssuerCertDer = Buffer.isBuffer(opts.issuerCertDer) ? opts.issuerCertDer : null;
|
|
1513
1604
|
var evald = evaluateOcspResponse(rv.ocspBytes, {
|
|
1514
1605
|
issuerPem: opts.issuerPem,
|
|
1606
|
+
issuerCertDer: rgIssuerCertDer,
|
|
1515
1607
|
// Bind to the connected peer's certificate serial (not the response's own
|
|
1516
1608
|
// entry) — without it evaluateOcspResponse fails closed.
|
|
1517
1609
|
serialHex: opts.serialHex || (rv.peerCert && rv.peerCert.serialNumber) || null,
|
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:9f023375-6f18-4c24-a12a-74b1fc204f53",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-06-
|
|
8
|
+
"timestamp": "2026-06-29T15:45:36.020Z",
|
|
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.15.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.15.60",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.15.
|
|
25
|
+
"version": "0.15.60",
|
|
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.15.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.15.60",
|
|
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.15.
|
|
57
|
+
"ref": "@blamejs/core@0.15.60",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|