KEYCLOAK-7479: Sanitize

This commit is contained in:
ssilvert@win.redhat.com 2018-07-31 14:51:42 -04:00 committed by Stan Silvert
parent f57cc3a9c0
commit e7e15652cf
30 changed files with 383 additions and 23 deletions

View file

@ -72,6 +72,16 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>com.thoughtworks.xstream</groupId> <groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId> <artifactId>xstream</artifactId>

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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.
-->
<module xmlns="urn:jboss:module:1.3" name="com.googlecode.owasp-java-html-sanitizer">
<resources>
<artifact name="${com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer}"/>
</resources>
<dependencies>
<module name="com.google.guava"/>
</dependencies>
</module>

View file

@ -44,6 +44,8 @@
<module name="org.keycloak.keycloak-authz-policy-common" services="import"/> <module name="org.keycloak.keycloak-authz-policy-common" services="import"/>
<module name="org.keycloak.keycloak-authz-policy-drools" services="import"/> <module name="org.keycloak.keycloak-authz-policy-drools" services="import"/>
<module name="com.googlecode.owasp-java-html-sanitizer"/>
<module name="com.google.guava"/>
<module name="org.freemarker"/> <module name="org.freemarker"/>
<module name="javax.ws.rs.api"/> <module name="javax.ws.rs.api"/>
<module name="javax.mail.api"/> <module name="javax.mail.api"/>

View file

