|
- // Sanitizer SVG focalizzato sul caso "logo del portale caricato dall'admin".
- // Approccio: rimozione regex-based dei costrutti pericolosi (deny-list aggressiva +
- // validazione che il contenuto sia davvero SVG). Non è una difesa esaustiva da
- // payload SVG sofisticati: per quello servirebbe DOMPurify+jsdom o xmldom.
- // Per il nostro modello di minaccia (admin trusted carica il proprio logo) basta.
- // Defense-in-depth: il file servito da /api/files include CSP `script-src 'none'`
- // quando il MIME è image/svg+xml.
-
- // Tag che vanno completamente rimossi (anche il contenuto):
- // - script/foreignObject/iframe/embed/object: codice eseguibile
- // - style/link/meta: possono caricare risorse esterne o iniettare CSS dannoso
- // - set/animate*: animation handlers possono triggerare eventi
- const FORBIDDEN_TAGS = [
- 'script',
- 'foreignObject',
- 'iframe',
- 'embed',
- 'object',
- 'link',
- 'meta',
- 'style',
- 'set',
- 'animate',
- 'animateMotion',
- 'animateTransform',
- ] as const;
-
- const EVENT_ATTR_RE = /\s+on\w+\s*=\s*("[^"]*"|'[^']*'|\S+)/gi;
-
- // href/xlink:href con schema non consentito.
- // Sono ammessi: #fragment, http(s)://, mailto:, tel:. Tutto il resto via.
- const UNSAFE_HREF_RE = /\s+(xlink:)?href\s*=\s*("|')\s*(?!#|https?:|mailto:|tel:|\2)[^"']*\2/gi;
-
- // Rimuove strighe `javascript:` o `data:text/html` anche dentro attributi che non
- // passano dal pattern href (es. attributeName, values, ecc.) — best effort.
- const SCHEME_INLINE_RE = /(javascript:|data:text\/html)/gi;
-
- export type SvgSanitizeResult =
- | { ok: true; sanitized: string }
- | { ok: false; error: string };
-
- export function sanitizeSvg(input: string): SvgSanitizeResult {
- if (typeof input !== 'string' || input.length === 0) {
- return { ok: false, error: 'The SVG file is empty. Choose a non-empty .svg file.' };
- }
- // Deve essere un documento SVG: opzionalmente un XML declaration, opzionalmente
- // commenti, e poi un tag <svg>.
- if (!/^\s*(<\?xml[^?]*\?>\s*)?(<!--[\s\S]*?-->\s*)*<svg[\s>]/i.test(input)) {
- return { ok: false, error: 'Not a valid SVG document: the file does not start with an <svg> tag. It may be corrupted or be a different format saved with an .svg extension.' };
- }
-
- let out = input;
-
- // 1) Rimuove i tag vietati (sia open+close che self-closing)
- for (const tag of FORBIDDEN_TAGS) {
- // <tag ...>...</tag>
- out = out.replace(new RegExp(`<${tag}\\b[^>]*>[\\s\\S]*?</${tag}>`, 'gi'), '');
- // <tag ... /> oppure <tag ...>
- out = out.replace(new RegExp(`<${tag}\\b[^>]*/?>`, 'gi'), '');
- }
-
- // 2) Rimuove event handler (onload, onclick, onerror, ecc.)
- out = out.replace(EVENT_ATTR_RE, '');
-
- // 3) Strippa href/xlink:href con schema non ammesso
- out = out.replace(UNSAFE_HREF_RE, '');
-
- // 4) Rimozione difensiva di `javascript:` / `data:text/html` ovunque appaiano
- out = out.replace(SCHEME_INLINE_RE, '');
-
- return { ok: true, sanitized: out };
- }
|