@blamejs/blamejs-shop 0.3.69 → 0.3.71

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 (92) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/README.md +1 -1
  3. package/lib/admin.js +254 -1
  4. package/lib/asset-manifest.json +1 -1
  5. package/lib/vendor/MANIFEST.json +95 -83
  6. package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
  7. package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +1 -1
  8. package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +1 -1
  9. package/lib/vendor/blamejs/.github/workflows/ci.yml +10 -10
  10. package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
  11. package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +2 -2
  12. package/lib/vendor/blamejs/.github/workflows/release-container.yml +4 -4
  13. package/lib/vendor/blamejs/.github/workflows/scorecard.yml +2 -2
  14. package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
  15. package/lib/vendor/blamejs/CHANGELOG.md +4 -0
  16. package/lib/vendor/blamejs/README.md +1 -1
  17. package/lib/vendor/blamejs/SECURITY.md +2 -0
  18. package/lib/vendor/blamejs/api-snapshot.json +108 -4
  19. package/lib/vendor/blamejs/lib/auth/oauth.js +736 -1
  20. package/lib/vendor/blamejs/lib/auth/oid4vci.js +124 -5
  21. package/lib/vendor/blamejs/lib/auth/oid4vp.js +14 -4
  22. package/lib/vendor/blamejs/lib/auth/sd-jwt-vc-holder.js +46 -1
  23. package/lib/vendor/blamejs/lib/break-glass.js +1 -2
  24. package/lib/vendor/blamejs/lib/config.js +28 -31
  25. package/lib/vendor/blamejs/lib/crypto-field.js +274 -17
  26. package/lib/vendor/blamejs/lib/dora.js +8 -5
  27. package/lib/vendor/blamejs/lib/dsr.js +2 -2
  28. package/lib/vendor/blamejs/lib/flag-evaluation-context.js +7 -0
  29. package/lib/vendor/blamejs/lib/guard-html-wcag-aria.js +4 -2
  30. package/lib/vendor/blamejs/lib/guard-html-wcag-forms.js +4 -2
  31. package/lib/vendor/blamejs/lib/guard-html-wcag-tables.js +4 -2
  32. package/lib/vendor/blamejs/lib/guard-html-wcag-tagwalk.js +20 -0
  33. package/lib/vendor/blamejs/lib/guard-html-wcag.js +1 -1
  34. package/lib/vendor/blamejs/lib/honeytoken.js +27 -20
  35. package/lib/vendor/blamejs/lib/mail-auth.js +333 -0
  36. package/lib/vendor/blamejs/lib/mail-deploy.js +1 -1
  37. package/lib/vendor/blamejs/lib/mail-send-deliver.js +13 -4
  38. package/lib/vendor/blamejs/lib/middleware/api-encrypt.js +140 -13
  39. package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +3 -0
  40. package/lib/vendor/blamejs/lib/middleware/csp-report.js +13 -9
  41. package/lib/vendor/blamejs/lib/middleware/fetch-metadata.js +115 -14
  42. package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +3 -0
  43. package/lib/vendor/blamejs/lib/middleware/scim-server.js +297 -19
  44. package/lib/vendor/blamejs/lib/middleware/security-headers.js +47 -0
  45. package/lib/vendor/blamejs/lib/middleware/security-txt.js +1 -2
  46. package/lib/vendor/blamejs/lib/middleware/trace-log-correlation.js +1 -2
  47. package/lib/vendor/blamejs/lib/network-smtp-policy.js +4 -4
  48. package/lib/vendor/blamejs/lib/object-store/sigv4-bucket-ops.js +11 -2
  49. package/lib/vendor/blamejs/lib/observability-tracer.js +1 -1
  50. package/lib/vendor/blamejs/lib/observability.js +39 -1
  51. package/lib/vendor/blamejs/lib/problem-details.js +56 -11
  52. package/lib/vendor/blamejs/lib/pubsub-cluster.js +16 -3
  53. package/lib/vendor/blamejs/lib/queue-sqs.js +20 -2
  54. package/lib/vendor/blamejs/lib/redis-client.js +32 -4
  55. package/lib/vendor/blamejs/lib/safe-redirect.js +16 -2
  56. package/lib/vendor/blamejs/memory/specs/node-26-map-getorinsert-migration.md +3 -2
  57. package/lib/vendor/blamejs/package.json +1 -1
  58. package/lib/vendor/blamejs/release-notes/v0.14.20.json +73 -0
  59. package/lib/vendor/blamejs/release-notes/v0.14.21.json +98 -0
  60. package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +339 -0
  61. package/lib/vendor/blamejs/test/layer-0-primitives/asyncapi.test.js +37 -0
  62. package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +22 -0
  63. package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +315 -5
  64. package/lib/vendor/blamejs/test/layer-0-primitives/config.test.js +46 -0
  65. package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-unseal-rate-cap.test.js +176 -0
  66. package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +86 -0
  67. package/lib/vendor/blamejs/test/layer-0-primitives/dora.test.js +38 -0
  68. package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +29 -0
  69. package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +236 -1
  70. package/lib/vendor/blamejs/test/layer-0-primitives/fetch-metadata.test.js +190 -0
  71. package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +23 -0
  72. package/lib/vendor/blamejs/test/layer-0-primitives/guard-html-wcag.test.js +59 -0
  73. package/lib/vendor/blamejs/test/layer-0-primitives/honeytoken.test.js +26 -0
  74. package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +179 -0
  75. package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy-tlsrpt.test.js +16 -0
  76. package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +108 -0
  77. package/lib/vendor/blamejs/test/layer-0-primitives/oauth-callback.test.js +269 -0
  78. package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +28 -0
  79. package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +39 -0
  80. package/lib/vendor/blamejs/test/layer-0-primitives/openapi.test.js +37 -0
  81. package/lib/vendor/blamejs/test/layer-0-primitives/problem-details.test.js +79 -0
  82. package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +49 -0
  83. package/lib/vendor/blamejs/test/layer-0-primitives/queue-sqs.test.js +48 -0
  84. package/lib/vendor/blamejs/test/layer-0-primitives/redis-client.test.js +60 -0
  85. package/lib/vendor/blamejs/test/layer-0-primitives/safe-redirect.test.js +118 -0
  86. package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +259 -0
  87. package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +46 -0
  88. package/lib/vendor/blamejs/test/layer-0-primitives/security-headers.test.js +113 -0
  89. package/lib/vendor/blamejs/test/layer-0-primitives/security-txt.test.js +111 -0
  90. package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +62 -0
  91. package/lib/vendor/blamejs/test/layer-0-primitives/smtp-policy.test.js +39 -0
  92. package/package.json +1 -1
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * b.middleware.securityTxt — RFC 9116 /.well-known/security.txt emitter.
4
+ *
5
+ * Covers: required-field enforcement (Contact / Expires), future-expiry
6
+ * check, the served body shape, root-path opt, method gating, and the
7
+ * de-advertised `audit` opt (the middleware serves a static public file
8
+ * on a hot path — no audit-worthy event, so the knob was removed).
9
+ */
10
+
11
+ var helpers = require("../helpers");
12
+ var b = helpers.b;
13
+ var check = helpers.check;
14
+
15
+ function _mkRes() {
16
+ return {
17
+ statusCode: null,
18
+ headers: null,
19
+ bodyChunks: [],
20
+ ended: false,
21
+ writeHead: function (code, headers) { this.statusCode = code; this.headers = headers; return this; },
22
+ end: function (chunk) { if (chunk !== undefined) this.bodyChunks.push(chunk); this.ended = true; return this; },
23
+ };
24
+ }
25
+
26
+ function testServesWellKnown() {
27
+ var mw = b.middleware.securityTxt({
28
+ contact: ["mailto:security@example.com"],
29
+ expires: "2099-01-01T00:00:00Z",
30
+ policy: "https://example.com/security/policy",
31
+ });
32
+ var res = _mkRes();
33
+ var nexted = false;
34
+ mw({ method: "GET", url: "/.well-known/security.txt" }, res, function () { nexted = true; });
35
+ check("securityTxt: serves at /.well-known/security.txt", res.statusCode === 200 && res.ended && !nexted);
36
+ var body = Buffer.concat(res.bodyChunks.map(function (c) { return Buffer.isBuffer(c) ? c : Buffer.from(String(c)); })).toString("utf8");
37
+ check("securityTxt: body carries Contact line", body.indexOf("Contact: mailto:security@example.com") !== -1);
38
+ check("securityTxt: body carries Expires line", body.indexOf("Expires: 2099-01-01T00:00:00Z") !== -1);
39
+ check("securityTxt: content-type text/plain", /text\/plain/.test(res.headers["Content-Type"]));
40
+ }
41
+
42
+ function testPassthroughOnOtherPaths() {
43
+ var mw = b.middleware.securityTxt({
44
+ contact: ["mailto:security@example.com"],
45
+ expires: "2099-01-01T00:00:00Z",
46
+ });
47
+ var res = _mkRes();
48
+ var nexted = false;
49
+ mw({ method: "GET", url: "/something-else" }, res, function () { nexted = true; });
50
+ check("securityTxt: non-matching path falls through to next", nexted && res.statusCode === null);
51
+ }
52
+
53
+ function testRequiredFieldEnforcement() {
54
+ var threwNoContact = false;
55
+ try { b.middleware.securityTxt({ expires: "2099-01-01T00:00:00Z" }); }
56
+ catch (_e) { threwNoContact = true; }
57
+ check("securityTxt: missing contact throws", threwNoContact);
58
+
59
+ var threwNoExpires = false;
60
+ try { b.middleware.securityTxt({ contact: ["mailto:s@example.com"] }); }
61
+ catch (_e) { threwNoExpires = true; }
62
+ check("securityTxt: missing expires throws", threwNoExpires);
63
+
64
+ var threwPastExpiry = false;
65
+ try {
66
+ b.middleware.securityTxt({
67
+ contact: ["mailto:s@example.com"],
68
+ expires: "2000-01-01T00:00:00Z",
69
+ });
70
+ } catch (_e) { threwPastExpiry = true; }
71
+ check("securityTxt: past expires throws", threwPastExpiry);
72
+ }
73
+
74
+ function testRejectsAuditOpt() {
75
+ // `audit` was accepted-but-unread (no audit-worthy event — the
76
+ // middleware serves a static public file and already uses the
77
+ // observability sink for the served counter). De-advertised: passing
78
+ // it now throws at config time.
79
+ var threw = false;
80
+ try {
81
+ b.middleware.securityTxt({
82
+ contact: ["mailto:security@example.com"],
83
+ expires: "2099-01-01T00:00:00Z",
84
+ audit: true,
85
+ });
86
+ } catch (_e) { threw = true; }
87
+ check("securityTxt: unknown 'audit' opt rejected", threw);
88
+
89
+ // The same opts WITHOUT audit construct fine.
90
+ var mw = b.middleware.securityTxt({
91
+ contact: ["mailto:security@example.com"],
92
+ expires: "2099-01-01T00:00:00Z",
93
+ });
94
+ check("securityTxt: constructs without audit opt", typeof mw === "function");
95
+ }
96
+
97
+ async function run() {
98
+ testServesWellKnown();
99
+ testPassthroughOnOtherPaths();
100
+ testRequiredFieldEnforcement();
101
+ testRejectsAuditOpt();
102
+ }
103
+
104
+ module.exports = { run: run };
105
+
106
+ if (require.main === module) {
107
+ run().then(
108
+ function () { console.log("OK — " + helpers.getChecks() + " checks passed"); },
109
+ function (e) { console.error("FAIL:", e && e.stack || e); process.exit(1); }
110
+ );
111
+ }
@@ -198,6 +198,18 @@ function testFactoryValidation() {
198
198
  shouldThrow("rejects unsupported protocol",
199
199
  { protocol: "gcs", region: "us-east-1", accessKeyId: "x", secretAccessKey: "y" },
200
200
  /INVALID_CONFIG/);
201
+
202
+ // `ca` was an accepted-but-dead config knob — nothing in the request
203
+ // path (reqOpts → http-request → httpClient.request) threads a custom
204
+ // CA cert (the framework's PQC-only TLS posture lives solely in
205
+ // lib/pqc-agent.js; operators use NODE_EXTRA_CA_CERTS / opts.agent).
206
+ // De-advertised: passing it must now throw as an unknown option.
207
+ var threwCa = null;
208
+ try {
209
+ bucketOps.create(Object.assign({}, _baseConfig(9999), { ca: "-----BEGIN CERTIFICATE-----" }));
210
+ } catch (e) { threwCa = e; }
211
+ check("factory: de-advertised `ca` knob rejected as unknown option",
212
+ threwCa && /unknown option 'ca'/.test(threwCa.message || ""));
201
213
  }
