@blamejs/core 0.7.106 → 0.8.0
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 +19 -1
- package/NOTICE +17 -1
- package/README.md +4 -3
- package/index.js +16 -0
- package/lib/asyncapi-bindings.js +160 -0
- package/lib/asyncapi-traits.js +143 -0
- package/lib/asyncapi.js +531 -0
- package/lib/audit.js +6 -0
- package/lib/auth/acr-vocabulary.js +265 -0
- package/lib/auth/auth-time-tracker.js +111 -0
- package/lib/auth/elevation-grant.js +306 -0
- package/lib/auth/sd-jwt-vc-disclosure.js +95 -0
- package/lib/auth/sd-jwt-vc-holder.js +203 -0
- package/lib/auth/sd-jwt-vc-issuer.js +197 -0
- package/lib/auth/sd-jwt-vc.js +526 -0
- package/lib/auth/step-up-policy.js +335 -0
- package/lib/auth/step-up.js +445 -0
- package/lib/compliance-ai-act-logging.js +186 -0
- package/lib/compliance-ai-act-prohibited.js +205 -0
- package/lib/compliance-ai-act-risk.js +189 -0
- package/lib/compliance-ai-act-transparency.js +200 -0
- package/lib/compliance-ai-act.js +558 -0
- package/lib/compliance.js +2 -0
- package/lib/crypto.js +32 -0
- package/lib/flag-cache.js +136 -0
- package/lib/flag-evaluation-context.js +135 -0
- package/lib/flag-providers.js +279 -0
- package/lib/flag-targeting.js +210 -0
- package/lib/flag.js +284 -0
- package/lib/inbox.js +367 -0
- package/lib/mail-arc-sign.js +372 -0
- package/lib/mail-auth.js +2 -0
- package/lib/middleware/ai-act-disclosure.js +166 -0
- package/lib/middleware/asyncapi-serve.js +136 -0
- package/lib/middleware/flag-context.js +76 -0
- package/lib/middleware/index.js +15 -0
- package/lib/middleware/openapi-serve.js +143 -0
- package/lib/middleware/require-step-up.js +186 -0
- package/lib/openapi-paths-builder.js +248 -0
- package/lib/openapi-schema-walk.js +192 -0
- package/lib/openapi-security.js +169 -0
- package/lib/openapi-yaml.js +154 -0
- package/lib/openapi.js +443 -0
- package/lib/pqc-software.js +195 -0
- package/lib/vault/index.js +3 -0
- package/lib/vault-aad.js +259 -0
- package/lib/vendor/MANIFEST.json +29 -0
- package/lib/vendor/noble-post-quantum.cjs +18 -0
- package/lib/ws-client.js +829 -0
- package/package.json +1 -1
- package/sbom.cyclonedx.json +6 -6
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Step-up policy DSL — compose RFC 9470 step-up requirements as a
|
|
4
|
+
* fluent expression rather than a flat object. Useful for routes that
|
|
5
|
+
* need "ACR >= loa3 OR a fresh hardware-key auth within the last 5 min".
|
|
6
|
+
*
|
|
7
|
+
* var policy = b.auth.stepUp.policy
|
|
8
|
+
* .acr("loa3")
|
|
9
|
+
* .and(b.auth.stepUp.policy.maxAge(300))
|
|
10
|
+
* .or(
|
|
11
|
+
* b.auth.stepUp.policy.amr(["hwk", "pop"])
|
|
12
|
+
* .and(b.auth.stepUp.policy.maxAge(120))
|
|
13
|
+
* );
|
|
14
|
+
*
|
|
15
|
+
* var result = policy.evaluate(claims);
|
|
16
|
+
*
|
|
17
|
+
* var middleware = policy.middleware({ realm: "billing-api" });
|
|
18
|
+
*
|
|
19
|
+
* The policy compiles down to RFC 9470 challenges by extracting the
|
|
20
|
+
* outermost-feasible (acr, maxAge, amr, ...) tuple. When an operator's
|
|
21
|
+
* policy is genuinely or-of-and-of-..., the challenge emitted is the
|
|
22
|
+
* UNION of individually-required atoms (so the IdP knows what to ask
|
|
23
|
+
* for) plus an `error_description` hint that there are alternatives.
|
|
24
|
+
*
|
|
25
|
+
* The DSL is pure. All node types are immutable once constructed;
|
|
26
|
+
* chaining returns a new policy object.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
var lazyRequire = require("../lazy-require");
|
|
30
|
+
var validateOpts = require("../validate-opts");
|
|
31
|
+
var { AuthError } = require("../framework-error");
|
|
32
|
+
|
|
33
|
+
var stepUp = lazyRequire(function () { return require("./step-up"); });
|
|
34
|
+
var requireStepUp = lazyRequire(function () { return require("../middleware/require-step-up"); });
|
|
35
|
+
|
|
36
|
+
function _mkNode(spec) {
|
|
37
|
+
spec.evaluate = function (claims) {
|
|
38
|
+
return spec._run(claims);
|
|
39
|
+
};
|
|
40
|
+
spec.toRequirement = function () {
|
|
41
|
+
return spec._toReq();
|
|
42
|
+
};
|
|
43
|
+
spec.and = function (other) {
|
|
44
|
+
return _and(spec, other);
|
|
45
|
+
};
|
|
46
|
+
spec.or = function (other) {
|
|
47
|
+
return _or(spec, other);
|
|
48
|
+
};
|
|
49
|
+
spec.not = function () {
|
|
50
|
+
return _not(spec);
|
|
51
|
+
};
|
|
52
|
+
spec.middleware = function (opts) {
|
|
53
|
+
opts = opts || {};
|
|
54
|
+
return requireStepUp().create(Object.assign({}, opts, {
|
|
55
|
+
requirement: spec._toReq(),
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
return Object.freeze(spec);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function acr(value) {
|
|
62
|
+
validateOpts.requireNonEmptyString(value, "policy.acr: value", AuthError, "auth-stepUp/bad-acr");
|
|
63
|
+
return _mkNode({
|
|
64
|
+
kind: "acr",
|
|
65
|
+
value: value,
|
|
66
|
+
_run: function (claims) {
|
|
67
|
+
var result = stepUp().evaluate({
|
|
68
|
+
claims: claims,
|
|
69
|
+
requirement: { acr: value },
|
|
70
|
+
});
|
|
71
|
+
return { ok: result.ok === true, atom: "acr:" + value, reason: result.reason || null };
|
|
72
|
+
},
|
|
73
|
+
_toReq: function () { return { acr: value }; },
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function acrAny(values) {
|
|
78
|
+
if (!Array.isArray(values) || values.length === 0) {
|
|
79
|
+
throw new AuthError("auth-stepUp/bad-policy",
|
|
80
|
+
"policy.acrAny: values must be a non-empty array");
|
|
81
|
+
}
|
|
82
|
+
for (var i = 0; i < values.length; i += 1) {
|
|
83
|
+
validateOpts.requireNonEmptyString(values[i],
|
|
84
|
+
"policy.acrAny: values[" + i + "]", AuthError, "auth-stepUp/bad-acr");
|
|
85
|
+
}
|
|
86
|
+
var copy = values.slice();
|
|
87
|
+
return _mkNode({
|
|
88
|
+
kind: "acrAny",
|
|
89
|
+
values: copy,
|
|
90
|
+
_run: function (claims) {
|
|
91
|
+
var result = stepUp().evaluate({
|
|
92
|
+
claims: claims,
|
|
93
|
+
requirement: { acrValues: copy },
|
|
94
|
+
});
|
|
95
|
+
return { ok: result.ok === true, atom: "acrAny:" + copy.join(","), reason: result.reason || null };
|
|
96
|
+
},
|
|
97
|
+
_toReq: function () { return { acrValues: copy }; },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function amr(required) {
|
|
102
|
+
if (!Array.isArray(required) || required.length === 0) {
|
|
103
|
+
throw new AuthError("auth-stepUp/bad-policy",
|
|
104
|
+
"policy.amr: required must be a non-empty array");
|
|
105
|
+
}
|
|
106
|
+
for (var i = 0; i < required.length; i += 1) {
|
|
107
|
+
validateOpts.requireNonEmptyString(required[i],
|
|
108
|
+
"policy.amr: required[" + i + "]", AuthError, "auth-stepUp/bad-amr");
|
|
109
|
+
}
|
|
110
|
+
var copy = required.slice();
|
|
111
|
+
return _mkNode({
|
|
112
|
+
kind: "amr",
|
|
113
|
+
required: copy,
|
|
114
|
+
_run: function (claims) {
|
|
115
|
+
var result = stepUp().evaluate({
|
|
116
|
+
claims: claims,
|
|
117
|
+
requirement: { requiredAmr: copy },
|
|
118
|
+
});
|
|
119
|
+
return { ok: result.ok === true, atom: "amr:" + copy.join("+"), reason: result.reason || null };
|
|
120
|
+
},
|
|
121
|
+
_toReq: function () { return { requiredAmr: copy }; },
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function phishingResistant() {
|
|
126
|
+
return _mkNode({
|
|
127
|
+
kind: "phishingResistant",
|
|
128
|
+
_run: function (claims) {
|
|
129
|
+
var result = stepUp().evaluate({
|
|
130
|
+
claims: claims,
|
|
131
|
+
requirement: { phishingResistant: true },
|
|
132
|
+
});
|
|
133
|
+
return { ok: result.ok === true, atom: "phr", reason: result.reason || null };
|
|
134
|
+
},
|
|
135
|
+
_toReq: function () { return { phishingResistant: true }; },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function maxAge(seconds) {
|
|
140
|
+
if (typeof seconds !== "number" || !isFinite(seconds) || seconds < 0) {
|
|
141
|
+
throw new AuthError("auth-stepUp/bad-policy",
|
|
142
|
+
"policy.maxAge: seconds must be a finite number >= 0 — got " +
|
|
143
|
+
JSON.stringify(seconds));
|
|
144
|
+
}
|
|
145
|
+
return _mkNode({
|
|
146
|
+
kind: "maxAge",
|
|
147
|
+
seconds: seconds,
|
|
148
|
+
_run: function (claims) {
|
|
149
|
+
var result = stepUp().evaluate({
|
|
150
|
+
claims: claims,
|
|
151
|
+
requirement: { maxAge: seconds },
|
|
152
|
+
});
|
|
153
|
+
return { ok: result.ok === true, atom: "maxAge:" + seconds, reason: result.reason || null };
|
|
154
|
+
},
|
|
155
|
+
_toReq: function () { return { maxAge: seconds }; },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function custom(name, fn) {
|
|
160
|
+
validateOpts.requireNonEmptyString(name, "policy.custom: name", AuthError, "auth-stepUp/bad-policy");
|
|
161
|
+
if (typeof fn !== "function") {
|
|
162
|
+
throw new AuthError("auth-stepUp/bad-policy",
|
|
163
|
+
"policy.custom: fn must be a function — got " + typeof fn);
|
|
164
|
+
}
|
|
165
|
+
return _mkNode({
|
|
166
|
+
kind: "custom",
|
|
167
|
+
name: name,
|
|
168
|
+
_run: function (claims) {
|
|
169
|
+
var ok = false;
|
|
170
|
+
try { ok = fn(claims) === true; } catch (_e) { ok = false; }
|
|
171
|
+
return { ok: ok, atom: "custom:" + name, reason: ok ? null : "custom predicate '" + name + "' returned false" };
|
|
172
|
+
},
|
|
173
|
+
_toReq: function () {
|
|
174
|
+
throw new AuthError("auth-stepUp/policy-no-challenge",
|
|
175
|
+
"policy.custom: cannot translate to RFC 9470 challenge — wrap in .or() with a translatable atom or use .middleware({ requirement: ... })");
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function _and(left, right) {
|
|
181
|
+
return _mkNode({
|
|
182
|
+
kind: "and",
|
|
183
|
+
left: left,
|
|
184
|
+
right: right,
|
|
185
|
+
_run: function (claims) {
|
|
186
|
+
var l = left._run(claims);
|
|
187
|
+
if (!l.ok) return { ok: false, atom: "and(" + l.atom + ")", reason: l.reason };
|
|
188
|
+
var r = right._run(claims);
|
|
189
|
+
if (!r.ok) return { ok: false, atom: "and(" + l.atom + "," + r.atom + ")", reason: r.reason };
|
|
190
|
+
return { ok: true, atom: "and(" + l.atom + "," + r.atom + ")", reason: null };
|
|
191
|
+
},
|
|
192
|
+
_toReq: function () {
|
|
193
|
+
var lr = left._toReq();
|
|
194
|
+
var rr = right._toReq();
|
|
195
|
+
return _mergeAnd(lr, rr);
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function _or(left, right) {
|
|
201
|
+
return _mkNode({
|
|
202
|
+
kind: "or",
|
|
203
|
+
left: left,
|
|
204
|
+
right: right,
|
|
205
|
+
_run: function (claims) {
|
|
206
|
+
var l = left._run(claims);
|
|
207
|
+
if (l.ok) return { ok: true, atom: "or(" + l.atom + ")", reason: null };
|
|
208
|
+
var r = right._run(claims);
|
|
209
|
+
if (r.ok) return { ok: true, atom: "or(" + r.atom + ")", reason: null };
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
atom: "or(" + l.atom + "," + r.atom + ")",
|
|
213
|
+
reason: l.reason + " AND " + r.reason,
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
_toReq: function () {
|
|
217
|
+
// RFC 9470 doesn't support OR semantics in WWW-Authenticate. We
|
|
218
|
+
// pick the LEFT branch and emit its challenge — operator can
|
|
219
|
+
// override via .middleware({ requirement }).
|
|
220
|
+
return left._toReq();
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function _not(inner) {
|
|
226
|
+
return _mkNode({
|
|
227
|
+
kind: "not",
|
|
228
|
+
inner: inner,
|
|
229
|
+
_run: function (claims) {
|
|
230
|
+
var i = inner._run(claims);
|
|
231
|
+
return { ok: !i.ok, atom: "not(" + i.atom + ")", reason: i.ok ? "not(" + i.atom + ") matched" : null };
|
|
232
|
+
},
|
|
233
|
+
_toReq: function () {
|
|
234
|
+
throw new AuthError("auth-stepUp/policy-no-challenge",
|
|
235
|
+
"policy.not: cannot translate to RFC 9470 challenge");
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function _mergeAnd(a, b) {
|
|
241
|
+
var out = {};
|
|
242
|
+
if (a.acr || b.acr) {
|
|
243
|
+
if (a.acr && b.acr && a.acr !== b.acr) {
|
|
244
|
+
throw new AuthError("auth-stepUp/policy-conflict",
|
|
245
|
+
"policy.and: conflicting acr requirements " +
|
|
246
|
+
JSON.stringify(a.acr) + " and " + JSON.stringify(b.acr));
|
|
247
|
+
}
|
|
248
|
+
out.acr = a.acr || b.acr;
|
|
249
|
+
}
|
|
250
|
+
if (a.acrValues || b.acrValues) {
|
|
251
|
+
out.acrValues = (a.acrValues || []).concat(b.acrValues || []);
|
|
252
|
+
}
|
|
253
|
+
if (a.maxAge != null && b.maxAge != null) {
|
|
254
|
+
out.maxAge = Math.min(a.maxAge, b.maxAge); // tighter wins
|
|
255
|
+
} else if (a.maxAge != null) {
|
|
256
|
+
out.maxAge = a.maxAge;
|
|
257
|
+
} else if (b.maxAge != null) {
|
|
258
|
+
out.maxAge = b.maxAge;
|
|
259
|
+
}
|
|
260
|
+
if (a.requiredAmr || b.requiredAmr) {
|
|
261
|
+
var combined = (a.requiredAmr || []).concat(b.requiredAmr || []);
|
|
262
|
+
var seen = Object.create(null);
|
|
263
|
+
out.requiredAmr = [];
|
|
264
|
+
for (var i = 0; i < combined.length; i += 1) {
|
|
265
|
+
if (!seen[combined[i]]) {
|
|
266
|
+
seen[combined[i]] = true;
|
|
267
|
+
out.requiredAmr.push(combined[i]);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (a.phishingResistant === true || b.phishingResistant === true) {
|
|
272
|
+
out.phishingResistant = true;
|
|
273
|
+
}
|
|
274
|
+
return out;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ---- Common preset policies operators reach for ----
|
|
278
|
+
|
|
279
|
+
var C = require("../constants");
|
|
280
|
+
var SEC_5_MIN = C.TIME.minutes(5) / C.TIME.seconds(1);
|
|
281
|
+
var SEC_2_MIN = C.TIME.minutes(2) / C.TIME.seconds(1);
|
|
282
|
+
var SEC_15_MIN = C.TIME.minutes(15) / C.TIME.seconds(1);
|
|
283
|
+
var SEC_1_MIN = C.TIME.minutes(1) / C.TIME.seconds(1);
|
|
284
|
+
|
|
285
|
+
var PRESETS = {
|
|
286
|
+
// Sensitive write: ACR >= loa2 + auth_time within 5 min
|
|
287
|
+
sensitiveWrite: function () {
|
|
288
|
+
return acr("loa2").and(maxAge(SEC_5_MIN));
|
|
289
|
+
},
|
|
290
|
+
// Admin bulk: ACR >= loa3 + max_age 5 min + phishing-resistant
|
|
291
|
+
adminBulk: function () {
|
|
292
|
+
return acr("loa3").and(maxAge(SEC_5_MIN)).and(phishingResistant());
|
|
293
|
+
},
|
|
294
|
+
// Financial: phr-class hardware + max_age 2 min
|
|
295
|
+
financial: function () {
|
|
296
|
+
return acr("loa3").and(amr(["hwk"])).and(maxAge(SEC_2_MIN));
|
|
297
|
+
},
|
|
298
|
+
// PHI read: ACR >= loa2 + max_age 15 min
|
|
299
|
+
phiRead: function () {
|
|
300
|
+
return acr("loa2").and(maxAge(SEC_15_MIN));
|
|
301
|
+
},
|
|
302
|
+
// PHI write: ACR >= loa3 + max_age 5 min + phishing-resistant
|
|
303
|
+
phiWrite: function () {
|
|
304
|
+
return acr("loa3").and(maxAge(SEC_5_MIN)).and(phishingResistant());
|
|
305
|
+
},
|
|
306
|
+
// Account-recovery: phishing-resistant + max_age 1 min
|
|
307
|
+
accountRecovery: function () {
|
|
308
|
+
return phishingResistant().and(maxAge(SEC_1_MIN));
|
|
309
|
+
},
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
function preset(name) {
|
|
313
|
+
if (!Object.prototype.hasOwnProperty.call(PRESETS, name)) {
|
|
314
|
+
throw new AuthError("auth-stepUp/bad-preset",
|
|
315
|
+
"policy.preset: unknown preset " + JSON.stringify(name) +
|
|
316
|
+
" — valid presets: " + Object.keys(PRESETS).join(", "));
|
|
317
|
+
}
|
|
318
|
+
return PRESETS[name]();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function listPresets() {
|
|
322
|
+
return Object.keys(PRESETS).slice();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = {
|
|
326
|
+
acr: acr,
|
|
327
|
+
acrAny: acrAny,
|
|
328
|
+
amr: amr,
|
|
329
|
+
phishingResistant: phishingResistant,
|
|
330
|
+
maxAge: maxAge,
|
|
331
|
+
custom: custom,
|
|
332
|
+
preset: preset,
|
|
333
|
+
listPresets: listPresets,
|
|
334
|
+
PRESETS: PRESETS,
|
|
335
|
+
};
|