@blamejs/core 0.13.43 → 0.13.45
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 +4 -0
- package/README.md +1 -1
- package/lib/a2a.js +11 -11
- package/lib/agent-snapshot.js +1 -1
- package/lib/ai-capability.js +20 -20
- package/lib/ai-dp.js +17 -17
- package/lib/ai-input.js +3 -3
- package/lib/ai-pref.js +9 -9
- package/lib/ai-quota.js +17 -17
- package/lib/arg-parser.js +38 -38
- package/lib/audit-sign.js +4 -4
- package/lib/auth/acr-vocabulary.js +4 -4
- package/lib/auth/auth-time-tracker.js +1 -1
- package/lib/auth/elevation-grant.js +10 -10
- package/lib/auth/step-up-policy.js +12 -12
- package/lib/auth/step-up.js +15 -15
- package/lib/boot-gates.js +6 -6
- package/lib/budr.js +6 -6
- package/lib/cert.js +71 -9
- package/lib/content-credentials.js +13 -13
- package/lib/dark-patterns.js +15 -15
- package/lib/ddl-change-control.js +37 -37
- package/lib/dr-runbook.js +7 -7
- package/lib/fapi2.js +9 -9
- package/lib/fdx.js +7 -7
- package/lib/graphql-federation.js +2 -2
- package/lib/iab-mspa.js +5 -5
- package/lib/iab-tcf.js +18 -18
- package/lib/mcp.js +13 -13
- package/lib/middleware/require-step-up.js +3 -3
- package/lib/network-tls.js +81 -5
- package/lib/sec-cyber.js +3 -3
- package/lib/sse.js +14 -14
- package/lib/tcpa-10dlc.js +5 -5
- package/lib/tenant-quota.js +18 -18
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/boot-gates.js
CHANGED
|
@@ -66,7 +66,7 @@ var DEFAULT_EXIT_CODE = 1;
|
|
|
66
66
|
async function run(gates, opts) {
|
|
67
67
|
opts = opts || {};
|
|
68
68
|
if (!Array.isArray(gates) || gates.length === 0) {
|
|
69
|
-
throw new BootGatesError("
|
|
69
|
+
throw new BootGatesError("boot-gates/bad-input",
|
|
70
70
|
"b.bootGates.run: gates must be a non-empty array");
|
|
71
71
|
}
|
|
72
72
|
var log = typeof opts.log === "function" ? opts.log : function (msg) {
|
|
@@ -82,7 +82,7 @@ async function run(gates, opts) {
|
|
|
82
82
|
// throw rather than terminating the process — operators that wire
|
|
83
83
|
// bootGates from their daemon main() pass `exit: process.exit.bind(process)`.
|
|
84
84
|
var exit = typeof opts.exit === "function" ? opts.exit : function (code) {
|
|
85
|
-
var e = new BootGatesError("
|
|
85
|
+
var e = new BootGatesError("boot-gates/no-exit-wired",
|
|
86
86
|
"b.bootGates.run: gate failed (exitCode=" + code + ") but no opts.exit handler was supplied; " +
|
|
87
87
|
"operators wire opts.exit to process.exit.bind(process) in their daemon main()");
|
|
88
88
|
e.exitCode = code;
|
|
@@ -96,26 +96,26 @@ async function run(gates, opts) {
|
|
|
96
96
|
var gate = gates[i];
|
|
97
97
|
if (!gate || typeof gate.name !== "string" || gate.name.length === 0 ||
|
|
98
98
|
typeof gate.fn !== "function") {
|
|
99
|
-
throw new BootGatesError("
|
|
99
|
+
throw new BootGatesError("boot-gates/bad-gate",
|
|
100
100
|
"b.bootGates.run: gates[" + i + "] must be { name: string, fn: function }");
|
|
101
101
|
}
|
|
102
102
|
var timeoutMs = gate.timeoutMs || DEFAULT_GATE_TIMEOUT_MS;
|
|
103
103
|
if (typeof timeoutMs !== "number" || !isFinite(timeoutMs) || timeoutMs < 1) {
|
|
104
|
-
throw new BootGatesError("
|
|
104
|
+
throw new BootGatesError("boot-gates/bad-timeout",
|
|
105
105
|
"b.bootGates.run: gates[" + i + "].timeoutMs must be a positive finite number");
|
|
106
106
|
}
|
|
107
107
|
var gateT0 = Date.now();
|
|
108
108
|
var failure = null;
|
|
109
109
|
try {
|
|
110
110
|
await safeAsync.withTimeout(Promise.resolve().then(gate.fn), timeoutMs,
|
|
111
|
-
new BootGatesError("
|
|
111
|
+
new BootGatesError("boot-gates/timeout",
|
|
112
112
|
"b.bootGates.run: gate '" + gate.name + "' exceeded " + timeoutMs + "ms"));
|
|
113
113
|
} catch (err) {
|
|
114
114
|
failure = err;
|
|
115
115
|
}
|
|
116
116
|
if (overallTimeoutMs !== undefined &&
|
|
117
117
|
Date.now() - t0 > overallTimeoutMs && failure === null) {
|
|
118
|
-
failure = new BootGatesError("
|
|
118
|
+
failure = new BootGatesError("boot-gates/overall-timeout",
|
|
119
119
|
"b.bootGates.run: overall budget " + overallTimeoutMs + "ms exceeded after gate '" +
|
|
120
120
|
gate.name + "'");
|
|
121
121
|
}
|
package/lib/budr.js
CHANGED
|
@@ -78,31 +78,31 @@ var declarations = new Map();
|
|
|
78
78
|
*/
|
|
79
79
|
function declare(opts) {
|
|
80
80
|
if (!opts || typeof opts !== "object") {
|
|
81
|
-
throw BudrError.factory("
|
|
81
|
+
throw BudrError.factory("budr/bad-opts", "budr.declare: opts required");
|
|
82
82
|
}
|
|
83
83
|
if (typeof opts.service !== "string" || opts.service.length === 0 ||
|
|
84
84
|
opts.service.length > SERVICE_MAX || !SERVICE_RE.test(opts.service)) {
|
|
85
|
-
throw BudrError.factory("
|
|
85
|
+
throw BudrError.factory("budr/bad-service",
|
|
86
86
|
"budr.declare: service must match " + SERVICE_RE);
|
|
87
87
|
}
|
|
88
88
|
numericBounds.requirePositiveFiniteIntIfPresent(opts.rtoMs, "budr.declare: rtoMs", BudrError, "BAD_RTO");
|
|
89
89
|
numericBounds.requirePositiveFiniteIntIfPresent(opts.rpoMs, "budr.declare: rpoMs", BudrError, "BAD_RPO");
|
|
90
90
|
if (typeof opts.rtoMs !== "number" || typeof opts.rpoMs !== "number") {
|
|
91
|
-
throw BudrError.factory("
|
|
91
|
+
throw BudrError.factory("budr/bad-targets",
|
|
92
92
|
"budr.declare: rtoMs and rpoMs are required positive integer milliseconds");
|
|
93
93
|
}
|
|
94
94
|
if (opts.tier !== undefined && TIERS.indexOf(opts.tier) === -1) {
|
|
95
|
-
throw BudrError.factory("
|
|
95
|
+
throw BudrError.factory("budr/bad-tier",
|
|
96
96
|
"budr.declare: tier must be one of " + TIERS.join(", "));
|
|
97
97
|
}
|
|
98
98
|
if (opts.criticality !== undefined && CRITICALITIES.indexOf(opts.criticality) === -1) {
|
|
99
|
-
throw BudrError.factory("
|
|
99
|
+
throw BudrError.factory("budr/bad-criticality",
|
|
100
100
|
"budr.declare: criticality must be one of " + CRITICALITIES.join(", "));
|
|
101
101
|
}
|
|
102
102
|
validateOpts.optionalNonEmptyString(opts.owner,
|
|
103
103
|
"budr.declare: owner", BudrError, "BAD_OWNER");
|
|
104
104
|
if (opts.citations !== undefined && !Array.isArray(opts.citations)) {
|
|
105
|
-
throw BudrError.factory("
|
|
105
|
+
throw BudrError.factory("budr/bad-citations",
|
|
106
106
|
"budr.declare: citations must be an array of strings");
|
|
107
107
|
}
|
|
108
108
|
|
package/lib/cert.js
CHANGED
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
* - `b.acme.create` → ACME orders, JWS, ARI fetch
|
|
23
23
|
* - `b.vault.seal` → sealed-disk persistence of certs + keys + account material
|
|
24
24
|
* - `b.safeAsync.repeating` → renewal scheduler with drop-silent error path
|
|
25
|
-
* - `b.network.tls.ocsp` → server-side stapling
|
|
25
|
+
* - `b.network.tls.ocsp` → fetches + caches a validated OCSP response per cert for server-side stapling
|
|
26
26
|
* - `b.audit` → cert.* lifecycle audit chain
|
|
27
|
-
* - `b.compliance` →
|
|
27
|
+
* - `b.compliance` → validates the declared posture names; storage-confidentiality postures hold because keys/certs are always sealed at rest
|
|
28
28
|
*
|
|
29
29
|
* Does NOT ship the challenge-solver implementations (HTTP-01 server,
|
|
30
30
|
* DNS provider integrations, TLS-ALPN-01 socket). Those are operator-
|
|
@@ -60,6 +60,7 @@ var acme = lazyRequire(function () { return require("./acme"); });
|
|
|
60
60
|
var vault = lazyRequire(function () { return require("./vault"); });
|
|
61
61
|
var audit = lazyRequire(function () { return require("./audit"); });
|
|
62
62
|
var networkTls = lazyRequire(function () { return require("./network-tls"); });
|
|
63
|
+
var compliance = lazyRequire(function () { return require("./compliance"); });
|
|
63
64
|
var bCrypto = lazyRequire(function () { return require("./crypto"); });
|
|
64
65
|
|
|
65
66
|
var CertError = defineClass("CertError");
|
|
@@ -222,7 +223,7 @@ function _createSealedDiskStorage(opts) {
|
|
|
222
223
|
* refreshMs: number, // default 12h — OCSP-response cache lifetime
|
|
223
224
|
* },
|
|
224
225
|
* audit: boolean | object, // default true — emit cert.* lifecycle events via b.audit.safeEmit
|
|
225
|
-
* compliance: Array<string>, // optional — posture
|
|
226
|
+
* compliance: Array<string>, // optional — posture names (e.g. ["hipaa"]); validated against b.compliance.KNOWN_POSTURES (throws on an unknown name) + surfaced on getContext().compliance. Cert keys/certs are always sealed at rest, so storage-confidentiality postures hold by construction.
|
|
226
227
|
*
|
|
227
228
|
* @example
|
|
228
229
|
* var mgr = b.cert.create({
|
|
@@ -383,13 +384,31 @@ function create(opts) {
|
|
|
383
384
|
|
|
384
385
|
// ---- Audit + compliance ----
|
|
385
386
|
var auditEnabled = opts.audit !== false;
|
|
386
|
-
var
|
|
387
|
+
var compliancePostures = Array.isArray(opts.compliance) ? opts.compliance.slice() : [];
|
|
388
|
+
// Validate posture names against the framework catalog so a typo is
|
|
389
|
+
// caught at create() rather than silently ignored. The cert manager
|
|
390
|
+
// satisfies the storage-confidentiality postures (HIPAA / PCI-DSS /
|
|
391
|
+
// GDPR …) by construction — keys + certs are always sealed at rest
|
|
392
|
+
// (storage.type is enforced to "sealed-disk"), so there is no plaintext-
|
|
393
|
+
// storage state for a posture to fail to. The postures are recorded +
|
|
394
|
+
// surfaced on the served context for an auditor.
|
|
395
|
+
if (compliancePostures.length > 0) {
|
|
396
|
+
var knownPostures = compliance().KNOWN_POSTURES;
|
|
397
|
+
compliancePostures.forEach(function (p) {
|
|
398
|
+
if (knownPostures.indexOf(p) === -1) {
|
|
399
|
+
throw new CertError("cert/unknown-compliance-posture",
|
|
400
|
+
"cert.create: opts.compliance posture '" + p + "' is not a known posture; " +
|
|
401
|
+
"see b.compliance.KNOWN_POSTURES");
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
387
405
|
|
|
388
406
|
// ---- Internal state ----
|
|
389
407
|
var emitter = new EventEmitter();
|
|
390
|
-
var loadedContexts = Object.create(null); // name → { cert, key, ca, expiresAt, fingerprintSha256, sniNames }
|
|
408
|
+
var loadedContexts = Object.create(null); // name → { cert, key, ca, expiresAt, fingerprintSha256, sniNames, ocspResponse }
|
|
391
409
|
var acmeClient = null;
|
|
392
410
|
var scheduler = null;
|
|
411
|
+
var ocspTimer = null;
|
|
393
412
|
var stopped = false;
|
|
394
413
|
|
|
395
414
|
function _emitAudit(action, outcome, metadata) {
|
|
@@ -689,6 +708,39 @@ function create(opts) {
|
|
|
689
708
|
}
|
|
690
709
|
}
|
|
691
710
|
|
|
711
|
+
// Split a PEM chain into individual certificate blocks (leaf first).
|
|
712
|
+
function _splitPemChain(pem) {
|
|
713
|
+
return pem.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g) || [];
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Fetch + cache a validated OCSP response for one managed cert, for
|
|
717
|
+
// server-side stapling. Fail-soft: a responder error, or no issuer in the
|
|
718
|
+
// served chain, leaves any prior staple in place and never throws — an
|
|
719
|
+
// absent staple degrades gracefully (clients fall back to their own
|
|
720
|
+
// revocation checking). The validated DER is exposed on
|
|
721
|
+
// getContext().ocspResponse for the operator's TLS server to staple via
|
|
722
|
+
// its 'OCSPRequest' handler.
|
|
723
|
+
async function _refreshOcspFor(name) {
|
|
724
|
+
var ctx = loadedContexts[name];
|
|
725
|
+
if (!ctx || !ocspStapling) return;
|
|
726
|
+
var chain = _splitPemChain(ctx.cert);
|
|
727
|
+
if (chain.length < 2) return; // no issuer in the served chain
|
|
728
|
+
try {
|
|
729
|
+
// allow:raw-outbound-http — b.network.tls.ocsp.fetch composes b.httpClient internally (SSRF guard + pinned DNS); not a raw outbound call
|
|
730
|
+
var rv = await networkTls().ocsp.fetch({ leafPem: chain[0], issuerPem: chain[1] });
|
|
731
|
+
ctx.ocspResponse = rv.ocspDer;
|
|
732
|
+
_emitAudit("cert.ocsp.refreshed", "success", { name: name });
|
|
733
|
+
} catch (e) {
|
|
734
|
+
_emitAudit("cert.ocsp.refresh-failed", "failure",
|
|
735
|
+
{ name: name, error: (e && e.message) || String(e) });
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
async function _refreshAllOcsp() {
|
|
740
|
+
var keys = Object.keys(loadedContexts);
|
|
741
|
+
for (var i = 0; i < keys.length; i += 1) { await _refreshOcspFor(keys[i]); }
|
|
742
|
+
}
|
|
743
|
+
|
|
692
744
|
async function start() {
|
|
693
745
|
if (stopped) {
|
|
694
746
|
throw new CertError("cert/already-stopped",
|
|
@@ -706,12 +758,21 @@ function create(opts) {
|
|
|
706
758
|
await _renewCheckOne(certsByName[keys[ki]]);
|
|
707
759
|
}
|
|
708
760
|
}, renewIntervalMs, { name: "cert-renew" });
|
|
761
|
+
// 3. OCSP stapling. The initial fetch runs in the background so a slow
|
|
762
|
+
// responder never delays start(); the staple becomes available
|
|
763
|
+
// shortly after, and the timer refreshes on the configured cadence.
|
|
764
|
+
if (ocspStapling) {
|
|
765
|
+
_refreshAllOcsp().catch(function () { /* per-cert errors already audited */ });
|
|
766
|
+
ocspTimer = safeAsync.repeating(_refreshAllOcsp, ocspRefreshMs, { name: "cert-ocsp" });
|
|
767
|
+
}
|
|
709
768
|
}
|
|
710
769
|
|
|
711
770
|
async function stop() {
|
|
712
771
|
stopped = true;
|
|
713
772
|
if (scheduler && typeof scheduler.stop === "function") scheduler.stop();
|
|
714
773
|
scheduler = null;
|
|
774
|
+
if (ocspTimer && typeof ocspTimer.stop === "function") ocspTimer.stop();
|
|
775
|
+
ocspTimer = null;
|
|
715
776
|
}
|
|
716
777
|
|
|
717
778
|
function getContext(name) {
|
|
@@ -729,6 +790,11 @@ function create(opts) {
|
|
|
729
790
|
key: ctx.key,
|
|
730
791
|
expiresAt: ctx.expiresAt,
|
|
731
792
|
fingerprintSha256: ctx.fingerprintSha256,
|
|
793
|
+
// The cached, validated OCSP response (DER Buffer) when ocsp.stapling
|
|
794
|
+
// is on and a response has been fetched; null otherwise. Staple it
|
|
795
|
+
// from the TLS server's 'OCSPRequest' handler: cb(null, ocspResponse).
|
|
796
|
+
ocspResponse: ctx.ocspResponse || null,
|
|
797
|
+
compliance: compliancePostures.slice(),
|
|
732
798
|
};
|
|
733
799
|
}
|
|
734
800
|
|
|
@@ -798,10 +864,6 @@ function create(opts) {
|
|
|
798
864
|
function off(event, handler) { emitter.off(event, handler); return this; }
|
|
799
865
|
function once(event, handler) { emitter.once(event, handler); return this; }
|
|
800
866
|
|
|
801
|
-
// Suppress unused-warnings for ocsp + compliance until those branches
|
|
802
|
-
// wire up in v0.11.23+ follow-up.
|
|
803
|
-
void ocspStapling; void ocspRefreshMs; void compliance; void networkTls;
|
|
804
|
-
|
|
805
867
|
return {
|
|
806
868
|
start: start,
|
|
807
869
|
stop: stop,
|
|
@@ -48,7 +48,7 @@ var REQUIRED_FIELDS = ["provider", "system", "systemVersion", "contentId"];
|
|
|
48
48
|
|
|
49
49
|
function _validateBuildOpts(opts) {
|
|
50
50
|
if (!opts || typeof opts !== "object") {
|
|
51
|
-
throw ContentCredentialsError.factory("
|
|
51
|
+
throw ContentCredentialsError.factory("content-credentials/bad-opts",
|
|
52
52
|
"contentCredentials.build: opts required");
|
|
53
53
|
}
|
|
54
54
|
for (var i = 0; i < REQUIRED_FIELDS.length; i += 1) {
|
|
@@ -57,32 +57,32 @@ function _validateBuildOpts(opts) {
|
|
|
57
57
|
"contentCredentials.build: " + f, ContentCredentialsError, "MISSING_" + f.toUpperCase());
|
|
58
58
|
}
|
|
59
59
|
if (opts.provider.length > STR_LEN_MAX) {
|
|
60
|
-
throw ContentCredentialsError.factory("
|
|
60
|
+
throw ContentCredentialsError.factory("content-credentials/bad-provider",
|
|
61
61
|
"provider exceeds " + STR_LEN_MAX + " chars");
|
|
62
62
|
}
|
|
63
63
|
if (opts.system.length > ID_LEN_MAX || !ID_RE.test(opts.system)) {
|
|
64
|
-
throw ContentCredentialsError.factory("
|
|
64
|
+
throw ContentCredentialsError.factory("content-credentials/bad-system",
|
|
65
65
|
"system must match " + ID_RE);
|
|
66
66
|
}
|
|
67
67
|
if (opts.systemVersion.length > 64 || !SEMVER_RE.test(opts.systemVersion)) { // allow:raw-byte-literal — semver length cap, not bytes
|
|
68
|
-
throw ContentCredentialsError.factory("
|
|
68
|
+
throw ContentCredentialsError.factory("content-credentials/bad-version",
|
|
69
69
|
"systemVersion must be semver");
|
|
70
70
|
}
|
|
71
71
|
if (opts.contentId.length > ID_LEN_MAX || !ID_RE.test(opts.contentId)) {
|
|
72
|
-
throw ContentCredentialsError.factory("
|
|
72
|
+
throw ContentCredentialsError.factory("content-credentials/bad-content-id",
|
|
73
73
|
"contentId must match " + ID_RE);
|
|
74
74
|
}
|
|
75
75
|
if (opts.contentType !== undefined) {
|
|
76
76
|
if (typeof opts.contentType !== "string" || opts.contentType.length === 0 ||
|
|
77
77
|
opts.contentType.length > ID_LEN_MAX || !/^[a-zA-Z]+\/[A-Za-z0-9._+-]+$/.test(opts.contentType)) {
|
|
78
|
-
throw ContentCredentialsError.factory("
|
|
78
|
+
throw ContentCredentialsError.factory("content-credentials/bad-content-type",
|
|
79
79
|
"contentType must be a valid IANA media type");
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
if (opts.contentSha3 !== undefined) {
|
|
83
83
|
if (typeof opts.contentSha3 !== "string" || opts.contentSha3.length !== SHA3_HEX_LEN ||
|
|
84
84
|
!/^[a-f0-9]+$/i.test(opts.contentSha3)) {
|
|
85
|
-
throw ContentCredentialsError.factory("
|
|
85
|
+
throw ContentCredentialsError.factory("content-credentials/bad-content-hash",
|
|
86
86
|
"contentSha3 must be lowercase hex SHA3-512 (" + SHA3_HEX_LEN + " chars)");
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -229,7 +229,7 @@ function required(opts) {
|
|
|
229
229
|
function sign(manifest, opts) {
|
|
230
230
|
opts = opts || {};
|
|
231
231
|
if (!manifest || typeof manifest !== "object") {
|
|
232
|
-
throw ContentCredentialsError.factory("
|
|
232
|
+
throw ContentCredentialsError.factory("content-credentials/bad-manifest",
|
|
233
233
|
"contentCredentials.sign: manifest required");
|
|
234
234
|
}
|
|
235
235
|
validateOpts.requireNonEmptyString(opts.privateKeyPem,
|
|
@@ -368,7 +368,7 @@ function _cborUint(n) {
|
|
|
368
368
|
if (n < 256) return Buffer.from([0x18, n]); // allow:raw-byte-literal — CBOR threshold
|
|
369
369
|
if (n < 65536) return Buffer.from([0x19, (n >> 8) & 0xFF, n & 0xFF]); // allow:raw-byte-literal — CBOR threshold
|
|
370
370
|
if (n < 4294967296) return Buffer.from([0x1A, (n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF]); // allow:raw-byte-literal — CBOR threshold
|
|
371
|
-
throw ContentCredentialsError.factory("
|
|
371
|
+
throw ContentCredentialsError.factory("content-credentials/cbor-overflow", "cbor uint too large: " + n);
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
function _cborNint(n) {
|
|
@@ -397,13 +397,13 @@ function _cborArrayHeader(n) {
|
|
|
397
397
|
if (n < 24) return Buffer.from([0x80 | n]); // allow:raw-byte-literal — CBOR threshold
|
|
398
398
|
if (n < 256) return Buffer.from([0x98, n]); // allow:raw-byte-literal — CBOR threshold
|
|
399
399
|
if (n < 65536) return Buffer.from([0x99, (n >> 8) & 0xFF, n & 0xFF]); // allow:raw-byte-literal — CBOR threshold
|
|
400
|
-
throw ContentCredentialsError.factory("
|
|
400
|
+
throw ContentCredentialsError.factory("content-credentials/cbor-overflow", "cbor array too large: " + n);
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
function _cborMapHeader(n) {
|
|
404
404
|
if (n < 24) return Buffer.from([0xA0 | n]); // allow:raw-byte-literal — CBOR threshold
|
|
405
405
|
if (n < 256) return Buffer.from([0xB8, n]); // allow:raw-byte-literal — CBOR threshold
|
|
406
|
-
throw ContentCredentialsError.factory("
|
|
406
|
+
throw ContentCredentialsError.factory("content-credentials/cbor-overflow", "cbor map too large: " + n);
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
function _cborTag(tag) {
|
|
@@ -454,14 +454,14 @@ function _cborTag(tag) {
|
|
|
454
454
|
function signCose(manifest, opts) {
|
|
455
455
|
opts = opts || {};
|
|
456
456
|
if (!manifest || typeof manifest !== "object") {
|
|
457
|
-
throw ContentCredentialsError.factory("
|
|
457
|
+
throw ContentCredentialsError.factory("content-credentials/bad-manifest",
|
|
458
458
|
"contentCredentials.signCose: manifest required");
|
|
459
459
|
}
|
|
460
460
|
validateOpts.requireNonEmptyString(opts.privateKeyPem,
|
|
461
461
|
"contentCredentials.signCose: privateKeyPem", ContentCredentialsError, "BAD_KEY");
|
|
462
462
|
var algName = (opts.alg || "ml-dsa-87").toLowerCase();
|
|
463
463
|
if (!(algName in COSE_ALGS)) {
|
|
464
|
-
throw ContentCredentialsError.factory("
|
|
464
|
+
throw ContentCredentialsError.factory("content-credentials/bad-alg",
|
|
465
465
|
"contentCredentials.signCose: alg '" + algName +
|
|
466
466
|
"' not in COSE alg registry. Known: " + Object.keys(COSE_ALGS).join(", "));
|
|
467
467
|
}
|
package/lib/dark-patterns.js
CHANGED
|
@@ -64,47 +64,47 @@ var POSTURES = {
|
|
|
64
64
|
|
|
65
65
|
function _validateFlowOpts(opts, label, errorClass) {
|
|
66
66
|
if (!opts || typeof opts !== "object") {
|
|
67
|
-
throw errorClass.factory("
|
|
67
|
+
throw errorClass.factory("dark-patterns/bad-opts",
|
|
68
68
|
"darkPatterns.record" + label + ": opts required");
|
|
69
69
|
}
|
|
70
70
|
if (CHANNELS.indexOf(opts.channel) === -1) {
|
|
71
|
-
throw errorClass.factory("
|
|
71
|
+
throw errorClass.factory("dark-patterns/bad-channel",
|
|
72
72
|
"darkPatterns: channel must be one of " + CHANNELS.join(","));
|
|
73
73
|
}
|
|
74
74
|
if (typeof opts.clickCount !== "number" || !isFinite(opts.clickCount) ||
|
|
75
75
|
opts.clickCount < 1 || opts.clickCount > 50 ||
|
|
76
76
|
Math.floor(opts.clickCount) !== opts.clickCount) {
|
|
77
|
-
throw errorClass.factory("
|
|
77
|
+
throw errorClass.factory("dark-patterns/bad-clicks",
|
|
78
78
|
"darkPatterns: clickCount must be integer 1..50");
|
|
79
79
|
}
|
|
80
80
|
if (!opts.cta || typeof opts.cta !== "object") {
|
|
81
|
-
throw errorClass.factory("
|
|
81
|
+
throw errorClass.factory("dark-patterns/bad-cta",
|
|
82
82
|
"darkPatterns: cta object required (text, fontWeight, contrastRatio)");
|
|
83
83
|
}
|
|
84
84
|
if (typeof opts.cta.text !== "string" || opts.cta.text.length === 0 ||
|
|
85
85
|
opts.cta.text.length > STR_LEN_MAX) {
|
|
86
|
-
throw errorClass.factory("
|
|
86
|
+
throw errorClass.factory("dark-patterns/bad-cta-text",
|
|
87
87
|
"darkPatterns: cta.text must be 1-256 char string");
|
|
88
88
|
}
|
|
89
89
|
if (typeof opts.cta.fontWeight !== "number" || opts.cta.fontWeight < 100 ||
|
|
90
90
|
opts.cta.fontWeight > FONT_WEIGHT_MAX) {
|
|
91
|
-
throw errorClass.factory("
|
|
91
|
+
throw errorClass.factory("dark-patterns/bad-font-weight",
|
|
92
92
|
"darkPatterns: cta.fontWeight must be 100..1000");
|
|
93
93
|
}
|
|
94
94
|
if (typeof opts.cta.contrastRatio !== "number" ||
|
|
95
95
|
opts.cta.contrastRatio < 1 || opts.cta.contrastRatio > 21) {
|
|
96
|
-
throw errorClass.factory("
|
|
96
|
+
throw errorClass.factory("dark-patterns/bad-contrast",
|
|
97
97
|
"darkPatterns: cta.contrastRatio must be 1..21");
|
|
98
98
|
}
|
|
99
99
|
if (typeof opts.confirmations !== "number" ||
|
|
100
100
|
opts.confirmations < 0 || opts.confirmations > 10 ||
|
|
101
101
|
Math.floor(opts.confirmations) !== opts.confirmations) {
|
|
102
|
-
throw errorClass.factory("
|
|
102
|
+
throw errorClass.factory("dark-patterns/bad-confirmations",
|
|
103
103
|
"darkPatterns: confirmations must be integer 0..10");
|
|
104
104
|
}
|
|
105
105
|
if (typeof opts.resourceId !== "string" || opts.resourceId.length === 0 ||
|
|
106
106
|
opts.resourceId.length > STR_LEN_MAX) {
|
|
107
|
-
throw errorClass.factory("
|
|
107
|
+
throw errorClass.factory("dark-patterns/bad-resource-id",
|
|
108
108
|
"darkPatterns: resourceId must be 1-256 char string");
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -252,21 +252,21 @@ function assertParity(signup, cancel, opts) {
|
|
|
252
252
|
opts = opts || {};
|
|
253
253
|
var errorClass = opts.errorClass || DarkPatternsError;
|
|
254
254
|
if (!signup || signup.kind !== "signup") {
|
|
255
|
-
throw errorClass.factory("
|
|
255
|
+
throw errorClass.factory("dark-patterns/bad-signup-flow",
|
|
256
256
|
"darkPatterns.assertParity: signup must be a recorded signup flow");
|
|
257
257
|
}
|
|
258
258
|
if (!cancel || cancel.kind !== "cancel") {
|
|
259
|
-
throw errorClass.factory("
|
|
259
|
+
throw errorClass.factory("dark-patterns/bad-cancel-flow",
|
|
260
260
|
"darkPatterns.assertParity: cancel must be a recorded cancel flow");
|
|
261
261
|
}
|
|
262
262
|
if (signup.resourceId !== cancel.resourceId) {
|
|
263
|
-
throw errorClass.factory("
|
|
263
|
+
throw errorClass.factory("dark-patterns/resource-mismatch",
|
|
264
264
|
"darkPatterns.assertParity: resourceId differs between flows");
|
|
265
265
|
}
|
|
266
266
|
var postureName = opts.posture || "ftc-2024";
|
|
267
267
|
var posture = POSTURES[postureName];
|
|
268
268
|
if (!posture) {
|
|
269
|
-
throw errorClass.factory("
|
|
269
|
+
throw errorClass.factory("dark-patterns/bad-posture",
|
|
270
270
|
"darkPatterns.assertParity: unknown posture " + postureName);
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -427,11 +427,11 @@ function middleware(opts) {
|
|
|
427
427
|
opts = opts || {};
|
|
428
428
|
var errorClass = opts.errorClass || DarkPatternsError;
|
|
429
429
|
if (typeof opts.lookupAttestation !== "function") {
|
|
430
|
-
throw errorClass.factory("
|
|
430
|
+
throw errorClass.factory("dark-patterns/bad-opts",
|
|
431
431
|
"darkPatterns.middleware: lookupAttestation function required");
|
|
432
432
|
}
|
|
433
433
|
if (typeof opts.resourceIdFromReq !== "function") {
|
|
434
|
-
throw errorClass.factory("
|
|
434
|
+
throw errorClass.factory("dark-patterns/bad-opts",
|
|
435
435
|
"darkPatterns.middleware: resourceIdFromReq function required");
|
|
436
436
|
}
|
|
437
437
|
|