@blamejs/core 0.8.76 → 0.8.78
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/index.js +2 -0
- package/lib/acme.js +200 -1
- package/lib/auth/oauth.js +329 -0
- package/lib/compliance-ai-act.js +161 -3
- package/lib/compliance.js +48 -0
- package/lib/config.js +129 -13
- package/lib/content-credentials.js +227 -0
- package/lib/cra-report.js +106 -2
- package/lib/crypto-field.js +5 -0
- package/lib/dsr.js +96 -0
- package/lib/mcp.js +239 -6
- package/lib/middleware/index.js +4 -0
- package/lib/middleware/protected-resource-metadata.js +165 -0
- package/lib/middleware/rate-limit.js +59 -3
- package/lib/middleware/scim-server.js +375 -0
- package/lib/middleware/security-headers.js +12 -0
- package/lib/nist-crosswalk.js +293 -0
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// SCIM 2.0 server middleware (RFC 7642 / 7643 / 7644).
|
|
3
|
+
// Provides /Users + /Groups + /ServiceProviderConfig + /ResourceTypes
|
|
4
|
+
// + /Schemas surfaces backed by operator-supplied CRUD callbacks.
|
|
5
|
+
|
|
6
|
+
var framework_error = require("../framework-error");
|
|
7
|
+
var validateOpts = require("../validate-opts");
|
|
8
|
+
var safeJson = require("../safe-json");
|
|
9
|
+
var safeBuffer = require("../safe-buffer");
|
|
10
|
+
var requestHelpers = require("../request-helpers");
|
|
11
|
+
var C = require("../constants");
|
|
12
|
+
|
|
13
|
+
var H = requestHelpers.HTTP_STATUS;
|
|
14
|
+
|
|
15
|
+
var ScimServerError = framework_error.defineClass(
|
|
16
|
+
"ScimServerError",
|
|
17
|
+
"middleware/scim-server"
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
var SCIM_CORE_SCHEMA_USER = "urn:ietf:params:scim:schemas:core:2.0:User";
|
|
21
|
+
var SCIM_CORE_SCHEMA_GROUP = "urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
22
|
+
var SCIM_MESSAGE_ERROR = "urn:ietf:params:scim:api:messages:2.0:Error";
|
|
23
|
+
var SCIM_MESSAGE_LIST = "urn:ietf:params:scim:api:messages:2.0:ListResponse";
|
|
24
|
+
|
|
25
|
+
var ALLOWED_FILTER_OPS = ["eq", "ne", "co", "sw", "ew", "pr", "gt", "ge", "lt", "le"];
|
|
26
|
+
|
|
27
|
+
var SCIM_FILTER_RE = /^\s*([a-zA-Z][a-zA-Z0-9._-]*)\s+(eq|ne|co|sw|ew|pr|gt|ge|lt|le)(?:\s+(.+))?\s*$/;
|
|
28
|
+
var RESOURCE_PATH_RE = /^\/(Users|Groups)(?:\/([^/]+))?$/;
|
|
29
|
+
var BEARER_RE = /^Bearer\s+(.+)$/i;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @primitive b.middleware.scimServer
|
|
33
|
+
* @signature b.middleware.scimServer(opts)
|
|
34
|
+
* @since 0.8.77
|
|
35
|
+
* @related b.auth.oauth.introspectToken
|
|
36
|
+
*
|
|
37
|
+
* Returns a request middleware that handles SCIM 2.0 requests
|
|
38
|
+
* (RFC 7642-7644). Operator supplies CRUD callbacks per resource.
|
|
39
|
+
*
|
|
40
|
+
* @opts
|
|
41
|
+
* {
|
|
42
|
+
* basePath?: string,
|
|
43
|
+
* users: ScimResourceImpl,
|
|
44
|
+
* groups?: ScimResourceImpl,
|
|
45
|
+
* bearer?: (token) => Promise<actor>,
|
|
46
|
+
* maxPageSize?: number,
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* var mw = b.middleware.scimServer({
|
|
51
|
+
* basePath: "/scim/v2",
|
|
52
|
+
* users: myUserAdapter,
|
|
53
|
+
* groups: myGroupAdapter,
|
|
54
|
+
* });
|
|
55
|
+
* app.use(mw);
|
|
56
|
+
*/
|
|
57
|
+
function create(opts) {
|
|
58
|
+
validateOpts.requireObject(opts, "middleware.scimServer",
|
|
59
|
+
ScimServerError, "middleware/scim-server/bad-opts");
|
|
60
|
+
_validateResourceImpl(opts.users, "users");
|
|
61
|
+
if (opts.groups) _validateResourceImpl(opts.groups, "groups");
|
|
62
|
+
|
|
63
|
+
var basePath = opts.basePath || "/scim/v2";
|
|
64
|
+
var maxPageSize = opts.maxPageSize || 200; // allow:raw-byte-literal — page-size count, not bytes
|
|
65
|
+
var bearer = opts.bearer || null;
|
|
66
|
+
|
|
67
|
+
function middleware(req, res, next) {
|
|
68
|
+
var url = req.url.split("?")[0];
|
|
69
|
+
if (url.indexOf(basePath) !== 0) { next(); return; }
|
|
70
|
+
_dispatch(req, res, basePath, bearer, opts, maxPageSize)
|
|
71
|
+
.catch(function (err) {
|
|
72
|
+
_writeScimError(res, err.statusCode || 500, err.scimType || "internal",
|
|
73
|
+
(err.message || String(err)).slice(0, 500));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
middleware.basePath = basePath;
|
|
78
|
+
middleware.serviceProviderDoc = _serviceProviderConfig(opts);
|
|
79
|
+
return middleware;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function _validateResourceImpl(impl, name) {
|
|
83
|
+
if (!impl || typeof impl !== "object") {
|
|
84
|
+
throw new ScimServerError("middleware/scim-server/no-" + name,
|
|
85
|
+
"middleware.scimServer: opts." + name + " must be an object implementing { create, read, update, patch, remove, list }");
|
|
86
|
+
}
|
|
87
|
+
["create", "read", "update", "patch", "remove", "list"].forEach(function (m) {
|
|
88
|
+
if (typeof impl[m] !== "function") {
|
|
89
|
+
throw new ScimServerError("middleware/scim-server/bad-" + name + "-impl",
|
|
90
|
+
"middleware.scimServer: opts." + name + "." + m + " must be a function");
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function _dispatch(req, res, basePath, bearer, opts, maxPageSize) {
|
|
96
|
+
var relUrl = req.url.slice(basePath.length) || "/";
|
|
97
|
+
var qIdx = relUrl.indexOf("?");
|
|
98
|
+
var path = qIdx === -1 ? relUrl : relUrl.slice(0, qIdx);
|
|
99
|
+
var query = _parseQuery(qIdx === -1 ? "" : relUrl.slice(qIdx + 1));
|
|
100
|
+
|
|
101
|
+
if (path === "/ServiceProviderConfig" && req.method === "GET") {
|
|
102
|
+
_writeJson(res, H.OK, _serviceProviderConfig(opts)); return;
|
|
103
|
+
}
|
|
104
|
+
if (path === "/ResourceTypes" && req.method === "GET") {
|
|
105
|
+
_writeJson(res, H.OK, _resourceTypes(opts)); return;
|
|
106
|
+
}
|
|
107
|
+
if (path === "/Schemas" && req.method === "GET") {
|
|
108
|
+
_writeJson(res, H.OK, _schemas()); return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
var actor = null;
|
|
112
|
+
if (bearer) {
|
|
113
|
+
var authz = req.headers && req.headers.authorization;
|
|
114
|
+
// BEARER_RE applies to a single header line; node http caps header lines at 8 KiB.
|
|
115
|
+
var m = authz && typeof authz === "string" && authz.length < C.BYTES.kib(8) && BEARER_RE.test(authz) // allow:regex-no-length-cap header line bounded above
|
|
116
|
+
? authz.match(BEARER_RE) : null;
|
|
117
|
+
if (!m) {
|
|
118
|
+
res.writeHead(H.UNAUTHORIZED, {
|
|
119
|
+
"Content-Type": "application/scim+json",
|
|
120
|
+
"WWW-Authenticate": "Bearer",
|
|
121
|
+
"Cache-Control": "no-store",
|
|
122
|
+
});
|
|
123
|
+
res.end(JSON.stringify({
|
|
124
|
+
schemas: [SCIM_MESSAGE_ERROR],
|
|
125
|
+
status: "401",
|
|
126
|
+
detail: "Missing Bearer token",
|
|
127
|
+
}));
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try { actor = await bearer(m[1]); }
|
|
131
|
+
catch (_e) { actor = null; }
|
|
132
|
+
if (!actor) {
|
|
133
|
+
res.writeHead(H.UNAUTHORIZED, {
|
|
134
|
+
"Content-Type": "application/scim+json",
|
|
135
|
+
"WWW-Authenticate": 'Bearer error="invalid_token"',
|
|
136
|
+
"Cache-Control": "no-store",
|
|
137
|
+
});
|
|
138
|
+
res.end(JSON.stringify({
|
|
139
|
+
schemas: [SCIM_MESSAGE_ERROR],
|
|
140
|
+
status: "401",
|
|
141
|
+
detail: "Bearer token rejected",
|
|
142
|
+
}));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
var ctx = { actor: actor, req: req };
|
|
148
|
+
var users = opts.users;
|
|
149
|
+
var groups = opts.groups;
|
|
150
|
+
|
|
151
|
+
// RESOURCE_PATH_RE applies to a URL path; node http caps URL at 8 KiB.
|
|
152
|
+
var match = path.length < C.BYTES.kib(8) && RESOURCE_PATH_RE.test(path) ? path.match(RESOURCE_PATH_RE) : null; // allow:regex-no-length-cap URL bounded above
|
|
153
|
+
if (!match) {
|
|
154
|
+
_writeScimError(res, H.NOT_FOUND, "notFound", "no SCIM resource at " + path);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
var resourceType = match[1];
|
|
158
|
+
var resourceId = match[2] || null;
|
|
159
|
+
var impl = resourceType === "Users" ? users : groups;
|
|
160
|
+
if (!impl) {
|
|
161
|
+
_writeScimError(res, 404, "notFound", "/" + resourceType + " not configured");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
var body = null;
|
|
166
|
+
if (req.method === "POST" || req.method === "PUT" || req.method === "PATCH") {
|
|
167
|
+
body = await _readJsonBody(req);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (req.method === "GET" && !resourceId) {
|
|
171
|
+
var filter = _parseFilter(query.filter);
|
|
172
|
+
var pageSize = Math.min(maxPageSize, parseInt(query.count || "100", 10) || 100);
|
|
173
|
+
var startIndex = Math.max(1, parseInt(query.startIndex || "1", 10) || 1);
|
|
174
|
+
var listRv = await impl.list({
|
|
175
|
+
filter: filter,
|
|
176
|
+
startIndex: startIndex,
|
|
177
|
+
count: pageSize,
|
|
178
|
+
sortBy: query.sortBy || null,
|
|
179
|
+
sortOrder: query.sortOrder || null,
|
|
180
|
+
attributes: query.attributes ? query.attributes.split(",") : null,
|
|
181
|
+
excludedAttributes: query.excludedAttributes ? query.excludedAttributes.split(",") : null,
|
|
182
|
+
}, ctx);
|
|
183
|
+
_writeJson(res, H.OK, {
|
|
184
|
+
schemas: [SCIM_MESSAGE_LIST],
|
|
185
|
+
totalResults: listRv.totalResults,
|
|
186
|
+
startIndex: startIndex,
|
|
187
|
+
itemsPerPage: listRv.Resources.length,
|
|
188
|
+
Resources: listRv.Resources,
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (req.method === "GET" && resourceId) {
|
|
194
|
+
var rec = await impl.read(resourceId, ctx);
|
|
195
|
+
if (!rec) { _writeScimError(res, H.NOT_FOUND, "notFound", "no resource with id " + resourceId); return; }
|
|
196
|
+
_writeJson(res, H.OK, rec);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (req.method === "POST" && !resourceId) {
|
|
201
|
+
_assertSchema(body, resourceType === "Users" ? SCIM_CORE_SCHEMA_USER : SCIM_CORE_SCHEMA_GROUP);
|
|
202
|
+
var created = await impl.create(body, ctx);
|
|
203
|
+
_writeJson(res, H.CREATED, created);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (req.method === "PUT" && resourceId) {
|
|
208
|
+
_assertSchema(body, resourceType === "Users" ? SCIM_CORE_SCHEMA_USER : SCIM_CORE_SCHEMA_GROUP);
|
|
209
|
+
var updated = await impl.update(resourceId, body, ctx);
|
|
210
|
+
_writeJson(res, H.OK, updated);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (req.method === "PATCH" && resourceId) {
|
|
215
|
+
if (!body || !Array.isArray(body.Operations) || body.Operations.length === 0) {
|
|
216
|
+
_writeScimError(res, H.BAD_REQUEST, "invalidValue", "PATCH body must include Operations[]");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
body.Operations.forEach(function (op, i) {
|
|
220
|
+
if (!op || typeof op !== "object" || typeof op.op !== "string") {
|
|
221
|
+
var e = new Error("Operations[" + i + "] missing op");
|
|
222
|
+
e.statusCode = H.BAD_REQUEST; e.scimType = "invalidValue";
|
|
223
|
+
throw e;
|
|
224
|
+
}
|
|
225
|
+
var verb = op.op.toLowerCase();
|
|
226
|
+
if (verb !== "add" && verb !== "remove" && verb !== "replace") {
|
|
227
|
+
var e2 = new Error("Operations[" + i + "].op = '" + op.op + "' not in add/remove/replace");
|
|
228
|
+
e2.statusCode = H.BAD_REQUEST; e2.scimType = "invalidValue";
|
|
229
|
+
throw e2;
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
var patched = await impl.patch(resourceId, body.Operations, ctx);
|
|
233
|
+
_writeJson(res, H.OK, patched);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (req.method === "DELETE" && resourceId) {
|
|
238
|
+
await impl.remove(resourceId, ctx);
|
|
239
|
+
res.writeHead(H.NO_CONTENT, { "Cache-Control": "no-store" });
|
|
240
|
+
res.end();
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
_writeScimError(res, H.METHOD_NOT_ALLOWED, "noTarget", req.method + " not allowed on " + path);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function _parseQuery(qs) {
|
|
248
|
+
var out = {};
|
|
249
|
+
if (!qs) return out;
|
|
250
|
+
qs.split("&").forEach(function (pair) {
|
|
251
|
+
var eq = pair.indexOf("=");
|
|
252
|
+
var k = eq === -1 ? pair : pair.slice(0, eq);
|
|
253
|
+
var v = eq === -1 ? "" : pair.slice(eq + 1);
|
|
254
|
+
try { out[decodeURIComponent(k)] = decodeURIComponent(v.replace(/\+/g, " ")); }
|
|
255
|
+
catch (_e) { /* skip malformed */ }
|
|
256
|
+
});
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function _parseFilter(filter) {
|
|
261
|
+
if (typeof filter !== "string" || filter.length === 0) return null;
|
|
262
|
+
if (!SCIM_FILTER_RE.test(filter)) return { raw: filter };
|
|
263
|
+
var m = filter.match(SCIM_FILTER_RE);
|
|
264
|
+
var op = m[2].toLowerCase();
|
|
265
|
+
if (ALLOWED_FILTER_OPS.indexOf(op) === -1) return { raw: filter };
|
|
266
|
+
var rv = { attribute: m[1], op: op, raw: filter };
|
|
267
|
+
if (op === "pr") return rv;
|
|
268
|
+
var v = (m[3] || "").trim();
|
|
269
|
+
if (v.charAt(0) === '"' && v.charAt(v.length - 1) === '"') {
|
|
270
|
+
v = v.slice(1, -1).replace(/\\"/g, '"');
|
|
271
|
+
}
|
|
272
|
+
rv.value = v;
|
|
273
|
+
return rv;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function _readJsonBody(req) {
|
|
277
|
+
var MAX = C.BYTES.mib(1);
|
|
278
|
+
if (req.body && Buffer.isBuffer(req.body)) {
|
|
279
|
+
return Promise.resolve(safeJson.parse(req.body.toString("utf8"), { maxBytes: MAX }));
|
|
280
|
+
}
|
|
281
|
+
if (req.body && typeof req.body === "object") return Promise.resolve(req.body);
|
|
282
|
+
return safeBuffer.boundedChunkCollector(req, MAX, ScimServerError, "middleware/scim-server/body-too-large")
|
|
283
|
+
.then(function (buf) {
|
|
284
|
+
return safeJson.parse(buf.toString("utf8"), { maxBytes: MAX });
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function _assertSchema(body, expectedSchema) {
|
|
289
|
+
if (!body || typeof body !== "object") {
|
|
290
|
+
var e = new Error("request body must be a JSON object");
|
|
291
|
+
e.statusCode = H.BAD_REQUEST; e.scimType = "invalidValue"; throw e;
|
|
292
|
+
}
|
|
293
|
+
if (!Array.isArray(body.schemas) || body.schemas.indexOf(expectedSchema) === -1) {
|
|
294
|
+
var e2 = new Error("body.schemas must include '" + expectedSchema + "'");
|
|
295
|
+
e2.statusCode = H.BAD_REQUEST; e2.scimType = "invalidValue"; throw e2;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function _writeJson(res, status, body) {
|
|
300
|
+
res.writeHead(status, {
|
|
301
|
+
"Content-Type": "application/scim+json",
|
|
302
|
+
"Cache-Control": "no-store",
|
|
303
|
+
});
|
|
304
|
+
res.end(JSON.stringify(body));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function _writeScimError(res, status, scimType, detail) {
|
|
308
|
+
res.writeHead(status, {
|
|
309
|
+
"Content-Type": "application/scim+json",
|
|
310
|
+
"Cache-Control": "no-store",
|
|
311
|
+
});
|
|
312
|
+
res.end(JSON.stringify({
|
|
313
|
+
schemas: [SCIM_MESSAGE_ERROR],
|
|
314
|
+
status: String(status),
|
|
315
|
+
scimType: scimType,
|
|
316
|
+
detail: detail,
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function _serviceProviderConfig(opts) {
|
|
321
|
+
return {
|
|
322
|
+
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"],
|
|
323
|
+
documentationUri: opts.documentationUri || "https://datatracker.ietf.org/doc/html/rfc7643",
|
|
324
|
+
patch: { supported: true },
|
|
325
|
+
bulk: { supported: false, maxOperations: 0, maxPayloadSize: 0 },
|
|
326
|
+
filter: { supported: true, maxResults: opts.maxPageSize || 200 },
|
|
327
|
+
changePassword: { supported: false },
|
|
328
|
+
sort: { supported: true },
|
|
329
|
+
etag: { supported: false },
|
|
330
|
+
authenticationSchemes: [
|
|
331
|
+
{
|
|
332
|
+
type: "oauthbearertoken",
|
|
333
|
+
name: "OAuth 2.0 Bearer Token",
|
|
334
|
+
description: "Authentication scheme using the OAuth Bearer Token Standard",
|
|
335
|
+
specUri: "https://www.rfc-editor.org/info/rfc6750",
|
|
336
|
+
primary: true,
|
|
337
|
+
},
|
|
338
|
+
],
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function _resourceTypes(opts) {
|
|
343
|
+
var rv = [
|
|
344
|
+
{
|
|
345
|
+
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ResourceType"],
|
|
346
|
+
id: "User", name: "User", endpoint: "/Users",
|
|
347
|
+
description: "User resource (RFC 7643 §4.1)",
|
|
348
|
+
schema: SCIM_CORE_SCHEMA_USER,
|
|
349
|
+
},
|
|
350
|
+
];
|
|
351
|
+
if (opts.groups) {
|
|
352
|
+
rv.push({
|
|
353
|
+
schemas: ["urn:ietf:params:scim:schemas:core:2.0:ResourceType"],
|
|
354
|
+
id: "Group", name: "Group", endpoint: "/Groups",
|
|
355
|
+
description: "Group resource (RFC 7643 §4.2)",
|
|
356
|
+
schema: SCIM_CORE_SCHEMA_GROUP,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return rv;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function _schemas() {
|
|
363
|
+
return [
|
|
364
|
+
{ id: SCIM_CORE_SCHEMA_USER, name: "User", description: "RFC 7643 §4.1 User resource" },
|
|
365
|
+
{ id: SCIM_CORE_SCHEMA_GROUP, name: "Group", description: "RFC 7643 §4.2 Group resource" },
|
|
366
|
+
];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
module.exports = {
|
|
370
|
+
create: create,
|
|
371
|
+
ScimServerError: ScimServerError,
|
|
372
|
+
SCIM_CORE_SCHEMA_USER: SCIM_CORE_SCHEMA_USER,
|
|
373
|
+
SCIM_CORE_SCHEMA_GROUP: SCIM_CORE_SCHEMA_GROUP,
|
|
374
|
+
ALLOWED_FILTER_OPS: ALLOWED_FILTER_OPS,
|
|
375
|
+
};
|
|
@@ -71,6 +71,18 @@ var DEFAULT_PERMISSIONS = [
|
|
|
71
71
|
// embedding APIs; deny-by-default.
|
|
72
72
|
"storage-access=()", "browsing-topics=()",
|
|
73
73
|
"private-aggregation=()", "controlled-frame=()", "captured-surface-control=()",
|
|
74
|
+
// v0.8.77 expansion — remaining Privacy Sandbox + Browser-API
|
|
75
|
+
// directives surfacing through Chrome 130+ / Firefox 132+ stable.
|
|
76
|
+
// Default-deny: FedCM (identity-credentials-get), cross-site
|
|
77
|
+
// attribution reporting, WebAuthn create flow (operators that need
|
|
78
|
+
// it opt in explicitly), FLEDGE/Topics auction APIs (join-ad-
|
|
79
|
+
// interest-group / run-ad-auction), Shared Storage API + selectURL,
|
|
80
|
+
// Smart Card API, all-screens capture, deferred-fetch (background
|
|
81
|
+
// resource sync).
|
|
82
|
+
"identity-credentials-get=()", "attribution-reporting-cross-site=()",
|
|
83
|
+
"publickey-credentials-create=()", "join-ad-interest-group=()",
|
|
84
|
+
"run-ad-auction=()", "shared-storage=()", "shared-storage-select-url=()",
|
|
85
|
+
"smartcard=()", "all-screens-capture=()", "deferred-fetch=()",
|
|
74
86
|
];
|
|
75
87
|
|
|
76
88
|
// Strict CSP — no 'unsafe-inline' on script-src OR style-src.
|