@blamejs/blamejs-shop 0.4.87 → 0.4.88

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/SECURITY.md +8 -0
  3. package/lib/asset-manifest.json +1 -1
  4. package/lib/vendor/MANIFEST.json +53 -31
  5. package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +7 -2
  6. package/lib/vendor/blamejs/.github/dependabot.yml +12 -0
  7. package/lib/vendor/blamejs/.github/workflows/ci.yml +16 -12
  8. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +3 -1
  9. package/lib/vendor/blamejs/.github/workflows/release-container.yml +23 -0
  10. package/lib/vendor/blamejs/CHANGELOG.md +6 -0
  11. package/lib/vendor/blamejs/api-snapshot.json +10 -2
  12. package/lib/vendor/blamejs/examples/wiki/Dockerfile +19 -2
  13. package/lib/vendor/blamejs/lib/atomic-file.js +32 -9
  14. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +105 -40
  15. package/lib/vendor/blamejs/lib/middleware/dpop.js +54 -31
  16. package/lib/vendor/blamejs/lib/middleware/span-http-server.js +7 -0
  17. package/lib/vendor/blamejs/lib/network-tls.js +12 -2
  18. package/lib/vendor/blamejs/lib/queue-local.js +123 -46
  19. package/lib/vendor/blamejs/lib/queue.js +13 -9
  20. package/lib/vendor/blamejs/lib/request-helpers.js +90 -0
  21. package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +69 -7
  22. package/lib/vendor/blamejs/lib/self-update.js +11 -2
  23. package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +11 -11
  24. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +6 -2
  25. package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +689 -688
  26. package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +5 -0
  27. package/lib/vendor/blamejs/package.json +1 -1
  28. package/lib/vendor/blamejs/release-notes/v0.15.17.json +52 -0
  29. package/lib/vendor/blamejs/release-notes/v0.15.18.json +49 -0
  30. package/lib/vendor/blamejs/release-notes/v0.15.19.json +18 -0
  31. package/lib/vendor/blamejs/scripts/check-vendor-currency.js +24 -0
  32. package/lib/vendor/blamejs/test/integration/queue-cluster-mysql.test.js +419 -0
  33. package/lib/vendor/blamejs/test/integration/queue-cluster-pg.test.js +471 -0
  34. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt-rejection-envelope.test.js +309 -0
  35. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +35 -8
  36. package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file-fd-read-errorfor-bypass.test.js +138 -0
  37. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +26 -0
  38. package/lib/vendor/blamejs/test/layer-0-primitives/dpop-htu-peergating.test.js +227 -0
  39. package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +83 -0
  40. package/lib/vendor/blamejs/test/layer-0-primitives/self-update-poll-asset-digest.test.js +90 -0
  41. package/lib/vendor/blamejs/test/layer-0-primitives/self-update-standalone-verifier-ecdsa-encoding.test.js +274 -0
  42. package/lib/vendor/blamejs/test/layer-0-primitives/tls-ocsp-freshness.test.js +192 -0
  43. package/lib/vendor/blamejs/test/layer-0-primitives/vendor-currency-classify.test.js +36 -0
  44. package/package.json +1 -1
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ /**
3
+ * b.network.tls.ocsp.evaluate — OCSP response FRESHNESS enforcement
4
+ * (RFC 6960 §4.2.2.1). Regression coverage for the dead staleness gate:
5
+ * evaluateOcspResponse called Date.parse() on thisUpdate/nextUpdate, but
6
+ * those fields are already unix-ms NUMBERS (parseOcspResponse → _parseTime
7
+ * returns Date.UTC(...)). Date.parse(<number>) coerces to a bare-integer
8
+ * string → NaN, so the !isFinite guard rejected EVERY signature-valid
9
+ * response (fresh or stale) with a misleading "missing thisUpdate", leaving
10
+ * the real future-thisUpdate / past-nextUpdate window checks as unreachable
11
+ * dead code (the past-nextUpdate branch latently fail-open).
12
+ *
13
+ * No existing test built a full SIGNED `successful` BasicOCSPResponse — every
14
+ * prior OCSP test short-circuited before the freshness gate — which is why
15
+ * the dead check shipped. This builds a real ECDSA-SHA256-signed response and
16
+ * drives the consumer path, asserting:
17
+ * - a STALE response (past nextUpdate) is REJECTED for the stale reason
18
+ * (RED before the fix: rejected, but with "missing thisUpdate");
19
+ * - a FRESH response is ACCEPTED (RED before the fix: rejected as "missing
20
+ * thisUpdate" — the bug cannot tell fresh from stale);
21
+ * - a FUTURE-thisUpdate response is rejected for the future reason.
22
+ *
23
+ * Run standalone: node test/layer-0-primitives/tls-ocsp-freshness.test.js
24
+ */
25
+
26
+ var nodeCrypto = require("node:crypto");
27
+ var helpers = require("../helpers");
28
+ var b = helpers.b;
29
+ var check = helpers.check;
30
+ var asn1 = require("../../lib/asn1-der");
31
+
32
+ // A fixed reference time so the test is wall-clock independent (passed to
33
+ // evaluate as opts.now). All thisUpdate/nextUpdate are offsets from this.
34
+ var FIXED_NOW = 1750000000000; // 2025-06-15T...Z, a stable ms value
35
+
36
+ function _pad(n, width) {
37
+ var s = String(n);
38
+ while (s.length < width) s = "0" + s;
39
+ return s;
40
+ }
41
+
42
+ // unix-ms → GeneralizedTime "YYYYMMDDhhmmssZ" (the on-the-wire shape
43
+ // _parseTime accepts; it truncates to whole seconds, which is fine — the
44
+ // offsets here are minutes/days, far from the second boundary).
45
+ function _genTime(ms) {
46
+ var d = new Date(ms);
47
+ return asn1.writeNode(0x18, Buffer.from(
48
+ _pad(d.getUTCFullYear(), 4) + _pad(d.getUTCMonth() + 1, 2) + _pad(d.getUTCDate(), 2) +
49
+ _pad(d.getUTCHours(), 2) + _pad(d.getUTCMinutes(), 2) + _pad(d.getUTCSeconds(), 2) + "Z",
50
+ "ascii"));
51
+ }
52
+
53
+ // Build a complete, validly-signed OCSP "successful" response (certStatus
54
+ // good) for a given serial, with the supplied thisUpdate/nextUpdate ms.
55
+ // Returns { der, issuerPem, serialHex }.
56
+ function _buildSignedOcsp(opts) {
57
+ var kp = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "prime256v1" });
58
+ var issuerPem = kp.publicKey.export({ type: "spki", format: "pem" });
59
+
60
+ var serial = Buffer.from([0x12, 0x34, 0x56, 0x78]);
61
+ var serialHex = serial.toString("hex");
62
+
63
+ // certID = SEQ { AlgId{sha1,NULL}, issuerNameHash OCTET(20), issuerKeyHash OCTET(20), serial INT }
64
+ // (the evaluator only reads the serial from certID; the two hashes are not
65
+ // checked, so fixed 20-byte fillers are fine.)
66
+ var hashAlg = asn1.writeSequence([asn1.writeOid("1.3.14.3.2.26"), asn1.writeNull()]); // sha1
67
+ var nameHash = asn1.writeOctetString(Buffer.alloc(20, 0xaa));
68
+ var keyHash = asn1.writeOctetString(Buffer.alloc(20, 0xbb));
69
+ var certId = asn1.writeSequence([hashAlg, nameHash, keyHash, asn1.writeInteger(serial)]);
70
+
71
+ // certStatus good = [0] IMPLICIT NULL (empty primitive context tag).
72
+ var certStatusGood = asn1.writeContextImplicit(0, Buffer.alloc(0));
73
+
74
+ // SingleResponse = SEQ { certID, certStatus, thisUpdate, [0] EXPLICIT nextUpdate }
75
+ var singleResponseChildren = [
76
+ certId,
77
+ certStatusGood,
78
+ _genTime(opts.thisUpdateMs),
79
+ ];
80
+ if (typeof opts.nextUpdateMs === "number") {
81
+ singleResponseChildren.push(asn1.writeContextExplicit(0, _genTime(opts.nextUpdateMs)));
82
+ }
83
+ var singleResponse = asn1.writeSequence(singleResponseChildren);
84
+
85
+ // ResponseData (tbs) = SEQ { responderID [2] EXPLICIT KeyHash, producedAt, responses SEQ-OF }
86
+ var responderId = asn1.writeContextExplicit(2, asn1.writeOctetString(Buffer.alloc(20, 0xcc)));
87
+ var producedAt = _genTime(FIXED_NOW);
88
+ var responses = asn1.writeSequence([singleResponse]);
89
+ var tbs = asn1.writeSequence([responderId, producedAt, responses]);
90
+
91
+ // Sign the tbsResponseData DER (header + value) — exactly the bytes the
92
+ // verifier slices and checks. node emits a DER ECDSA-Sig-Value, which the
93
+ // verifier accepts for OID 1.2.840.10045.4.3.2.
94
+ var sig = nodeCrypto.sign("sha256", tbs, kp.privateKey);
95
+
96
+ var sigAlg = asn1.writeSequence([asn1.writeOid("1.2.840.10045.4.3.2")]); // ecdsa-with-SHA256
97
+ var basic = asn1.writeSequence([tbs, sigAlg, asn1.writeBitString(sig)]);
98
+
99
+ // responseBytes [0] EXPLICIT SEQ { id-pkix-ocsp-basic, OCTET(basic) }
100
+ var responseBytesInner = asn1.writeSequence([
101
+ asn1.writeOid("1.3.6.1.5.5.7.48.1.1"),
102
+ asn1.writeOctetString(basic),
103
+ ]);
104
+ var responseBytes = asn1.writeContextExplicit(0, responseBytesInner);
105
+
106
+ // OCSPResponse = SEQ { responseStatus ENUMERATED(0 successful), responseBytes }
107
+ var responseStatus = asn1.writeNode(0x0a, Buffer.from([0]));
108
+ var der = asn1.writeSequence([responseStatus, responseBytes]);
109
+
110
+ return { der: der, issuerPem: issuerPem, serialHex: serialHex };
111
+ }
112
+
113
+ // A STALE response (nextUpdate well in the past) MUST be rejected — and for
114
+ // the STALE reason, not the misleading "missing thisUpdate" the dead gate
115
+ // produced. RED before the fix: ok:false but errors=["...missing thisUpdate..."].
116
+ function testRejectsStaleResponse() {
117
+ var fx = _buildSignedOcsp({
118
+ thisUpdateMs: FIXED_NOW - 3 * 86400000, // 3 days ago
119
+ nextUpdateMs: FIXED_NOW - 2 * 86400000, // 2 days ago → STALE
120
+ });
121
+ var rv = b.network.tls.ocsp.evaluate(fx.der, {
122
+ issuerPem: fx.issuerPem, serialHex: fx.serialHex, now: FIXED_NOW,
123
+ });
124
+ check("stale: rejected (ok=false)", rv.ok === false);
125
+ check("stale: signature still verified (proves we reached the freshness gate)",
126
+ rv.signatureValid === true);
127
+ var errs = (rv.errors || []).join(" ; ");
128
+ check("stale: rejected for the STALE reason, not 'missing thisUpdate'",
129
+ /nextUpdate|stale/i.test(errs) && !/missing thisUpdate/i.test(errs));
130
+ }
131
+
132
+ // A FRESH response (thisUpdate just past, nextUpdate in the future) MUST be
133
+ // accepted. RED before the fix: rejected as "missing thisUpdate" (the bug
134
+ // cannot distinguish fresh from stale — both wrongly ok:false).
135
+ function testAcceptsFreshResponse() {
136
+ var fx = _buildSignedOcsp({
137
+ thisUpdateMs: FIXED_NOW - 3600000, // 1 hour ago
138
+ nextUpdateMs: FIXED_NOW + 86400000, // +1 day → FRESH
139
+ });
140
+ var rv = b.network.tls.ocsp.evaluate(fx.der, {
141
+ issuerPem: fx.issuerPem, serialHex: fx.serialHex, now: FIXED_NOW,
142
+ });
143
+ check("fresh: accepted (ok=true)", rv.ok === true);
144
+ check("fresh: certStatus good", rv.certStatus === "good");
145
+ check("fresh: signature verified", rv.signatureValid === true);
146
+ check("fresh: no errors", Array.isArray(rv.errors) && rv.errors.length === 0);
147
+ }
148
+
149
+ // A FUTURE-dated thisUpdate (clock skew / replay) MUST be rejected for the
150
+ // future reason — proves the future-window check is live, not dead.
151
+ function testRejectsFutureThisUpdate() {
152
+ var fx = _buildSignedOcsp({
153
+ thisUpdateMs: FIXED_NOW + 2 * 86400000, // 2 days in the future
154
+ nextUpdateMs: FIXED_NOW + 3 * 86400000,
155
+ });
156
+ var rv = b.network.tls.ocsp.evaluate(fx.der, {
157
+ issuerPem: fx.issuerPem, serialHex: fx.serialHex, now: FIXED_NOW,
158
+ });
159
+ check("future: rejected (ok=false)", rv.ok === false);
160
+ check("future: rejected for the FUTURE reason",
161
+ /future/i.test((rv.errors || []).join(" ; ")));
162
+ }
163
+
164
+ // A fresh response with NO nextUpdate (optional field absent) MUST be
165
+ // accepted — guards the typeof-guard's null→NaN handling of the optional
166
+ // nextUpdate branch.
167
+ function testAcceptsFreshNoNextUpdate() {
168
+ var fx = _buildSignedOcsp({
169
+ thisUpdateMs: FIXED_NOW - 3600000, // 1 hour ago, no nextUpdate
170
+ });
171
+ var rv = b.network.tls.ocsp.evaluate(fx.der, {
172
+ issuerPem: fx.issuerPem, serialHex: fx.serialHex, now: FIXED_NOW,
173
+ });
174
+ check("no-nextUpdate fresh: accepted (ok=true)", rv.ok === true);
175
+ check("no-nextUpdate fresh: certStatus good", rv.certStatus === "good");
176
+ }
177
+
178
+ async function run() {
179
+ testRejectsStaleResponse();
180
+ testAcceptsFreshResponse();
181
+ testRejectsFutureThisUpdate();
182
+ testAcceptsFreshNoNextUpdate();
183
+ }
184
+
185
+ module.exports = { run: run };
186
+
187
+ if (require.main === module) {
188
+ run().then(
189
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
190
+ function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
191
+ );
192
+ }
@@ -45,6 +45,39 @@ function testVersionFallbackWhenNoCommit() {
45
45
  check("no COMMIT, matching VERSION -> current via version", same.stale === false && same.basis === "version");
46
46
  }