@ -77,6 +77,7 @@
<jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>1.0.4.Final</jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version> <jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>1.0.4.Final</jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>
<log4j.version>1.2.17</log4j.version> <log4j.version>1.2.17</log4j.version>
<resteasy.version>3.0.26.Final</resteasy.version> <resteasy.version>3.0.26.Final</resteasy.version>
<owasp.html.sanitizer.version>20180219.1</owasp.html.sanitizer.version>
<slf4j.version>1.7.22</slf4j.version> <slf4j.version>1.7.22</slf4j.version>
<sun.istack.version>2.21</sun.istack.version> <sun.istack.version>2.21</sun.istack.version>
<sun.jaxb.version>2.2.11</sun.jaxb.version> <sun.jaxb.version>2.2.11</sun.jaxb.version>
@ -370,6 +371,11 @@
<version>${log4j.version}</version> <version>${log4j.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
<version>${owasp.html.sanitizer.version}</version>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View file

@ -16,6 +16,7 @@
"dependencyExclusion.io.undertow:*@*": "1.4.18.SP2-redhat-1", "dependencyExclusion.io.undertow:*@*": "1.4.18.SP2-redhat-1",
"dependencyExclusion.org.wildfly.security:*@*": "1.1.8.Final-redhat-1", "dependencyExclusion.org.wildfly.security:*@*": "1.1.8.Final-redhat-1",
"dependencyExclusion.org.freemarker:freemarker@*": "2.3.26.incubating-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.liquibase:liquibase-core@*": "$COMMONCFG_LIQUIBASE_3_4_1",
"dependencyExclusion.org.twitter4j:twitter4j-core@*": "$COMMONCFG_TWITTER4J_4_0_4", "dependencyExclusion.org.twitter4j:twitter4j-core@*": "$COMMONCFG_TWITTER4J_4_0_4",
"dependencyExclusion.com.google.zxing:core@*": "$COMMONCFG_ZXING_3_2_1", "dependencyExclusion.com.google.zxing:core@*": "$COMMONCFG_ZXING_3_2_1",

View file

@ -136,6 +136,10 @@
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId> <artifactId>resteasy-multipart-provider</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
<artifactId>owasp-java-html-sanitizer</artifactId>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId> <artifactId>jackson-core</artifactId>

View file

@ -27,6 +27,7 @@ import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.net.URL; import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
@ -35,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class FreeMarkerUtil { public class FreeMarkerUtil {
private ConcurrentHashMap<String, Template> cache; private ConcurrentHashMap<String, Template> cache;
private final KeycloakSanitizerMethod kcSanitizeMethod = new KeycloakSanitizerMethod();
public FreeMarkerUtil() { public FreeMarkerUtil() {
if (Config.scope("theme").getBoolean("cacheTemplates", true)) { 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 { public String processTemplate(Object data, String templateName, Theme theme) throws FreeMarkerException {
if (data instanceof Map) {
((Map)data).put("kcSanitize", kcSanitizeMethod);
}
try { try {
Template template; Template template;
cache = null; cache = null;

View file

@ -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;
}
}

View file

@ -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<String> COLOR_NAME_OR_COLOR_CODE
= matchesEither(COLOR_NAME, COLOR_CODE);
private static final Predicate<String> 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<String> matchesEither(
final Pattern a, final Pattern b) {
return new Predicate<String>() {
public boolean apply(String s) {
return a.matcher(s).matches()|| b.matcher(s).matches();
}
};
}
}

View file

@ -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<String> html = new ArrayList();
html.add("<div class=\"kc-logo-text\"><script>alert('foo');</script><span>Keycloak</span></div>");
String expectedResult = "<div class=\"kc-logo-text\"><span>Keycloak</span></div>";
assertResult(expectedResult, html);
html.set(0, "<h1>Foo</h1>");
expectedResult = "<h1>Foo</h1>";
assertResult(expectedResult, html);
html.set(0, "<div class=\"kc-logo-text\"><span>Keycloak</span></div><svg onload=alert(document.cookie);>");
expectedResult = "<div class=\"kc-logo-text\"><span>Keycloak</span></div>";
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<String> html) throws Exception {
String result = kcEscape.exec(html).toString();
assertEquals(expectedResult, result);
}
}

View file

@ -47,7 +47,13 @@ public class MailUtils {
public static String getPasswordResetEmailLink(EmailBody body) throws IOException, MessagingException { public static String getPasswordResetEmailLink(EmailBody body) throws IOException, MessagingException {
final String textChangePwdUrl = getLink(body.getText()); 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("&#61;", "=");
htmlChangePwdUrl = htmlChangePwdUrl.replace("..", ".");
htmlChangePwdUrl = htmlChangePwdUrl.replace("&amp;", "&");
assertEquals(htmlChangePwdUrl, textChangePwdUrl); assertEquals(htmlChangePwdUrl, textChangePwdUrl);
return htmlChangePwdUrl; return htmlChangePwdUrl;

View file

@ -357,7 +357,13 @@ public abstract class AbstractIdentityProviderTest {
final String htmlBody = (String) multipart.getBodyPart(1).getContent(); 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("&#61;", "=");
htmlChangePwdUrl = htmlChangePwdUrl.replace("..", ".");
htmlChangePwdUrl = htmlChangePwdUrl.replace("&amp;", "&");
assertEquals(htmlChangePwdUrl, textVerificationUrl); assertEquals(htmlChangePwdUrl, textVerificationUrl);
return htmlChangePwdUrl; return htmlChangePwdUrl;

View file

@ -59,7 +59,7 @@
<div class="form-group"> <div class="form-group">
<div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit"> <div id="kc-form-buttons" class="col-md-offset-2 col-md-10 submit">
<div class=""> <div class="">
<#if url.referrerURI??><a href="${url.referrerURI}">${msg("backToApplication")?no_esc}/a></#if> <#if url.referrerURI??><a href="${url.referrerURI}">${kcSanitize(msg("backToApplication")?no_esc)}</a></#if>
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Save">${msg("doSave")}</button> <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Save">${msg("doSave")}</button>
<button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Cancel">${msg("doCancel")}</button> <button type="submit" class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonLargeClass!}" name="submitAction" value="Cancel">${msg("doCancel")}</button>
</div> </div>

View file

@ -70,7 +70,7 @@
<div class="alert alert-${message.type}"> <div class="alert alert-${message.type}">
<#if message.type=='success' ><span class="pficon pficon-ok"></span></#if> <#if message.type=='success' ><span class="pficon pficon-ok"></span></#if>
<#if message.type=='error' ><span class="pficon pficon-error-octagon"></span><span class="pficon pficon-error-exclamation"></span></#if> <#if message.type=='error' ><span class="pficon pficon-error-octagon"></span><span class="pficon pficon-error-exclamation"></span></#if>
${message.summary?no_esc} ${kcSanitize(message.summary)?no_esc}
</div> </div>
</#if> </#if>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("emailTestBodyHtml",realmName)?no_esc} ${kcSanitize(msg("emailTestBodyHtml",realmName))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("emailVerificationBodyCodeHtml",code)?no_esc} ${kcSanitize(msg("emailVerificationBodyCodeHtml",code))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("emailVerificationBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))?no_esc} ${kcSanitize(msg("emailVerificationBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration)))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("eventLoginErrorBodyHtml",event.date,event.ipAddress)?no_esc} ${kcSanitize(msg("eventLoginErrorBodyHtml",event.date,event.ipAddress))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("eventRemoveTotpBodyHtml",event.date, event.ipAddress)?no_esc} ${kcSanitize(msg("eventRemoveTotpBodyHtml",event.date, event.ipAddress))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("eventUpdatePasswordBodyHtml",event.date, event.ipAddress)?no_esc} ${kcSanitize(msg("eventUpdatePasswordBodyHtml",event.date, event.ipAddress))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("eventUpdateTotpBodyHtml",event.date, event.ipAddress)?no_esc} ${kcSanitize(msg("eventUpdateTotpBodyHtml",event.date, event.ipAddress))?no_esc}
</body> </body>
</html> </html>

View file

@ -4,6 +4,6 @@
<html> <html>
<body> <body>
${msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration))?no_esc} ${kcSanitize(msg("executeActionsBodyHtml",link, linkExpiration, realmName, requiredActionsText, linkExpirationFormatter(linkExpiration)))?no_esc}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${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}
</body> </body>
</html> </html>

View file

@ -1,5 +1,5 @@
<html> <html>
<body> <body>
${msg("passwordResetBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration))?no_esc} ${kcSanitize(msg("passwordResetBodyHtml",link, linkExpiration, realmName, linkExpirationFormatter(linkExpiration)))?no_esc}
</body> </body>
</html> </html>

View file

@ -6,7 +6,7 @@
<div id="kc-error-message"> <div id="kc-error-message">
<p class="instruction">${message.summary}</p> <p class="instruction">${message.summary}</p>
<#if client?? && client.baseUrl?has_content> <#if client?? && client.baseUrl?has_content>
<p><a id="backToApplication" href="${client.baseUrl}">${msg("backToApplication")?no_esc}</a></p> <p><a id="backToApplication" href="${client.baseUrl}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
</#if> </#if>
</div> </div>
</#if> </#if>

View file

@ -12,11 +12,11 @@
<#if skipLink??> <#if skipLink??>
<#else> <#else>
<#if pageRedirectUri??> <#if pageRedirectUri??>
<p><a href="${pageRedirectUri}">${msg("backToApplication")?no_esc}</a></p> <p><a href="${pageRedirectUri}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
<#elseif actionUri??> <#elseif actionUri??>
<p><a href="${actionUri}">${msg("proceedWithAction")?no_esc}</a></p> <p><a href="${actionUri}">${kcSanitize(msg("proceedWithAction"))?no_esc}</a></p>
<#elseif client.baseUrl??> <#elseif client.baseUrl??>
<p><a href="${client.baseUrl}">${msg("backToApplication")?no_esc}</a></p> <p><a href="${client.baseUrl}">${kcSanitize(msg("backToApplication"))?no_esc}</a></p>
</#if> </#if>
</#if> </#if>
</div> </div>

View file

@ -16,7 +16,7 @@
<div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}"> <div class="${properties.kcFormGroupClass!} ${properties.kcFormSettingClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}"> <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}"> <div class="${properties.kcFormOptionsWrapperClass!}">
<span><a href="${url.loginUrl}">${msg("backToLogin")?no_esc}</a></span> <span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
</div> </div>
</div> </div>

View file

@ -73,7 +73,7 @@
<div class="${properties.kcFormGroupClass!}"> <div class="${properties.kcFormGroupClass!}">
<div id="kc-form-options" class="${properties.kcFormOptionsClass!}"> <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
<div class="${properties.kcFormOptionsWrapperClass!}"> <div class="${properties.kcFormOptionsWrapperClass!}">
<span><a href="${url.loginUrl}">${msg("backToLogin")?no_esc}</a></span> <span><a href="${url.loginUrl}">${kcSanitize(msg("backToLogin"))?no_esc}</a></span>
</div> </div>
</div> </div>

View file

@ -34,7 +34,7 @@
<body class="${properties.kcBodyClass!}"> <body class="${properties.kcBodyClass!}">
<div class="${properties.kcLoginClass!}"> <div class="${properties.kcLoginClass!}">
<div id="kc-header" class="${properties.kcHeaderClass!}"> <div id="kc-header" class="${properties.kcHeaderClass!}">
<div id="kc-header-wrapper" class="${properties.kcHeaderWrapperClass!}">${msg("loginTitleHtml",(realm.displayNameHtml!''))?no_esc}</div> <div id="kc-header-wrapper" class="${properties.kcHeaderWrapperClass!}">${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}</div>
</div> </div>
<div class="${properties.kcFormCardClass!} <#if displayWide>${properties.kcFormCardAccountClass!}</#if>"> <div class="${properties.kcFormCardClass!} <#if displayWide>${properties.kcFormCardAccountClass!}</#if>">
<header class="${properties.kcFormHeaderClass!}"> <header class="${properties.kcFormHeaderClass!}">
@ -63,7 +63,7 @@
<#if message.type = 'warning'><span class="${properties.kcFeedbackWarningIcon!}"></span></#if> <#if message.type = 'warning'><span class="${properties.kcFeedbackWarningIcon!}"></span></#if>
<#if message.type = 'error'><span class="${properties.kcFeedbackErrorIcon!}"></span></#if> <#if message.type = 'error'><span class="${properties.kcFeedbackErrorIcon!}"></span></#if>
<#if message.type = 'info'><span class="${properties.kcFeedbackInfoIcon!}"></span></#if> <#if message.type = 'info'><span class="${properties.kcFeedbackInfoIcon!}"></span></#if>
<span class="kc-feedback-text">${message.summary?no_esc}</span> <span class="kc-feedback-text">${kcSanitize(message.summary)?no_esc}</span>
</div> </div>
</#if> </#if>

View file

@ -4,7 +4,7 @@
${msg("termsTitle")} ${msg("termsTitle")}
<#elseif section = "form"> <#elseif section = "form">
<div id="kc-terms-text"> <div id="kc-terms-text">
${msg("termsText")?no_esc} ${kcSanitize(msg("termsText"))?no_esc}
</div> </div>
<form class="form-actions" action="${url.loginAction}" method="POST"> <form class="form-actions" action="${url.loginAction}" method="POST">
<input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="accept" id="kc-accept" type="submit" value="${msg("doAccept")}"/> <input class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonLargeClass!}" name="accept" id="kc-accept" type="submit" value="${msg("doAccept")}"/>