@blamejs/blamejs-shop 0.5.6 → 0.5.8

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 (28) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/lib/asset-manifest.json +1 -1
  3. package/lib/cost-layers.js +86 -49
  4. package/lib/plan-changes.js +14 -0
  5. package/lib/storefront.js +30 -17
  6. package/lib/vendor/MANIFEST.json +31 -15
  7. package/lib/vendor/blamejs/.github/workflows/release-container.yml +2 -2
  8. package/lib/vendor/blamejs/CHANGELOG.md +2 -0
  9. package/lib/vendor/blamejs/api-snapshot.json +2 -2
  10. package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
  11. package/lib/vendor/blamejs/lib/content-credentials.js +43 -2
  12. package/lib/vendor/blamejs/lib/guard-sql.js +29 -0
  13. package/lib/vendor/blamejs/lib/mcp.js +37 -4
  14. package/lib/vendor/blamejs/lib/metrics.js +15 -3
  15. package/lib/vendor/blamejs/lib/middleware/tus-upload.js +9 -2
  16. package/lib/vendor/blamejs/lib/parsers/safe-yaml.js +33 -2
  17. package/lib/vendor/blamejs/lib/safe-buffer.js +11 -1
  18. package/lib/vendor/blamejs/package-lock.json +2 -2
  19. package/lib/vendor/blamejs/package.json +1 -1
  20. package/lib/vendor/blamejs/release-notes/v0.16.3.json +71 -0
  21. package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +319 -0
  22. package/lib/vendor/blamejs/test/layer-0-primitives/cycle2-bugfixes.test.js +142 -0
  23. package/lib/vendor/blamejs/test/layer-0-primitives/guard-sql-coverage.test.js +432 -0
  24. package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +443 -0
  25. package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +395 -0
  26. package/lib/vendor/blamejs/test/layer-0-primitives/middleware-tus-upload-coverage.test.js +402 -0
  27. package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-yaml-coverage.test.js +292 -0
  28. package/package.json +1 -1