47
47
 
48
+ function testTimestampDirectionalNewerLocalNotStale() {
49
+ // The CDN-serves-an-older-edge case. publicsuffix.org is CDN-served and
50
+ // different edges can return an OLDER cached copy than the one we vendored.
51
+ // Our bundle is NEWER, so it is NOT stale — but the old exact commit/version
52
+ // inequality flagged it stale (commits differ) and failed the release gate
53
+ // non-deterministically. With a parseable VERSION timestamp on both sides the
54
+ // comparison is directional: only a bundle OLDER than upstream is stale.
55
+ // RED on the old logic (basis "commit", stale true); GREEN now.
56
+ var v = cur._classifyContentCurrency(
57
+ _doc("9186eee", "2026-06-13_21-47-18_UTC"), // upstream edge: older, different commit
58
+ _doc("27a7b5d", "2026-06-22_11-46-12_UTC"), // our bundle: newer
59
+ PSL);
60
+ check("local timestamp NEWER than upstream -> not stale (stale CDN edge)",
61
+ v.stale === false && v.basis === "version-timestamp");
62
+ }
63
+
64
+ function testTimestampDirectionalOlderLocalStale() {
65
+ // Genuine drift: upstream advanced past our bundle -> stale (gate fires).
66
+ var v = cur._classifyContentCurrency(
67
+ _doc("27a7b5d", "2026-06-22_11-46-12_UTC"), // upstream: newer
68
+ _doc("9186eee", "2026-06-13_21-47-18_UTC"), // our bundle: older
69
+ PSL);
70
+ check("local timestamp OLDER than upstream -> stale", v.stale === true && v.basis === "version-timestamp");
71
+ }
72
+
73
+ function testTimestampDirectionalEqualNotStale() {
74
+ var v = cur._classifyContentCurrency(
75
+ _doc("27a7b5d", "2026-06-22_11-46-12_UTC"),
76
+ _doc("27a7b5d", "2026-06-22_11-46-12_UTC"),
77
+ PSL);
78
+ check("equal timestamps -> not stale", v.stale === false && v.basis === "version-timestamp");
79
+ }
80
+
48
81
  function testNoIdentifierThrows() {
49
82
  // Neither side yields a comparable id (e.g. upstream format changed) —
50
83
  // must throw so the caller reports registry-error, never a silent pass.
@@ -66,6 +99,9 @@ function run() {
66
99
  testCommitMatchIsCurrent();
67
100
  testCommitDifferIsStale();
68
101
  testVersionFallbackWhenNoCommit();
102
+ testTimestampDirectionalNewerLocalNotStale();
103
+ testTimestampDirectionalOlderLocalStale();
104
+ testTimestampDirectionalEqualNotStale();
69
105
  testNoIdentifierThrows();
70
106
  testGateConfigLockedIn();
71
107
  console.log("[vendor-currency-classify] OK — " + helpers.getChecks() + " checks passed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.4.87",
3
+ "version": "0.4.88",
4
4
  "description": "Open-source framework built on blamejs. Vendored stack, zero npm runtime deps, PQC-first crypto, security-on by default.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {