Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2026-10-12 - [Improved SVG Sanitization against mXSS]
**Vulnerability:** The manual SVG sanitizer `apps/web/src/lib/sanitize-svg.ts` used an insufficient allowlist/blocklist. It allowed `<foreignObject>`, `<use>`, `<animate>`, `<set>`, `<image>`, and `<math>` which could lead to mutation XSS (mXSS) and other attack vectors when rendering untrusted Mermaid or Math blocks.
**Learning:** Manual DOM tree sanitization is notoriously hard to get right because attackers can hide active elements (e.g. `<a>` tags with `javascript:` payloads) inside grandchild trees (like `<math><mtext><a>`) or use SVG-specific tricks (like `<animate href... values="javascript:...">`) which naive list-based cleaners overlook.
**Prevention:** Rather than adding heavy third-party dependencies (like DOMPurify) against explicit project instructions, expanding the `BLOCKED` Set to include these structural injection vectors provides a robust, zero-dependency mitigation. Future expansions should verify nested elements carefully.
3 changes: 1 addition & 2 deletions apps/web/src/lib/markdown-rich.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ describe('Markdown riche (P5-01)', () => {

it('sanitizeSvg retire scripts et gestionnaires', () => {
const out = sanitizeSvg('<svg><script>x</script><rect onload="x" style="fill:url(javascript:x)" /><image href="https://e.x/a.png"/></svg>');
expect(out).not.toMatch(/script|onload|javascript/);
expect(out).toContain('href="https://e.x/a.png"');
expect(out).not.toMatch(/script|onload|javascript|image/);
});
});
2 changes: 1 addition & 1 deletion apps/web/src/lib/sanitize-svg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Nettoyage défensif d'un SVG produit par mermaid (déjà en `securityLevel: 'strict'`) :
* pas de script, pas de gestionnaire `on*`, pas de lien hors ancre/http(s).
*/
const BLOCKED = new Set(['script', 'iframe', 'object', 'embed', 'link', 'meta', 'base', 'form', 'input', 'textarea', 'button']);
const BLOCKED = new Set(['script', 'iframe', 'object', 'embed', 'link', 'meta', 'base', 'form', 'input', 'textarea', 'button', 'foreignobject', 'use', 'animate', 'set', 'image', 'math']);
const SAFE_LINK = /^(#|https?:\/\/)/i;

export function sanitizeSvg(svg: string): string {
Expand Down
Loading