@@ -0,0 +1,292 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (c) blamejs contributors
3
+ "use strict";
4
+ /**
5
+ * b.parsers.yaml — error-path and adversarial-branch coverage.
6
+ *
7
+ * Companion to the happy-path yaml checks in test/00-primitives.js
8
+ * (string/number/bool/null scalars, block+flow collections, block
9
+ * scalars, quoted strings, and the headline security rejections). This
10
+ * file drives the branches those tests leave uncovered: opt-shape
11
+ * refusals, wrong-input-type rejection, the resource caps (maxBytes /
12
+ * maxKeys / maxDepth for both block and flow nesting), the full escape-
13
+ * error family, the flow-parse error surface, block-scalar header
14
+ * refusals, the prototype-pollution key guard across every key-decode
15
+ * path, and the core-schema float edges (.inf / -.inf / .nan) that the
16
+ * smoke happy-path never exercises.
17
+ *
18
+ * Every assertion runs offline through the public b.parsers.yaml.parse
19
+ * consumer path — no private internals, no live backend.
20
+ */
21
+
22
+ var helpers = require("../helpers");
23
+ var b = helpers.b;
24
+ var check = helpers.check;
25
+
26
+ var yaml = b.parsers.yaml;
27
+
28
+ // Capture the SafeYamlError code from a synchronous parse throw. Returns
29
+ // the literal string "NO-THROW" when parse returns a value, so a missing
30
+ // rejection fails the check loudly rather than silently matching.
31
+ function _code(src, opts) {
32
+ try {
33
+ yaml.parse(src, opts);
34
+ return "NO-THROW";
35
+ } catch (e) {
36
+ return (e && e.code) || ("PLAIN:" + (e && e.message));
37
+ }
38
+ }
39
+
40
+ // ---- opt-shape refusals (config-time throw: yaml/bad-opt) ----
41
+
42
+ function testBadNumericOpts() {
43
+ // maxBytes / maxDepth / maxKeys must each be a positive finite integer.
44
+ // Infinity / NaN / negative / zero / non-integer all silently lift the
45
+ // DoS cap they enforce, so each is rejected at the entry point.
46
+ check("maxBytes=Infinity → bad-opt", _code("a: 1", { maxBytes: Infinity }) === "yaml/bad-opt");
47
+ check("maxBytes=NaN → bad-opt", _code("a: 1", { maxBytes: NaN }) === "yaml/bad-opt");
48
+ check("maxBytes=-1 → bad-opt", _code("a: 1", { maxBytes: -1 }) === "yaml/bad-opt");
49
+ check("maxBytes=1.5 → bad-opt", _code("a: 1", { maxBytes: 1.5 }) === "yaml/bad-opt");
50
+ check("maxBytes=0 → bad-opt", _code("a: 1", { maxBytes: 0 }) === "yaml/bad-opt");
51
+ check("maxDepth=Infinity → bad-opt", _code("a: 1", { maxDepth: Infinity }) === "yaml/bad-opt");
52
+ check("maxDepth=0 → bad-opt", _code("a: 1", { maxDepth: 0 }) === "yaml/bad-opt");
53
+ check("maxKeys=NaN → bad-opt", _code("a: 1", { maxKeys: NaN }) === "yaml/bad-opt");
54
+ check("maxKeys=-5 → bad-opt", _code("a: 1", { maxKeys: -5 }) === "yaml/bad-opt");
55
+ check("maxKeys=0 → bad-opt", _code("a: 1", { maxKeys: 0 }) === "yaml/bad-opt");
56
+ }
57
+
58
+ // ---- wrong input type (yaml/wrong-input-type) ----
59
+
60
+ function testWrongInputType() {
61
+ check("number input rejected", _code(42) === "yaml/wrong-input-type");
62
+ check("object input rejected", _code({ a: 1 }) === "yaml/wrong-input-type");
63
+ check("null input rejected", _code(null) === "yaml/wrong-input-type");
64
+ check("undefined input rejected", _code(undefined) === "yaml/wrong-input-type");
65
+ check("array input rejected", _code([1, 2]) === "yaml/wrong-input-type");
66
+ check("boolean input rejected", _code(true) === "yaml/wrong-input-type");
67
+ }
68
+
69
+ function testByteInputsParse() {
70
+ // Buffer / Uint8Array are the accepted non-string shapes — they decode
71
+ // as UTF-8 and parse identically to the string form.
72
+ var fromBuf = yaml.parse(Buffer.from("a: 1\n", "utf8"));
73
+ check("Buffer input parses", fromBuf.a === 1);
74
+ var fromU8 = yaml.parse(new Uint8Array([0x61, 0x3a, 0x20, 0x31])); // "a: 1"
75
+ check("Uint8Array input parses", fromU8.a === 1);
76
+ }
77
+
78
+ // ---- resource caps ----
79
+
80
+ function testTooLargeAndKeys() {
81
+ // maxBytes cap on the whole input.
82
+ check("oversize input → too-large",
83
+ _code("a: " + "x".repeat(4000), { maxBytes: b.constants.BYTES.kib(1) }) === "yaml/too-large");
84
+ // maxKeys cap — the 3rd mapping key trips a 2-key ceiling.
85
+ check("maxKeys exceeded → too-many-keys",
86
+ _code("a: 1\nb: 2\nc: 3", { maxKeys: 2 }) === "yaml/too-many-keys");
87
+ }
88
+
89
+ function testDepthCaps() {
90
+ // Block nesting deeper than maxDepth.
91
+ check("block nesting → too-deep",
92
+ _code("a:\n b:\n c:\n d: 1", { maxDepth: 3 }) === "yaml/too-deep");
93
+ // Flow sequence nesting deeper than maxDepth (independent recursion path).
94
+ var deepFlow = "root: " + "[".repeat(10) + "1" + "]".repeat(10);
95
+ check("flow-sequence nesting → too-deep",
96
+ _code(deepFlow, { maxDepth: 3 }) === "yaml/too-deep");
97
+ // Flow mapping nesting also caps.
98
+ var deepMap = "root: " + "{a: ".repeat(10) + "1" + "}".repeat(10);
99
+ check("flow-mapping nesting → too-deep",
100
+ _code(deepMap, { maxDepth: 3 }) === "yaml/too-deep");
101
+ }
102
+
103
+ // ---- banned constructs the smoke set does not reach ----
104
+
105
+ function testBannedConstructs() {
106
+ check("complex key '? key' → complex-key-banned",
107
+ _code("? key\n: value") === "yaml/complex-key-banned");
108
+ check("%TAG directive → directives-banned",
109
+ _code("%TAG ! tag:example,2000:\n---\na: 1") === "yaml/directives-banned");
110
+ check("'...' end-of-doc marker → multi-document",
111
+ _code("a: 1\n...") === "yaml/multi-document");
112
+ check("bare alias '*a' → aliases-banned",
113
+ _code("a: *ref") === "yaml/aliases-banned");
114
+ }
115
+
116
+ // ---- prototype-pollution key guard, every decode path ----
117
+
118
+ function testPoisonedKeys() {
119
+ // Block plain key.
120
+ check("block 'constructor:' → poisoned-key",
121
+ _code("constructor: 1") === "yaml/poisoned-key");
122
+ check("block 'prototype:' → poisoned-key",
123
+ _code("prototype: 1") === "yaml/poisoned-key");
124
+ // Double-quoted key that decodes to a poisoned name.
125
+ check("double-quoted '__proto__' key → poisoned-key",
126
+ _code('"__proto__": 1') === "yaml/poisoned-key");
127
+ // Single-quoted key.
128
+ check("single-quoted '__proto__' key → poisoned-key",
129
+ _code("'__proto__': 1") === "yaml/poisoned-key");
130
+ // Flow-mapping key (no space after colon keeps it in flow style).
131
+ check("flow-mapping '__proto__' key → poisoned-key",
132
+ _code("root: {__proto__: 1}") === "yaml/poisoned-key");
133
+ // Compact mapping inside a block sequence item.
134
+ check("compact-sequence 'constructor:' key → poisoned-key",
135
+ _code("- constructor: 1") === "yaml/poisoned-key");
136
+ // None of the above may have mutated Object.prototype.
137
+ check("Object.prototype not polluted", typeof ({}).polluted === "undefined");
138
+ check("Object.prototype constructor intact", ({}).constructor === Object);
139
+ }
140
+
141
+ // ---- double-quoted escape errors (yaml/bad-escape) ----
142
+
143
+ function testEscapeErrors() {
144
+ check("bad \\u hex → bad-escape", _code('a: "\\uZZZZ"') === "yaml/bad-escape");
145
+ check("short \\u → bad-escape", _code('a: "\\u12"') === "yaml/bad-escape");
146
+ check("bad \\U hex → bad-escape", _code('a: "\\UZZZZZZZZ"') === "yaml/bad-escape");
147
+ check("\\U past U+10FFFF → bad-escape", _code('a: "\\UFFFFFFFF"') === "yaml/bad-escape");
148
+ check("unknown escape → bad-escape", _code('a: "\\q"') === "yaml/bad-escape");
149
+ }
150
+
151
+ function testEscapesDecode() {
152
+ // The printable accepted escape set decodes to the intended chars.
153
+ var d = yaml.parse('a: "x\\ny\\tz\\\\w\\"q\\r\\b\\f\\/p"');
154
+ check("escape set decodes",
155
+ d.a === "x\ny\tz\\w\"q\r\b\f/p");
156
+ // The \0 escape decodes to a real NUL — built via fromCharCode so no
157
+ // raw NUL byte lands in this source file.
158
+ var dz = yaml.parse('a: "A\\0B"');
159
+ check("\\0 decodes to NUL", dz.a === "A" + String.fromCharCode(0) + "B");
160
+ // \u (BMP) and \U (astral) surrogate-pair handling.
161
+ var d2 = yaml.parse('a: "\\u0041\\U0001F600"');
162
+ check("\\u + \\U decode", d2.a === "A\u{1F600}");
163
+ }
164
+
165
+ // ---- unterminated strings (yaml/unterminated-string) ----
166
+
167
+ function testUnterminatedStrings() {
168
+ check("unterminated double-quoted → unterminated-string",
169
+ _code('a: "no close') === "yaml/unterminated-string");
170
+ check("unterminated single-quoted → unterminated-string",
171
+ _code("a: 'no close") === "yaml/unterminated-string");
172
+ check("unterminated quote inside flow → unterminated-string",
173
+ _code('root: ["abc') === "yaml/unterminated-string");
174
+ }
175
+
176
+ // ---- flow-collection parse errors ----
177
+
178
+ function testFlowErrors() {
179
+ check("flow mapping missing ':' → bad-flow",
180
+ _code("root: {x 1}") === "yaml/bad-flow");
181
+ check("flow sequence bad separator → bad-flow",
182
+ _code("root: [1 2 junk") === "yaml/bad-flow");
183
+ check("flow sequence trailing comma+EOF → unterminated-flow",
184
+ _code("root: [1,") === "yaml/unterminated-flow");
185
+ check("flow mapping trailing comma+EOF → unterminated-flow",
186
+ _code("root: {a: 1,") === "yaml/unterminated-flow");
187
+ }
188
+
189
+ // ---- block-scalar header refusals (yaml/bad-block-scalar) ----
190
+
191
+ function testBlockScalarHeaderErrors() {
192
+ check("inline '|-+' (two chomps) → bad-block-scalar",
193
+ _code("a: |-+\n x") === "yaml/bad-block-scalar");
194
+ check("inline '|23' (two indent digits) → bad-block-scalar",
195
+ _code("a: |23\n x") === "yaml/bad-block-scalar");
196
+ check("inline '|x' (garbage indicator) → bad-block-scalar",
197
+ _code("a: |x\n y") === "yaml/bad-block-scalar");
198
+ }
199
+
200
+ function testBlockScalarChomp() {
201
+ // Exercise every chomp branch through the public path.
202
+ var keep = yaml.parse("a: |+\n line\n\n");
203
+ check("keep chomp retains trailing blanks", keep.a === "line\n\n\n");
204
+ var strip = yaml.parse("a: |-\n line\n\n");
205
+ check("strip chomp removes trailing newlines", strip.a === "line");
206
+ var foldedStrip = yaml.parse("a: >-\n one\n two\n");
207
+ check("folded+strip collapses and strips", foldedStrip.a === "one two");
208
+ var explicit = yaml.parse("a: |2\n hello\n");
209
+ check("explicit indent indicator honored", explicit.a === " hello\n");
210
+ }
211
+
212
+ // ---- structural refusals (expected-key / bad-indent / tab-indent) ----
213
+
214
+ function testStructuralErrors() {
215
+ check("non-key line in mapping → expected-key",
216
+ _code("a: 1\nplain line no colon here") === "yaml/expected-key");
217
+ check("sequence item where key expected → expected-key",
218
+ _code("a: 1\n- item") === "yaml/expected-key");
219
+ check("over-indented sibling key → bad-indent",
220
+ _code("a: 1\n b: 2") === "yaml/bad-indent");
221
+ check("mis-indented sequence item → bad-indent",
222
+ _code("- a\n - b") === "yaml/bad-indent");
223
+ check("tab in indentation → tab-indent",
224
+ _code("a:\n\tb: 1") === "yaml/tab-indent");
225
+ }
226
+
227
+ function testTrailingContentAfterQuoted() {
228
+ // A quoted scalar followed by non-comment content is rejected — the
229
+ // whole line must be the string.
230
+ check("junk after quoted scalar → trailing-content",
231
+ _code('a: "x" junk') === "yaml/trailing-content");
232
+ }
233
+
234
+ // ---- core-schema float edges (uncovered by the happy-path smoke) ----
235
+
236
+ function testFloatEdges() {
237
+ check(".inf → +Infinity", yaml.parse("a: .inf").a === Infinity);
238
+ check("-.inf → -Infinity", yaml.parse("a: -.inf").a === -Infinity);
239
+ check("+.inf → +Infinity", yaml.parse("a: +.inf").a === Infinity);
240
+ check(".INF (caps) → +Infinity", yaml.parse("a: .INF").a === Infinity);
241
+ check(".nan → NaN", Number.isNaN(yaml.parse("a: .nan").a));
242
+ // A huge integer that would lose precision stays a string, not a
243
+ // silently-rounded Number.
244
+ var huge = yaml.parse("a: 999999999999999999999999").a;
245
+ check("precision-losing int stays string", huge === "999999999999999999999999");
246
+ // Octal / hex base prefixes resolve to numbers.
247
+ check("0o755 → 493", yaml.parse("a: 0o755").a === 493);
248
+ check("0x1F → 31", yaml.parse("a: 0x1F").a === 31);
249
+ }
250
+
251
+ // ---- empty / comment-only documents resolve to null ----
252
+
253
+ function testEmptyDocuments() {
254
+ check("empty string → null", yaml.parse("") === null);
255
+ check("comment-only doc → null", yaml.parse("# just a comment\n# more") === null);
256
+ check("blank-only doc → null", yaml.parse("\n\n\n") === null);
257
+ check("key with empty value → null", yaml.parse("a:").a === null);
258
+ }
259
+
260
+ function run() {
261
+ testBadNumericOpts();
262
+ testWrongInputType();
263
+ testByteInputsParse();
264
+ testTooLargeAndKeys();
265
+ testDepthCaps();
266
+ testBannedConstructs();
267
+ testPoisonedKeys();
268
+ testEscapeErrors();
269
+ testEscapesDecode();
270
+ testUnterminatedStrings();
271
+ testFlowErrors();
272
+ testBlockScalarHeaderErrors();
273
+ testBlockScalarChomp();
274
+ testStructuralErrors();
275
+ testTrailingContentAfterQuoted();
276
+ testFloatEdges();
277
+ testEmptyDocuments();
278
+ }
279
+
280
+ module.exports = { run: run };
281
+
282
+ // Allow direct execution: `node test/layer-0-primitives/parsers-safe-yaml-coverage.test.js`
283
+ if (require.main === module) {
284
+ try {
285
+ run();
286
+ console.log("OK — parsers-safe-yaml-coverage " + helpers.getChecks() + " checks passed");
287
+ process.exit(0);
288
+ } catch (e) {
289
+ console.error(helpers.formatErr(e));
290
+ process.exit(1);
291
+ }
292
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blamejs/blamejs-shop",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
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": {