@instructure/platform-sanitize 0.3.11 → 0.3.13

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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sanitizeHtml.browser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sanitizeHtml.browser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sanitizeHtml.browser.test.ts"],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import c from "dompurify";
2
- const g = /* @__PURE__ */ new Set([
2
+ const p = /* @__PURE__ */ new Set([
3
3
  // layout
4
4
  "display",
5
5
  "float",
@@ -106,6 +106,13 @@ const g = /* @__PURE__ */ new Set([
106
106
  "border-style",
107
107
  "border-width",
108
108
  "border-radius",
109
+ // Chrome's CSSOM expands border-radius to these four longhands when iterating
110
+ // element.style — the shorthand itself never appears in the iterator, so the
111
+ // hook would strip all four corners unless they are explicitly listed here.
112
+ "border-top-left-radius",
113
+ "border-top-right-radius",
114
+ "border-bottom-right-radius",
115
+ "border-bottom-left-radius",
109
116
  "border-collapse",
110
117
  "border-spacing",
111
118
  "border-top",
@@ -130,6 +137,9 @@ const g = /* @__PURE__ */ new Set([
130
137
  "outline-color",
131
138
  "outline-style",
132
139
  "outline-width",
140
+ // outline-offset is not part of the `outline` shorthand, so it must be listed
141
+ // separately or it is stripped when authored on its own (focus-ring styling).
142
+ "outline-offset",
133
143
  // list
134
144
  "list-style",
135
145
  "list-style-image",
@@ -225,7 +235,7 @@ const g = /* @__PURE__ */ new Set([
225
235
  "caret-color",
226
236
  "accent-color",
227
237
  "appearance"
228
- ]), b = /* @__PURE__ */ new Set([
238
+ ]), m = /* @__PURE__ */ new Set([
229
239
  "src",
230
240
  "href",
231
241
  "action",
@@ -236,7 +246,7 @@ const g = /* @__PURE__ */ new Set([
236
246
  "cite",
237
247
  "longdesc",
238
248
  "xlink:href"
239
- ]), u = /^\s*(\/\/|\\)/, m = [
249
+ ]), u = /^\s*(\/\/|\\)/, w = [
240
250
  "background",
241
251
  "background-image",
242
252
  "list-style",
@@ -245,7 +255,7 @@ const g = /* @__PURE__ */ new Set([
245
255
  // content: url(...) triggers an HTTP GET even on non-pseudo elements in some
246
256
  // browsers; strip it as defense-in-depth against tracking-pixel exfiltration.
247
257
  "content"
248
- ], w = /url\s*\(\s*['"]?(?:[a-z][a-z0-9+\-.]*:|\/\/)/i, h = /* @__PURE__ */ new Set([
258
+ ], h = /url\s*\(\s*['"]?(?:[a-z][a-z0-9+\-.]*:|\/\/)/i, y = /* @__PURE__ */ new Set([
249
259
  "allow-downloads",
250
260
  "allow-forms",
251
261
  "allow-modals",
@@ -258,7 +268,7 @@ const g = /* @__PURE__ */ new Set([
258
268
  "allow-scripts",
259
269
  "allow-storage-access-by-user-activation",
260
270
  "allow-top-navigation-by-user-activation"
261
- ]), y = {
271
+ ]), d = {
262
272
  ADD_TAGS: ["iframe"],
263
273
  ADD_ATTR: [
264
274
  "allowfullscreen",
@@ -307,7 +317,7 @@ function k() {
307
317
  const e = t.style, o = [];
308
318
  for (let n = 0; n < e.length; n++) {
309
319
  const a = e.item(n);
310
- g.has(a) || o.push(a);
320
+ p.has(a) || o.push(a);
311
321
  }
312
322
  for (const n of o) e.removeProperty(n);
313
323
  const r = /* @__PURE__ */ new Set([
@@ -321,13 +331,13 @@ function k() {
321
331
  "revert-layer"
322
332
  ]), s = e.getPropertyValue("position").trim().toLowerCase();
323
333
  s && !r.has(s) && e.removeProperty("position");
324
- for (const n of m) {
334
+ for (const n of w) {
325
335
  const a = e.getPropertyValue(n);
326
- a && w.test(a) && e.removeProperty(n);
336
+ a && h.test(a) && e.removeProperty(n);
327
337
  }
328
338
  e.length === 0 && t.removeAttribute("style");
329
339
  }), i.addHook("uponSanitizeAttribute", (t, e) => {
330
- b.has(e.attrName) && u.test(e.attrValue) && (e.keepAttr = !1);
340
+ m.has(e.attrName) && u.test(e.attrValue) && (e.keepAttr = !1);
331
341
  }), i.addHook("afterSanitizeAttributes", (t) => {
332
342
  if (!(t instanceof Element) || !t.hasAttribute("srcset")) return;
333
343
  (t.getAttribute("srcset") ?? "").split(",").map((r) => r.trim().split(/\s+/)[0]).some((r) => u.test(r)) && t.removeAttribute("srcset");
@@ -338,16 +348,17 @@ function k() {
338
348
  o.add("noopener"), t.setAttribute("rel", [...o].join(" "));
339
349
  }), i.addHook("afterSanitizeAttributes", (t) => {
340
350
  if (!(t instanceof Element) || t.tagName !== "IFRAME" || !t.hasAttribute("sandbox")) return;
341
- const o = (t.getAttribute("sandbox") ?? "").toLowerCase().split(/\s+/).filter(Boolean), r = o.filter((s) => h.has(s));
351
+ const o = (t.getAttribute("sandbox") ?? "").toLowerCase().split(/\s+/).filter(Boolean), r = o.filter((s) => y.has(s));
342
352
  r.length !== o.length && t.setAttribute("sandbox", r.join(" "));
343
353
  }), i);
344
354
  }
345
- function R(t) {
355
+ function R(t, e) {
346
356
  if (typeof window > "u")
347
357
  throw new Error("sanitizeHtml requires a DOM environment (window is not defined)");
348
- return k().sanitize(t ?? "", y);
358
+ const o = e != null && e.allowFormAttributeNames ? { ...d, SANITIZE_DOM: !1 } : d;
359
+ return k().sanitize(t ?? "", o);
349
360
  }
350
- const d = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]), l = "http://platform-sanitize.invalid/", f = /^\s*\/\//, p = (
361
+ const f = /* @__PURE__ */ new Set(["http:", "https:", "mailto:", "tel:"]), l = "http://platform-sanitize.invalid/", b = /^\s*\/\//, g = (
351
362
  // oxlint-disable-next-line no-control-regex -- intentional security guard
352
363
  /^[\u0000-\u0020\u007F-\u00A0\u2000-\u200F\u2028\u2029\u202F\u205F\u2060\u3000\uFEFF]*(?:javascript|data|vbscript|file):/i
353
364
  ), A = {
@@ -385,14 +396,14 @@ function E(t) {
385
396
  function S(t) {
386
397
  if (!t || !t.trim()) return "about:blank";
387
398
  const e = t.replace(/\\/g, "/");
388
- if (f.test(e) || p.test(e)) return "about:blank";
399
+ if (b.test(e) || g.test(e)) return "about:blank";
389
400
  if (/&[#A-Za-z]/.test(e)) {
390
401
  const o = E(e);
391
- if (f.test(o) || p.test(o))
402
+ if (b.test(o) || g.test(o))
392
403
  return "about:blank";
393
404
  try {
394
405
  const r = new URL(o, l);
395
- if (!r.href.startsWith(l) && !d.has(r.protocol))
406
+ if (!r.href.startsWith(l) && !f.has(r.protocol))
396
407
  return "about:blank";
397
408
  } catch {
398
409
  return "about:blank";
@@ -400,7 +411,7 @@ function S(t) {
400
411
  }
401
412
  try {
402
413
  const o = new URL(e, l);
403
- return !d.has(o.protocol) || (o.protocol === "http:" || o.protocol === "https:") && (o.username || o.password) ? "about:blank" : o.href.startsWith(l) ? t : e.replace(/[\x00-\x1F\u2028\u2029]/g, "").replace(/%250[9ad]/gi, "").replace(/%0[9ad]/gi, "");
414
+ return !f.has(o.protocol) || (o.protocol === "http:" || o.protocol === "https:") && (o.username || o.password) ? "about:blank" : o.href.startsWith(l) ? t : e.replace(/[\x00-\x1F\u2028\u2029]/g, "").replace(/%250[9ad]/gi, "").replace(/%0[9ad]/gi, "");
404
415
  } catch {
405
416
  return "about:blank";
406
417
  }
@@ -1,2 +1,4 @@
1
- export declare function sanitizeHtml(html: string | null | undefined): string;
1
+ export declare function sanitizeHtml(html: string | null | undefined, options?: {
2
+ allowFormAttributeNames?: boolean;
3
+ }): string;
2
4
  //# sourceMappingURL=sanitizeHtml.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../src/sanitizeHtml.ts"],"names":[],"mappings":"AA6aA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAKpE"}
1
+ {"version":3,"file":"sanitizeHtml.d.ts","sourceRoot":"","sources":["../src/sanitizeHtml.ts"],"names":[],"mappings":"AAubA,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/platform-sanitize",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -17,25 +17,29 @@
17
17
  "publishConfig": {
18
18
  "access": "public"
19
19
  },
20
- "scripts": {
21
- "build": "vite build",
22
- "dev": "vite build --watch",
23
- "test": "vitest run",
24
- "test:watch": "vitest",
25
- "test:coverage": "vitest run --coverage",
26
- "type-check": "tsc --noEmit"
27
- },
28
20
  "peerDependencies": {
29
21
  "dompurify": "^3.4.0"
30
22
  },
31
23
  "devDependencies": {
32
24
  "@types/trusted-types": "^2.0.7",
25
+ "@vitest/browser": "^4.0.17",
26
+ "@vitest/browser-playwright": "^4.0.17",
33
27
  "@vitest/coverage-v8": "^4.0.17",
34
28
  "dompurify": "^3.4.0",
35
29
  "jsdom": "^25.0.0",
30
+ "playwright": "^1.60.0",
36
31
  "typescript": "^5.3.0",
37
32
  "vite": "^6.0.0",
38
33
  "vite-plugin-dts": "^4.0.0",
39
34
  "vitest": "^4.0.0"
35
+ },
36
+ "scripts": {
37
+ "build": "vite build",
38
+ "dev": "vite build --watch",
39
+ "test": "vitest run",
40
+ "test:browser": "vitest run --config vitest.browser.config.ts",
41
+ "test:watch": "vitest",
42
+ "test:coverage": "vitest run --coverage",
43
+ "type-check": "tsc --noEmit"
40
44
  }
41
- }
45
+ }