lexxy 0.9.31.beta → 0.9.31

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3d24c12a246fe66d7a9a9c3921aa609ca8c4155f37fc6b98ed022199ffd59af
4
- data.tar.gz: 6abd54bd70b2fb8fb558c2673cd8192e5c7843d15cea7df34eeaa18354d9c070
3
+ metadata.gz: 1f277ca33ab6c3af9fee1f22a40fe43fd177b1f26edc9e77495aaa0e8f95b387
4
+ data.tar.gz: 8db39c85a58a117b317d218089255909d1c56e2aaa65a82f2a0a9353b3d295a5
5
5
  SHA512:
6
- metadata.gz: c27e4e5e9b880330b1ef962dcfea0e514eff42f2cdf51294956d8261b1d7e2c923f990755768e64d3dd4003532ccd83b4d97aade1ab5b673d5ad80fe6c5e4b7d
7
- data.tar.gz: 6c3a620bd1634ad982080999499367301624b94d9a3acdb523e858e5f63db0cc4a77880f2d40e863d9586a9213d94f9cc39d48c3858b30dbb32598703a4bdb38
6
+ metadata.gz: 168d725c2c61d400b246f817ba2eb479240963121308403d206438cb3aecde44d39689e63a416d66b43afb3e0b1fe5b82ff5698ec75e14029725db63fbcd3657
7
+ data.tar.gz: bb669baeee9675aa52d7904aa0b712138500a7340aa72dc562a5386019843e0d5b3fd39be74c8e594f0faf4a8eb02aa6dc4af559eb5f3dbb4a91c67bd2985575
@@ -7658,7 +7658,8 @@ var Lexxy = {
7658
7658
  // Until 3.3.2 that never came up: attributes admitted by a *functional* ADD_ATTR
7659
7659
  // skipped URI validation entirely (GHSA-cjmm-f4jc-qw8r), which is how data:
7660
7660
  // URLs worked here — and, less happily, how `url="javascript:…"` survived too.
7661
- // The fix restored validation for both.
7661
+ // The fix restored validation for both. The bypass was reported to us by
7662
+ // @petitpois via HackerOne.
7662
7663
  //
7663
7664
  // So `url` is marked URI-safe, which hands the decision to this hook. The hook
7664
7665
  // only ever removes an attribute — it never force-keeps one — so scoping stays
@@ -7681,9 +7682,41 @@ var Lexxy = {
7681
7682
  // ordinary prose like "Q4: results" is not a URI any regex here would accept.
7682
7683
  const URI_BEARING_ATTACHMENT_ATTRIBUTES = [ "url" ];
7683
7684
 
7684
- // DOMPurify's own IS_ALLOWED_URI, reproduced rather than narrowed, so `url` on a
7685
- // non-attachment tag is treated exactly as DOMPurify would have treated it.
7686
- const ALLOWED_URI = /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.-]+(?:[^a-z+.:-]|$))/i;
7685
+ // DOMPurify's own IS_ALLOWED_URI scheme list, reproduced rather than narrowed, so
7686
+ // `url` on a non-attachment tag is treated exactly as DOMPurify would have treated
7687
+ // it. Kept as a source string so allowedUriRegexp() can widen it with an editor's
7688
+ // declared schemes without re-deriving the rest of the pattern.
7689
+ const BASE_URI_SCHEMES = "(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix";
7690
+
7691
+ // A scheme name (not a full pattern), so a caller can't inject regexp syntax.
7692
+ const SCHEME_NAME = /^[a-z][a-z0-9+.-]*$/;
7693
+
7694
+ // The executable schemes, by DOMPurify's own IS_SCRIPT_OR_DATA definition
7695
+ // (/^(?:\w+script|data):/i): javascript, vbscript, …script, and data. Widening the
7696
+ // allowlist to admit one would let it survive on href/object[data], which the
7697
+ // override otherwise defeats — DOMPurify does not re-block a scheme its
7698
+ // ALLOWED_URI_REGEXP accepts. A caller declaring one is dropped, so it stays
7699
+ // refused. This is a closed set (the schemes a browser executes), not a growing
7700
+ // denylist.
7701
+ const EXECUTABLE_SCHEME = /^(?:\w+script|data)$/i;
7702
+
7703
+ // Builds DOMPurify's default IS_ALLOWED_URI, optionally with extra schemes folded
7704
+ // into the scheme alternation. Widening the recognised-safe scheme set is how a
7705
+ // custom-scheme identifier (a mention's `gid://…`) passes validation without
7706
+ // exempting any attribute from it. Passing no schemes reproduces DOMPurify's
7707
+ // default exactly.
7708
+ function allowedUriRegexp(extraSchemes = []) {
7709
+ const extra = extraSchemes
7710
+ .map(scheme => String(scheme).toLowerCase())
7711
+ .filter(scheme => SCHEME_NAME.test(scheme) && !EXECUTABLE_SCHEME.test(scheme))
7712
+ .map(scheme => scheme.replace(/[.+-]/g, "\\$&"));
7713
+
7714
+ const schemes = [ BASE_URI_SCHEMES, ...extra ].join("|");
7715
+
7716
+ return new RegExp(`^(?:(?:${schemes}):|[^a-z]|[a-z+.-]+(?:[^a-z+.:-]|$))`, "i")
7717
+ }
7718
+
7719
+ const ALLOWED_URI = allowedUriRegexp();
7687
7720
 
