@blamejs/core 0.15.33 → 0.15.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/crypto-field.js +1 -1
- package/lib/external-db.js +1 -1
- package/lib/mail-crypto-pgp.js +12 -1
- package/lib/middleware/idempotency-key.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,8 @@ upgrading across more than a few patches at a time.
|
|
|
8
8
|
|
|
9
9
|
## v0.15.x
|
|
10
10
|
|
|
11
|
+
- v0.15.34 (2026-06-26) — **`b.mail.crypto.pgp.verify` now accepts every valid RSA OpenPGP signature — one whose value happened to have a high zero byte (about 1 in 256) was being rejected.** An OpenPGP RSA signature is an integer modulo the key's modulus, and ~1 in 256 signatures have a value that begins with a zero byte. The MPI encoding strips those leading zero bytes (RFC 9580 §3.2), but b.mail.crypto.pgp.verify passed the stripped value straight to the RSA verification, which requires a signature exactly the modulus byte length — so a perfectly valid signature was intermittently reported invalid. The signature is now left-padded back to the modulus width before verification, exactly as the Ed25519 path already pads its components. Verification of every valid RSA signature is now reliable, including signatures produced by other OpenPGP implementations. Also includes internal test-tooling: two more guard-suite suppression classes were re-verified and their tokens retired. **Fixed:** *RSA OpenPGP signatures with a high zero byte now verify reliably* — b.mail.crypto.pgp.verify read the RSA signature MPI (whose leading zero bytes the OpenPGP wire format strips) and handed it to the RSA verification without restoring the stripped bytes. Node's RSA verify requires the signature to be exactly the modulus byte length, so a signature whose value had one or more high zero bytes — about 1 in 256 of all signatures, for any key — was rejected as invalid even though it was correct. The signature is now left-padded to the modulus width before verification (the same correction the Ed25519 verification path already applied to its R/S components). This affects signatures the framework produces and signatures from other OpenPGP implementations alike. A deterministic regression test searches for a short-MPI signature and asserts it verifies. **Detectors:** *Two more suppression-marker classes re-verified and their tokens retired* — The raw-hash-compare and seal-without-aad guard classes were re-read and confirmed (a data-residency region tag compared with === — not a secret; and two intentional non-AEAD-bound seals — a non-regulated plain-mode column whose AAD is enforced by the posture seal-envelope floor where it matters, and a throwaway vault-readiness probe), then renamed to descriptive tokens with their old names added to the retired-token set. Test-suite tooling only.
|
|
12
|
+
|
|
11
13
|
- v0.15.33 (2026-06-26) — **Internal test-suite hardening only — the published library's runtime behavior and public API are unchanged (source-comment marker text aside).** Three more suppression classes in the codebase-patterns guard suite were re-verified from scratch and renamed to descriptive tokens, with their old names recorded as retired and their in-source marker comments updated to match. No runtime code, public API, or wire format changed. **Detectors:** *Three more suppression-marker classes re-verified and their tokens retired* — Continuing the re-verification pass: each marked site for the math-random-noncrypto, raw-new-url, and dynamic-require guard classes was re-read and confirmed (non-security jitter/sampling; URL parsing for shape/origin inspection or behind the safe wrapper; operator-supplied module loads), then the class was renamed to a descriptive token and its old name added to the retired-token set. This is test-suite tooling; no shipped framework behavior changed.
|
|
12
14
|
|
|
13
15
|
- v0.15.32 (2026-06-26) — **Internal test-suite hardening only — the published library's runtime behavior and public API are unchanged (source-comment marker text aside).** Three more suppression classes in the codebase-patterns guard suite were re-verified from scratch and renamed to descriptive tokens, with their old names recorded as retired so the prior approval cannot be silently resurrected and their in-source marker comments updated to match. No runtime code, public API, or wire format changed. **Detectors:** *Three more suppression-marker classes re-verified and their tokens retired* — Continuing the re-verification pass: each marked site for the process-exit, hand-rolled buffer-collect, and raw-outbound-http guard classes was re-read and confirmed (operator-opt-in exits; bounded protocol-framing / TLV assembly; framework-routed outbound calls), then the class was renamed to a descriptive token and its old name added to the retired-token set. This is test-suite tooling; no shipped framework behavior changed.
|
package/lib/crypto-field.js
CHANGED
|
@@ -1090,7 +1090,7 @@ function sealRow(table, row, opts) {
|
|
|
1090
1090
|
out[field] = vaultAad.seal(_encodeTyped(out[field]),
|
|
1091
1091
|
_aadParts(s, table, field, out));
|
|
1092
1092
|
} else {
|
|
1093
|
-
// allow:seal-without-aad — plain-mode legacy table; operator
|
|
1093
|
+
// allow:seal-without-aad-by-design — plain-mode legacy table; operator
|
|
1094
1094
|
// opts into AAD via registerTable({aad:true})
|
|
1095
1095
|
out[field] = vault.seal(_encodeTyped(out[field]));
|
|
1096
1096
|
}
|
package/lib/external-db.js
CHANGED
|
@@ -1709,7 +1709,7 @@ function _crossBorderRegulated(posture) {
|
|
|
1709
1709
|
|
|
1710
1710
|
function _residencyCompatible(primaryTag, replicaTag) {
|
|
1711
1711
|
if (!primaryTag || !replicaTag) return true;
|
|
1712
|
-
if (primaryTag === replicaTag) return true; // allow:raw-hash-compare — residency tag string, not a secret hash
|
|
1712
|
+
if (primaryTag === replicaTag) return true; // allow:raw-hash-compare-nonsecret-tag — residency tag string, not a secret hash
|
|
1713
1713
|
if (primaryTag === "unrestricted" || replicaTag === "unrestricted") return true;
|
|
1714
1714
|
return false;
|
|
1715
1715
|
}
|
package/lib/mail-crypto-pgp.js
CHANGED
|
@@ -865,11 +865,22 @@ function verify(opts) {
|
|
|
865
865
|
var ok;
|
|
866
866
|
if (parsed.pubAlg === PUB_ALG_RSA) {
|
|
867
867
|
var rsaMpi = _readMpi(parsed.sigMpisBytes, 0);
|
|
868
|
+
// The signature is an integer in [0, n); when its value has one or more
|
|
869
|
+
// high zero bytes (~1/256 of signatures) the OpenPGP MPI encoding strips
|
|
870
|
+
// them (RFC 9580 §3.2), but node's RSA verify requires a signature exactly
|
|
871
|
+
// the modulus byte length. Left-pad the stripped MPI back to the modulus
|
|
872
|
+
// width — the same correction the Ed25519 branch applies to its R/S
|
|
873
|
+
// components below — or a valid signature is rejected.
|
|
874
|
+
var rsaSigBytes = rsaMpi.value;
|
|
875
|
+
var modLen = rsaPub.n.length;
|
|
876
|
+
if (rsaSigBytes.length < modLen) {
|
|
877
|
+
rsaSigBytes = Buffer.concat([Buffer.alloc(modLen - rsaSigBytes.length), rsaSigBytes]);
|
|
878
|
+
}
|
|
868
879
|
try {
|
|
869
880
|
ok = nodeCrypto.verify(hashName, hashInput, {
|
|
870
881
|
key: publicKey,
|
|
871
882
|
padding: nodeCrypto.constants.RSA_PKCS1_PADDING,
|
|
872
|
-
},
|
|
883
|
+
}, rsaSigBytes);
|
|
873
884
|
} catch (e) {
|
|
874
885
|
return _fail("mail-crypto/pgp/verify-error",
|
|
875
886
|
"RSA verify threw: " + ((e && e.message) || String(e)));
|
|
@@ -290,7 +290,7 @@ function dbStore(opts) {
|
|
|
290
290
|
var sealEnabled = false;
|
|
291
291
|
if (sealReq) {
|
|
292
292
|
try {
|
|
293
|
-
// allow:seal-without-aad — vault-readiness probe; throwaway
|
|
293
|
+
// allow:seal-without-aad-by-design — vault-readiness probe; throwaway
|
|
294
294
|
// sentinel value, not row-bound data
|
|
295
295
|
vault.seal("__idempotency_seal_probe__");
|
|
296
296
|
sealEnabled = true;
|
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:4006bc32-7ae3-4f18-bad1-057410956255",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-06-
|
|
8
|
+
"timestamp": "2026-06-27T01:06:19.602Z",
|
|
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.34",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.15.
|
|
25
|
+
"version": "0.15.34",
|
|
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.34",
|
|
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.34",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|