fix: keep target/rel on links in plain-text message bodies

Plain-text bodies render into the main document rather than the sandboxed
iframe, so an anchor without target="_blank" navigates the whole app away
instead of opening a new tab.

plainTextToSafeHtml emits target and rel correctly, but
sanitizePlainTextRenderedHtml stripped both back off: DOMPurify URI-tests
every attribute value not on its URI-safe list, and "_blank" does not match
PLAIN_TEXT_RENDERED_CONFIG's ALLOWED_URI_REGEXP. EMAIL_SANITIZE_CONFIG avoids
this only because its regex carries a catch-all alternation for non-URI values.

Mark target and rel as URI-safe so they survive the URI test, rather than
loosening href validation.
This commit is contained in:
honzup
2026-07-11 10:45:01 +02:00
committed by Linus Rath
parent 38a396d150
commit 75d17d4e37
2 changed files with 27 additions and 0 deletions
+21
View File
@@ -6,6 +6,7 @@ import {
parseHtmlSafely,
hasRichFormatting,
plainTextToSafeHtml,
sanitizePlainTextRenderedHtml,
EMAIL_SANITIZE_CONFIG,
EMAIL_IFRAME_SANITIZE_CONFIG,
isExternalResourceUrl,
@@ -580,4 +581,24 @@ describe('email-sanitization', () => {
expect(result).toContain('javascript:alert(1)');
});
});
describe('sanitizePlainTextRenderedHtml', () => {
// This branch renders into the main document, not the sandboxed iframe, so
// an anchor that loses target="_blank" navigates the whole app away.
it('preserves target and rel on links emitted by plainTextToSafeHtml', () => {
const rendered = sanitizePlainTextRenderedHtml(
plainTextToSafeHtml('see https://github.com/honzup/webmail/pull/560'),
);
expect(rendered).toContain('target="_blank"');
expect(rendered).toContain('rel="noopener noreferrer"');
});
it('still strips dangerous schemes and tags', () => {
const rendered = sanitizePlainTextRenderedHtml(
'<a href="javascript:alert(1)" target="_blank">x</a><script>alert(1)</script>',
);
expect(rendered).not.toContain('javascript:');
expect(rendered).not.toContain('<script');
});
});
});