Made email html body sanitization configurable
Until now the email sanitization was fixed and could not be configured. That means that if ppl wanted a feature and our sanitization didn't support it, they were forced to disable sanitizaion all together. In this commit a new `HtmlSanitizer` construct was introduced that is bound by Guice and can be configured via the settings. Closes elastic/elasticsearch#586 Original commit: elastic/x-pack-elasticsearch@0081d1bf41
This commit is contained in:
parent
b6e8df6a32
commit
09fcecc069
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.inject.multibindings.MapBinder;
|
|||
import org.elasticsearch.watcher.actions.email.EmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.EmailActionFactory;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.actions.email.service.InternalEmailService;
|
||||
import org.elasticsearch.watcher.actions.index.IndexAction;
|
||||
import org.elasticsearch.watcher.actions.index.IndexActionFactory;
|
||||
|
@ -54,6 +55,7 @@ public class ActionModule extends AbstractModule {
|
|||
}
|
||||
|
||||
bind(ActionRegistry.class).asEagerSingleton();
|
||||
bind(HtmlSanitizer.class).asEagerSingleton();
|
||||
bind(EmailService.class).to(InternalEmailService.class).asEagerSingleton();
|
||||
}
|
||||
|
||||
|
|
|
@ -113,8 +113,8 @@ public class EmailAction implements Action {
|
|||
return builder.endObject();
|
||||
}
|
||||
|
||||
public static EmailAction parse(String watchId, String actionId, XContentParser parser, boolean sanitizeHtmlBody) throws IOException {
|
||||
EmailTemplate.Parser emailParser = new EmailTemplate.Parser(sanitizeHtmlBody);
|
||||
public static EmailAction parse(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
EmailTemplate.Parser emailParser = new EmailTemplate.Parser();
|
||||
String account = null;
|
||||
String user = null;
|
||||
Secret password = null;
|
||||
|
|
|
@ -9,10 +9,9 @@ import org.elasticsearch.common.inject.Inject;
|
|||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.watcher.actions.Action;
|
||||
import org.elasticsearch.watcher.actions.ActionFactory;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.execution.Wid;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -24,16 +23,14 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
|
|||
|
||||
private final EmailService emailService;
|
||||
private final TemplateEngine templateEngine;
|
||||
private final boolean sanitizeHtmlBodyOfEmails;
|
||||
|
||||
private static String SANITIZE_HTML_SETTING = "watcher.actions.email.sanitize_html";
|
||||
private final HtmlSanitizer htmlSanitizer;
|
||||
|
||||
@Inject
|
||||
public EmailActionFactory(Settings settings, EmailService emailService, TemplateEngine templateEngine) {
|
||||
public EmailActionFactory(Settings settings, EmailService emailService, TemplateEngine templateEngine, HtmlSanitizer htmlSanitizer) {
|
||||
super(Loggers.getLogger(ExecutableEmailAction.class, settings));
|
||||
this.emailService = emailService;
|
||||
this.templateEngine = templateEngine;
|
||||
sanitizeHtmlBodyOfEmails = settings.getAsBoolean(SANITIZE_HTML_SETTING, true);
|
||||
this.htmlSanitizer = htmlSanitizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,11 +40,11 @@ public class EmailActionFactory extends ActionFactory<EmailAction, ExecutableEma
|
|||
|
||||
@Override
|
||||
public EmailAction parseAction(String watchId, String actionId, XContentParser parser) throws IOException {
|
||||
return EmailAction.parse(watchId, actionId, parser, sanitizeHtmlBodyOfEmails);
|
||||
return EmailAction.parse(watchId, actionId, parser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutableEmailAction createExecutable(EmailAction action) {
|
||||
return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine);
|
||||
return new ExecutableEmailAction(action, actionLogger, emailService, templateEngine, htmlSanitizer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.elasticsearch.watcher.actions.ExecutableAction;
|
|||
import org.elasticsearch.watcher.actions.email.service.Attachment;
|
||||
import org.elasticsearch.watcher.actions.email.service.Email;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.execution.WatchExecutionContext;
|
||||
import org.elasticsearch.watcher.support.Variables;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
|
@ -25,11 +26,13 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
|
|||
|
||||
final EmailService emailService;
|
||||
final TemplateEngine templateEngine;
|
||||
final HtmlSanitizer htmlSanitizer;
|
||||
|
||||
public ExecutableEmailAction(EmailAction action, ESLogger logger, EmailService emailService, TemplateEngine templateEngine) {
|
||||
public ExecutableEmailAction(EmailAction action, ESLogger logger, EmailService emailService, TemplateEngine templateEngine, HtmlSanitizer htmlSanitizer) {
|
||||
super(action, logger);
|
||||
this.emailService = emailService;
|
||||
this.templateEngine = templateEngine;
|
||||
this.htmlSanitizer = htmlSanitizer;
|
||||
}
|
||||
|
||||
public Action.Result execute(String actionId, WatchExecutionContext ctx, Payload payload) throws Exception {
|
||||
|
@ -42,7 +45,7 @@ public class ExecutableEmailAction extends ExecutableAction<EmailAction> {
|
|||
attachments.put(attachment.id(), attachment);
|
||||
}
|
||||
|
||||
Email.Builder email = action.getEmail().render(templateEngine, model, attachments);
|
||||
Email.Builder email = action.getEmail().render(templateEngine, model, htmlSanitizer, attachments);
|
||||
email.id(ctx.id().value());
|
||||
|
||||
if (ctx.simulateAction(actionId)) {
|
||||
|
|
|
@ -11,9 +11,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
|||
import org.elasticsearch.watcher.WatcherException;
|
||||
import org.elasticsearch.watcher.support.template.Template;
|
||||
import org.elasticsearch.watcher.support.template.TemplateEngine;
|
||||
import org.owasp.html.*;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import javax.mail.internet.AddressException;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
@ -32,11 +30,10 @@ public class EmailTemplate implements ToXContent {
|
|||
final Template subject;
|
||||
final Template textBody;
|
||||
final Template htmlBody;
|
||||
final boolean sanitizeHtmlBody;
|
||||
|
||||
public EmailTemplate(Template from, Template[] replyTo, Template priority, Template[] to,
|
||||
Template[] cc, Template[] bcc, Template subject, Template textBody,
|
||||
Template htmlBody, boolean sanitizeHtmlBody) {
|
||||
Template htmlBody) {
|
||||
this.from = from;
|
||||
this.replyTo = replyTo;
|
||||
this.priority = priority;
|
||||
|
@ -46,7 +43,6 @@ public class EmailTemplate implements ToXContent {
|
|||
this.subject = subject;
|
||||
this.textBody = textBody;
|
||||
this.htmlBody = htmlBody;
|
||||
this.sanitizeHtmlBody = sanitizeHtmlBody;
|
||||
}
|
||||
|
||||
public Template from() {
|
||||
|
@ -85,11 +81,7 @@ public class EmailTemplate implements ToXContent {
|
|||
return htmlBody;
|
||||
}
|
||||
|
||||
public boolean sanitizeHtmlBody() {
|
||||
return sanitizeHtmlBody;
|
||||
}
|
||||
|
||||
public Email.Builder render(TemplateEngine engine, Map<String, Object> model, Map<String, Attachment> attachments) throws AddressException {
|
||||
public Email.Builder render(TemplateEngine engine, Map<String, Object> model, HtmlSanitizer htmlSanitizer, Map<String, Attachment> attachments) throws AddressException {
|
||||
Email.Builder builder = Email.builder();
|
||||
if (from != null) {
|
||||
builder.from(engine.render(from, model));
|
||||
|
@ -126,9 +118,7 @@ public class EmailTemplate implements ToXContent {
|
|||
}
|
||||
if (htmlBody != null) {
|
||||
String renderedHtml = engine.render(htmlBody, model);
|
||||
if (sanitizeHtmlBody && htmlBody != null) {
|
||||
renderedHtml = sanitizeHtml(renderedHtml, attachments);
|
||||
}
|
||||
renderedHtml = htmlSanitizer.sanitize(renderedHtml);
|
||||
builder.htmlBody(renderedHtml);
|
||||
}
|
||||
return builder;
|
||||
|
@ -236,7 +226,6 @@ public class EmailTemplate implements ToXContent {
|
|||
private Template subject;
|
||||
private Template textBody;
|
||||
private Template htmlBody;
|
||||
private boolean sanitizeHtmlBody = true;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
@ -377,81 +366,27 @@ public class EmailTemplate implements ToXContent {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder htmlBody(String html, boolean sanitizeHtmlBody) {
|
||||
return htmlBody(Template.defaultType(html), sanitizeHtmlBody);
|
||||
public Builder htmlBody(String html) {
|
||||
return htmlBody(Template.defaultType(html));
|
||||
}
|
||||
|
||||
public Builder htmlBody(Template.Builder html, boolean sanitizeHtmlBody) {
|
||||
return htmlBody(html.build(), sanitizeHtmlBody);
|
||||
public Builder htmlBody(Template.Builder html) {
|
||||
return htmlBody(html.build());
|
||||
}
|
||||
|
||||
public Builder htmlBody(Template html, boolean sanitizeHtmlBody) {
|
||||
public Builder htmlBody(Template html) {
|
||||
this.htmlBody = html;
|
||||
this.sanitizeHtmlBody = sanitizeHtmlBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EmailTemplate build() {
|
||||
return new EmailTemplate(from, replyTo, priority, to, cc, bcc, subject, textBody, htmlBody, sanitizeHtmlBody);
|
||||
}
|
||||
}
|
||||
|
||||
static String sanitizeHtml(String html, final Map<String, Attachment> attachments){
|
||||
ElementPolicy onlyCIDImgPolicy = new AttachementVerifyElementPolicy(attachments);
|
||||
PolicyFactory policy = Sanitizers.FORMATTING
|
||||
.and(new HtmlPolicyBuilder()
|
||||
.allowElements("img", "table", "tr", "td", "style", "body", "head", "hr")
|
||||
.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 Map<String, Attachment> attachments;
|
||||
|
||||
AttachementVerifyElementPolicy(Map<String, Attachment> attachments) {
|
||||
this.attachments = attachments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(@ParametersAreNonnullByDefault String elementName, @ParametersAreNonnullByDefault List<String> 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;
|
||||
return new EmailTemplate(from, replyTo, priority, to, cc, bcc, subject, textBody, htmlBody);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Parser {
|
||||
|
||||
private final EmailTemplate.Builder builder = builder();
|
||||
private final boolean sanitizeHtmlBody;
|
||||
|
||||
public Parser(boolean sanitizeHtmlBody) {
|
||||
this.sanitizeHtmlBody = sanitizeHtmlBody;
|
||||
}
|
||||
|
||||
public boolean handle(String fieldName, XContentParser parser) throws IOException {
|
||||
if (Email.Field.FROM.match(fieldName)) {
|
||||
|
@ -514,7 +449,7 @@ public class EmailTemplate implements ToXContent {
|
|||
} else if (Email.Field.BODY_TEXT.match(currentFieldName)) {
|
||||
builder.textBody(Template.parse(parser));
|
||||
} else if (Email.Field.BODY_HTML.match(currentFieldName)) {
|
||||
builder.htmlBody(Template.parse(parser), sanitizeHtmlBody);
|
||||
builder.htmlBody(Template.parse(parser));
|
||||
} else {
|
||||
throw new ParseException("could not parse email template. unknown field [{}.{}] field", fieldName, currentFieldName);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* 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.Strings;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.owasp.html.CssSchema;
|
||||
import org.owasp.html.ElementPolicy;
|
||||
import org.owasp.html.HtmlPolicyBuilder;
|
||||
import org.owasp.html.PolicyFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HtmlSanitizer {
|
||||
|
||||
static final String[] FORMATTING_TAGS = new String[] {
|
||||
"b", "i", "s", "u", "o", "sup", "sub", "ins", "del", "strong",
|
||||
"strike", "tt", "code", "big", "small", "br", "span", "em"
|
||||
};
|
||||
static final String[] BLOCK_TAGS = new String[] {
|
||||
"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "ul", "ol", "li", "blockquote"
|
||||
};
|
||||
static final String[] TABLE_TAGS = new String[] {
|
||||
"table", "hr", "tr", "td"
|
||||
};
|
||||
static final String[] DEFAULT_ALLOWED = new String[] {
|
||||
"body", "head", "_tables", "_links", "_blocks", "_formatting", "img:embedded"
|
||||
};
|
||||
|
||||
private final boolean enabled;
|
||||
private final PolicyFactory policy;
|
||||
|
||||
@Inject
|
||||
public HtmlSanitizer(Settings settings) {
|
||||
enabled = settings.getAsBoolean("watcher.actions.email.html.sanitization.enabled", true);
|
||||
String[] allow = settings.getAsArray("watcher.actions.email.html.sanitization.allow", DEFAULT_ALLOWED);
|
||||
String[] disallow = settings.getAsArray("watcher.actions.email.html.sanitization.disallow", Strings.EMPTY_ARRAY);
|
||||
policy = createCommonPolicy(allow, disallow);
|
||||
}
|
||||
|
||||
public String sanitize(String html) {
|
||||
if (!enabled) {
|
||||
return html;
|
||||
}
|
||||
return policy.sanitize(html);
|
||||
}
|
||||
|
||||
static PolicyFactory createCommonPolicy(String[] allow, String[] disallow) {
|
||||
HtmlPolicyBuilder policyBuilder = new HtmlPolicyBuilder();
|
||||
|
||||
if (Arrays.binarySearch(allow, "_all") > -1) {
|
||||
return policyBuilder
|
||||
.allowElements(TABLE_TAGS)
|
||||
.allowElements(BLOCK_TAGS)
|
||||
.allowElements(FORMATTING_TAGS)
|
||||
.allowStyling(CssSchema.DEFAULT)
|
||||
.allowStandardUrlProtocols().allowElements("a")
|
||||
.allowAttributes("href").onElements("a").requireRelNofollowOnLinks()
|
||||
.allowElements("img")
|
||||
.allowAttributes("src").onElements("img")
|
||||
.allowStandardUrlProtocols()
|
||||
.allowUrlProtocols("cid")
|
||||
.toFactory();
|
||||
}
|
||||
|
||||
EnumSet<Images> images = EnumSet.noneOf(Images.class);
|
||||
|
||||
for (String tag : allow) {
|
||||
tag = tag.toLowerCase(Locale.ROOT);
|
||||
switch (tag) {
|
||||
case "_tables":
|
||||
policyBuilder.allowElements(TABLE_TAGS);
|
||||
break;
|
||||
case "_links":
|
||||
policyBuilder.allowElements("a")
|
||||
.allowAttributes("href").onElements("a")
|
||||
.allowStandardUrlProtocols()
|
||||
.requireRelNofollowOnLinks();
|
||||
break;
|
||||
case "_blocks":
|
||||
policyBuilder.allowElements(BLOCK_TAGS);
|
||||
break;
|
||||
case "_formatting":
|
||||
policyBuilder.allowElements(FORMATTING_TAGS);
|
||||
break;
|
||||
case "_styles":
|
||||
policyBuilder.allowStyling(CssSchema.DEFAULT);
|
||||
break;
|
||||
case "img:all":
|
||||
case "img":
|
||||
images.add(Images.ALL);
|
||||
break;
|
||||
case "img:embedded":
|
||||
images.add(Images.EMBEDDED);
|
||||
break;
|
||||
default:
|
||||
policyBuilder.allowElements(tag);
|
||||
}
|
||||
}
|
||||
for (String tag : disallow) {
|
||||
tag = tag.toLowerCase(Locale.ROOT);
|
||||
switch (tag) {
|
||||
case "_tables":
|
||||
policyBuilder.disallowElements(TABLE_TAGS);
|
||||
break;
|
||||
case "_links":
|
||||
policyBuilder.disallowElements("a");
|
||||
break;
|
||||
case "_blocks":
|
||||
policyBuilder.disallowElements(BLOCK_TAGS);
|
||||
break;
|
||||
case "_formatting":
|
||||
policyBuilder.disallowElements(FORMATTING_TAGS);
|
||||
break;
|
||||
case "_styles":
|
||||
policyBuilder.disallowAttributes("style");
|
||||
break;
|
||||
case "img:all":
|
||||
case "img":
|
||||
images.remove(Images.ALL);
|
||||
break;
|
||||
case "img:embedded":
|
||||
images.remove(Images.EMBEDDED);
|
||||
break;
|
||||
default:
|
||||
policyBuilder.disallowElements(tag);
|
||||
}
|
||||
}
|
||||
|
||||
if (!images.isEmpty()) {
|
||||
policyBuilder.allowAttributes("src").onElements("img").allowUrlProtocols("cid");
|
||||
if (images.contains(Images.ALL)) {
|
||||
policyBuilder.allowElements("img");
|
||||
policyBuilder.allowStandardUrlProtocols();
|
||||
} else {
|
||||
// embedded
|
||||
policyBuilder.allowElements(EmbeddedImgOnlyPolicy.INSTANCE, "img");
|
||||
}
|
||||
}
|
||||
|
||||
return policyBuilder.toFactory();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An {@code img} tag policy that only accept {@code cid:} values in its {@code src} attribute.
|
||||
* If such value is found, the content id is verified against the available attachements of the
|
||||
* email and if the content/attachment is not found, the element is dropped.
|
||||
*/
|
||||
private static class EmbeddedImgOnlyPolicy implements ElementPolicy {
|
||||
|
||||
private static EmbeddedImgOnlyPolicy INSTANCE = new EmbeddedImgOnlyPolicy();
|
||||
|
||||
@Override
|
||||
public String apply(String elementName, List<String> attrs) {
|
||||
if (!"img".equals(elementName) || attrs.size() == 0) {
|
||||
return elementName;
|
||||
}
|
||||
String attrName = null;
|
||||
for (String attr : attrs) {
|
||||
if (attrName == null) {
|
||||
attrName = attr.toLowerCase(Locale.ROOT);
|
||||
continue;
|
||||
}
|
||||
// reject external image source (only allow embedded ones)
|
||||
if ("src".equals(attrName) && !attr.startsWith("cid:")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return elementName;
|
||||
}
|
||||
}
|
||||
|
||||
enum Images {
|
||||
ALL,
|
||||
EMBEDDED
|
||||
}
|
||||
}
|
|
@ -6,7 +6,6 @@
|
|||
package org.elasticsearch.watcher.actions.email;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.carrotsearch.randomizedtesting.annotations.Seed;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
|
@ -58,6 +57,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
}
|
||||
};
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
|
||||
EmailTemplate.Builder emailBuilder = EmailTemplate.builder();
|
||||
Template subject = null;
|
||||
|
@ -73,7 +73,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
Template htmlBody = null;
|
||||
if (randomBoolean()) {
|
||||
htmlBody = Template.inline("_html_body").build();
|
||||
emailBuilder.htmlBody(htmlBody, true);
|
||||
emailBuilder.htmlBody(htmlBody);
|
||||
}
|
||||
EmailTemplate email = emailBuilder.build();
|
||||
|
||||
|
@ -83,7 +83,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
DataAttachment dataAttachment = randomDataAttachment();
|
||||
|
||||
EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
Payload payload = new Payload.Simple(data);
|
||||
|
@ -121,6 +121,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
when(engine.render(textBody, expectedModel)).thenReturn(textBody.getTemplate());
|
||||
}
|
||||
if (htmlBody != null) {
|
||||
when(htmlSanitizer.sanitize(htmlBody.getTemplate())).thenReturn(htmlBody.getTemplate());
|
||||
when(engine.render(htmlBody, expectedModel)).thenReturn(htmlBody.getTemplate());
|
||||
}
|
||||
|
||||
|
@ -143,6 +144,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
@Test @Repeat(iterations = 20)
|
||||
public void testParser() throws Exception {
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
EmailService emailService = mock(EmailService.class);
|
||||
Profile profile = randomFrom(Profile.values());
|
||||
Email.Priority priority = randomFrom(Email.Priority.values());
|
||||
|
@ -242,7 +244,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
|
||||
ExecutableEmailAction executable = new EmailActionFactory(ImmutableSettings.EMPTY, emailService, engine)
|
||||
ExecutableEmailAction executable = new EmailActionFactory(ImmutableSettings.EMPTY, emailService, engine, htmlSanitizer)
|
||||
.parseExecutable(randomAsciiOfLength(8), randomAsciiOfLength(3), parser);
|
||||
|
||||
assertThat(executable, notNullValue());
|
||||
|
@ -290,6 +292,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
public void testParser_SelfGenerated() throws Exception {
|
||||
EmailService service = mock(EmailService.class);
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
EmailTemplate.Builder emailTemplate = EmailTemplate.builder();
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.from("from@domain");
|
||||
|
@ -313,7 +316,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
emailTemplate.textBody("_text_body");
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
emailTemplate.htmlBody("_html_body", randomBoolean());
|
||||
emailTemplate.htmlBody("_html_body");
|
||||
}
|
||||
EmailTemplate email = emailTemplate.build();
|
||||
Authentication auth = randomBoolean() ? null : new Authentication("_user", new Secret("_passwd".toCharArray()));
|
||||
|
@ -322,7 +325,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
DataAttachment dataAttachment = randomDataAttachment();
|
||||
|
||||
EmailAction action = new EmailAction(email, account, auth, profile, dataAttachment);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine);
|
||||
ExecutableEmailAction executable = new ExecutableEmailAction(action, logger, service, engine, htmlSanitizer);
|
||||
|
||||
boolean hideSecrets = randomBoolean();
|
||||
ToXContent.Params params = WatcherParams.builder().hideSecrets(hideSecrets).build();
|
||||
|
@ -333,7 +336,7 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
logger.info(bytes.toUtf8());
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(bytes);
|
||||
parser.nextToken();
|
||||
ExecutableEmailAction parsed = new EmailActionFactory(ImmutableSettings.EMPTY, service, engine)
|
||||
ExecutableEmailAction parsed = new EmailActionFactory(ImmutableSettings.EMPTY, service, engine, htmlSanitizer)
|
||||
.parseExecutable(randomAsciiOfLength(4), randomAsciiOfLength(10), parser);
|
||||
|
||||
if (!hideSecrets) {
|
||||
|
@ -358,10 +361,11 @@ public class EmailActionTests extends ElasticsearchTestCase {
|
|||
public void testParser_Invalid() throws Exception {
|
||||
EmailService emailService = mock(EmailService.class);
|
||||
TemplateEngine engine = mock(TemplateEngine.class);
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
XContentBuilder builder = jsonBuilder().startObject().field("unknown_field", "value");
|
||||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
new EmailActionFactory(ImmutableSettings.EMPTY, emailService, engine)
|
||||
new EmailActionFactory(ImmutableSettings.EMPTY, emailService, engine, htmlSanitizer)
|
||||
.parseExecutable(randomAsciiOfLength(3), randomAsciiOfLength(7), parser);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,25 +43,18 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
Template[] cc = randomFrom(possibleList, null);
|
||||
Template[] bcc = randomFrom(possibleList, null);
|
||||
Template priority = Template.inline(randomFrom(Email.Priority.values()).name()).build();
|
||||
boolean sanitizeHtml = randomBoolean();
|
||||
|
||||
Template templatedSubject = Template.inline("Templated Subject {{foo}}").build();
|
||||
String renderedTemplatedSubject = "Templated Subject bar";
|
||||
Template subjectTemplate = Template.inline("Templated Subject {{foo}}").build();
|
||||
String subject = "Templated Subject bar";
|
||||
|
||||
Template templatedBody = Template.inline("Templated Body {{foo}}").build();
|
||||
String renderedTemplatedBody = "Templated Body bar";
|
||||
Template textBodyTemplate = Template.inline("Templated Body {{foo}}").build();
|
||||
String textBody = "Templated Body bar";
|
||||
|
||||
Template templatedHtmlBodyGood = Template.inline("Templated Html Body <hr />").build();
|
||||
String renderedTemplatedHtmlBodyGood = "Templated Html Body <hr /> bar";
|
||||
Template htmlBodyTemplate = Template.inline("Templated Html Body <script>nefarious scripting</script>").build();
|
||||
String htmlBody = "Templated Html Body <script>nefarious scripting</script>";
|
||||
String sanitizedHtmlBody = "Templated Html Body";
|
||||
|
||||
Template templatedHtmlBodyBad = Template.inline("Templated Html Body <script>nefarious scripting</script>").build();
|
||||
String renderedTemplatedHtmlBodyBad = "Templated Html Body<script>nefarious scripting</script>";
|
||||
String renderedSanitizedHtmlBodyBad = "Templated Html Body";
|
||||
|
||||
Template htmlBody = randomFrom(templatedHtmlBodyGood, templatedHtmlBodyBad);
|
||||
|
||||
EmailTemplate emailTemplate = new EmailTemplate(from, replyTo, priority, to, cc, bcc,
|
||||
templatedSubject, templatedBody, htmlBody, sanitizeHtml);
|
||||
EmailTemplate emailTemplate = new EmailTemplate(from, replyTo, priority, to, cc, bcc, subjectTemplate, textBodyTemplate, htmlBodyTemplate);
|
||||
|
||||
XContentBuilder builder = XContentFactory.jsonBuilder();
|
||||
emailTemplate.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||
|
@ -69,7 +62,7 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
XContentParser parser = JsonXContent.jsonXContent.createParser(builder.bytes());
|
||||
parser.nextToken();
|
||||
|
||||
EmailTemplate.Parser emailTemplateParser = new EmailTemplate.Parser(sanitizeHtml);
|
||||
EmailTemplate.Parser emailTemplateParser = new EmailTemplate.Parser();
|
||||
|
||||
String currentFieldName = null;
|
||||
XContentParser.Token token;
|
||||
|
@ -83,11 +76,14 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
EmailTemplate parsedEmailTemplate = emailTemplateParser.parsedTemplate();
|
||||
|
||||
Map<String, Object> model = new HashMap<>();
|
||||
|
||||
HtmlSanitizer htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
when(htmlSanitizer.sanitize(htmlBody)).thenReturn(sanitizedHtmlBody);
|
||||
|
||||
TemplateEngine templateEngine = mock(TemplateEngine.class);
|
||||
when(templateEngine.render(templatedSubject, model)).thenReturn(renderedTemplatedSubject);
|
||||
when(templateEngine.render(templatedBody, model)).thenReturn(renderedTemplatedBody);
|
||||
when(templateEngine.render(templatedHtmlBodyGood, model)).thenReturn(renderedTemplatedHtmlBodyGood);
|
||||
when(templateEngine.render(templatedHtmlBodyBad, model)).thenReturn(renderedTemplatedHtmlBodyBad);
|
||||
when(templateEngine.render(subjectTemplate, model)).thenReturn(subject);
|
||||
when(templateEngine.render(textBodyTemplate, model)).thenReturn(textBody);
|
||||
when(templateEngine.render(htmlBodyTemplate, model)).thenReturn(htmlBody);
|
||||
for (Template possibleAddress : possibleList) {
|
||||
when(templateEngine.render(possibleAddress, model)).thenReturn(possibleAddress.getTemplate());
|
||||
}
|
||||
|
@ -96,7 +92,7 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
}
|
||||
when(templateEngine.render(priority, model)).thenReturn(priority.getTemplate());
|
||||
|
||||
Email.Builder emailBuilder = parsedEmailTemplate.render(templateEngine, model, new HashMap<String, Attachment>());
|
||||
Email.Builder emailBuilder = parsedEmailTemplate.render(templateEngine, model, htmlSanitizer, new HashMap<String, Attachment>());
|
||||
|
||||
assertThat(emailTemplate.from, equalTo(parsedEmailTemplate.from));
|
||||
assertThat(emailTemplate.replyTo, equalTo(parsedEmailTemplate.replyTo));
|
||||
|
@ -110,17 +106,9 @@ public class EmailTemplateTests extends ElasticsearchTestCase {
|
|||
|
||||
emailBuilder.id("_id");
|
||||
Email email = emailBuilder.build();
|
||||
assertThat(email.subject, equalTo(renderedTemplatedSubject));
|
||||
assertThat(email.textBody, equalTo(renderedTemplatedBody));
|
||||
if (htmlBody.equals(templatedHtmlBodyBad)) {
|
||||
if (sanitizeHtml) {
|
||||
assertThat(email.htmlBody, equalTo(renderedSanitizedHtmlBodyBad));
|
||||
} else {
|
||||
assertThat(email.htmlBody, equalTo(renderedTemplatedHtmlBodyBad));
|
||||
}
|
||||
} else {
|
||||
assertThat(email.htmlBody, equalTo(renderedTemplatedHtmlBodyGood));
|
||||
}
|
||||
assertThat(email.subject, equalTo(subject));
|
||||
assertThat(email.textBody, equalTo(textBody));
|
||||
assertThat(email.htmlBody, equalTo(sanitizedHtmlBody));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* 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.elasticsearch.watcher.actions.email.service.EmailTemplate.sanitizeHtml;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
|
||||
/**
|
||||
*/
|
||||
public class HtmlSanitizeTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_onclick() {
|
||||
String badHtml = "<button type=\"button\"" +
|
||||
"onclick=\"document.getElementById('demo').innerHTML = Date()\">" +
|
||||
"Click me to display Date and Time.</button>";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(badHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("Click me to display Date and Time."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Nonattachment_img() {
|
||||
String badHtml = "<img src=\"http://test.com/nastyimage.jpg\"/>This is a bad image";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(badHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("This is a bad image"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Goodattachment_img() {
|
||||
String goodHtml = "<img src=\"cid:foo\" />This is a good image";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(goodHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo(goodHtml));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_table() {
|
||||
String goodHtml = "<table><tr><td>cell1</td><td>cell2</td></tr></table>";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(goodHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo(goodHtml));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Badattachment_img() {
|
||||
String goodHtml = "<img src=\"cid:bad\" />This is a bad image";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(goodHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("This is a bad image"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_Script() {
|
||||
String badHtml = "<script>doSomethingNefarious()</script>This was a dangerous script";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(badHtml, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("This was a dangerous script"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_HtmlSanitizer_FullHtmlWithMetaString() {
|
||||
String needsSanitation = "<html><head></head><body><h1>Hello {{ctx.metadata.name}}</h1> meta <a href='https://www.google.com/search?q={{ctx.metadata.name}}'>Testlink</a>meta</body></html>";
|
||||
byte[] bytes = new byte[0];
|
||||
String sanitizedHtml = sanitizeHtml(needsSanitation, ImmutableMap.of("foo", (Attachment) new Attachment.Bytes("foo", bytes, "")));
|
||||
assertThat(sanitizedHtml, equalTo("<head></head><body><h1>Hello {{ctx.metadata.name}}</h1> meta <a href=\"https://www.google.com/search?q={{ctx.metadata.name}}\" rel=\"nofollow\">Testlink</a>meta</body>"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* 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 com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class HtmlSanitizerTests extends ElasticsearchTestCase {
|
||||
|
||||
@Test @Repeat(iterations = 20)
|
||||
public void testDefault_WithTemplatePlaceholders() {
|
||||
String blockTag = randomFrom(HtmlSanitizer.BLOCK_TAGS);
|
||||
while (blockTag.equals("li")) {
|
||||
blockTag = randomFrom(HtmlSanitizer.BLOCK_TAGS);
|
||||
}
|
||||
String html =
|
||||
"<html>" +
|
||||
"<head></head>" +
|
||||
"<body>" +
|
||||
"<" + blockTag + ">Hello {{ctx.metadata.name}}</" + blockTag + ">" +
|
||||
"<ul><li>item1</li></ul>" +
|
||||
"<ol><li>item2</li></ol>" +
|
||||
"meta <a href='https://www.google.com/search?q={{ctx.metadata.name}}'>Testlink</a> meta" +
|
||||
"</body>" +
|
||||
"</html>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
if (blockTag.equals("ol") || blockTag.equals("ul")) {
|
||||
assertThat(sanitizedHtml, equalTo(
|
||||
"<head></head><body>" +
|
||||
"<" + blockTag + "><li>Hello {{ctx.metadata.name}}</li></" + blockTag + ">" +
|
||||
"<ul><li>item1</li></ul>" +
|
||||
"<ol><li>item2</li></ol>" +
|
||||
"meta <a href=\"https://www.google.com/search?q={{ctx.metadata.name}}\" rel=\"nofollow\">Testlink</a> meta" +
|
||||
"</body>"));
|
||||
} else {
|
||||
assertThat(sanitizedHtml, equalTo(
|
||||
"<head></head><body>" +
|
||||
"<" + blockTag + ">Hello {{ctx.metadata.name}}</" + blockTag + ">" +
|
||||
"<ul><li>item1</li></ul>" +
|
||||
"<ol><li>item2</li></ol>" +
|
||||
"meta <a href=\"https://www.google.com/search?q={{ctx.metadata.name}}\" rel=\"nofollow\">Testlink</a> meta" +
|
||||
"</body>"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDefault_onclick_Disallowed() {
|
||||
String badHtml = "<button type=\"button\"" +
|
||||
"onclick=\"document.getElementById('demo').innerHTML = Date()\">" +
|
||||
"Click me to display Date and Time.</button>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(badHtml);
|
||||
assertThat(sanitizedHtml, equalTo("Click me to display Date and Time."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_ExternalImage_Disallowed() {
|
||||
String html = "<img src=\"http://test.com/nastyimage.jpg\"/>This is a bad image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo("This is a bad image"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_EmbeddedImage_Allowed() {
|
||||
String html = "<img src=\"cid:foo\" />This is a good image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_Tables_Allowed() {
|
||||
String html = "<table><tr><td>cell1</td><td>cell2</td></tr></table>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefault_Scipts_Disallowed() {
|
||||
String html = "<script>doSomethingNefarious()</script>This was a dangerous script";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.EMPTY);
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo("This was a dangerous script"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustom_Disabled() {
|
||||
String html = "<img src=\"http://test.com/nastyimage.jpg\" />This is a bad image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.builder()
|
||||
.put("watcher.actions.email.html.sanitization.enabled", false)
|
||||
.build());
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustom_AllImage_Allowed() {
|
||||
String html = "<img src=\"http://test.com/nastyimage.jpg\" />This is a bad image";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.builder()
|
||||
.put("watcher.actions.email.html.sanitization.allow", "img:all")
|
||||
.build());
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo(html));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustom_Tables_Disallowed() {
|
||||
String html = "<table><tr><td>cell1</td><td>cell2</td></tr></table>";
|
||||
HtmlSanitizer sanitizer = new HtmlSanitizer(ImmutableSettings.builder()
|
||||
.put("watcher.actions.email.html.sanitization.disallow", "_tables")
|
||||
.build());
|
||||
String sanitizedHtml = sanitizer.sanitize(html);
|
||||
assertThat(sanitizedHtml, equalTo("cell1cell2"));
|
||||
}
|
||||
|
||||
}
|
|
@ -34,10 +34,7 @@ import org.elasticsearch.watcher.actions.ActionWrapper;
|
|||
import org.elasticsearch.watcher.actions.ExecutableActions;
|
||||
import org.elasticsearch.watcher.actions.email.EmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.ExecutableEmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.service.Authentication;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.Profile;
|
||||
import org.elasticsearch.watcher.actions.email.service.*;
|
||||
import org.elasticsearch.watcher.actions.webhook.ExecutableWebhookAction;
|
||||
import org.elasticsearch.watcher.actions.webhook.WebhookAction;
|
||||
import org.elasticsearch.watcher.condition.script.ExecutableScriptCondition;
|
||||
|
@ -197,7 +194,7 @@ public final class WatcherTestUtils {
|
|||
Authentication auth = new Authentication("testname", new Secret("testpassword".toCharArray()));
|
||||
|
||||
EmailAction action = new EmailAction(email, "testaccount", auth, Profile.STANDARD, null);
|
||||
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine);
|
||||
ExecutableEmailAction executale = new ExecutableEmailAction(action, logger, emailService, templateEngine, new HtmlSanitizer(ImmutableSettings.EMPTY));
|
||||
|
||||
actions.add(new ActionWrapper("_email", executale));
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.watcher.actions.email.EmailActionFactory;
|
|||
import org.elasticsearch.watcher.actions.email.ExecutableEmailAction;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailService;
|
||||
import org.elasticsearch.watcher.actions.email.service.EmailTemplate;
|
||||
import org.elasticsearch.watcher.actions.email.service.HtmlSanitizer;
|
||||
import org.elasticsearch.watcher.actions.email.service.Profile;
|
||||
import org.elasticsearch.watcher.actions.index.ExecutableIndexAction;
|
||||
import org.elasticsearch.watcher.actions.index.IndexAction;
|
||||
|
@ -114,6 +115,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
private HttpClient httpClient;
|
||||
private EmailService emailService;
|
||||
private TemplateEngine templateEngine;
|
||||
private HtmlSanitizer htmlSanitizer;
|
||||
private HttpAuthRegistry authRegistry;
|
||||
private SecretService secretService;
|
||||
private LicenseService licenseService;
|
||||
|
@ -127,6 +129,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
httpClient = mock(HttpClient.class);
|
||||
emailService = mock(EmailService.class);
|
||||
templateEngine = mock(TemplateEngine.class);
|
||||
htmlSanitizer = mock(HtmlSanitizer.class);
|
||||
secretService = mock(SecretService.class);
|
||||
licenseService = mock(LicenseService.class);
|
||||
authRegistry = new HttpAuthRegistry(ImmutableMap.of("basic", (HttpAuthFactory) new BasicAuthFactory(secretService)));
|
||||
|
@ -384,7 +387,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
if (randomBoolean()) {
|
||||
ExecutableTransform transform = randomTransform();
|
||||
EmailAction action = new EmailAction(EmailTemplate.builder().build(), null, null, Profile.STANDARD, randomFrom(DataAttachment.JSON, DataAttachment.YAML, null));
|
||||
list.add(new ActionWrapper("_email_" + randomAsciiOfLength(8), randomThrottler(), transform, new ExecutableEmailAction(action, logger, emailService, templateEngine)));
|
||||
list.add(new ActionWrapper("_email_" + randomAsciiOfLength(8), randomThrottler(), transform, new ExecutableEmailAction(action, logger, emailService, templateEngine, htmlSanitizer)));
|
||||
}
|
||||
if (randomBoolean()) {
|
||||
IndexAction aciton = new IndexAction("_index", "_type", null);
|
||||
|
@ -406,7 +409,7 @@ public class WatchTests extends ElasticsearchTestCase {
|
|||
for (ActionWrapper action : actions) {
|
||||
switch (action.action().type()) {
|
||||
case EmailAction.TYPE:
|
||||
parsers.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine));
|
||||
parsers.put(EmailAction.TYPE, new EmailActionFactory(settings, emailService, templateEngine, htmlSanitizer));
|
||||
break;
|
||||
case IndexAction.TYPE:
|
||||
parsers.put(IndexAction.TYPE, new IndexActionFactory(settings, client));
|
||||
|
|
Loading…
Reference in New Issue