@blamejs/pki 0.7.0 → 0.7.1
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 +8 -0
- package/lib/path-validate.js +24 -5
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,14 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.7.1 — 2026-09-10
|
|
8
|
+
|
|
9
|
+
A wildcard name in a certificate is held to an excluded name constraint it can reach.
|
|
10
|
+
|
|
11
|
+
### Security
|
|
12
|
+
|
|
13
|
+
- pki.path.validate refuses a certificate whose wildcard dNSName reaches a name an excluded subtree forbids. An excluded subtree states the names a certificate must not present, and a wildcard subject alternative name stands for every name one label below its parent rather than for the literal text. Comparing the text alone let a leaf carrying only *.example.com pass an exclusion of bar.example.com, which is a name that leaf presents. Any chain whose certificate authority relies on an excluded dNSName subtree to keep a subordinate away from a name was affected, in every version that shipped name-constraint validation. The permitted direction is unchanged and is not widened by this: a permitted subtree still has to cover the whole of a wildcard, so a base of foo.com admits *.foo.com while a base of bar.example.com does not admit *.example.com. A wildcard reaches exactly one label, so an excluded name deeper than that is out of its reach and is not refused: *.example.com is still admitted against an exclusion of deep.bar.example.com.
|
|
14
|
+
|
|
7
15
|
## v0.7.0 — 2026-09-09
|
|
8
16
|
|
|
9
17
|
A Sigstore bundle signed over an artifact's own bytes is verified, not just one wrapping an attestation.
|
package/lib/path-validate.js
CHANGED
|
@@ -52,6 +52,8 @@ var cmpSession = require("./cmp-session");
|
|
|
52
52
|
var guard = require("./guard-all");
|
|
53
53
|
var intrinsic = require("./guard-intrinsic");
|
|
54
54
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
55
|
+
var _strSlice = intrinsic.uncurry(String.prototype.slice);
|
|
56
|
+
var _strIndexOf = intrinsic.uncurry(String.prototype.indexOf);
|
|
55
57
|
var _resolve = intrinsic.uncurry(intrinsic.promiseResolve);
|
|
56
58
|
var constants = require("./constants");
|
|
57
59
|
var validator = require("./validator-all");
|
|
@@ -420,11 +422,28 @@ function emailMatch(constraint, mailbox) {
|
|
|
420
422
|
|
|
421
423
|
var stripTrailingDot = guard.name.stripTrailingDot;
|
|
422
424
|
|
|
423
|
-
|
|
425
|
+
/** @internal `reachIntoBase` asks the excluded question rather than the permitted one. A wildcard
|
|
426
|
+
* dNSName stands for every name one label below its parent, so it is a set rather than a name. A
|
|
427
|
+
* permitted subtree asks whether that whole set stays inside the base, which the literal comparison
|
|
428
|
+
* already answers: `*.foo.com` ends with `.foo.com`, so a base of `foo.com` covers all of it. An
|
|
429
|
+
* excluded subtree asks the other question, whether any member of the set falls inside the base, and
|
|
430
|
+
* the literal comparison answers it in the fail-open direction: `*.example.com` is not the string
|
|
431
|
+
* `bar.example.com`, so an exclusion of `bar.example.com` admitted a certificate that presents
|
|
432
|
+
* exactly the name the exclusion forbids. Widening this for the permitted direction would be the
|
|
433
|
+
* fail-open mirror, so it is asked for only where the caller wants reach. A wildcard reaches exactly
|
|
434
|
+
* one label, so a base below the wildcard's parent is reached only when it is one label below it. */
|
|
435
|
+
function hostConstraintMatch(constraint, host, reachIntoBase) {
|
|
424
436
|
var c = stripTrailingDot(constraint.toLowerCase()), h = stripTrailingDot(host.toLowerCase());
|
|
425
437
|
if (c === "") return true;
|
|
426
438
|
if (c.charAt(0) === ".") return h.length > c.length && h.slice(-c.length) === c;
|
|
427
|
-
|
|
439
|
+
if (h === c || (h.length > c.length && h.slice(-(c.length + 1)) === "." + c)) return true;
|
|
440
|
+
if (reachIntoBase === true && h.length > 2 && _charCodeAt(h, 0) === 42 && _charCodeAt(h, 1) === 46) {
|
|
441
|
+
var parent = _strSlice(h, 2);
|
|
442
|
+
if (parent.length > 0 && c.length > parent.length && _strSlice(c, -(parent.length + 1)) === "." + parent) {
|
|
443
|
+
return _strIndexOf(_strSlice(c, 0, c.length - parent.length - 1), ".") === -1;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return false;
|
|
428
447
|
}
|
|
429
448
|
|
|
430
449
|
function _charTable(chars) { var t = []; for (var i = 0; i < chars.length; i++) t[_charCodeAt(chars, i)] = true; return t; }
|
|
@@ -491,14 +510,14 @@ function ipMatch(constraint, addr) {
|
|
|
491
510
|
* certificate carries in its own nameConstraints extension met no door, and the same rule decides it
|
|
492
511
|
* here. Answering a base outside its tag's form with no match rather than no verdict would make an
|
|
493
512
|
* excluded subtree that covers nothing exclude nothing. */
|
|
494
|
-
function nameMatchesConstraint(gnTag, gnValue, base) {
|
|
513
|
+
function nameMatchesConstraint(gnTag, gnValue, base, reachIntoBase) {
|
|
495
514
|
if (base.tagNumber !== gnTag) return null;
|
|
496
515
|
if (gnTag === 1 || gnTag === 2 || gnTag === 6) {
|
|
497
516
|
if (guard.name.constraintBaseUnreadable(gnTag, base.value)) return "unsupported";
|
|
498
517
|
}
|
|
499
518
|
switch (gnTag) {
|
|
500
519
|
case 1: return emailMatch(base.value, gnValue);
|
|
501
|
-
case 2: return hostConstraintMatch(base.value, gnValue);
|
|
520
|
+
case 2: return hostConstraintMatch(base.value, gnValue, reachIntoBase);
|
|
502
521
|
case 6: return uriMatch(base.value, gnValue);
|
|
503
522
|
case 7: return ipMatch(base.value, gnValue);
|
|
504
523
|
case 4: return dnStartsWith(gnValue, base.value);
|
|
@@ -540,7 +559,7 @@ function checkNameConstraints(state, cert) {
|
|
|
540
559
|
for (var e = 0; e < state.excludedSubtrees.length; e++) {
|
|
541
560
|
var ex = state.excludedSubtrees[e];
|
|
542
561
|
for (var i = 0; i < forms.length; i++) {
|
|
543
|
-
var m = nameMatchesConstraint(forms[i].tag, forms[i].value, { tagNumber: ex.tag, value: ex.base });
|
|
562
|
+
var m = nameMatchesConstraint(forms[i].tag, forms[i].value, { tagNumber: ex.tag, value: ex.base }, true);
|
|
544
563
|
if (m === true) return { ok: false, code: "path/name-constraint-excluded" };
|
|
545
564
|
if (m === "unsupported") return { ok: false, code: "path/name-constraint-unsupported" };
|
|
546
565
|
}
|
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:f4cef9a3-338a-48f8-81a2-c156e38dfb4b",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-09-
|
|
8
|
+
"timestamp": "2026-09-10T05:54:34.594Z",
|
|
9
9
|
"lifecycles": [
|
|
10
10
|
{
|
|
11
11
|
"phase": "build"
|
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
}
|
|
20
20
|
],
|
|
21
21
|
"component": {
|
|
22
|
-
"bom-ref": "@blamejs/pki@0.7.
|
|
22
|
+
"bom-ref": "@blamejs/pki@0.7.1",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "pki",
|
|
25
|
-
"version": "0.7.
|
|
25
|
+
"version": "0.7.1",
|
|
26
26
|
"scope": "required",
|
|
27
27
|
"author": "blamejs contributors",
|
|
28
28
|
"description": "Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.",
|
|
29
|
-
"purl": "pkg:npm/%40blamejs/pki@0.7.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/pki@0.7.1",
|
|
30
30
|
"properties": [],
|
|
31
31
|
"externalReferences": [
|
|
32
32
|
{
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"components": [],
|
|
55
55
|
"dependencies": [
|
|
56
56
|
{
|
|
57
|
-
"ref": "@blamejs/pki@0.7.
|
|
57
|
+
"ref": "@blamejs/pki@0.7.1",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|