From 8577bdee5afa95b08c6f16e6aff0afea22ab52d0 Mon Sep 17 00:00:00 2001 From: David Vandorpe Date: Tue, 14 Jan 2020 00:04:25 +0100 Subject: [PATCH 1/5] Add option for stripping unsafe URLs Fixes #155 --- .../renderer/html/CoreHtmlNodeRenderer.java | 12 ++- .../renderer/html/DefaultUrlSanitizer.java | 83 +++++++++++++++++++ .../html/HtmlNodeRendererContext.java | 14 ++++ .../renderer/html/HtmlRenderer.java | 42 ++++++++++ .../renderer/html/UrlSanitizer.java | 26 ++++++ .../org/commonmark/test/HtmlRendererTest.java | 41 +++++++++ 6 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java create mode 100644 commonmark/src/main/java/org/commonmark/renderer/html/UrlSanitizer.java diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java index 3ffeba0cf..eb387cf58 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java @@ -141,7 +141,14 @@ public void visit(IndentedCodeBlock indentedCodeBlock) { @Override public void visit(Link link) { Map attrs = new LinkedHashMap<>(); - String url = context.encodeUrl(link.getDestination()); + String url = link.getDestination(); + + if (context.shouldBeSafe()) { + url = context.urlSanitizer().sanitizeLinkUrl(url); + attrs.put("rel", "nofollow"); + } + + url = context.encodeUrl(url); attrs.put("href", url); if (link.getTitle() != null) { attrs.put("title", link.getTitle()); @@ -178,6 +185,9 @@ public void visit(Image image) { String altText = altTextVisitor.getAltText(); Map attrs = new LinkedHashMap<>(); + if(context.shouldBeSafe()) { + url = context.urlSanitizer().sanitizeImageUrl(url); + } attrs.put("src", url); attrs.put("alt", altText); if (image.getTitle() != null) { diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java b/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java new file mode 100644 index 000000000..6cdd31214 --- /dev/null +++ b/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java @@ -0,0 +1,83 @@ +package org.commonmark.renderer.html; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * + * Allows http, https and mailto protocols for url. + * Also allows protocol relative urls, and relative urls. + * Implementation based on https://github.com/OWASP/java-html-sanitizer/blob/f07e44b034a45d94d6fd010279073c38b6933072/src/main/java/org/owasp/html/FilterUrlByProtocolAttributePolicy.java + */ +public class DefaultUrlSanitizer implements UrlSanitizer { + private Collection protocols; + + private static final long HTML_SPACE_CHAR_BITMASK = + (1L << ' ') + | (1L << '\t') + | (1L << '\n') + | (1L << '\u000c') + | (1L << '\r'); + + + public DefaultUrlSanitizer() { + this(new ArrayList() {{ + add("http"); + add("https"); + add("mailto"); + }}); + } + + public DefaultUrlSanitizer(Collection protocols) { + this.protocols = protocols; + } + + @Override + public String sanitizeLinkUrl(String url) { + url = stripHtmlSpaces(url); + protocol_loop: + for (int i = 0, n = url.length(); i < n; ++i) { + switch (url.charAt(i)) { + case '/': + case '#': + case '?': // No protocol. + break protocol_loop; + case ':': + String protocol = url.substring(0, i).toLowerCase(); + if (!protocols.contains(protocol)) { + return ""; + } + break protocol_loop; + } + } + return url; + } + + + @Override + public String sanitizeImageUrl(String url) { + return sanitizeLinkUrl(url); + } + + private String stripHtmlSpaces(String s) { + int i = 0, n = s.length(); + for (; n > i; --n) { + if (!isHtmlSpace(s.charAt(n - 1))) { + break; + } + } + for (; i < n; ++i) { + if (!isHtmlSpace(s.charAt(i))) { + break; + } + } + if (i == 0 && n == s.length()) { + return s; + } + return s.substring(i, n); + } + + private boolean isHtmlSpace(int ch) { + return ch <= 0x20 && (HTML_SPACE_CHAR_BITMASK & (1L << ch)) != 0; + } +} diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java index fd077a277..6f694fb7c 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java @@ -1,5 +1,7 @@ package org.commonmark.renderer.html; +import org.commonmark.node.Link; +import org.commonmark.node.Image; import org.commonmark.node.Node; import java.util.Map; @@ -44,4 +46,16 @@ public interface HtmlNodeRendererContext { * @return whether HTML blocks and tags should be escaped or not */ boolean shouldEscapeHtml(); + + /** + * + * @return true if the {@link UrlSanitizer} should be used. + */ + boolean shouldBeSafe(); + + /** + * + * @return Sanitizer to use for securing {@link Link} href and {@link Image} src if safe is true. + */ + UrlSanitizer urlSanitizer(); } diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java index fac3b3ba2..f1cac8795 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java @@ -5,6 +5,8 @@ import org.commonmark.internal.util.Escaping; import org.commonmark.node.HtmlBlock; import org.commonmark.node.HtmlInline; +import org.commonmark.node.Link; +import org.commonmark.node.Image; import org.commonmark.node.Node; import org.commonmark.renderer.NodeRenderer; import org.commonmark.renderer.Renderer; @@ -27,6 +29,8 @@ public class HtmlRenderer implements Renderer { private final String softbreak; private final boolean escapeHtml; + private final boolean safe; + private final UrlSanitizer urlSanitizer; private final boolean percentEncodeUrls; private final List attributeProviderFactories; private final List nodeRendererFactories; @@ -34,7 +38,9 @@ public class HtmlRenderer implements Renderer { private HtmlRenderer(Builder builder) { this.softbreak = builder.softbreak; this.escapeHtml = builder.escapeHtml; + this.safe = builder.safe; this.percentEncodeUrls = builder.percentEncodeUrls; + this.urlSanitizer = builder.urlSanitizer; this.attributeProviderFactories = new ArrayList<>(builder.attributeProviderFactories); this.nodeRendererFactories = new ArrayList<>(builder.nodeRendererFactories.size() + 1); @@ -83,6 +89,8 @@ public static class Builder { private String softbreak = "\n"; private boolean escapeHtml = false; + private boolean safe = false; + private UrlSanitizer urlSanitizer = new DefaultUrlSanitizer(); private boolean percentEncodeUrls = false; private List attributeProviderFactories = new ArrayList<>(); private List nodeRendererFactories = new ArrayList<>(); @@ -124,6 +132,30 @@ public Builder escapeHtml(boolean escapeHtml) { return this; } + /** + * Whether {@link Image} src and {@link Link} href should be filtered, defaults to {@code false}. + *