7688
7721
  // eslint-disable-next-line no-control-regex -- mirrors DOMPurify's own ATTR_WHITESPACE
7689
7722
  const ATTR_WHITESPACE = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g;
@@ -7880,7 +7913,17 @@ function buildConfig(allowedElements = null) {
7880
7913
  // default tag and attribute policy stands. `ALLOWED_TAGS: []` would not be a
7881
7914
  // default, it would be a refusal: it strips every tag. An editor that declares
7882
7915
  // an empty allowlist still gets that refusal, because it asked for it.
7883
- if (allowedElements) Object.assign(config, allowlistFor(allowedElements));
7916
+ //
7917
+ // uriSafeSchemes widens the scheme validation rather than exempting an attribute
7918
+ // from it: ALLOWED_URI_REGEXP is set only when an editor declares custom schemes,
7919
+ // so a value like a mention's `gid://…` passes while javascript:/data: stay
7920
+ // refused on every attribute — including href and object[data]. Nothing is taken
7921
+ // out of URI checking, so there is no attribute to guard against exempting.
7922
+ if (allowedElements) {
7923
+ const { uriSafeSchemes, ...tagPolicy } = allowlistFor(allowedElements);
7924
+ if (uriSafeSchemes.length) config.ALLOWED_URI_REGEXP = allowedUriRegexp(uriSafeSchemes);
7925
+ Object.assign(config, tagPolicy);
7926
+ }
7884
7927
 
7885
7928
  // Always assigned, including when we have no policy — `null` is what
7886
7929
  // trustedTypesPolicy() returns then, and `TRUSTED_TYPES_POLICY: null` is
@@ -7903,6 +7946,7 @@ function allowlistFor(allowedElements) {
7903
7946
  // Object.prototype key: `tagAttributes["constructor"]` would answer with a
7904
7947
  // function, and ADD_ATTR would call .includes on it.
7905
7948
  const tagAttributes = Object.create(null);
7949
+ const uriSafeSchemes = [];
7906
7950
 
7907
7951
  // Lowercased, because DOMPurify lowercases ALLOWED_TAGS and calls ADD_ATTR
7908
7952
  // with the lowercased tag and attribute names. Keeping the caller's casing
@@ -7915,6 +7959,15 @@ function allowlistFor(allowedElements) {
7915
7959
 
7916
7960
  tagAttributes[tag] ||= [];
7917
7961
  tagAttributes[tag].push(...attributes);
7962
+
7963
+ // A custom scheme a caller declares is folded into the editor's URI-scheme
7964
+ // allowlist (ALLOWED_URI_REGEXP in buildConfig), so a value like a mention's
7965
+ // `gid="gid://…"` — which DOMPurify otherwise drops as an unknown scheme, even
7966
+ // once the `gid` name is allowed — passes validation. It widens the recognised
7967
+ // schemes, it does not exempt an attribute: javascript:/data: stay refused
7968
+ // everywhere, so there is no navigational attribute to guard. Declared per
7969
+ // element for locality, but a scheme is editor-wide once allowed.
7970
+ uriSafeSchemes.push(...(element.uriSafeSchemes ?? []));
7918
7971
  }
7919
7972
 
7920
7973
  // Only for tags the caller already permits — this widens what an allowed
@@ -7926,7 +7979,8 @@ function allowlistFor(allowedElements) {
7926
7979
  return {
7927
7980
  ALLOWED_TAGS: Object.keys(tagAttributes),
7928
7981
  ALLOWED_ATTR: ALLOWED_HTML_ATTRIBUTES,
7929
- ADD_ATTR: (attribute, tag) => tagAttributes[tag]?.includes(attribute)
7982
+ ADD_ATTR: (attribute, tag) => tagAttributes[tag]?.includes(attribute),
7983
+ uriSafeSchemes
7930
7984
  }
7931
7985
  }
7932
7986
 
Binary file
Binary file