@instructure/platform-sanitize 0.3.13 → 0.3.15
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/dist/index.js +86 -69
- package/dist/sanitizeHtml.d.ts.map +1 -1
- package/dist/sanitizeUrl.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
const
|
|
1
|
+
import u from "dompurify";
|
|
2
|
+
const m = /* @__PURE__ */ new Set([
|
|
3
3
|
// layout
|
|
4
4
|
"display",
|
|
5
5
|
"float",
|
|
@@ -228,14 +228,15 @@ const p = /* @__PURE__ */ new Set([
|
|
|
228
228
|
"counter-increment",
|
|
229
229
|
"content",
|
|
230
230
|
// UI / interaction
|
|
231
|
-
// pointer-events
|
|
232
|
-
//
|
|
233
|
-
//
|
|
231
|
+
// pointer-events and z-index are safe: neither can reposition elements
|
|
232
|
+
// outside the container. The overlay-phishing risk comes from position:fixed/sticky,
|
|
233
|
+
// which SAFE_POSITION_VALUES strips.
|
|
234
234
|
"pointer-events",
|
|
235
|
+
"z-index",
|
|
235
236
|
"caret-color",
|
|
236
237
|
"accent-color",
|
|
237
238
|
"appearance"
|
|
238
|
-
]),
|
|
239
|
+
]), w = /* @__PURE__ */ new Set([
|
|
239
240
|
"src",
|
|
240
241
|
"href",
|
|
241
242
|
"action",
|
|
@@ -246,7 +247,7 @@ const p = /* @__PURE__ */ new Set([
|
|
|
246
247
|
"cite",
|
|
247
248
|
"longdesc",
|
|
248
249
|
"xlink:href"
|
|
249
|
-
]),
|
|
250
|
+
]), h = [
|
|
250
251
|
"background",
|
|
251
252
|
"background-image",
|
|
252
253
|
"list-style",
|
|
@@ -255,7 +256,7 @@ const p = /* @__PURE__ */ new Set([
|
|
|
255
256
|
// content: url(...) triggers an HTTP GET even on non-pseudo elements in some
|
|
256
257
|
// browsers; strip it as defense-in-depth against tracking-pixel exfiltration.
|
|
257
258
|
"content"
|
|
258
|
-
],
|
|
259
|
+
], y = /url\s*\(\s*['"]?(?:[a-z][a-z0-9+\-.]*:|\/\/)/i, k = /* @__PURE__ */ new Set([
|
|
259
260
|
"allow-downloads",
|
|
260
261
|
"allow-forms",
|
|
261
262
|
"allow-modals",
|
|
@@ -268,7 +269,7 @@ const p = /* @__PURE__ */ new Set([
|
|
|
268
269
|
"allow-scripts",
|
|
269
270
|
"allow-storage-access-by-user-activation",
|
|
270
271
|
"allow-top-navigation-by-user-activation"
|
|
271
|
-
]),
|
|
272
|
+
]), f = {
|
|
272
273
|
ADD_TAGS: ["iframe"],
|
|
273
274
|
ADD_ATTR: [
|
|
274
275
|
"allowfullscreen",
|
|
@@ -289,7 +290,8 @@ const p = /* @__PURE__ */ new Set([
|
|
|
289
290
|
// expressions; `arg` labels sub-expressions referenced by intent.
|
|
290
291
|
// These are plain string annotations — no URL-loading, no code execution.
|
|
291
292
|
"intent",
|
|
292
|
-
"arg"
|
|
293
|
+
"arg",
|
|
294
|
+
"loading"
|
|
293
295
|
],
|
|
294
296
|
// Rails UJS turns data-method/data-remote/etc. on clickable elements into
|
|
295
297
|
// state-changing requests carrying the victim's CSRF token. Strip them so
|
|
@@ -310,17 +312,17 @@ const p = /* @__PURE__ */ new Set([
|
|
|
310
312
|
// confusion attacks where fragments like <svg> could influence parse context.
|
|
311
313
|
FORCE_BODY: !0
|
|
312
314
|
};
|
|
313
|
-
let
|
|
314
|
-
function
|
|
315
|
-
return
|
|
316
|
-
if (!(
|
|
317
|
-
const
|
|
318
|
-
for (let n = 0; n <
|
|
319
|
-
const
|
|
320
|
-
|
|
315
|
+
let a = null;
|
|
316
|
+
function A() {
|
|
317
|
+
return a || (a = typeof u == "function" ? u(window) : u, a.addHook("afterSanitizeAttributes", (e) => {
|
|
318
|
+
if (!(e instanceof Element) || !e.hasAttribute("style")) return;
|
|
319
|
+
const r = e.style, t = [];
|
|
320
|
+
for (let n = 0; n < r.length; n++) {
|
|
321
|
+
const i = r.item(n);
|
|
322
|
+
m.has(i) || t.push(i);
|
|
321
323
|
}
|
|
322
|
-
for (const n of
|
|
323
|
-
const
|
|
324
|
+
for (const n of t) r.removeProperty(n);
|
|
325
|
+
const o = /* @__PURE__ */ new Set([
|
|
324
326
|
"static",
|
|
325
327
|
"relative",
|
|
326
328
|
"absolute",
|
|
@@ -329,39 +331,54 @@ function k() {
|
|
|
329
331
|
"unset",
|
|
330
332
|
"revert",
|
|
331
333
|
"revert-layer"
|
|
332
|
-
]), s =
|
|
333
|
-
s && !
|
|
334
|
-
for (const n of
|
|
335
|
-
const
|
|
336
|
-
|
|
334
|
+
]), s = r.getPropertyValue("position").trim().toLowerCase();
|
|
335
|
+
s && !o.has(s) && r.removeProperty("position");
|
|
336
|
+
for (const n of h) {
|
|
337
|
+
const i = r.getPropertyValue(n);
|
|
338
|
+
i && y.test(i) && r.removeProperty(n);
|
|
337
339
|
}
|
|
338
|
-
|
|
339
|
-
}),
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
340
|
+
r.length === 0 && e.removeAttribute("style");
|
|
341
|
+
}), a.addHook("uponSanitizeAttribute", (e, r) => {
|
|
342
|
+
if (!w.has(r.attrName)) return;
|
|
343
|
+
const t = r.attrValue;
|
|
344
|
+
/^\s*\/\//.test(t) ? (r.attrValue = t.trimStart().replace(/^\/\//, "https://"), r.keepAttr = !0) : /^\s*\\/.test(t) && (r.keepAttr = !1);
|
|
345
|
+
}), a.addHook("afterSanitizeAttributes", (e) => {
|
|
346
|
+
if (!(e instanceof Element) || !e.hasAttribute("srcset")) return;
|
|
347
|
+
const t = (e.getAttribute("srcset") ?? "").split(","), o = (i) => i.trim().split(/\s+/)[0];
|
|
348
|
+
if (t.some((i) => /^\s*\\/.test(o(i)))) {
|
|
349
|
+
e.removeAttribute("srcset");
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
let s = !1;
|
|
353
|
+
const n = t.map((i) => {
|
|
354
|
+
const l = o(i);
|
|
355
|
+
if (!l.startsWith("//")) return i;
|
|
356
|
+
s = !0;
|
|
357
|
+
const d = i.indexOf(l);
|
|
358
|
+
return i.slice(0, d) + "https://" + l.slice(2) + i.slice(d + l.length);
|
|
359
|
+
});
|
|
360
|
+
s && e.setAttribute("srcset", n.join(","));
|
|
361
|
+
}), a.addHook("afterSanitizeAttributes", (e) => {
|
|
362
|
+
var o;
|
|
363
|
+
if (!(e instanceof Element) || e.tagName !== "A" && e.tagName !== "AREA" || ((o = e.getAttribute("target")) == null ? void 0 : o.toLowerCase()) !== "_blank") return;
|
|
364
|
+
const r = e.getAttribute("rel") ?? "", t = new Set(r.split(/\s+/).filter(Boolean));
|
|
365
|
+
t.add("noopener"), e.setAttribute("rel", [...t].join(" "));
|
|
366
|
+
}), a.addHook("afterSanitizeAttributes", (e) => {
|
|
367
|
+
if (!(e instanceof Element) || e.tagName !== "IFRAME" || !e.hasAttribute("sandbox")) return;
|
|
368
|
+
const t = (e.getAttribute("sandbox") ?? "").toLowerCase().split(/\s+/).filter(Boolean), o = t.filter((s) => k.has(s));
|
|
369
|
+
o.length !== t.length && e.setAttribute("sandbox", o.join(" "));
|
|
370
|
+
}), a);
|
|
354
371
|
}
|
|
355
|
-
function
|
|
372
|
+
function v(e, r) {
|
|
356
373
|
if (typeof window > "u")
|
|
357
374
|
throw new Error("sanitizeHtml requires a DOM environment (window is not defined)");
|
|
358
|
-
const
|
|
359
|
-
return
|
|
375
|
+
const t = r != null && r.allowFormAttributeNames ? { ...f, SANITIZE_DOM: !1 } : f;
|
|
376
|
+
return A().sanitize(e ?? "", t);
|
|
360
377
|
}
|
|
361
|
-
const
|
|
378
|
+
const p = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]), c = "http://platform-sanitize.invalid/", g = /^\s*\/\//, b = (
|
|
362
379
|
// oxlint-disable-next-line no-control-regex -- intentional security guard
|
|
363
380
|
/^[\u0000-\u0020\u007F-\u00A0\u2000-\u200F\u2028\u2029\u202F\u205F\u2060\u3000\uFEFF]*(?:javascript|data|vbscript|file):/i
|
|
364
|
-
),
|
|
381
|
+
), x = {
|
|
365
382
|
colon: ":",
|
|
366
383
|
// javascript:alert(1) → javascript:alert(1)
|
|
367
384
|
sol: "/",
|
|
@@ -374,49 +391,49 @@ const f = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]), l = "
|
|
|
374
391
|
`
|
|
375
392
|
// java
script:
|
|
376
393
|
};
|
|
377
|
-
function E(
|
|
378
|
-
return
|
|
379
|
-
const
|
|
380
|
-
if (!Number.isFinite(
|
|
394
|
+
function E(e) {
|
|
395
|
+
return e.replace(/&#(\d+);/g, (r, t) => {
|
|
396
|
+
const o = Number(t);
|
|
397
|
+
if (!Number.isFinite(o) || o < 0 || o > 1114111) return "";
|
|
381
398
|
try {
|
|
382
|
-
return String.fromCodePoint(
|
|
399
|
+
return String.fromCodePoint(o);
|
|
383
400
|
} catch {
|
|
384
401
|
return "";
|
|
385
402
|
}
|
|
386
|
-
}).replace(/&#x([\da-f]+);/gi, (
|
|
387
|
-
const
|
|
388
|
-
if (!Number.isFinite(
|
|
403
|
+
}).replace(/&#x([\da-f]+);/gi, (r, t) => {
|
|
404
|
+
const o = parseInt(t, 16);
|
|
405
|
+
if (!Number.isFinite(o) || o < 0 || o > 1114111) return "";
|
|
389
406
|
try {
|
|
390
|
-
return String.fromCodePoint(
|
|
407
|
+
return String.fromCodePoint(o);
|
|
391
408
|
} catch {
|
|
392
409
|
return "";
|
|
393
410
|
}
|
|
394
|
-
}).replace(/&([A-Za-z][A-Za-z0-9]*);/g, (
|
|
411
|
+
}).replace(/&([A-Za-z][A-Za-z0-9]*);/g, (r, t) => x[t] ?? r);
|
|
395
412
|
}
|
|
396
|
-
function
|
|
397
|
-
if (!
|
|
398
|
-
const
|
|
399
|
-
if (
|
|
400
|
-
if (/&[#A-Za-z]/.test(
|
|
401
|
-
const
|
|
402
|
-
if (
|
|
413
|
+
function R(e) {
|
|
414
|
+
if (!e || !e.trim()) return "about:blank";
|
|
415
|
+
const r = e.replace(/\\/g, "/");
|
|
416
|
+
if (g.test(r) || b.test(r)) return "about:blank";
|
|
417
|
+
if (/&[#A-Za-z]/.test(r)) {
|
|
418
|
+
const t = E(r);
|
|
419
|
+
if (g.test(t) || b.test(t))
|
|
403
420
|
return "about:blank";
|
|
404
421
|
try {
|
|
405
|
-
const
|
|
406
|
-
if (!
|
|
422
|
+
const o = new URL(t, c);
|
|
423
|
+
if (!o.href.startsWith(c) && !p.has(o.protocol))
|
|
407
424
|
return "about:blank";
|
|
408
425
|
} catch {
|
|
409
426
|
return "about:blank";
|
|
410
427
|
}
|
|
411
428
|
}
|
|
412
429
|
try {
|
|
413
|
-
const
|
|
414
|
-
return !
|
|
430
|
+
const t = new URL(r, c);
|
|
431
|
+
return !p.has(t.protocol) || (t.protocol === "http:" || t.protocol === "https:") && (t.username || t.password) ? "about:blank" : t.href.startsWith(c) ? e : r.replace(/[\x00-\x1F\u2028\u2029]/g, "").replace(/%250[9ad]/gi, "").replace(/%0[9ad]/gi, "");
|
|
415
432
|
} catch {
|
|
416
433
|
return "about:blank";
|
|
417
434
|
}
|
|
418
435
|
}
|
|
419
436
|
export {
|
|
420
|
-
|
|
421
|
-
|
|
437
|
+
v as sanitizeHtml,
|
|
438
|
+
R as sanitizeUrl
|
|
422
439
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../src/sanitizeHtml.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../src/sanitizeHtml.ts"],"names":[],"mappings":"AA6cA,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,OAAO,CAAC,EAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9C,MAAM,CASR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sanitizeUrl.d.ts","sourceRoot":"","sources":["../src/sanitizeUrl.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sanitizeUrl.d.ts","sourceRoot":"","sources":["../src/sanitizeUrl.ts"],"names":[],"mappings":"AAsFA,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAwDlE"}
|