diff --git a/pom.xml b/pom.xml
index 2bd118273e3..093a844ad2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -149,6 +149,12 @@
true
+
+ com.googlecode.owasp-java-html-sanitizer
+ owasp-java-html-sanitizer
+ r239
+
+
org.apache.lucene
lucene-core
@@ -328,6 +334,7 @@
true
+ com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer
org.quartz-scheduler:quartz
org.slf4j:slf4j-api
org.slf4j:slf4j-nop
diff --git a/src/main/java/org/elasticsearch/watcher/actions/email/service/Profile.java b/src/main/java/org/elasticsearch/watcher/actions/email/service/Profile.java
index 08513239d50..405041ddec0 100644
--- a/src/main/java/org/elasticsearch/watcher/actions/email/service/Profile.java
+++ b/src/main/java/org/elasticsearch/watcher/actions/email/service/Profile.java
@@ -6,9 +6,13 @@
package org.elasticsearch.watcher.actions.email.service;
import org.elasticsearch.common.base.Charsets;
+import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.owasp.html.*;
+import javax.annotation.Nullable;
+import javax.annotation.ParametersAreNonnullByDefault;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
@@ -16,6 +20,7 @@ import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.IOException;
+import java.util.List;
import java.util.Locale;
/**
@@ -87,7 +92,8 @@ public enum Profile implements ToXContent {
if (email.htmlBody != null) {
MimeBodyPart html = new MimeBodyPart();
- html.setText(email.htmlBody, Charsets.UTF_8.name(), "html");
+ String sanitizedHtml = sanitizeHtml(email.attachments, email.htmlBody);
+ html.setText(sanitizedHtml, Charsets.UTF_8.name(), "html");
alternative.addBodyPart(html);
}
@@ -217,4 +223,54 @@ public enum Profile implements ToXContent {
return part;
}
+ static String sanitizeHtml(final ImmutableMap attachments, String html){
+ ElementPolicy onlyCIDImgPolicy = new AttachementVerifyElementPolicy(attachments);
+ PolicyFactory policy = Sanitizers.FORMATTING
+ .and(new HtmlPolicyBuilder()
+ .allowElements("img", "table", "tr", "td", "style", "body", "head")
+ .allowAttributes("src").onElements("img")
+ .allowAttributes("class").onElements("style")
+ .allowUrlProtocols("cid")
+ .allowCommonInlineFormattingElements()
+ .allowElements(onlyCIDImgPolicy, "img")
+ .allowStyling(CssSchema.DEFAULT)
+ .toFactory())
+ .and(Sanitizers.LINKS)
+ .and(Sanitizers.BLOCKS);
+ return policy.sanitize(html);
+ }
+
+
+
+ private static class AttachementVerifyElementPolicy implements ElementPolicy {
+
+ private final ImmutableMap attachments;
+
+ AttachementVerifyElementPolicy(ImmutableMap attchments) {
+ this.attachments = attchments;
+ }
+
+ @Nullable
+ @Override
+ public String apply(@ParametersAreNonnullByDefault String elementName, @ParametersAreNonnullByDefault List attrs) {
+ if (attrs.size() == 0) {
+ return elementName;
+ }
+ for (int i = 0; i < attrs.size(); ++i) {
+ if(attrs.get(i).equals("src") && i < attrs.size() - 1) {
+ String srcValue = attrs.get(i+1);
+ if (!srcValue.startsWith("cid:")) {
+ return null; //Disallow anything other than content ids
+ }
+ String contentId = srcValue.substring(4);
+ if (attachments.containsKey(contentId)) {
+ return elementName;
+ } else {
+ return null; //This cid wasn't found
+ }
+ }
+ }
+ return elementName;
+ }
+ }
}
diff --git a/src/test/java/org/elasticsearch/watcher/actions/email/service/HtmlSanitizeTests.java b/src/test/java/org/elasticsearch/watcher/actions/email/service/HtmlSanitizeTests.java
new file mode 100644
index 00000000000..7d415a81525
--- /dev/null
+++ b/src/test/java/org/elasticsearch/watcher/actions/email/service/HtmlSanitizeTests.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+package org.elasticsearch.watcher.actions.email.service;
+
+import org.elasticsearch.common.collect.ImmutableMap;
+import org.elasticsearch.test.ElasticsearchTestCase;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.equalTo;
+
+/**
+ */
+public class HtmlSanitizeTests extends ElasticsearchTestCase {
+
+ @Test
+ public void test_HtmlSanitizer_onclick() {
+ String badHtml = "";
+ byte[] bytes = new byte[0];
+ String sanitizedHtml = Profile.sanitizeHtml(ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")), badHtml);
+ assertThat(sanitizedHtml, equalTo("Click me to display Date and Time."));
+ }
+
+ @Test
+ public void test_HtmlSanitizer_Nonattachment_img() {
+ String badHtml = "This is a bad image";
+ byte[] bytes = new byte[0];
+ String sanitizedHtml = Profile.sanitizeHtml(ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")), badHtml);
+ assertThat(sanitizedHtml, equalTo("This is a bad image"));
+ }
+
+ @Test
+ public void test_HtmlSanitizer_Goodattachment_img() {
+ String goodHtml = "This is a good image";
+ byte[] bytes = new byte[0];
+ String sanitizedHtml = Profile.sanitizeHtml(ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")), goodHtml);
+ assertThat(sanitizedHtml, equalTo(goodHtml));
+ }
+
+ @Test
+ public void test_HtmlSanitizer_table() {
+ String goodHtml = "";
+ byte[] bytes = new byte[0];
+ String sanitizedHtml = Profile.sanitizeHtml(ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")), goodHtml);
+ assertThat(sanitizedHtml, equalTo(goodHtml));
+
+ }
+
+ @Test
+ public void test_HtmlSanitizer_Badattachment_img() {
+ String goodHtml = "This is a bad image";
+ byte[] bytes = new byte[0];
+ String sanitizedHtml = Profile.sanitizeHtml(ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")), goodHtml);
+ assertThat(sanitizedHtml, equalTo("This is a bad image"));
+ }
+
+ @Test
+ public void test_HtmlSanitizer_Script() {
+ String badHtml = "This was a dangerous script";
+ byte[] bytes = new byte[0];
+ String sanitizedHtml = Profile.sanitizeHtml(ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")), badHtml);
+ assertThat(sanitizedHtml, equalTo("This was a dangerous script"));
+ }
+
+
+}