@instructure/platform-sanitize 0.3.15 → 0.3.17
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 +69 -60
- package/dist/sanitizeHtml.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import d from "dompurify";
|
|
2
2
|
const m = /* @__PURE__ */ new Set([
|
|
3
3
|
// layout
|
|
4
4
|
"display",
|
|
@@ -8,6 +8,7 @@ const m = /* @__PURE__ */ new Set([
|
|
|
8
8
|
"overflow-x",
|
|
9
9
|
"overflow-y",
|
|
10
10
|
"visibility",
|
|
11
|
+
"opacity",
|
|
11
12
|
"cursor",
|
|
12
13
|
"direction",
|
|
13
14
|
"user-select",
|
|
@@ -205,6 +206,10 @@ const m = /* @__PURE__ */ new Set([
|
|
|
205
206
|
"animation-direction",
|
|
206
207
|
"animation-fill-mode",
|
|
207
208
|
"animation-play-state",
|
|
209
|
+
// transforms — no value filter needed; .user_content has overflow-y:hidden and overflow-x:auto
|
|
210
|
+
// (canvas-lms g/412928), which together contain transformed content within the container on
|
|
211
|
+
// both axes and prevent overlay of Canvas UI.
|
|
212
|
+
"transform",
|
|
208
213
|
// columns
|
|
209
214
|
"column-count",
|
|
210
215
|
"column-width",
|
|
@@ -285,13 +290,15 @@ const m = /* @__PURE__ */ new Set([
|
|
|
285
290
|
"webkitallowfullscreen",
|
|
286
291
|
"mozallowfullscreen",
|
|
287
292
|
"scrolling",
|
|
293
|
+
// loading="lazy" is not in DOMPurify's default ALLOWED_ATTR and must be
|
|
294
|
+
// explicitly added. No security concern — it is a performance hint only.
|
|
295
|
+
"loading",
|
|
288
296
|
// MathML 4 (W3C) annotation attributes for screen-reader accessibility.
|
|
289
297
|
// MathCAT, JAWS, and NVDA use `intent` to know how to pronounce
|
|
290
298
|
// expressions; `arg` labels sub-expressions referenced by intent.
|
|
291
299
|
// These are plain string annotations — no URL-loading, no code execution.
|
|
292
300
|
"intent",
|
|
293
|
-
"arg"
|
|
294
|
-
"loading"
|
|
301
|
+
"arg"
|
|
295
302
|
],
|
|
296
303
|
// Rails UJS turns data-method/data-remote/etc. on clickable elements into
|
|
297
304
|
// state-changing requests carrying the victim's CSRF token. Strip them so
|
|
@@ -314,14 +321,14 @@ const m = /* @__PURE__ */ new Set([
|
|
|
314
321
|
};
|
|
315
322
|
let a = null;
|
|
316
323
|
function A() {
|
|
317
|
-
return a || (a = typeof
|
|
318
|
-
if (!(
|
|
319
|
-
const
|
|
320
|
-
for (let n = 0; n <
|
|
321
|
-
const
|
|
322
|
-
m.has(
|
|
324
|
+
return a || (a = typeof d == "function" ? d(window) : d, a.addHook("afterSanitizeAttributes", (r) => {
|
|
325
|
+
if (!(r instanceof Element) || !r.hasAttribute("style")) return;
|
|
326
|
+
const t = r.style, e = [];
|
|
327
|
+
for (let n = 0; n < t.length; n++) {
|
|
328
|
+
const s = t.item(n);
|
|
329
|
+
m.has(s) || e.push(s);
|
|
323
330
|
}
|
|
324
|
-
for (const n of
|
|
331
|
+
for (const n of e) t.removeProperty(n);
|
|
325
332
|
const o = /* @__PURE__ */ new Set([
|
|
326
333
|
"static",
|
|
327
334
|
"relative",
|
|
@@ -331,54 +338,56 @@ function A() {
|
|
|
331
338
|
"unset",
|
|
332
339
|
"revert",
|
|
333
340
|
"revert-layer"
|
|
334
|
-
]),
|
|
335
|
-
|
|
341
|
+
]), l = t.getPropertyValue("position").trim().toLowerCase();
|
|
342
|
+
l && !o.has(l) && t.removeProperty("position");
|
|
343
|
+
const u = /* @__PURE__ */ new Set(["initial", "inherit", "unset", "revert", "revert-layer"]), i = t.getPropertyValue("opacity").trim().toLowerCase();
|
|
344
|
+
i && !u.has(i) && (i.endsWith("%") ? parseFloat(i) / 100 : parseFloat(i)) < 0.05 && t.removeProperty("opacity");
|
|
336
345
|
for (const n of h) {
|
|
337
|
-
const
|
|
338
|
-
|
|
346
|
+
const s = t.getPropertyValue(n);
|
|
347
|
+
s && y.test(s) && t.removeProperty(n);
|
|
339
348
|
}
|
|
340
|
-
|
|
341
|
-
}), a.addHook("uponSanitizeAttribute", (
|
|
342
|
-
if (!w.has(
|
|
343
|
-
const
|
|
344
|
-
/^\s*\/\//.test(
|
|
345
|
-
}), a.addHook("afterSanitizeAttributes", (
|
|
346
|
-
if (!(
|
|
347
|
-
const
|
|
348
|
-
if (
|
|
349
|
-
|
|
349
|
+
t.length === 0 && r.removeAttribute("style");
|
|
350
|
+
}), a.addHook("uponSanitizeAttribute", (r, t) => {
|
|
351
|
+
if (!w.has(t.attrName)) return;
|
|
352
|
+
const e = t.attrValue;
|
|
353
|
+
/^\s*\/\//.test(e) ? (t.attrValue = e.trimStart().replace(/^\/\//, "https://"), t.keepAttr = !0) : /^\s*\\/.test(e) && (t.keepAttr = !1);
|
|
354
|
+
}), a.addHook("afterSanitizeAttributes", (r) => {
|
|
355
|
+
if (!(r instanceof Element) || !r.hasAttribute("srcset")) return;
|
|
356
|
+
const e = (r.getAttribute("srcset") ?? "").split(","), o = (i) => i.trim().split(/\s+/)[0];
|
|
357
|
+
if (e.some((i) => /^\s*\\/.test(o(i)))) {
|
|
358
|
+
r.removeAttribute("srcset");
|
|
350
359
|
return;
|
|
351
360
|
}
|
|
352
|
-
let
|
|
353
|
-
const
|
|
354
|
-
const
|
|
355
|
-
if (!
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
return i.slice(0,
|
|
361
|
+
let l = !1;
|
|
362
|
+
const u = e.map((i) => {
|
|
363
|
+
const n = o(i);
|
|
364
|
+
if (!n.startsWith("//")) return i;
|
|
365
|
+
l = !0;
|
|
366
|
+
const s = i.indexOf(n);
|
|
367
|
+
return i.slice(0, s) + "https://" + n.slice(2) + i.slice(s + n.length);
|
|
359
368
|
});
|
|
360
|
-
|
|
361
|
-
}), a.addHook("afterSanitizeAttributes", (
|
|
369
|
+
l && r.setAttribute("srcset", u.join(","));
|
|
370
|
+
}), a.addHook("afterSanitizeAttributes", (r) => {
|
|
362
371
|
var o;
|
|
363
|
-
if (!(
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
}), a.addHook("afterSanitizeAttributes", (
|
|
367
|
-
if (!(
|
|
368
|
-
const
|
|
369
|
-
o.length !==
|
|
372
|
+
if (!(r instanceof Element) || r.tagName !== "A" && r.tagName !== "AREA" || ((o = r.getAttribute("target")) == null ? void 0 : o.toLowerCase()) !== "_blank") return;
|
|
373
|
+
const t = r.getAttribute("rel") ?? "", e = new Set(t.split(/\s+/).filter(Boolean));
|
|
374
|
+
e.add("noopener"), r.setAttribute("rel", [...e].join(" "));
|
|
375
|
+
}), a.addHook("afterSanitizeAttributes", (r) => {
|
|
376
|
+
if (!(r instanceof Element) || r.tagName !== "IFRAME" || !r.hasAttribute("sandbox")) return;
|
|
377
|
+
const e = (r.getAttribute("sandbox") ?? "").toLowerCase().split(/\s+/).filter(Boolean), o = e.filter((l) => k.has(l));
|
|
378
|
+
o.length !== e.length && r.setAttribute("sandbox", o.join(" "));
|
|
370
379
|
}), a);
|
|
371
380
|
}
|
|
372
|
-
function v(
|
|
381
|
+
function v(r, t) {
|
|
373
382
|
if (typeof window > "u")
|
|
374
383
|
throw new Error("sanitizeHtml requires a DOM environment (window is not defined)");
|
|
375
|
-
const
|
|
376
|
-
return A().sanitize(
|
|
384
|
+
const e = t != null && t.allowFormAttributeNames ? { ...f, SANITIZE_DOM: !1 } : f;
|
|
385
|
+
return A().sanitize(r ?? "", e);
|
|
377
386
|
}
|
|
378
387
|
const p = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]), c = "http://platform-sanitize.invalid/", g = /^\s*\/\//, b = (
|
|
379
388
|
// oxlint-disable-next-line no-control-regex -- intentional security guard
|
|
380
389
|
/^[\u0000-\u0020\u007F-\u00A0\u2000-\u200F\u2028\u2029\u202F\u205F\u2060\u3000\uFEFF]*(?:javascript|data|vbscript|file):/i
|
|
381
|
-
),
|
|
390
|
+
), S = {
|
|
382
391
|
colon: ":",
|
|
383
392
|
// javascript:alert(1) → javascript:alert(1)
|
|
384
393
|
sol: "/",
|
|
@@ -391,35 +400,35 @@ const p = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]), c = "
|
|
|
391
400
|
`
|
|
392
401
|
// java
script:
|
|
393
402
|
};
|
|
394
|
-
function E(
|
|
395
|
-
return
|
|
396
|
-
const o = Number(
|
|
403
|
+
function E(r) {
|
|
404
|
+
return r.replace(/&#(\d+);/g, (t, e) => {
|
|
405
|
+
const o = Number(e);
|
|
397
406
|
if (!Number.isFinite(o) || o < 0 || o > 1114111) return "";
|
|
398
407
|
try {
|
|
399
408
|
return String.fromCodePoint(o);
|
|
400
409
|
} catch {
|
|
401
410
|
return "";
|
|
402
411
|
}
|
|
403
|
-
}).replace(/&#x([\da-f]+);/gi, (
|
|
404
|
-
const o = parseInt(
|
|
412
|
+
}).replace(/&#x([\da-f]+);/gi, (t, e) => {
|
|
413
|
+
const o = parseInt(e, 16);
|
|
405
414
|
if (!Number.isFinite(o) || o < 0 || o > 1114111) return "";
|
|
406
415
|
try {
|
|
407
416
|
return String.fromCodePoint(o);
|
|
408
417
|
} catch {
|
|
409
418
|
return "";
|
|
410
419
|
}
|
|
411
|
-
}).replace(/&([A-Za-z][A-Za-z0-9]*);/g, (
|
|
420
|
+
}).replace(/&([A-Za-z][A-Za-z0-9]*);/g, (t, e) => S[e] ?? t);
|
|
412
421
|
}
|
|
413
|
-
function R(
|
|
414
|
-
if (!
|
|
415
|
-
const
|
|
416
|
-
if (g.test(
|
|
417
|
-
if (/&[#A-Za-z]/.test(
|
|
418
|
-
const
|
|
419
|
-
if (g.test(
|
|
422
|
+
function R(r) {
|
|
423
|
+
if (!r || !r.trim()) return "about:blank";
|
|
424
|
+
const t = r.replace(/\\/g, "/");
|
|
425
|
+
if (g.test(t) || b.test(t)) return "about:blank";
|
|
426
|
+
if (/&[#A-Za-z]/.test(t)) {
|
|
427
|
+
const e = E(t);
|
|
428
|
+
if (g.test(e) || b.test(e))
|
|
420
429
|
return "about:blank";
|
|
421
430
|
try {
|
|
422
|
-
const o = new URL(
|
|
431
|
+
const o = new URL(e, c);
|
|
423
432
|
if (!o.href.startsWith(c) && !p.has(o.protocol))
|
|
424
433
|
return "about:blank";
|
|
425
434
|
} catch {
|
|
@@ -427,8 +436,8 @@ function R(e) {
|
|
|
427
436
|
}
|
|
428
437
|
}
|
|
429
438
|
try {
|
|
430
|
-
const
|
|
431
|
-
return !p.has(
|
|
439
|
+
const e = new URL(t, c);
|
|
440
|
+
return !p.has(e.protocol) || (e.protocol === "http:" || e.protocol === "https:") && (e.username || e.password) ? "about:blank" : e.href.startsWith(c) ? r : t.replace(/[\x00-\x1F\u2028\u2029]/g, "").replace(/%250[9ad]/gi, "").replace(/%0[9ad]/gi, "");
|
|
432
441
|
} catch {
|
|
433
442
|
return "about:blank";
|
|
434
443
|
}
|
|
@@ -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":"AAgeA,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"}
|