@uniweb/semantic-parser 1.8.0 → 1.8.1
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/package.json +1 -1
- package/src/sanitize.js +100 -0
package/package.json
CHANGED
package/src/sanitize.js
CHANGED
|
@@ -441,6 +441,19 @@ export function sanitizeSVG(svg) {
|
|
|
441
441
|
return rebuild(svg, SVG);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
/**
|
|
445
|
+
* What in an SVG string falls outside the allowlist `sanitizeSVG` rebuilds
|
|
446
|
+
* from, reported as `inspectMathML` reports MathML. Empty when the markup is
|
|
447
|
+
* within it.
|
|
448
|
+
*
|
|
449
|
+
* @param {string} svg
|
|
450
|
+
* @returns {Array<{reason: 'element'|'attribute'|'style'|'markup', element?: string, attribute?: string, value?: string}>}
|
|
451
|
+
*/
|
|
452
|
+
export function inspectSVG(svg) {
|
|
453
|
+
if (typeof svg !== "string" || !svg) return [];
|
|
454
|
+
return inspect(svg, SVG);
|
|
455
|
+
}
|
|
456
|
+
|
|
444
457
|
/**
|
|
445
458
|
* Whether a link may be followed: a relative URL, or `http:`, `https:`,
|
|
446
459
|
* `mailto:`, `tel:`, or the framework's `page:` / `topic:` references. Control
|
|
@@ -454,6 +467,75 @@ export function isSafeLink(url) {
|
|
|
454
467
|
return typeof url === "string" && isSafeUrl(url);
|
|
455
468
|
}
|
|
456
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Whether an address may be loaded as an image: a link a page may follow
|
|
472
|
+
* (`isSafeLink`), or the image itself, as data (`isImageData`).
|
|
473
|
+
*
|
|
474
|
+
* @param {string} url
|
|
475
|
+
* @returns {boolean}
|
|
476
|
+
*/
|
|
477
|
+
export function isImageSource(url) {
|
|
478
|
+
return isSafeLink(url) || isImageData(url);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Whether a `data:` URL holds an image a page may show: a PNG, JPEG, GIF, WebP
|
|
483
|
+
* or AVIF in base64, or SVG — percent-encoded or base64 — whose markup is within
|
|
484
|
+
* the allowlist `sanitizeSVG` rebuilds from. ⭐ Admitted [Diego, 2026-10-16]: a
|
|
485
|
+
* picture an `<img>` loads runs no script, and a built-in starter image is one.
|
|
486
|
+
* The URL is read as a browser reads it: tab and newline removed, leading and
|
|
487
|
+
* trailing spaces and controls trimmed.
|
|
488
|
+
*
|
|
489
|
+
* @param {string} url
|
|
490
|
+
* @returns {boolean}
|
|
491
|
+
*/
|
|
492
|
+
export function isImageData(url) {
|
|
493
|
+
if (typeof url !== "string") return false;
|
|
494
|
+
const address = url.replace(/[\t\n\r]/g, "").replace(/^[\u0000- ]+|[\u0000- ]+$/g, "");
|
|
495
|
+
if (RASTER_DATA.test(address)) return true;
|
|
496
|
+
const svg = svgOfData(address);
|
|
497
|
+
return svg !== null && inspect(svg, SVG).length === 0;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const SVG_DATA = /^data:image\/svg\+xml((?:;[^,;]*)*?)(;base64)?,([\s\S]*)$/i;
|
|
501
|
+
|
|
502
|
+
/** The markup an SVG `data:` URL holds, decoded as a browser decodes it; null for any other URL. */
|
|
503
|
+
function svgOfData(address) {
|
|
504
|
+
const data = SVG_DATA.exec(address);
|
|
505
|
+
if (!data) return null;
|
|
506
|
+
const body = percentDecode(data[3]);
|
|
507
|
+
if (!data[2]) return body;
|
|
508
|
+
try {
|
|
509
|
+
const binary = atob(body.replace(/[\t\n\f\r ]+/g, ""));
|
|
510
|
+
return new TextDecoder().decode(Uint8Array.from(binary, (c) => c.charCodeAt(0)));
|
|
511
|
+
} catch {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Percent-decode as a URL is: `%` and two hex digits is a byte, anything else stays, and the bytes are read as UTF-8. */
|
|
517
|
+
function percentDecode(text) {
|
|
518
|
+
if (!text.includes("%")) return text;
|
|
519
|
+
const bytes = [];
|
|
520
|
+
const utf8 = new TextEncoder();
|
|
521
|
+
let run = "";
|
|
522
|
+
const flush = () => {
|
|
523
|
+
for (const byte of utf8.encode(run)) bytes.push(byte);
|
|
524
|
+
run = "";
|
|
525
|
+
};
|
|
526
|
+
for (let i = 0; i < text.length; i++) {
|
|
527
|
+
if (text[i] === "%" && /^[0-9a-f]{2}$/i.test(text.slice(i + 1, i + 3))) {
|
|
528
|
+
flush();
|
|
529
|
+
bytes.push(parseInt(text.slice(i + 1, i + 3), 16));
|
|
530
|
+
i += 2;
|
|
531
|
+
} else {
|
|
532
|
+
run += text[i];
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
flush();
|
|
536
|
+
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
537
|
+
}
|
|
538
|
+
|
|
457
539
|
// ─── Values content may hold ─────────────────────────────────────────────────
|
|
458
540
|
//
|
|
459
541
|
// The rules below decide what a text's marks may put into the markup the parser
|
|
@@ -552,5 +634,23 @@ export function allowlists() {
|
|
|
552
634
|
style: "declaration by declaration: a property of letters and -, a value with none of \\ < > { } /* and no unsafe value",
|
|
553
635
|
markup: "no comment, doctype or processing instruction; CDATA is text",
|
|
554
636
|
},
|
|
637
|
+
imageData: {
|
|
638
|
+
raster: RASTER_DATA.source,
|
|
639
|
+
rasterFlags: "i",
|
|
640
|
+
svg: {
|
|
641
|
+
mediaType: "image/svg+xml, with any parameters, and `;base64` when the body is base64",
|
|
642
|
+
decoding: "the body percent-decoded as a URL is (`%` and two hex digits is a byte, anything else stays), then base64-decoded when marked, read as UTF-8",
|
|
643
|
+
elements: [...SVG_ELEMENTS.values()],
|
|
644
|
+
attributes: [...SVG_ATTRIBUTES.values()],
|
|
645
|
+
commonAttributes: "class, id, role, dir, aria-<letters>, data-<letters, digits and ->",
|
|
646
|
+
droppedWithContent: [...DROP_WITH_CONTENT],
|
|
647
|
+
neverAttributes: "on…, in any case; src",
|
|
648
|
+
references: "href and xlink:href only: a fragment, `#` then a letter then letters, digits, _ . : or -; on `image`, also a raster image matching `raster`, or a URL with no scheme or one of the link schemes",
|
|
649
|
+
unsafeValue: UNSAFE_VALUE.source,
|
|
650
|
+
urlInAValue: "only url(#id), a reference within the document",
|
|
651
|
+
style: "declaration by declaration: a property of letters and -, a value with none of \\ < > { } /* and no unsafe value",
|
|
652
|
+
markup: "no comment, doctype or processing instruction; CDATA is text",
|
|
653
|
+
},
|
|
654
|
+
},
|
|
555
655
|
};
|
|
556
656
|
}
|