url-sanitizer 0.7.1 → 0.7.2

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/README.md CHANGED
@@ -45,6 +45,7 @@ Sanitize the given URL.
45
45
  * `opt.deny` **[Array][4]<[string][1]>** Array of denied schemes, e.g. `['web+foo']`.
46
46
  * `opt.only` **[Array][4]<[string][1]>** Array of specific schemes to allow, e.g. `['git', 'https']`.
47
47
  `only` takes precedence over `allow` and `deny`.
48
+ * `opt.truncate` **[boolean][2]** Truncate strings matching tags and quotes.
48
49
 
49
50
  Returns **[Promise][5]<[string][1]?>** Sanitized URL, `null`able.
50
51
 
@@ -110,6 +111,17 @@ const res8 = await sanitizeURL('git+https://example.com/foo.git?<script>alert(1)
110
111
 
111
112
  console.log(decodeURIComponent(res8));
112
113
  // => 'git+https://example.com/foo.git?&lt;script&gt;alert(1)&lt;/script&gt;'
114
+
115
+ // Truncate the tag or quote and the rest of the URL following it.
116
+ const res9 = await sanitizeURL('https://example.com/" onclick="alert(1)"', {
117
+ truncate: true
118
+ });
119
+ // => 'https://example.com/'
120
+
121
+ const res10 = await sanitizeURL('https://example.com/?<script>alert(1)</script>', {
122
+ truncate: true
123
+ });
124
+ // => 'https://example.com/?'
113
125
  ```
114
126
 
115
127
 
@@ -130,7 +142,7 @@ Returns **[Promise][5]<[ParsedURL](#parsedurl)>** Result.
130
142
 
131
143
  ### ParsedURL
132
144
 
133
- Object with enhanced properties based on [URL](https://developer.mozilla.org/ja/docs/Web/API/URL) API.
145
+ Object with additional properties based on [URL](https://developer.mozilla.org/ja/docs/Web/API/URL) API.
134
146
 
135
147
  Type: [object][3]
136
148
 
@@ -157,11 +169,11 @@ Type: [object][3]
157
169
  ```javascript
158
170
  const res1 = await parseURL('javascript:alert(1)');
159
171
  /* => {
160
- input: 'javascript:alert(1)',
161
- valid: false
162
- } */
172
+ input: 'javascript:alert(1)',
173
+ valid: false
174
+ } */
163
175
 
164
- const res2 = await parseURL('https://example.com/?foo=bar#baz');
176
+ const res2 = await parseURL('https://www.example.com/?foo=bar#baz');
165
177
  /* => {
166
178
  input: 'https://www.example.com/?foo=bar#baz',
167
179
  valid: true,
@@ -193,7 +205,7 @@ const res3 = await parseURL('data:image/svg+xml;base64,PHN2Zz48ZyBvbmNsaWNrPSJhb
193
205
  ...
194
206
  } */
195
207
 
196
- // base64 encoded png
208
+ // base64 encoded PNG
197
209
  const res4 = await parseURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==');
198
210
  /* => {
199
211
  input: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
@@ -1599,6 +1599,7 @@ var REG_DATA_URL = /data:[^,]*,[^"]+/g;
1599
1599
  var REG_DATA_URL_BASE64 = /data:[^,]*;?base64,[\da-z+/\-_=]+/i;
1600
1600
  var REG_HTML_SP = /[<>"'\s]/g;
1601
1601
  var REG_HTML_SP_URL_ENC = /%(?:2(?:2|7)|3(?:C|E))/g;
1602
+ var REG_HTML_SP_URL_ENC_SHORT = /%(?:2(?:2|7)|3(?:C|E))+?/;
1602
1603
  var REG_MIME_DOM = /^(?:text\/(?:ht|x)ml|application\/(?:xhtml\+)?xml|image\/svg\+xml)/;
1603
1604
  var REG_NUM_REF = /&#(x(?:00)?[\dA-F]{2}|0?\d{1,3});?/ig;
1604
1605
  var REG_SCHEME = /^[a-z][\da-z+\-.]*$/;
@@ -1813,6 +1814,7 @@ var URLSanitizer = class extends URISchemes {
1813
1814
  * @param {Array.<string>} opt.allow - array of allowed schemes
1814
1815
  * @param {Array.<string>} opt.deny - array of denied schemes
1815
1816
  * @param {Array.<string>} opt.only - array of specific schemes to allow
1817
+ * @param {boolean} opt.truncate - truncate tag and/or quote and the rest
1816
1818
  * @returns {?string} - sanitized URL
1817
1819
  */
1818
1820
  sanitize(url, opt = { allow: [], deny: [], only: [] }) {
@@ -1820,7 +1822,7 @@ var URLSanitizer = class extends URISchemes {
1820
1822
  this.#nest = 0;
1821
1823
  throw new Error("Data URLs nested too deeply.");
1822
1824
  }
1823
- const { allow, deny, only } = opt ?? {};
1825
+ const { allow, deny, only, truncate } = opt ?? {};
1824
1826
  const schemeMap = /* @__PURE__ */ new Map([
1825
1827
  ["data", false],
1826
1828
  ["file", false],
@@ -1970,6 +1972,11 @@ var URLSanitizer = class extends URISchemes {
1970
1972
  } else {
1971
1973
  escapeHtml = true;
1972
1974
  }
1975
+ if (!schemeParts.includes("data") && truncate && REG_HTML_SP_URL_ENC_SHORT.test(urlToSanitize)) {
1976
+ const item = REG_HTML_SP_URL_ENC_SHORT.exec(urlToSanitize);
1977
+ const { index } = item;
1978
+ urlToSanitize = urlToSanitize.substring(0, index);
1979
+ }
1973
1980
  if (urlToSanitize) {
1974
1981
  sanitizedUrl = urlToSanitize.replace(REG_HTML_SP, getURLEncodedString).replace(REG_URL_ENC_AMP, escapeURLEncodedHTMLChars);
1975
1982
  if (escapeHtml) {
@@ -1985,7 +1992,7 @@ var URLSanitizer = class extends URISchemes {
1985
1992
  return sanitizedUrl || null;
1986
1993
  }
1987
1994
  /**
1988
- * object with extended props based on URL API
1995
+ * object with additional properties based on URL API
1989
1996
  *
1990
1997
  * @typedef {object} ParsedURL
1991
1998
  * @property {string} input - URL input
@@ -2052,8 +2059,8 @@ var URLSanitizer = class extends URISchemes {
2052
2059
  parsedData.substring(0, index),
2053
2060
  parsedData.substring(index + htmlChar.length)
2054
2061
  ];
2055
- const unescapedHTMLChar = unescapeURLEncodedHTMLChars(htmlChar);
2056
- parsedData = `${preHtmlChar}${unescapedHTMLChar}${postHtmlChar}`;
2062
+ const unescapedHtmlChar = unescapeURLEncodedHTMLChars(htmlChar);
2063
+ parsedData = `${preHtmlChar}${unescapedHtmlChar}${postHtmlChar}`;
2057
2064
  }
2058
2065
  purifiedDom = purifyURLEncodedDOM(parsedData);
2059
2066
  dataUrl.set("data", purifiedDom);