diff --git a/distribution/feature-packs/server-feature-pack/pom.xml b/distribution/feature-packs/server-feature-pack/pom.xml index 525a3a99252..05613dfbbb8 100644 --- a/distribution/feature-packs/server-feature-pack/pom.xml +++ b/distribution/feature-packs/server-feature-pack/pom.xml @@ -72,6 +72,16 @@ + + com.googlecode.owasp-java-html-sanitizer + owasp-java-html-sanitizer + + + * + * + + + com.thoughtworks.xstream xstream diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/com/googlecode/owasp-java-html-sanitizer/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/com/googlecode/owasp-java-html-sanitizer/main/module.xml new file mode 100644 index 00000000000..4a1bbbe5d7d --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/com/googlecode/owasp-java-html-sanitizer/main/module.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml index e85db0e5b51..055f30fa963 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml @@ -44,6 +44,8 @@ + + diff --git a/pom.xml b/pom.xml index d861abcfb26..ed7fafe996f 100755 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ 1.0.4.Final 1.2.17 3.0.26.Final + 20180219.1 1.7.22 2.21 2.2.11 @@ -370,6 +371,11 @@ ${log4j.version} test + + com.googlecode.owasp-java-html-sanitizer + owasp-java-html-sanitizer + ${owasp.html.sanitizer.version} + junit junit diff --git a/prod-arguments.json b/prod-arguments.json index ae500abba51..cd9828b0712 100644 --- a/prod-arguments.json +++ b/prod-arguments.json @@ -16,6 +16,7 @@ "dependencyExclusion.io.undertow:*@*": "1.4.18.SP2-redhat-1", "dependencyExclusion.org.wildfly.security:*@*": "1.1.8.Final-redhat-1", "dependencyExclusion.org.freemarker:freemarker@*": "2.3.26.incubating-redhat-1", + "dependencyExclusion.com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer@*": "20180219.1.0.redhat-1", "dependencyExclusion.org.liquibase:liquibase-core@*": "$COMMONCFG_LIQUIBASE_3_4_1", "dependencyExclusion.org.twitter4j:twitter4j-core@*": "$COMMONCFG_TWITTER4J_4_0_4", "dependencyExclusion.com.google.zxing:core@*": "$COMMONCFG_ZXING_3_2_1", diff --git a/services/pom.xml b/services/pom.xml index 77f35970270..82d173e72fc 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -136,6 +136,10 @@ org.jboss.resteasy resteasy-multipart-provider + + com.googlecode.owasp-java-html-sanitizer + owasp-java-html-sanitizer + com.fasterxml.jackson.core jackson-core diff --git a/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java b/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java index 7415bf1cc97..660e35a0758 100755 --- a/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java +++ b/services/src/main/java/org/keycloak/theme/FreeMarkerUtil.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.net.URL; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** @@ -35,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap; public class FreeMarkerUtil { private ConcurrentHashMap cache; + private final KeycloakSanitizerMethod kcSanitizeMethod = new KeycloakSanitizerMethod(); public FreeMarkerUtil() { if (Config.scope("theme").getBoolean("cacheTemplates", true)) { @@ -43,6 +45,10 @@ public class FreeMarkerUtil { } public String processTemplate(Object data, String templateName, Theme theme) throws FreeMarkerException { + if (data instanceof Map) { + ((Map)data).put("kcSanitize", kcSanitizeMethod); + } + try { Template template; cache = null; diff --git a/services/src/main/java/org/keycloak/theme/KeycloakSanitizerMethod.java b/services/src/main/java/org/keycloak/theme/KeycloakSanitizerMethod.java new file mode 100644 index 00000000000..f893ff4f68b --- /dev/null +++ b/services/src/main/java/org/keycloak/theme/KeycloakSanitizerMethod.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.theme; + +import freemarker.template.TemplateMethodModelEx; +import freemarker.template.TemplateModelException; + +import java.util.List; +import org.owasp.html.PolicyFactory; + +/** + * Allows sanitizing of html that uses Freemarker ?no_esc. This way, html + * can be allowed but it is still cleaned up for safety. Tags and attributes + * deemed unsafe will be stripped out. + */ +public class KeycloakSanitizerMethod implements TemplateMethodModelEx { + + private static final PolicyFactory KEYCLOAK_POLICY = KeycloakSanitizerPolicy.POLICY_DEFINITION; + + @Override + public Object exec(List list) throws TemplateModelException { + if ((list.isEmpty()) || (list.get(0) == null)) { + throw new NullPointerException("Can not escape null value."); + } + + String html = list.get(0).toString(); + String sanitized = KEYCLOAK_POLICY.sanitize(html); + + return sanitized; + } + +} diff --git a/services/src/main/java/org/keycloak/theme/KeycloakSanitizerPolicy.java b/services/src/main/java/org/keycloak/theme/KeycloakSanitizerPolicy.java new file mode 100644 index 00000000000..bb8a08eeb3f --- /dev/null +++ b/services/src/main/java/org/keycloak/theme/KeycloakSanitizerPolicy.java @@ -0,0 +1,177 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.theme; + +import java.util.regex.Pattern; + +import org.owasp.html.HtmlPolicyBuilder; +import org.owasp.html.PolicyFactory; + +import com.google.common.base.Predicate; + +/** + * Based on the EbayPolicyExample in owasp java-html-sanitizer. + * + */ +public class KeycloakSanitizerPolicy { + + // Some common regular expression definitions. + + // The 16 colors defined by the HTML Spec (also used by the CSS Spec) + private static final Pattern COLOR_NAME = Pattern.compile( + "(?:aqua|black|blue|fuchsia|gray|grey|green|lime|maroon|navy|olive|purple" + + "|red|silver|teal|white|yellow)"); + + // HTML/CSS Spec allows 3 or 6 digit hex to specify color + private static final Pattern COLOR_CODE = Pattern.compile( + "(?:#(?:[0-9a-fA-F]{3}(?:[0-9a-fA-F]{3})?))"); + + private static final Pattern NUMBER_OR_PERCENT = Pattern.compile( + "[0-9]+%?"); + private static final Pattern PARAGRAPH = Pattern.compile( + "(?:[\\p{L}\\p{N},'\\.\\s\\-_\\(\\)]|&[0-9]{2};)*"); + private static final Pattern HTML_ID = Pattern.compile( + "[a-zA-Z0-9\\:\\-_\\.]+"); + // force non-empty with a '+' at the end instead of '*' + private static final Pattern HTML_TITLE = Pattern.compile( + "[\\p{L}\\p{N}\\s\\-_',:\\[\\]!\\./\\\\\\(\\)&]*"); + private static final Pattern HTML_CLASS = Pattern.compile( + "[a-zA-Z0-9\\s,\\-_]+"); + + private static final Pattern ONSITE_URL = Pattern.compile( + "(?:[\\p{L}\\p{N}\\\\\\.\\#@\\$%\\+&;\\-_~,\\?=/!]+|\\#(\\w)+)"); + private static final Pattern OFFSITE_URL = Pattern.compile( + "\\s*(?:(?:ht|f)tps?://|mailto:)[\\p{L}\\p{N}]" + + "[\\p{L}\\p{N}\\p{Zs}\\.\\#@\\$%\\+&;:\\-_~,\\?=/!\\(\\)]*+\\s*"); + + private static final Pattern NUMBER = Pattern.compile( + "[+-]?(?:(?:[0-9]+(?:\\.[0-9]*)?)|\\.[0-9]+)"); + + private static final Pattern NAME = Pattern.compile("[a-zA-Z0-9\\-_\\$]+"); + + private static final Pattern ALIGN = Pattern.compile( + "(?i)center|left|right|justify|char"); + + private static final Pattern VALIGN = Pattern.compile( + "(?i)baseline|bottom|middle|top"); + + private static final Predicate COLOR_NAME_OR_COLOR_CODE + = matchesEither(COLOR_NAME, COLOR_CODE); + + private static final Predicate ONSITE_OR_OFFSITE_URL + = matchesEither(ONSITE_URL, OFFSITE_URL); + + private static final Pattern HISTORY_BACK = Pattern.compile( + "(?:javascript:)?\\Qhistory.go(-1)\\E"); + + private static final Pattern ONE_CHAR = Pattern.compile( + ".?", Pattern.DOTALL); + + + public static final PolicyFactory POLICY_DEFINITION = new HtmlPolicyBuilder() + .allowWithoutAttributes("span") // this is added to ebay example to allow span without attributes + .allowAttributes("id").matching(HTML_ID).globally() + .allowAttributes("class").matching(HTML_CLASS).globally() + .allowAttributes("lang").matching(Pattern.compile("[a-zA-Z]{2,20}")) + .globally() + .allowAttributes("title").matching(HTML_TITLE).globally() + .allowStyling() + .allowAttributes("align").matching(ALIGN).onElements("p") + .allowAttributes("for").matching(HTML_ID).onElements("label") + .allowAttributes("color").matching(COLOR_NAME_OR_COLOR_CODE) + .onElements("font") + .allowAttributes("face") + .matching(Pattern.compile("[\\w;, \\-]+")) + .onElements("font") + .allowAttributes("size").matching(NUMBER).onElements("font") + .allowAttributes("href").matching(ONSITE_OR_OFFSITE_URL) + .onElements("a") + .allowStandardUrlProtocols() + .allowAttributes("nohref").onElements("a") + .allowAttributes("name").matching(NAME).onElements("a") + .allowAttributes( + "onfocus", "onblur", "onclick", "onmousedown", "onmouseup") + .matching(HISTORY_BACK).onElements("a") + .requireRelNofollowOnLinks() + .allowAttributes("src").matching(ONSITE_OR_OFFSITE_URL) + .onElements("img") + .allowAttributes("name").matching(NAME) + .onElements("img") + .allowAttributes("alt").matching(PARAGRAPH) + .onElements("img") + .allowAttributes("border", "hspace", "vspace").matching(NUMBER) + .onElements("img") + .allowAttributes("border", "cellpadding", "cellspacing") + .matching(NUMBER).onElements("table") + .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE) + .onElements("table") + .allowAttributes("background").matching(ONSITE_URL) + .onElements("table") + .allowAttributes("align").matching(ALIGN) + .onElements("table") + .allowAttributes("noresize").matching(Pattern.compile("(?i)noresize")) + .onElements("table") + .allowAttributes("background").matching(ONSITE_URL) + .onElements("td", "th", "tr") + .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE) + .onElements("td", "th") + .allowAttributes("abbr").matching(PARAGRAPH) + .onElements("td", "th") + .allowAttributes("axis", "headers").matching(NAME) + .onElements("td", "th") + .allowAttributes("scope") + .matching(Pattern.compile("(?i)(?:row|col)(?:group)?")) + .onElements("td", "th") + .allowAttributes("nowrap") + .onElements("td", "th") + .allowAttributes("height", "width").matching(NUMBER_OR_PERCENT) + .onElements("table", "td", "th", "tr", "img") + .allowAttributes("align").matching(ALIGN) + .onElements("thead", "tbody", "tfoot", "img", + "td", "th", "tr", "colgroup", "col") + .allowAttributes("valign").matching(VALIGN) + .onElements("thead", "tbody", "tfoot", + "td", "th", "tr", "colgroup", "col") + .allowAttributes("charoff").matching(NUMBER_OR_PERCENT) + .onElements("td", "th", "tr", "colgroup", "col", + "thead", "tbody", "tfoot") + .allowAttributes("char").matching(ONE_CHAR) + .onElements("td", "th", "tr", "colgroup", "col", + "thead", "tbody", "tfoot") + .allowAttributes("colspan", "rowspan").matching(NUMBER) + .onElements("td", "th") + .allowAttributes("span", "width").matching(NUMBER_OR_PERCENT) + .onElements("colgroup", "col") + .allowElements( + "a", "label", "noscript", "h1", "h2", "h3", "h4", "h5", "h6", + "p", "i", "b", "u", "strong", "em", "small", "big", "pre", "code", + "cite", "samp", "sub", "sup", "strike", "center", "blockquote", + "hr", "br", "col", "font", "map", "span", "div", "img", + "ul", "ol", "li", "dd", "dt", "dl", "tbody", "thead", "tfoot", + "table", "td", "th", "tr", "colgroup", "fieldset", "legend") + .toFactory(); + + private static Predicate matchesEither( + final Pattern a, final Pattern b) { + return new Predicate() { + public boolean apply(String s) { + return a.matcher(s).matches()|| b.matcher(s).matches(); + } + }; + } +} diff --git a/services/src/test/java/org/keycloak/theme/KeycloakSanitizerTest.java b/services/src/test/java/org/keycloak/theme/KeycloakSanitizerTest.java new file mode 100644 index 00000000000..9e503f5b4a2 --- /dev/null +++ b/services/src/test/java/org/keycloak/theme/KeycloakSanitizerTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2018 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.theme; + +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * Test the KeycloakEscape utility. + * + * @author Stan Silvert + */ +public class KeycloakSanitizerTest { + private KeycloakSanitizerMethod kcEscape = new KeycloakSanitizerMethod(); + + @Test + public void testEscapes() throws Exception { + List html = new ArrayList(); + + html.add("
Keycloak
"); + String expectedResult = "
Keycloak
"; + assertResult(expectedResult, html); + + html.set(0, "

Foo

"); + expectedResult = "

Foo

"; + assertResult(expectedResult, html); + + html.set(0, "
Keycloak
"); + expectedResult = "
Keycloak
"; + assertResult(expectedResult, html); + + html.set(0, null); + expectedResult = null; + try { + assertResult(expectedResult, html); + fail("Expected NPE"); + } catch (NullPointerException npe) {} + + html.set(0, ""); + expectedResult = ""; + assertResult(expectedResult, html); + } + + private void assertResult(String expectedResult, List html) throws Exception { + String result = kcEscape.exec(html).toString(); + assertEquals(expectedResult, result); + } + +} + diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java index 7fa362da419..ffa7f309150 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailUtils.java @@ -47,7 +47,13 @@ public class MailUtils { public static String getPasswordResetEmailLink(EmailBody body) throws IOException, MessagingException { final String textChangePwdUrl = getLink(body.getText()); - final String htmlChangePwdUrl = getLink(body.getHtml()); + String htmlChangePwdUrl = getLink(body.getHtml()); + + // undo changes that may have been made by html sanitizer + htmlChangePwdUrl = htmlChangePwdUrl.replace("=", "="); + htmlChangePwdUrl = htmlChangePwdUrl.replace("..", "."); + htmlChangePwdUrl = htmlChangePwdUrl.replace("&", "&"); + assertEquals(htmlChangePwdUrl, textChangePwdUrl); return htmlChangePwdUrl; diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java index a6bda732f6d..fa927b22978 100755 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java @@ -357,7 +357,13 @@ public abstract class AbstractIdentityProviderTest { final String htmlBody = (String) multipart.getBodyPart(1).getContent(); - final String htmlChangePwdUrl = MailUtil.getLink(htmlBody); + String htmlChangePwdUrl = MailUtil.getLink(htmlBody); + + // undo changes that may have been made by html sanitizer + htmlChangePwdUrl = htmlChangePwdUrl.replace("=", "="); + htmlChangePwdUrl = htmlChangePwdUrl.replace("..", "."); + htmlChangePwdUrl = htmlChangePwdUrl.replace("&", "&"); + assertEquals(htmlChangePwdUrl, textVerificationUrl); return htmlChangePwdUrl; diff --git a/themes/src/main/resources/theme/base/account/account.ftl b/themes/src/main/resources/theme/base/account/account.ftl index 2fc3bdb33f0..fb9d222878f 100755 --- a/themes/src/main/resources/theme/base/account/account.ftl +++ b/themes/src/main/resources/theme/base/account/account.ftl @@ -59,7 +59,7 @@
diff --git a/themes/src/main/resources/theme/base/account/template.ftl b/themes/src/main/resources/theme/base/account/template.ftl index e49c664a6e1..d372ad5b176 100644 --- a/themes/src/main/resources/theme/base/account/template.ftl +++ b/themes/src/main/resources/theme/base/account/template.ftl @@ -70,7 +70,7 @@
<#if message.type=='success' > <#if message.type=='error' > - ${message.summary?no_esc} + ${kcSanitize(message.summary)?no_esc}
diff --git a/themes/src/main/resources/theme/base/email/html/email-test.ftl b/themes/src/main/resources/theme/base/email/html/email-test.ftl index d5d18b73cd1..3a522728f46 100644 --- a/themes/src/main/resources/theme/base/email/html/email-test.ftl +++ b/themes/src/main/resources/theme/base/email/html/email-test.ftl @@ -1,5 +1,5 @@ -${msg("emailTestBodyHtml",realmName)?no_esc} +${kcSanitize(msg("emailTestBodyHtml",realmName))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl b/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl index b4a01c9eee9..66e89258743 100644 --- a/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl +++ b/themes/src/main/resources/theme/base/email/html/email-verification-with-code.ftl @@ -1,5 +1,5 @@ -${msg("emailVerificationBodyCodeHtml",code)?no_esc} +${kcSanitize(msg("emailVerificationBodyCodeHtml",code))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/email-verification.ftl b/themes/src/main/resources/theme/base/email/html/email-verification.ftl index bd371d9db1f..dacabd2525e 100644 --- a/themes/src/main/resources/theme/base/email/html/email-verification.ftl +++ b/themes/src/main/resources/theme/base/email/html/email-verification.ftl @@ -1,5 +1,5 @@ -${msg("emailVerificationBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))?no_esc} +${kcSanitize(msg("emailVerificationBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration)))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/event-login_error.ftl b/themes/src/main/resources/theme/base/email/html/event-login_error.ftl index 68ba81d6cb7..022c024dae4 100644 --- a/themes/src/main/resources/theme/base/email/html/event-login_error.ftl +++ b/themes/src/main/resources/theme/base/email/html/event-login_error.ftl @@ -1,5 +1,5 @@ -${msg("eventLoginErrorBodyHtml",event.date,event.ipAddress)?no_esc} +${kcSanitize(msg("eventLoginErrorBodyHtml",event.date,event.ipAddress))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/event-remove_totp.ftl b/themes/src/main/resources/theme/base/email/html/event-remove_totp.ftl index e5ddadbac85..9a56ed387c6 100644 --- a/themes/src/main/resources/theme/base/email/html/event-remove_totp.ftl +++ b/themes/src/main/resources/theme/base/email/html/event-remove_totp.ftl @@ -1,5 +1,5 @@ -${msg("eventRemoveTotpBodyHtml",event.date, event.ipAddress)?no_esc} +${kcSanitize(msg("eventRemoveTotpBodyHtml",event.date, event.ipAddress))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/event-update_password.ftl b/themes/src/main/resources/theme/base/email/html/event-update_password.ftl index dd5fda31ae1..27825c72df0 100644 --- a/themes/src/main/resources/theme/base/email/html/event-update_password.ftl +++ b/themes/src/main/resources/theme/base/email/html/event-update_password.ftl @@ -1,5 +1,5 @@ -${msg("eventUpdatePasswordBodyHtml",event.date, event.ipAddress)?no_esc} +${kcSanitize(msg("eventUpdatePasswordBodyHtml",event.date, event.ipAddress))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/event-update_totp.ftl b/themes/src/main/resources/theme/base/email/html/event-update_totp.ftl index 050abab7ea0..3ed37c38b6e 100644 --- a/themes/src/main/resources/theme/base/email/html/event-update_totp.ftl +++ b/themes/src/main/resources/theme/base/email/html/event-update_totp.ftl @@ -1,5 +1,5 @@ -${msg("eventUpdateTotpBodyHtml",event.date, event.ipAddress)?no_esc} +${kcSanitize(msg("eventUpdateTotpBodyHtml",event.date, event.ipAddress))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/executeActions.ftl b/themes/src/main/resources/theme/base/email/html/executeActions.ftl index 6510dfc1160..4c837bcbb64 100755 --- a/themes/src/main/resources/theme/base/email/html/executeActions.ftl +++ b/themes/src/main/resources/theme/base/email/html/executeActions.ftl @@ -4,6 +4,6 @@ -${msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration))?no_esc} +${kcSanitize(msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration)))?no_esc} diff --git a/themes/src/main/resources/theme/base/email/html/identity-provider-link.ftl b/themes/src/main/resources/theme/base/email/html/identity-provider-link.ftl index fff38fc330e..8b67968e17e 100644 --- a/themes/src/main/resources/theme/base/email/html/identity-provider-link.ftl +++ b/themes/src/main/resources/theme/base/email/html/identity-provider-link.ftl @@ -1,5 +1,5 @@ -${msg("identityProviderLinkBodyHtml", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration, linkExpirationFormatter(linkExpiration))?no_esc} +${kcSanitize(msg("identityProviderLinkBodyHtml", identityProviderAlias, realmName, identityProviderContext.username, link, linkExpiration, linkExpirationFormatter(linkExpiration)))?no_esc} \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/email/html/password-reset.ftl b/themes/src/main/resources/theme/base/email/html/password-reset.ftl index e56ae1ea897..b2840b63002 100755 --- a/themes/src/main/resources/theme/base/email/html/password-reset.ftl +++ b/themes/src/main/resources/theme/base/email/html/password-reset.ftl @@ -1,5 +1,5 @@ -${msg("passwordResetBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))?no_esc} +${kcSanitize(msg("passwordResetBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration)))?no_esc} \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/login/error.ftl b/themes/src/main/resources/theme/base/login/error.ftl index 23468c56d7d..f237f7e0b46 100755 --- a/themes/src/main/resources/theme/base/login/error.ftl +++ b/themes/src/main/resources/theme/base/login/error.ftl @@ -6,7 +6,7 @@

${message.summary}

<#if client?? && client.baseUrl?has_content> -

${msg("backToApplication")?no_esc}

+

${kcSanitize(msg("backToApplication"))?no_esc}

diff --git a/themes/src/main/resources/theme/base/login/info.ftl b/themes/src/main/resources/theme/base/login/info.ftl index 8eff9c36220..fc3a1aa310c 100755 --- a/themes/src/main/resources/theme/base/login/info.ftl +++ b/themes/src/main/resources/theme/base/login/info.ftl @@ -12,11 +12,11 @@ <#if skipLink??> <#else> <#if pageRedirectUri??> -

${msg("backToApplication")?no_esc}

+

${kcSanitize(msg("backToApplication"))?no_esc}

<#elseif actionUri??> -

${msg("proceedWithAction")?no_esc}

+

${kcSanitize(msg("proceedWithAction"))?no_esc}

<#elseif client.baseUrl??> -

${msg("backToApplication")?no_esc}

+

${kcSanitize(msg("backToApplication"))?no_esc}

diff --git a/themes/src/main/resources/theme/base/login/login-reset-password.ftl b/themes/src/main/resources/theme/base/login/login-reset-password.ftl index 79481bce73d..720c274ebfa 100755 --- a/themes/src/main/resources/theme/base/login/login-reset-password.ftl +++ b/themes/src/main/resources/theme/base/login/login-reset-password.ftl @@ -16,7 +16,7 @@
diff --git a/themes/src/main/resources/theme/base/login/register.ftl b/themes/src/main/resources/theme/base/login/register.ftl index fcf8aa92e6f..c9378dac847 100755 --- a/themes/src/main/resources/theme/base/login/register.ftl +++ b/themes/src/main/resources/theme/base/login/register.ftl @@ -73,7 +73,7 @@
diff --git a/themes/src/main/resources/theme/base/login/template.ftl b/themes/src/main/resources/theme/base/login/template.ftl index a7130715c69..a09fd8f95c3 100644 --- a/themes/src/main/resources/theme/base/login/template.ftl +++ b/themes/src/main/resources/theme/base/login/template.ftl @@ -34,7 +34,7 @@
-
${msg("loginTitleHtml",(realm.displayNameHtml!''))?no_esc}
+
${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}
@@ -63,7 +63,7 @@ <#if message.type = 'warning'> <#if message.type = 'error'> <#if message.type = 'info'> - +
diff --git a/themes/src/main/resources/theme/base/login/terms.ftl b/themes/src/main/resources/theme/base/login/terms.ftl index daed9ecf7f7..687b192c94f 100755 --- a/themes/src/main/resources/theme/base/login/terms.ftl +++ b/themes/src/main/resources/theme/base/login/terms.ftl @@ -4,7 +4,7 @@ ${msg("termsTitle")} <#elseif section = "form">
- ${msg("termsText")?no_esc} + ${kcSanitize(msg("termsText"))?no_esc}