202
214
 
203
215
  // ---- Bucket name validation ----
@@ -908,6 +920,55 @@ async function testAuditSuccessFalseDisablesSuccessAudit() {
908
920
  }
909
921
  }
910
922
 
923
+ async function testPerCallActorOverrideHonored() {
924
+ // Per-method opts accept `req` (resolves IP / user-agent / userId from a
925
+ // live request) and `actor` (an explicit identity override for callers
926
+ // performing a compliance-sensitive change on behalf of an operator).
927
+ // Both must land on the emitted audit row's `actor` field; `actor`-set
928
+ // keys win over the request-derived ones.
929
+ var auditCap = _captureAudit();
930
+ var fake = _fakeS3();
931
+ var port = await listenOnRandomPort(fake.server);
932
+ try {
933
+ var ops = bucketOps.create(Object.assign({}, _baseConfig(port), {
934
+ audit: auditCap,
935
+ }));
936
+
937
+ var fakeReq = {
938
+ ip: "203.0.113.9",
939
+ method: "PUT",
940
+ headers: { "user-agent": "ops-cli/1.0" },
941
+ };
942
+ await ops.create("actor-bucket", {
943
+ req: fakeReq,
944
+ actor: { userId: "ops-admin" },
945
+ });
946
+
947
+ var rows = auditCap.byAction("objectstore.bucket.create");
948
+ check("actor override: audit row emitted", rows.length === 1);
949
+ var actor = rows.length === 1 ? rows[0].actor : {};
950
+ check("actor override: explicit actor.userId lands on the audit row",
951
+ actor.userId === "ops-admin");
952
+ check("actor override: request-derived ip lands on the audit row",
953
+ actor.ip === "203.0.113.9");
954
+ check("actor override: request-derived userAgent lands on the audit row",
955
+ actor.userAgent === "ops-cli/1.0");
956
+
957
+ // Without actor/req, the resolved actor has the resolver's null
958
+ // defaults (no override) — default behavior unchanged.
959
+ var auditCap2 = _captureAudit();
960
+ var ops2 = bucketOps.create(Object.assign({}, _baseConfig(port), {
961
+ audit: auditCap2,
962
+ }));
963
+ await ops2.create("plain-bucket");
964
+ var rows2 = auditCap2.byAction("objectstore.bucket.create");
965
+ check("actor override: default behavior unchanged when actor/req absent",
966
+ rows2.length === 1 && rows2[0].actor && rows2[0].actor.userId === null);
967
+ } finally {
968
+ await new Promise(function (r) { fake.server.close(function () { r(); }); });
969
+ }
970
+ }
971
+
911
972
  async function run() {
912
973
  testSurface();
913
974
  testFactoryValidation();
@@ -940,6 +1001,7 @@ async function run() {
940
1001
  // v0.6.53 — audit + observability emissions
941
1002
  await testAuditObservabilityWiring();
942
1003
  await testAuditSuccessFalseDisablesSuccessAudit();
1004
+ await testPerCallActorOverrideHonored();
943
1005
  }
944
1006
 
945
1007
  module.exports = { run: run };
@@ -206,6 +206,43 @@ async function testTlsRptSubmitRejectsEmptyRua() {
206
206
  check("submit rejects empty rua", threw && /tls-rpt-bad-rua/.test(threw.code || ""));
207
207
  }
208
208
 
209
+ function testTlsRptRecordShapeRejectsReportingMta() {
210
+ // `reportingMta` was documented in the namespace example + accepted by
211
+ // recordShape's allowlist but never read (RFC 8460 §4.4 has no such
212
+ // top-level field). De-advertised: passing it throws at config time.
213
+ var threw = false;
214
+ try {
215
+ b.network.smtp.tlsRpt.recordShape({
216
+ organization: "example.com",
217
+ reportingMta: "mx1.example.com",
218
+ policies: [{ type: "sts", domain: "example.com" }],
219
+ });
220
+ } catch (_e) { threw = true; }
221
+ check("tlsRpt.recordShape: unknown 'reportingMta' opt rejected", threw);
222
+
223
+ // `reportId` IS read (report-id field) and is now in the allowlist —
224
+ // it must be accepted and flow through to the output.
225
+ var rpt = b.network.smtp.tlsRpt.recordShape({
226
+ organization: "example.com",
227
+ reportId: "rpt-fixed-1234",
228
+ policies: [{ type: "sts", domain: "example.com" }],
229
+ });
230
+ check("tlsRpt.recordShape: operator reportId flows to report-id",
231
+ rpt["report-id"] === "rpt-fixed-1234");
232
+ }
233
+
234
+ async function testTlsRptSubmitRejectsAuditOpt() {
235
+ // `audit` was accepted by submit's allowlist but never read — the
236
+ // function emits no audit row. De-advertised.
237
+ var threw = false;
238
+ try {
239
+ await b.network.smtp.tlsRpt.submit(
240
+ { "organization-name": "example.com" },
241
+ { rua: ["https://reports.example.com/submit"], audit: true });
242
+ } catch (_e) { threw = true; }
243
+ check("tlsRpt.submit: unknown 'audit' opt rejected", threw);
244
+ }
245
+
209
246
  async function run() {
210
247
  testSurface();
211
248
  testMtaStsParse();
@@ -223,6 +260,8 @@ async function run() {
223
260
  await testTlsRptFetchPolicyMissing();
224
261
  await testTlsRptSubmitMixedRua();
225
262
  await testTlsRptSubmitRejectsEmptyRua();
263
+ testTlsRptRecordShapeRejectsReportingMta();
264
+ await testTlsRptSubmitRejectsAuditOpt();
226
265
  }
227
266
 
228
267
  module.exports = { run: run };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.3.69",
3
+ "version": "0.3.71",
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": {