@blamejs/core
Advanced tools
+13
-13
@@ -43,2 +43,4 @@ "use strict"; | ||
| var traitsMod = require("./asyncapi-traits"); | ||
| var safeJson = require("./safe-json"); | ||
| var C = require("./constants"); | ||
| var { defineClass } = require("./framework-error"); | ||
@@ -474,15 +476,13 @@ var AsyncApiError = defineClass("AsyncApiError", { alwaysPermanent: true }); | ||
| function parse(jsonStringOrObject) { | ||
| var doc; | ||
| if (typeof jsonStringOrObject === "string") { | ||
| try { doc = JSON.parse(jsonStringOrObject); } // allow:bare-json-parse — operator-supplied AsyncAPI doc; size-bounded by caller | ||
| catch (e) { | ||
| throw new AsyncApiError("asyncapi/bad-json", | ||
| "asyncapi.parse: invalid JSON — " + e.message); | ||
| } | ||
| } else if (jsonStringOrObject != null && typeof jsonStringOrObject === "object") { | ||
| doc = jsonStringOrObject; | ||
| } else { | ||
| throw new AsyncApiError("asyncapi/bad-input", | ||
| "asyncapi.parse: input must be a JSON string or a plain object"); | ||
| } | ||
| // A JSON string is parsed through safeJson (proto-pollution-key strip + depth | ||
| // / size caps — a raw JSON.parse here kept a "__proto__" member and was | ||
| // unbounded on an operator-supplied document); a pre-built object passes | ||
| // through. The 16 MiB cap is generous for any real AsyncAPI document. | ||
| var doc = safeJson.parseStringOrObject(jsonStringOrObject, { | ||
| maxBytes: C.BYTES.mib(16), | ||
| errorClass: AsyncApiError, | ||
| jsonCode: "asyncapi/bad-json", | ||
| inputCode: "asyncapi/bad-input", | ||
| label: "asyncapi.parse", | ||
| }); | ||
| var errors = []; | ||
@@ -489,0 +489,0 @@ if (typeof doc.asyncapi !== "string") { |
@@ -168,4 +168,4 @@ "use strict"; | ||
| return { | ||
| header: safeJson.parse(headerStr, { maxBytes: 64 * 1024 }), // allow:bare-json-parse — header from cryptographically-verified JWT; signature verifies the bytes // allow:raw-byte-literal — JWT header cap (64 KB) | ||
| payload: safeJson.parse(payloadStr, { maxBytes: 1024 * 1024 }), // allow:bare-json-parse — payload from cryptographically-verified JWT; signature verifies the bytes // allow:raw-byte-literal — JWT payload cap (1 MB) | ||
| header: safeJson.parse(headerStr, { maxBytes: 64 * 1024 }), // allow:raw-byte-literal — JWT header cap (64 KB) | ||
| payload: safeJson.parse(payloadStr, { maxBytes: 1024 * 1024 }), // allow:raw-byte-literal — JWT payload cap (1 MB) | ||
| }; | ||
@@ -334,3 +334,3 @@ } | ||
| _issuerPayload = safeJson.parse(_b64uDecodeStr(_jwtParts[1]), | ||
| { maxBytes: 64 * 1024 }); // allow:bare-json-parse — payload only read to pull _sd_alg; final auth happens in verify() // allow:raw-byte-literal — JWT payload cap (64 KB) | ||
| { maxBytes: 64 * 1024 }); // allow:raw-byte-literal — JWT payload cap (64 KB) | ||
| } catch (_e) { _issuerPayload = null; } | ||
@@ -453,3 +453,3 @@ } | ||
| var headerObj; | ||
| try { headerObj = safeJson.parse(_b64uDecodeStr(jwtParts[0]), { maxBytes: 64 * 1024 }); } // allow:bare-json-parse — pre-verify header parse to look up the key resolver; checked again post-signature // allow:raw-byte-literal — JWT header cap (64 KB) | ||
| try { headerObj = safeJson.parse(_b64uDecodeStr(jwtParts[0]), { maxBytes: 64 * 1024 }); } // allow:raw-byte-literal — JWT header cap (64 KB) | ||
| catch (e) { | ||
@@ -628,3 +628,3 @@ throw new AuthError("auth-sd-jwt-vc/bad-header", | ||
| var kbHeaderObj; | ||
| try { kbHeaderObj = safeJson.parse(_b64uDecodeStr(maybeKbJwt.split(".")[0]), { maxBytes: 4096 }); } // allow:bare-json-parse — kb header from validated KB-JWT; signature verifies | ||
| try { kbHeaderObj = safeJson.parse(_b64uDecodeStr(maybeKbJwt.split(".")[0]), { maxBytes: 4096 }); } | ||
| catch (e) { | ||
@@ -631,0 +631,0 @@ throw new AuthError("auth-sd-jwt-vc/bad-kb-header", |
+13
-13
@@ -50,2 +50,4 @@ "use strict"; | ||
| var openapiYaml = require("./openapi-yaml"); | ||
| var safeJson = require("./safe-json"); | ||
| var C = require("./constants"); | ||
| var { defineClass } = require("./framework-error"); | ||
@@ -524,15 +526,13 @@ var audit = lazyRequire(function () { return require("./audit"); }); | ||
| function parse(jsonStringOrObject) { | ||
| var doc; | ||
| if (typeof jsonStringOrObject === "string") { | ||
| try { doc = JSON.parse(jsonStringOrObject); } // allow:bare-json-parse — operator-supplied OpenAPI doc; size-bounded by caller | ||
| catch (e) { | ||
| throw new OpenApiError("openapi/bad-json", | ||
| "openapi.parse: invalid JSON — " + e.message); | ||
| } | ||
| } else if (jsonStringOrObject != null && typeof jsonStringOrObject === "object") { | ||
| doc = jsonStringOrObject; | ||
| } else { | ||
| throw new OpenApiError("openapi/bad-input", | ||
| "openapi.parse: input must be a JSON string or a plain object"); | ||
| } | ||
| // A JSON string is parsed through safeJson (proto-pollution-key strip + depth | ||
| // / size caps — a raw JSON.parse here kept a "__proto__" member and was | ||
| // unbounded on an operator-supplied document); a pre-built object passes | ||
| // through. The 16 MiB cap is generous for any real OpenAPI document. | ||
| var doc = safeJson.parseStringOrObject(jsonStringOrObject, { | ||
| maxBytes: C.BYTES.mib(16), | ||
| errorClass: OpenApiError, | ||
| jsonCode: "openapi/bad-json", | ||
| inputCode: "openapi/bad-input", | ||
| label: "openapi.parse", | ||
| }); | ||
| var errors = []; | ||
@@ -539,0 +539,0 @@ if (typeof doc.openapi !== "string") { |
+56
-0
@@ -243,2 +243,57 @@ "use strict"; | ||
| /** | ||
| * @primitive b.safeJson.parseStringOrObject | ||
| * @signature b.safeJson.parseStringOrObject(input, opts?) | ||
| * @since 0.15.29 | ||
| * @status stable | ||
| * @related b.safeJson.parse | ||
| * | ||
| * Accept EITHER a JSON string — parsed through `parse`, so the | ||
| * proto-pollution-key strip, depth/key caps, and size cap all apply — OR an | ||
| * already-decoded plain object (returned unchanged). This is the recurring | ||
| * "operator hands me a document as a JSON string or a pre-built object" surface | ||
| * (b.openapi / b.asyncapi). Routing it here means a raw `JSON.parse` on operator | ||
| * input — which keeps a `"__proto__"` member as an own key and imposes no size | ||
| * bound — cannot be hand-rolled per consumer. The divergence each consumer needs | ||
| * (its typed error class + codes + a generous document size cap) is carried as | ||
| * data, so there is no per-consumer branch. | ||
| * | ||
| * @opts | ||
| * maxBytes: number, // forwarded to parse (default 1 MiB; capped 64 MiB) | ||
| * maxDepth: number, // forwarded to parse | ||
| * maxKeys: number, // forwarded to parse | ||
| * errorClass: function, // typed error class to throw (else SafeJsonError) | ||
| * jsonCode: string, // error code for invalid JSON (used with errorClass) | ||
| * inputCode: string, // error code for a non-string/non-object input | ||
| * label: string, // message prefix (default "safeJson.parseStringOrObject") | ||
| * | ||
| * @example | ||
| * var doc = b.safeJson.parseStringOrObject(input, { | ||
| * maxBytes: C.BYTES.mib(16), errorClass: OpenApiError, | ||
| * jsonCode: "openapi/bad-json", inputCode: "openapi/bad-input", | ||
| * label: "openapi.parse", | ||
| * }); | ||
| */ | ||
| function parseStringOrObject(input, opts) { | ||
| opts = opts || {}; | ||
| var label = opts.label || "safeJson.parseStringOrObject"; | ||
| if (typeof input === "string") { | ||
| try { return parse(input, opts); } | ||
| catch (e) { | ||
| if (typeof opts.errorClass === "function") { | ||
| throw new opts.errorClass(opts.jsonCode, label + ": invalid JSON — " + (e && e.message)); | ||
| } | ||
| throw e; | ||
| } | ||
| } | ||
| if (input !== null && typeof input === "object" && | ||
| !Buffer.isBuffer(input) && !(input instanceof Uint8Array)) { | ||
| return input; | ||
| } | ||
| if (typeof opts.errorClass === "function") { | ||
| throw new opts.errorClass(opts.inputCode, label + ": input must be a JSON string or a plain object"); | ||
| } | ||
| throw new SafeJsonError(label + ": input must be a JSON string or a plain object", "json/wrong-input-type"); | ||
| } | ||
| function _stripProtoKeys(key, value) { | ||
@@ -1001,2 +1056,3 @@ if (pick.isPoisonedKey(key)) return undefined; | ||
| parseOrDefault: parseOrDefault, | ||
| parseStringOrObject: parseStringOrObject, | ||
| isJsonObject: isJsonObject, | ||
@@ -1003,0 +1059,0 @@ stringify: stringify, |
+18
-6
@@ -34,3 +34,5 @@ "use strict"; | ||
| * derived from maxBytes; v8 kills the worker on overflow. | ||
| * - Result size cap = maxBytes / 4. | ||
| * - Result size cap = min(maxBytes / 4, 64 MiB) - the host re-parses | ||
| * the untrusted result through safeJson, whose hard ceiling bounds | ||
| * it at 64 MiB regardless of a larger maxBytes. | ||
| * | ||
@@ -67,3 +69,3 @@ * Allowed-globals list: | ||
| * - sandbox/timeout - worker exceeded timeoutMs | ||
| * - sandbox/oversized-result - worker output > maxBytes / 4 | ||
| * - sandbox/oversized-result - worker output > min(maxBytes / 4, 64 MiB) | ||
| * - sandbox/parse-error - source did not parse inside the worker | ||
@@ -88,2 +90,3 @@ * - sandbox/runtime-error - operator transform threw | ||
| var safeBuffer = require("./safe-buffer"); | ||
| var safeJson = require("./safe-json"); | ||
| var C = require("./constants"); | ||
@@ -242,5 +245,10 @@ var { SandboxError } = require("./framework-error"); | ||
| // Reserve 1/4 of maxBytes as the per-result hard cap. The worker | ||
| // refuses any result whose stringified form exceeds this. | ||
| var maxResultBytes = Math.floor(maxBytes / 4); | ||
| // Reserve 1/4 of maxBytes as the per-result hard cap, clamped to the | ||
| // host-side JSON parse ceiling so the worker's output cap and the host's | ||
| // safeJson.parse cap agree exactly. Without the clamp a sandbox with | ||
| // maxBytes over 256 MiB would let the worker emit a result the host then | ||
| // refuses (safeJson hard-caps at ABSOLUTE_MAX_BYTES) — the worker refuses | ||
| // it too, so an oversized untrusted result fails the same way on both | ||
| // sides rather than passing the worker and crossing to a rejecting host. | ||
| var maxResultBytes = Math.min(Math.floor(maxBytes / 4), safeJson.ABSOLUTE_MAX_BYTES); | ||
@@ -319,3 +327,7 @@ return new Promise(function (resolve, reject) { | ||
| var parsed; | ||
| try { parsed = (msg.resultJson === undefined) ? undefined : JSON.parse(msg.resultJson); } // allow:bare-json-parse — resultJson is produced by lib/sandbox-worker.js via JSON.stringify and bounded by maxResultBytes; never directly from operator/network input | ||
| // resultJson is the JSON.stringify of UNTRUSTED sandboxed code's return | ||
| // value: parse it through safeJson so a "__proto__" member is stripped | ||
| // and a pathologically deep/large result is bounded (maxResultBytes), not | ||
| // a raw JSON.parse that re-creates the key and is depth-unbounded. | ||
| try { parsed = (msg.resultJson === undefined) ? undefined : safeJson.parse(msg.resultJson, { maxBytes: maxResultBytes }); } | ||
| catch (eParse) { | ||
@@ -322,0 +334,0 @@ _emitAudit("sandbox.run.refused", "failure", { |
+6
-1
@@ -51,2 +51,4 @@ "use strict"; | ||
| var canonicalJson = require("./canonical-json"); | ||
| var safeJson = require("./safe-json"); | ||
| var C = require("./constants"); | ||
| var validateOpts = require("./validate-opts"); | ||
@@ -637,3 +639,6 @@ var { defineClass } = require("./framework-error"); | ||
| var canonical = canonicalJson.stringify(doc); | ||
| return JSON.stringify(JSON.parse(canonical), null, 2); // allow:bare-json-parse — canonical is canonicalJson.stringify output, not operator input | ||
| // Re-indent the canonical bytes for human-diffable output. allowProto keeps | ||
| // the canonicalizer's exact key set (this is a faithful re-format of trusted | ||
| // framework output, not a defense boundary); the size cap bounds the parse. | ||
| return JSON.stringify(safeJson.parse(canonical, { allowProto: true, maxBytes: C.BYTES.mib(16) }), null, 2); | ||
| } | ||
@@ -640,0 +645,0 @@ |
+1
-1
| { | ||
| "name": "@blamejs/core", | ||
| "version": "0.15.28", | ||
| "version": "0.15.29", | ||
| "description": "The Node framework that owns its stack.", | ||
@@ -5,0 +5,0 @@ "license": "Apache-2.0", |
+6
-6
@@ -5,6 +5,6 @@ { | ||
| "specVersion": "1.5", | ||
| "serialNumber": "urn:uuid:1bbafc03-fa7f-407c-b852-9caa724d07c2", | ||
| "serialNumber": "urn:uuid:9d6b0acf-4b88-47bc-b14b-ba2342410a0e", | ||
| "version": 1, | ||
| "metadata": { | ||
| "timestamp": "2026-06-26T06:34:07.791Z", | ||
| "timestamp": "2026-06-26T09:20:08.812Z", | ||
| "lifecycles": [ | ||
@@ -23,10 +23,10 @@ { | ||
| "component": { | ||
| "bom-ref": "@blamejs/core@0.15.28", | ||
| "bom-ref": "@blamejs/core@0.15.29", | ||
| "type": "application", | ||
| "name": "blamejs", | ||
| "version": "0.15.28", | ||
| "version": "0.15.29", | ||
| "scope": "required", | ||
| "author": "blamejs contributors", | ||
| "description": "The Node framework that owns its stack.", | ||
| "purl": "pkg:npm/%40blamejs/core@0.15.28", | ||
| "purl": "pkg:npm/%40blamejs/core@0.15.29", | ||
| "properties": [], | ||
@@ -59,3 +59,3 @@ "externalReferences": [ | ||
| { | ||
| "ref": "@blamejs/core@0.15.28", | ||
| "ref": "@blamejs/core@0.15.29", | ||
| "dependsOn": [] | ||
@@ -62,0 +62,0 @@ } |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
17250599
0.04%300509
0.03%