@blamejs/core 0.17.0 → 0.17.1
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/dark-patterns.js +5 -2
- package/lib/gate-contract.js +10 -2
- package/lib/guard-imap-command.js +10 -2
- package/lib/guard-jmap.js +19 -3
- package/lib/guard-managesieve-command.js +10 -2
- package/lib/guard-pop3-command.js +10 -2
- package/lib/mtls-ca.js +18 -2
- package/lib/safe-ical.js +7 -3
- package/lib/safe-mime.js +12 -1
- package/lib/safe-vcard.js +7 -3
- 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.17.x
|
|
10
10
|
|
|
11
|
+
- v0.17.1 (2026-07-17) — **Profile, posture, and capability name lookups across the content-safety and mail-protocol guards reject a prototype-member name instead of running under it, the mTLS CA can generate a CRL after a fingerprint-only revocation, and a hostile MIME filename no longer crashes attachment extraction.** A second family of guards resolved a profile, posture, or capability name against a plain-object lookup table with a truthiness or in-operator guard, so a name that is an inherited Object.prototype member (constructor, __proto__, toString) slipped the guard and the gate ran under the inherited member instead of refusing the unknown name. The shared profile resolver (b.gateContract.makeProfileResolver, which the guard family composes), the iCal and vCard content guards, the dark-patterns posture check, the JMAP capability allowlist, and the IMAP/POP3/ManageSieve command guards now all resolve names through an own-property check. Separately, b.mtlsCa.generateCrl crashed with a null-serial error once any certificate had been revoked by fingerprint (the mode the require-mTLS gate pins on), dropping every serial-keyed revocation from the published CRL; and b.safeMime.extractAttachments threw an uncaught URIError on a hostile Content-Disposition filename with a malformed percent-escape, crashing the caller. **Fixed:** *mTLS CA generates a CRL after a fingerprint-only revocation* — b.mtlsCa.revoke accepts a certificate fingerprint (the value the require-mTLS gate pins on and generateClientCert surfaces for exactly this use), which is stored without a serial number. b.mtlsCa.generateCrl then mapped the whole revocation registry into CRL entries and handed the null serial to the CRL encoder, which threw -- aborting CRL generation entirely. Because one fingerprint-only revocation broke every subsequent CRL build, all serial-keyed revocations were silently dropped from the published CRL and it could never be regenerated, going stale for external CRL-based revocation checking. generateCrl now projects out fingerprint-only entries (which a standard X.509 CRL cannot represent) before encoding and reports how many were omitted; fingerprint-only revocations remain enforced through the mTLS gate, and the CRL publishes every serial-keyed revocation. · *A hostile MIME filename no longer crashes attachment extraction* — b.safeMime.extractAttachments decoded an RFC 2231 / RFC 5987 extended filename parameter (filename*=charset''percent-encoded) with an unguarded percent-decode, so a malformed escape -- a truncated %, a non-hex %ZZ, or a sequence that decodes to invalid UTF-8 -- threw an uncaught URIError that escaped the parser's typed-error contract and crashed the caller (for example a mail store extracting attachments). A one-line hostile Content-Disposition header was a trivial denial of service. The decode now degrades to the still-encoded filename on failure, matching the framework's handling of every other percent-decode site, so a hostile filename yields a best-effort name that downstream filename guards still vet rather than a crash. **Security:** *Guard profile, posture, and capability lookups reject a prototype-member name* — A group of content-safety and mail-protocol guards resolved a caller- or request-supplied profile / posture / capability name against a plain-object lookup table using a truthiness check (var caps = TABLE[name]; if (!caps)) or the prototype-chain-aware in operator. A name that names an inherited Object.prototype member -- constructor, __proto__, toString -- is truthy (or present via the prototype chain), so it slipped the guard and the gate ran under the inherited member instead of refusing the unknown name. The shared resolver b.gateContract.makeProfileResolver (composed by the idempotency-key, mail-compose, message-id and other guards) now guards both its posture and profile lookups with hasOwnProperty; b.safeIcal, b.safeVcard, b.darkPatterns, b.guardJmap (capability allowlist and profile/posture), and the b.guardImapCommand / b.guardPop3Command / b.guardManageSieveCommand command guards do the same. An unknown or prototype-member name is now rejected with the guard's typed error; supported names are unaffected. This extends the same own-property hardening applied to the crypto algorithm-table lookups in 0.17.0 to the guard-family profile resolvers.
|
|
12
|
+
|
|
11
13
|
- v0.17.0 (2026-07-17) — **Algorithm and key-type lookups across the crypto verifiers reject a prototype-member name instead of accepting it, the ZIP reader honors the operator's decompression-ratio policy, and signed S3/GCS query parameters transmit the space encoding they were signed with.** A family of verifiers resolved an algorithm, hash, or key-type name against a plain-object lookup table with a truthiness or `in`-operator guard, so a name that is an inherited Object.prototype member (constructor, __proto__, toString, valueOf) slipped the guard and resolved to the inherited member: b.jwk thumbprinted an attacker-crafted key to a predictable digest, b.vc and b.contentCredentials emitted a real signature under a bogus algorithm, and b.sdJwtVc accepted an attacker-controlled hash name from an unsigned issuer payload. All of them now reject a name that is not an own property of the table. Separately, b.archive's ZIP reader silently capped the decompression ratio at its composed default and ignored the operator's configured bomb policy, refusing legitimate highly-compressible entries; and b.storage's SigV4 (and the GCS V4 presigner) signed a query space as %20 but transmitted it as +, so a signed parameter carrying a space was rejected as a signature mismatch. **Fixed:** *ZIP extraction honors the operator's decompression-ratio policy* — b.archive's random-access ZIP reader composed b.safeDecompress for each DEFLATE entry without forwarding the reader's own expansion-ratio cap, so the decompression inherited safeDecompress's stricter default ratio and silently overrode the operator's configured bomb policy. A ZIP entry that legitimately compressed better than the default ratio (logs, JSON, telemetry, zero-padded or sparse binaries) was refused even when the operator's policy permitted it. The reader now forwards its configured maximum expansion ratio into the decode, so the actual-bytes ratio re-check uses the same cap the declared-size gate already enforced. · *Signed S3 and GCS query parameters transmit the space encoding they were signed with* — b.storage's SigV4 request signing (S3, R2, MinIO, and the S3-compatible backends) and the GCS V4 presigner sign the canonical query string, which encodes a space as %20 per the AWS/GCS specification, but transmitted the request via the WHATWG URL serializer, which encodes a space as +. A signed query parameter carrying a literal space -- a response-content-disposition filename or a list prefix -- was therefore signed over %20 but sent as +, so the storage server re-canonicalized to different bytes and rejected the request with a signature mismatch. The signer now aligns the wire query to the signed canonical encoding (rewriting a bare + back to %20) at each signing sink, so the transmitted query is byte-identical to what the signature commits to. **Security:** *Crypto algorithm and key-type lookups reject a prototype-member name* — Several verifiers resolved a caller- or attacker-supplied algorithm / hash / key-type name against a plain-object lookup table using a truthiness check (`var v = TABLE[name]; if (!v) reject`) or the prototype-chain-aware `in` operator. A name that names an inherited Object.prototype member -- constructor, __proto__, toString, valueOf, toLocaleString -- is truthy (or present via the prototype chain), so it slipped the guard and resolved to the inherited member. The observed consequences: b.jwk.thumbprint / canonicalize accepted an unsupported kty and thumbprinted the key to a single predictable digest; b.vc.issue (JOSE) and b.contentCredentials.signCose emitted a real signature under a bogus algorithm id; and b.sdJwtVc.present accepted an attacker-controlled _sd_alg read from the unsigned issuer payload. b.tsa and the SD-JWT hash-disclosure path returned a raw runtime error rather than a typed rejection. Every one of these table lookups now checks Object.prototype.hasOwnProperty before indexing, so a name that is not an own member of the table is rejected with the primitive's typed error. Supported algorithm and key-type names are unaffected.
|
|
12
14
|
|
|
13
15
|
## v0.16.x
|
package/lib/dark-patterns.js
CHANGED
|
@@ -266,11 +266,14 @@ function assertParity(signup, cancel, opts) {
|
|
|
266
266
|
"darkPatterns.assertParity: resourceId differs between flows");
|
|
267
267
|
}
|
|
268
268
|
var postureName = opts.posture || "ftc-2024";
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
// Own-property lookup: postureName is operator input, so a bare
|
|
270
|
+
// `POSTURES[postureName]` with a truthiness guard would let a prototype key
|
|
271
|
+
// ("constructor") resolve to an inherited member and run under it (fail-open).
|
|
272
|
+
if (!Object.prototype.hasOwnProperty.call(POSTURES, postureName)) {
|
|
271
273
|
throw errorClass.factory("dark-patterns/bad-posture",
|
|
272
274
|
"darkPatterns.assertParity: unknown posture " + postureName);
|
|
273
275
|
}
|
|
276
|
+
var posture = POSTURES[postureName];
|
|
274
277
|
|
|
275
278
|
var toleranceClicks = typeof opts.toleranceClicks === "number"
|
|
276
279
|
? opts.toleranceClicks : posture.toleranceClicks;
|
package/lib/gate-contract.js
CHANGED
|
@@ -1190,12 +1190,20 @@ function makeProfileResolver(cfg) {
|
|
|
1190
1190
|
var label = _guardLabelFromError(ErrorClass);
|
|
1191
1191
|
return function resolveProfile(opts) {
|
|
1192
1192
|
opts = opts || {};
|
|
1193
|
-
|
|
1193
|
+
// Own-property lookups only: opts.posture / opts.profile are attacker/
|
|
1194
|
+
// operator input, so a bare bracket read would let a prototype key
|
|
1195
|
+
// ("constructor" / "__proto__" / "toString") resolve to an inherited
|
|
1196
|
+
// truthy member — the posture read would return it as the profile name,
|
|
1197
|
+
// and `!profiles[p]` would treat it as a known profile and skip the
|
|
1198
|
+
// bad-profile refusal, running the gate under a bogus profile (fail-open).
|
|
1199
|
+
if (opts.posture && postures &&
|
|
1200
|
+
Object.prototype.hasOwnProperty.call(postures, opts.posture) &&
|
|
1201
|
+
postures[opts.posture]) {
|
|
1194
1202
|
var pn = postures[opts.posture];
|
|
1195
1203
|
return byObject ? profiles[pn] : pn;
|
|
1196
1204
|
}
|
|
1197
1205
|
var p = opts.profile || dft;
|
|
1198
|
-
if (!profiles
|
|
1206
|
+
if (!Object.prototype.hasOwnProperty.call(profiles, p)) {
|
|
1199
1207
|
throw ErrorClass.factory(codePrefix + "/bad-profile",
|
|
1200
1208
|
label + ": unknown profile '" + p + "' (use " +
|
|
1201
1209
|
Object.keys(profiles).join(" / ") + ")");
|
|
@@ -198,10 +198,18 @@ var LITERAL_SMUGGLE_RE = /\{[0-9]+\+?\}(?!\s*$)/;
|
|
|
198
198
|
function validate(line, opts) {
|
|
199
199
|
opts = opts || {};
|
|
200
200
|
var profileName = typeof opts.profile === "string" ? opts.profile : DEFAULT_PROFILE;
|
|
201
|
-
|
|
201
|
+
// hasOwnProperty on both lookups: opts.profile / opts.posture are caller
|
|
202
|
+
// input, and a bare bracket read resolves an inherited Object.prototype
|
|
203
|
+
// key (constructor / __proto__ / toString / ...) to a truthy value —
|
|
204
|
+
// PROFILES["constructor"] is the Object function, so `if (!caps)` never
|
|
205
|
+
// fires and every byte / literal cap compares against `undefined` (fails
|
|
206
|
+
// open). An unknown posture is ignored (falls through to profile/default);
|
|
207
|
+
// an unknown profile throws bad-profile.
|
|
208
|
+
if (opts.posture && Object.prototype.hasOwnProperty.call(COMPLIANCE_POSTURES, opts.posture)) {
|
|
202
209
|
profileName = COMPLIANCE_POSTURES[opts.posture];
|
|
203
210
|
}
|
|
204
|
-
var caps = PROFILES
|
|
211
|
+
var caps = Object.prototype.hasOwnProperty.call(PROFILES, profileName)
|
|
212
|
+
? PROFILES[profileName] : undefined;
|
|
205
213
|
if (!caps) {
|
|
206
214
|
throw new GuardImapCommandError("guard-imap-command/bad-profile",
|
|
207
215
|
"guardImapCommand.validate: unknown profile '" + profileName + "'");
|
package/lib/guard-jmap.js
CHANGED
|
@@ -133,10 +133,18 @@ var CORE_CAPABILITIES = Object.freeze({
|
|
|
133
133
|
function validate(rawBody, opts) {
|
|
134
134
|
opts = opts || {};
|
|
135
135
|
var profileName = typeof opts.profile === "string" ? opts.profile : DEFAULT_PROFILE;
|
|
136
|
-
|
|
136
|
+
// hasOwnProperty on both lookups: opts.profile / opts.posture are caller
|
|
137
|
+
// input, and a bare bracket read resolves an inherited Object.prototype
|
|
138
|
+
// key (constructor / __proto__ / toString / ...) to a truthy value —
|
|
139
|
+
// PROFILES["constructor"] is the Object function, so `if (!caps)` never
|
|
140
|
+
// fires and every size / count cap compares against `undefined` (fails
|
|
141
|
+
// open). An unknown posture is ignored (falls through to profile/default)
|
|
142
|
+
// as before; an unknown profile throws bad-profile.
|
|
143
|
+
if (opts.posture && Object.prototype.hasOwnProperty.call(COMPLIANCE_POSTURES, opts.posture)) {
|
|
137
144
|
profileName = COMPLIANCE_POSTURES[opts.posture];
|
|
138
145
|
}
|
|
139
|
-
var caps = PROFILES
|
|
146
|
+
var caps = Object.prototype.hasOwnProperty.call(PROFILES, profileName)
|
|
147
|
+
? PROFILES[profileName] : undefined;
|
|
140
148
|
if (!caps) {
|
|
141
149
|
throw new GuardJmapError("guard-jmap/bad-profile",
|
|
142
150
|
"guardJmap.validate: unknown profile '" + profileName + "'");
|
|
@@ -198,7 +206,15 @@ function validate(rawBody, opts) {
|
|
|
198
206
|
throw new GuardJmapError("urn:ietf:params:jmap:error:invalidArguments",
|
|
199
207
|
"guardJmap.validate: `using[" + ui + "]` must be a string capability URI");
|
|
200
208
|
}
|
|
201
|
-
|
|
209
|
+
// hasOwnProperty on both tables: `cap` is attacker-supplied (from the
|
|
210
|
+
// request body). A bare `serverCaps[cap]` bracket read resolves an
|
|
211
|
+
// inherited Object.prototype member (constructor / __proto__ / toString
|
|
212
|
+
// / ...) to a truthy value, letting a prototype-key capability pass the
|
|
213
|
+
// allowlist the server never advertised. Own-key AND truthy preserves
|
|
214
|
+
// the operator's "capability: false disables it" semantics.
|
|
215
|
+
var advertised = Object.prototype.hasOwnProperty.call(CORE_CAPABILITIES, cap) ||
|
|
216
|
+
(Object.prototype.hasOwnProperty.call(serverCaps, cap) && serverCaps[cap]);
|
|
217
|
+
if (!advertised) {
|
|
202
218
|
throw new GuardJmapError("urn:ietf:params:jmap:error:unknownCapability",
|
|
203
219
|
"guardJmap.validate: capability '" + cap + "' not advertised by this server");
|
|
204
220
|
}
|
|
@@ -202,10 +202,18 @@ var LITERAL_RE = /^\{([0-9]{1,10})(\+?)\}$/;
|
|
|
202
202
|
function validate(line, opts) {
|
|
203
203
|
opts = opts || {};
|
|
204
204
|
var profileName = typeof opts.profile === "string" ? opts.profile : DEFAULT_PROFILE;
|
|
205
|
-
|
|
205
|
+
// hasOwnProperty on both lookups: opts.profile / opts.posture are caller
|
|
206
|
+
// input, and a bare bracket read resolves an inherited Object.prototype
|
|
207
|
+
// key (constructor / __proto__ / toString / ...) to a truthy value —
|
|
208
|
+
// PROFILES["constructor"] is the Object function, so `if (!caps)` never
|
|
209
|
+
// fires and every line / script / script-name cap compares against
|
|
210
|
+
// `undefined` (fails open). An unknown posture is ignored (falls through
|
|
211
|
+
// to profile/default); an unknown profile throws bad-profile.
|
|
212
|
+
if (opts.posture && Object.prototype.hasOwnProperty.call(COMPLIANCE_POSTURES, opts.posture)) {
|
|
206
213
|
profileName = COMPLIANCE_POSTURES[opts.posture];
|
|
207
214
|
}
|
|
208
|
-
var caps = PROFILES
|
|
215
|
+
var caps = Object.prototype.hasOwnProperty.call(PROFILES, profileName)
|
|
216
|
+
? PROFILES[profileName] : undefined;
|
|
209
217
|
if (!caps) {
|
|
210
218
|
throw new GuardManageSieveCommandError("guard-managesieve-command/bad-profile",
|
|
211
219
|
"guardManageSieveCommand.validate: unknown profile '" + profileName + "'");
|
|
@@ -155,10 +155,18 @@ var MSG_NUM_RE = /^[1-9][0-9]{0,9}$/;
|
|
|
155
155
|
function validate(line, opts) {
|
|
156
156
|
opts = opts || {};
|
|
157
157
|
var profileName = typeof opts.profile === "string" ? opts.profile : DEFAULT_PROFILE;
|
|
158
|
-
|
|
158
|
+
// hasOwnProperty on both lookups: opts.profile / opts.posture are caller
|
|
159
|
+
// input, and a bare bracket read resolves an inherited Object.prototype
|
|
160
|
+
// key (constructor / __proto__ / toString / ...) to a truthy value —
|
|
161
|
+
// PROFILES["constructor"] is the Object function, so `if (!caps)` never
|
|
162
|
+
// fires and every line / username / password cap compares against
|
|
163
|
+
// `undefined` (fails open). An unknown posture is ignored (falls through
|
|
164
|
+
// to profile/default); an unknown profile throws bad-profile.
|
|
165
|
+
if (opts.posture && Object.prototype.hasOwnProperty.call(COMPLIANCE_POSTURES, opts.posture)) {
|
|
159
166
|
profileName = COMPLIANCE_POSTURES[opts.posture];
|
|
160
167
|
}
|
|
161
|
-
var caps = PROFILES
|
|
168
|
+
var caps = Object.prototype.hasOwnProperty.call(PROFILES, profileName)
|
|
169
|
+
? PROFILES[profileName] : undefined;
|
|
162
170
|
if (!caps) {
|
|
163
171
|
throw new GuardPop3CommandError("guard-pop3-command/bad-profile",
|
|
164
172
|
"guardPop3Command.validate: unknown profile '" + profileName + "'");
|
package/lib/mtls-ca.js
CHANGED
|
@@ -607,7 +607,21 @@ function create(opts) {
|
|
|
607
607
|
"framework's bundled CA engine, which supports it");
|
|
608
608
|
}
|
|
609
609
|
var ca = await initCA();
|
|
610
|
-
var
|
|
610
|
+
var allRevocations = revocationStore.list();
|
|
611
|
+
// A standard X.509 CRL (RFC 5280 §5.1) is keyed by certificate serial
|
|
612
|
+
// number. revoke({ fingerprint }) — a first-class revocation mode, and the
|
|
613
|
+
// value the require-mtls gate pins on — stores no serial (serialNumber is
|
|
614
|
+
// null). Such an entry cannot be represented in a CRL, so project it out
|
|
615
|
+
// here rather than handing a null serial to the CRL encoder: the encoder
|
|
616
|
+
// throws on it, which would break CRL generation for the ENTIRE registry,
|
|
617
|
+
// dropping the serial-keyed certs that CAN be published from every fresh
|
|
618
|
+
// CRL (a fail-open for those certs' published revocation). Fingerprint-only
|
|
619
|
+
// revocations stay enforced through isRevoked()/the mTLS gate, which is
|
|
620
|
+
// fingerprint-aware; the count that could not be represented is surfaced.
|
|
621
|
+
var revocations = allRevocations.filter(function (r) {
|
|
622
|
+
return r && r.serialNumber != null;
|
|
623
|
+
});
|
|
624
|
+
var fingerprintOnlyOmitted = allRevocations.length - revocations.length;
|
|
611
625
|
var nowMs = Date.now();
|
|
612
626
|
var thisUpdate = opts3.thisUpdate || new Date(nowMs);
|
|
613
627
|
var nextUpdate = opts3.nextUpdate ||
|
|
@@ -627,7 +641,9 @@ function create(opts) {
|
|
|
627
641
|
atomicFile.writeSync(paths.crl, crlPem, { mode: 0o644 });
|
|
628
642
|
}
|
|
629
643
|
return { crlPem: crlPem, thisUpdate: thisUpdate, nextUpdate: nextUpdate,
|
|
630
|
-
entryCount: revocations.length,
|
|
644
|
+
entryCount: revocations.length,
|
|
645
|
+
fingerprintOnlyOmitted: fingerprintOnlyOmitted,
|
|
646
|
+
path: paths.crl };
|
|
631
647
|
}
|
|
632
648
|
|
|
633
649
|
return {
|
package/lib/safe-ical.js
CHANGED
|
@@ -281,14 +281,18 @@ function _resolveCaps(opts) {
|
|
|
281
281
|
if (typeof opts.profile === "string") {
|
|
282
282
|
name = opts.profile;
|
|
283
283
|
} else if (typeof opts.compliancePosture === "string") {
|
|
284
|
-
name = COMPLIANCE_POSTURES
|
|
284
|
+
name = Object.prototype.hasOwnProperty.call(COMPLIANCE_POSTURES, opts.compliancePosture)
|
|
285
|
+
? COMPLIANCE_POSTURES[opts.compliancePosture] : "strict";
|
|
285
286
|
}
|
|
286
|
-
|
|
287
|
-
if (!
|
|
287
|
+
// Own-property lookup: `name` derives from attacker/operator opts.profile, so
|
|
288
|
+
// a bare `if (!PROFILES[name])` would let a prototype key ("constructor") pass
|
|
289
|
+
// as a known profile and run under an inherited member (fail-open).
|
|
290
|
+
if (!Object.prototype.hasOwnProperty.call(PROFILES, name)) {
|
|
288
291
|
throw new SafeIcalError("safe-ical/bad-opt",
|
|
289
292
|
"safeIcal.parse: unknown profile '" + name +
|
|
290
293
|
"' (expected strict|balanced|permissive)");
|
|
291
294
|
}
|
|
295
|
+
var caps = PROFILES[name];
|
|
292
296
|
return caps;
|
|
293
297
|
}
|
|
294
298
|
|
package/lib/safe-mime.js
CHANGED
|
@@ -750,7 +750,18 @@ function _filenameFromHeaders(headers) {
|
|
|
750
750
|
}
|
|
751
751
|
if (/^[A-Za-z0-9_-]+'[A-Za-z0-9_-]*'/.test(raw)) {
|
|
752
752
|
var enc = raw.split("'");
|
|
753
|
-
|
|
753
|
+
// RFC 2231 / 5987 ext-value percent-decode. A hostile
|
|
754
|
+
// `filename*=UTF-8''%` (truncated %-escape, non-hex digits, or a
|
|
755
|
+
// %-escape sequence that isn't valid UTF-8) makes decodeURIComponent
|
|
756
|
+
// throw a raw URIError, which would escape the SafeMimeError contract
|
|
757
|
+
// and crash the attachment walk. Degrade to the still-encoded segment
|
|
758
|
+
// — best-effort, and downstream filename guards handle the raw form —
|
|
759
|
+
// matching every other decodeURIComponent site in the framework.
|
|
760
|
+
try {
|
|
761
|
+
return decodeURIComponent(enc[2]);
|
|
762
|
+
} catch (_e) {
|
|
763
|
+
return enc[2];
|
|
764
|
+
}
|
|
754
765
|
}
|
|
755
766
|
return raw;
|
|
756
767
|
}
|
package/lib/safe-vcard.js
CHANGED
|
@@ -225,14 +225,18 @@ function _resolveCaps(opts) {
|
|
|
225
225
|
if (typeof opts.profile === "string") {
|
|
226
226
|
name = opts.profile;
|
|
227
227
|
} else if (typeof opts.compliancePosture === "string") {
|
|
228
|
-
name = COMPLIANCE_POSTURES
|
|
228
|
+
name = Object.prototype.hasOwnProperty.call(COMPLIANCE_POSTURES, opts.compliancePosture)
|
|
229
|
+
? COMPLIANCE_POSTURES[opts.compliancePosture] : "strict";
|
|
229
230
|
}
|
|
230
|
-
|
|
231
|
-
if (!
|
|
231
|
+
// Own-property lookup: `name` derives from attacker/operator opts.profile, so
|
|
232
|
+
// a bare `if (!PROFILES[name])` would let a prototype key ("constructor") pass
|
|
233
|
+
// as a known profile and run under an inherited member (fail-open).
|
|
234
|
+
if (!Object.prototype.hasOwnProperty.call(PROFILES, name)) {
|
|
232
235
|
throw new SafeVcardError("safe-vcard/bad-opt",
|
|
233
236
|
"safeVcard.parse: unknown profile '" + name +
|
|
234
237
|
"' (expected strict|balanced|permissive)");
|
|
235
238
|
}
|
|
239
|
+
var caps = PROFILES[name];
|
|
236
240
|
return caps;
|
|
237
241
|
}
|
|
238
242
|
|
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:04849b6a-ae7a-43f3-939a-e67a488f6a49",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-07-
|
|
8
|
+
"timestamp": "2026-07-17T08:07:44.038Z",
|
|
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.17.
|
|
22
|
+
"bom-ref": "@blamejs/core@0.17.1",
|
|
23
23
|
"type": "application",
|
|
24
24
|
"name": "blamejs",
|
|
25
|
-
"version": "0.17.
|
|
25
|
+
"version": "0.17.1",
|
|
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.17.
|
|
29
|
+
"purl": "pkg:npm/%40blamejs/core@0.17.1",
|
|
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.17.
|
|
57
|
+
"ref": "@blamejs/core@0.17.1",
|
|
58
58
|
"dependsOn": []
|
|
59
59
|
}
|
|
60
60
|
]
|