+ * + * @param safe true for filtering, false for preserving raw attribute + * @return {@code this} + */ + public Builder safe(boolean safe) { + this.safe = safe; + return this; + } + + /** + * {@link UrlSanitizer} used to filter URL's if safe is true. + *

+ * + * @param urlSanitizer Filterer used to filter {@link Image} src and {@link Link}. + * @return {@code this} + */ + public Builder urlSanitizer(UrlSanitizer urlSanitizer) { + this.urlSanitizer = urlSanitizer; + return this; + } + /** * Whether URLs of link or images should be percent-encoded, defaults to {@code false}. *

@@ -227,6 +259,16 @@ public boolean shouldEscapeHtml() { return escapeHtml; } + @Override + public boolean shouldBeSafe() { + return safe; + } + + @Override + public UrlSanitizer urlSanitizer() { + return urlSanitizer; + } + @Override public String encodeUrl(String url) { if (percentEncodeUrls) { diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/UrlSanitizer.java b/commonmark/src/main/java/org/commonmark/renderer/html/UrlSanitizer.java new file mode 100644 index 000000000..6ad93e41d --- /dev/null +++ b/commonmark/src/main/java/org/commonmark/renderer/html/UrlSanitizer.java @@ -0,0 +1,26 @@ +package org.commonmark.renderer.html; + +import org.commonmark.node.Image; +import org.commonmark.node.Link; + +/** + * Sanitizes urls for img and a elements by whitelisting protocols. + * This is intended to prevent XSS payloads like [Click this totally safe url](javascript:document.xss=true;) + * + * Implementation based on https://github.com/OWASP/java-html-sanitizer/blob/f07e44b034a45d94d6fd010279073c38b6933072/src/main/java/org/owasp/html/FilterUrlByProtocolAttributePolicy.java + */ +public interface UrlSanitizer { + /** + * Sanitize a url for use in the href attribute of a {@link Link}. + * @param url Link to sanitize + * @return Sanitized link + */ + public String sanitizeLinkUrl(String url); + + /** + * Sanitize a url for use in the src attribute of a {@link Image}. + * @param url Link to sanitize + * @return Sanitized link {@link Image} + */ + public String sanitizeImageUrl(String url); +} diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java index 30cbf24f3..4ba1554df 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java @@ -59,6 +59,39 @@ public void attributeEscaping() { assertEquals("

\n", defaultRenderer().render(paragraph)); } + @Test + public void unsafeShouldNotEscapeDangerousProtocols() { + Paragraph paragraph = new Paragraph(); + Link link = new Link(); + link.setDestination("javascript:alert(5);"); + paragraph.appendChild(link); + assertEquals("

\n", unsafeRenderer().render(paragraph)); + } + + @Test + public void safeShouldSetRelNoFollow() { + Paragraph paragraph = new Paragraph(); + Link link = new Link(); + link.setDestination("/exampleUrl"); + paragraph.appendChild(link); + assertEquals("

\n", safeRenderer().render(paragraph)); + + paragraph = new Paragraph(); + link = new Link(); + link.setDestination("https://google.com"); + paragraph.appendChild(link); + assertEquals("

\n", safeRenderer().render(paragraph)); + } + + @Test + public void safeShouldEscapeDangerousProtocols() { + Paragraph paragraph = new Paragraph(); + Link link = new Link(); + link.setDestination("javascript:alert(5);"); + paragraph.appendChild(link); + assertEquals("

\n", safeRenderer().render(paragraph)); + } + @Test public void percentEncodeUrlDisabled() { assertEquals("

a

\n", defaultRenderer().render(parse("[a](foo&bar)"))); @@ -267,6 +300,14 @@ private static HtmlRenderer htmlAllowingRenderer() { return HtmlRenderer.builder().escapeHtml(false).build(); } + private static HtmlRenderer safeRenderer() { + return HtmlRenderer.builder().safe(true).urlSanitizer(new DefaultUrlSanitizer()).build(); + } + + private static HtmlRenderer unsafeRenderer() { + return HtmlRenderer.builder().safe(false).build(); + } + private static HtmlRenderer htmlEscapingRenderer() { return HtmlRenderer.builder().escapeHtml(true).build(); } From 6ac90912b715cf7169a5b713fcffa63ae9bf3fe2 Mon Sep 17 00:00:00 2001 From: David Vandorpe Date: Tue, 14 Jan 2020 10:07:46 +0100 Subject: [PATCH 2/5] Fix review remarks, part 1 --- .../renderer/html/CoreHtmlNodeRenderer.java | 2 +- .../renderer/html/DefaultUrlSanitizer.java | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java index eb387cf58..3e0fba571 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java @@ -185,7 +185,7 @@ public void visit(Image image) { String altText = altTextVisitor.getAltText(); Map attrs = new LinkedHashMap<>(); - if(context.shouldBeSafe()) { + if (context.shouldBeSafe()) { url = context.urlSanitizer().sanitizeImageUrl(url); } attrs.put("src", url); diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java b/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java index 6cdd31214..6ada69b49 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java @@ -1,7 +1,9 @@ package org.commonmark.renderer.html; -import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; +import java.util.Set; /** * @@ -10,7 +12,7 @@ * Implementation based on https://github.com/OWASP/java-html-sanitizer/blob/f07e44b034a45d94d6fd010279073c38b6933072/src/main/java/org/owasp/html/FilterUrlByProtocolAttributePolicy.java */ public class DefaultUrlSanitizer implements UrlSanitizer { - private Collection protocols; + private Set protocols; private static final long HTML_SPACE_CHAR_BITMASK = (1L << ' ') @@ -21,15 +23,11 @@ public class DefaultUrlSanitizer implements UrlSanitizer { public DefaultUrlSanitizer() { - this(new ArrayList() {{ - add("http"); - add("https"); - add("mailto"); - }}); + this(Arrays.asList("http", "https", "mailto")); } public DefaultUrlSanitizer(Collection protocols) { - this.protocols = protocols; + this.protocols = new HashSet<>(protocols); } @Override @@ -78,6 +76,16 @@ private String stripHtmlSpaces(String s) { } private boolean isHtmlSpace(int ch) { - return ch <= 0x20 && (HTML_SPACE_CHAR_BITMASK & (1L << ch)) != 0; + switch (ch) { + case ' ': + case '\t': + case '\n': + case '\u000c': + case '\r': + return true; + default: + return false; + + } } } From 1b159cce2a829982fdbf16a64d01f7e930ace63f Mon Sep 17 00:00:00 2001 From: David Vandorpe Date: Tue, 14 Jan 2020 10:28:38 +0100 Subject: [PATCH 3/5] Fix review remark, part 2 --- .../renderer/html/CoreHtmlNodeRenderer.java | 4 ++-- .../html/HtmlNodeRendererContext.java | 4 ++-- .../renderer/html/HtmlRenderer.java | 20 ++++++++--------- .../org/commonmark/test/HtmlRendererTest.java | 22 +++++++++---------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java index 3e0fba571..4a1ab6e5a 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java @@ -143,7 +143,7 @@ public void visit(Link link) { Map attrs = new LinkedHashMap<>(); String url = link.getDestination(); - if (context.shouldBeSafe()) { + if (context.shouldSanitizeUrls()) { url = context.urlSanitizer().sanitizeLinkUrl(url); attrs.put("rel", "nofollow"); } @@ -185,7 +185,7 @@ public void visit(Image image) { String altText = altTextVisitor.getAltText(); Map attrs = new LinkedHashMap<>(); - if (context.shouldBeSafe()) { + if (context.shouldSanitizeUrls()) { url = context.urlSanitizer().sanitizeImageUrl(url); } attrs.put("src", url); diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java index 6f694fb7c..653f189be 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlNodeRendererContext.java @@ -51,11 +51,11 @@ public interface HtmlNodeRendererContext { * * @return true if the {@link UrlSanitizer} should be used. */ - boolean shouldBeSafe(); + boolean shouldSanitizeUrls(); /** * - * @return Sanitizer to use for securing {@link Link} href and {@link Image} src if safe is true. + * @return Sanitizer to use for securing {@link Link} href and {@link Image} src if sanitizeUrls is true. */ UrlSanitizer urlSanitizer(); } diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java index f1cac8795..e410ae32d 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/HtmlRenderer.java @@ -29,7 +29,7 @@ public class HtmlRenderer implements Renderer { private final String softbreak; private final boolean escapeHtml; - private final boolean safe; + private final boolean sanitizeUrls; private final UrlSanitizer urlSanitizer; private final boolean percentEncodeUrls; private final List attributeProviderFactories; @@ -38,7 +38,7 @@ public class HtmlRenderer implements Renderer { private HtmlRenderer(Builder builder) { this.softbreak = builder.softbreak; this.escapeHtml = builder.escapeHtml; - this.safe = builder.safe; + this.sanitizeUrls = builder.sanitizeUrls; this.percentEncodeUrls = builder.percentEncodeUrls; this.urlSanitizer = builder.urlSanitizer; this.attributeProviderFactories = new ArrayList<>(builder.attributeProviderFactories); @@ -89,7 +89,7 @@ public static class Builder { private String softbreak = "\n"; private boolean escapeHtml = false; - private boolean safe = false; + private boolean sanitizeUrls = false; private UrlSanitizer urlSanitizer = new DefaultUrlSanitizer(); private boolean percentEncodeUrls = false; private List attributeProviderFactories = new ArrayList<>(); @@ -133,19 +133,19 @@ public Builder escapeHtml(boolean escapeHtml) { } /** - * Whether {@link Image} src and {@link Link} href should be filtered, defaults to {@code false}. + * Whether {@link Image} src and {@link Link} href should be sanitized, defaults to {@code false}. *

* - * @param safe true for filtering, false for preserving raw attribute + * @param sanitizeUrls true for sanitization, false for preserving raw attribute * @return {@code this} */ - public Builder safe(boolean safe) { - this.safe = safe; + public Builder sanitizeUrls(boolean sanitizeUrls) { + this.sanitizeUrls = sanitizeUrls; return this; } /** - * {@link UrlSanitizer} used to filter URL's if safe is true. + * {@link UrlSanitizer} used to filter URL's if sanitizeUrls is true. *

* * @param urlSanitizer Filterer used to filter {@link Image} src and {@link Link}. @@ -260,8 +260,8 @@ public boolean shouldEscapeHtml() { } @Override - public boolean shouldBeSafe() { - return safe; + public boolean shouldSanitizeUrls() { + return sanitizeUrls; } @Override diff --git a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java index 4ba1554df..04b493cba 100644 --- a/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java +++ b/commonmark/src/test/java/org/commonmark/test/HtmlRendererTest.java @@ -60,36 +60,36 @@ public void attributeEscaping() { } @Test - public void unsafeShouldNotEscapeDangerousProtocols() { + public void rawUrlsShouldNotFilterDangerousProtocols() { Paragraph paragraph = new Paragraph(); Link link = new Link(); link.setDestination("javascript:alert(5);"); paragraph.appendChild(link); - assertEquals("

\n", unsafeRenderer().render(paragraph)); + assertEquals("

\n", rawUrlsRenderer().render(paragraph)); } @Test - public void safeShouldSetRelNoFollow() { + public void sanitizedUrlsShouldSetRelNoFollow() { Paragraph paragraph = new Paragraph(); Link link = new Link(); link.setDestination("/exampleUrl"); paragraph.appendChild(link); - assertEquals("

\n", safeRenderer().render(paragraph)); + assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); paragraph = new Paragraph(); link = new Link(); link.setDestination("https://google.com"); paragraph.appendChild(link); - assertEquals("

\n", safeRenderer().render(paragraph)); + assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); } @Test - public void safeShouldEscapeDangerousProtocols() { + public void sanitizedUrlsShouldFilterDangerousProtocols() { Paragraph paragraph = new Paragraph(); Link link = new Link(); link.setDestination("javascript:alert(5);"); paragraph.appendChild(link); - assertEquals("

\n", safeRenderer().render(paragraph)); + assertEquals("

\n", sanitizeUrlsRenderer().render(paragraph)); } @Test @@ -300,12 +300,12 @@ private static HtmlRenderer htmlAllowingRenderer() { return HtmlRenderer.builder().escapeHtml(false).build(); } - private static HtmlRenderer safeRenderer() { - return HtmlRenderer.builder().safe(true).urlSanitizer(new DefaultUrlSanitizer()).build(); + private static HtmlRenderer sanitizeUrlsRenderer() { + return HtmlRenderer.builder().sanitizeUrls(true).urlSanitizer(new DefaultUrlSanitizer()).build(); } - private static HtmlRenderer unsafeRenderer() { - return HtmlRenderer.builder().safe(false).build(); + private static HtmlRenderer rawUrlsRenderer() { + return HtmlRenderer.builder().sanitizeUrls(false).build(); } private static HtmlRenderer htmlEscapingRenderer() { From 6b7e42718211d76b324ae61b6bb888bc536ff130 Mon Sep 17 00:00:00 2001 From: David Vandorpe Date: Tue, 14 Jan 2020 10:29:43 +0100 Subject: [PATCH 4/5] Remove redundant field --- .../org/commonmark/renderer/html/DefaultUrlSanitizer.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java b/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java index 6ada69b49..6cc96c5e7 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/DefaultUrlSanitizer.java @@ -14,14 +14,6 @@ public class DefaultUrlSanitizer implements UrlSanitizer { private Set protocols; - private static final long HTML_SPACE_CHAR_BITMASK = - (1L << ' ') - | (1L << '\t') - | (1L << '\n') - | (1L << '\u000c') - | (1L << '\r'); - - public DefaultUrlSanitizer() { this(Arrays.asList("http", "https", "mailto")); } From cf3bd456b1f5cf5d55a3dde56325c04692552b1c Mon Sep 17 00:00:00 2001 From: David Vandorpe Date: Wed, 15 Jan 2020 10:16:12 +0100 Subject: [PATCH 5/5] Encode after sanitization --- .../org/commonmark/renderer/html/CoreHtmlNodeRenderer.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java index 4a1ab6e5a..7d3552668 100644 --- a/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java +++ b/commonmark/src/main/java/org/commonmark/renderer/html/CoreHtmlNodeRenderer.java @@ -178,7 +178,7 @@ public void visit(OrderedList orderedList) { @Override public void visit(Image image) { - String url = context.encodeUrl(image.getDestination()); + String url = image.getDestination(); AltTextVisitor altTextVisitor = new AltTextVisitor(); image.accept(altTextVisitor); @@ -188,7 +188,8 @@ public void visit(Image image) { if (context.shouldSanitizeUrls()) { url = context.urlSanitizer().sanitizeImageUrl(url); } - attrs.put("src", url); + + attrs.put("src", context.encodeUrl(url)); attrs.put("alt", altText); if (image.getTitle() != null) { attrs.put("title", image.getTitle());