diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java b/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java index 7e8de5c706..585c650960 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/CspSettings.java @@ -35,6 +35,7 @@ public interface CspSettings { String CSP_REPORT_HEADER = "Content-Security-Policy-Report-Only"; String OBJECT_SRC = "object-src"; String SCRIPT_SRC = "script-src"; + String STYLE_SRC = "style-src"; String BASE_URI = "base-uri"; String REPORT_URI = "report-uri"; String REPORT_TO = "report-to"; diff --git a/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java b/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java index 0343006f21..00945850cd 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java +++ b/core/src/main/java/org/apache/struts2/interceptor/csp/DefaultCspSettings.java @@ -110,6 +110,10 @@ protected String createPolicyFormat(String nonceValue) { .append(format(" 'nonce-%s' ", nonceValue)) .append(format("'%s' ", STRICT_DYNAMIC)) .append(format("%s %s; ", HTTP, HTTPS)) + .append(STYLE_SRC) + .append(format(" 'nonce-%s' ", nonceValue)) + .append(format("'%s' ", STRICT_DYNAMIC)) + .append(format("%s %s; ", HTTP, HTTPS)) .append(BASE_URI) .append(format(" '%s'; ", NONE)); diff --git a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java index 7bc8ab616d..9a0dcd52da 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java @@ -33,6 +33,8 @@ import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.url.QueryStringBuilder; +import org.apache.commons.text.StringEscapeUtils; + import java.io.IOException; import java.io.Serial; import java.net.MalformedURLException; @@ -248,7 +250,7 @@ protected void sendRedirect(HttpServletResponse response, String finalLocation) response.setStatus(statusCode); response.setHeader("Location", finalLocation); try { - response.getWriter().write(finalLocation); + response.getWriter().write(StringEscapeUtils.escapeHtml4(finalLocation)); } finally { response.getWriter().close(); } diff --git a/core/src/main/resources/template/html5/radiomap.ftl b/core/src/main/resources/template/html5/radiomap.ftl index 5826fe47ff..105e7e86dd 100644 --- a/core/src/main/resources/template/html5/radiomap.ftl +++ b/core/src/main/resources/template/html5/radiomap.ftl @@ -64,7 +64,7 @@ file for it's localized value. This is then used as a label --> <#if attributes.name?has_content> - name="${attributes.name?no_esc}"<#rt/> + name="${attributes.name?replace('"', '"')?no_esc}"<#rt/> id="${attributes.id}${itemKeyStr?replace(".", "_")}"<#rt/> <#if tag.contains(attributes.nameValue!'', itemKey)> diff --git a/core/src/main/resources/template/simple/radiomap.ftl b/core/src/main/resources/template/simple/radiomap.ftl index 524bc7f7f7..0fed41e019 100644 --- a/core/src/main/resources/template/simple/radiomap.ftl +++ b/core/src/main/resources/template/simple/radiomap.ftl @@ -63,7 +63,7 @@ <#if attributes.name?has_content> - name="${attributes.name?no_esc}"<#rt/> + name="${attributes.name?replace('"', '"')?no_esc}"<#rt/> id="${attributes.id}${itemKeyStr?replace(".", "_")}"<#rt/> <#if tag.contains(attributes.nameValue!'', itemKey)> diff --git a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java index 3a9f83348d..794aadf27f 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CspInterceptorTest.java @@ -240,23 +240,26 @@ public void checkHeader(String reportUri, boolean enforcingMode) { public void checkHeader(String reportUri, String reportTo, boolean enforcingMode) { String expectedCspHeader; if (Strings.isEmpty(reportUri)) { - expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; ", + expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s 'nonce-%s' '%s' %s %s; %s '%s'; ", CspSettings.OBJECT_SRC, CspSettings.NONE, CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, + CspSettings.STYLE_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, CspSettings.BASE_URI, CspSettings.NONE ); } else { if (Strings.isEmpty(reportTo)) { - expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; ", + expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; ", CspSettings.OBJECT_SRC, CspSettings.NONE, CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, + CspSettings.STYLE_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, CspSettings.BASE_URI, CspSettings.NONE, CspSettings.REPORT_URI, reportUri ); } else { - expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; %s %s; ", + expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s; %s %s; ", CspSettings.OBJECT_SRC, CspSettings.NONE, CspSettings.SCRIPT_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, + CspSettings.STYLE_SRC, session.getAttribute("nonce"), CspSettings.STRICT_DYNAMIC, CspSettings.HTTP, CspSettings.HTTPS, CspSettings.BASE_URI, CspSettings.NONE, CspSettings.REPORT_URI, reportUri, CspSettings.REPORT_TO, reportTo