Merge pull request #430 from salcho/post-ww-5083

WW-5084: Add Content Security Policy support to Struts
This commit is contained in:
Aleksandr Mashchenko
2020-08-30 23:37:16 +03:00
committed by GitHub
28 changed files with 2727 additions and 0 deletions
@@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.action;
import com.opensymphony.xwork2.ActionSupport;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import static org.apache.struts2.interceptor.csp.CspSettings.CSP_REPORT_TYPE;
/**
* An abstract Action that can be extended to process the incoming CSP violation reports. Performs
* necessary checks to extract the JSON string of the CSP report and make sure it's a valid report.
* Always returns a 204 response.
*
* Override the <code>processReport(String jsonCspReport)</code> method to customize how the action processes
* the CSP report. See {@link DefaultCspReportAction} for the default implementation.
*
* Add the action to the endpoint that is the <code>reportUri</code> in the {@link org.apache.struts2.interceptor.csp.CspInterceptor}
* to collect the reports.
*
* <pre>
* &lt;package name="csp-reports" namespace="/" extends="struts-default"&gt;
* &lt;action name="csp-reports" class="org.apache.struts2.action.DefaultCspReportAction"&gt;
* &lt;result type="httpheader"&gt;
* &lt;param name="statusCode">200&lt;/param&gt;
* &lt;/result&gt;
* &lt;/action&gt;
* &lt;/package&gt;
* </pre>
*
* @see DefaultCspReportAction
*/
public abstract class CspReportAction extends ActionSupport implements ServletRequestAware, ServletResponseAware {
private HttpServletRequest request;
@Override
public void withServletRequest(HttpServletRequest request) {
if (!isCspReportRequest(request)) {
return;
}
try {
BufferedReader reader = request.getReader();
String cspReport = reader.readLine();
processReport(cspReport);
} catch (IOException ignored) {
}
}
private boolean isCspReportRequest(HttpServletRequest request) {
if (!"POST".equals(request.getMethod()) || request.getContentLength() <= 0){
return false;
}
String contentType = request.getContentType();
return CSP_REPORT_TYPE.equals(contentType);
}
@Override
public void withServletResponse(HttpServletResponse response) {
response.setStatus(204);
}
abstract void processReport(String jsonCspReport);
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
public HttpServletRequest getServletRequest() {
return request;
}
}
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.action;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* The default implementation of {@link CspReportAction} that simply logs the JSON object
* that contains the details of the CSP violation.
*
* @see CspReportAction
*/
public class DefaultCspReportAction extends CspReportAction {
protected static final Logger LOG = LogManager.getLogger(DefaultCspReportAction.class);
@Override
void processReport(String jsonCspReport) {
LOG.error(jsonCspReport);
}
}
@@ -0,0 +1,175 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.components;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>
* Add nonce propagation feature to implement CSP in link tags
* </p>
*
* <p>
* The link tag allows the user to load external resources, most usually style sheets. External resources
* can inject malicious code and perform XSS and data injection attacks. The s:link tag includes a nonce
* attribute that is being randomly generated with each request and only allows links with the valid
* nonce value to be executed.
* </p>
*
* <p><b>Examples</b></p>
*
* <pre>
*
* &lt;s:link ... /&gt;
*
* </pre>
*
*/
@StrutsTag(name="link",
tldTagClass="org.apache.struts2.views.jsp.ui.LinkTag",
description="Link tag automatically adds nonces to link elements - should be used in combination with Struts' CSP Interceptor.",
allowDynamicAttributes=true)
public class Link extends UIBean{
private static final String TEMPLATE="link";
protected String href;
protected String hreflang;
protected String rel;
protected String media;
protected String referrerpolicy;
protected String sizes;
protected String crossorigin;
protected String type;
protected String as;
public Link(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@StrutsTagAttribute(description="HTML link href attribute")
public void setHref(String href) {
this.href = href;
}
@StrutsTagAttribute(description="HTML link hreflang attribute")
public void setHreflang(String hreflang) {
this.hreflang = hreflang;
}
@StrutsTagAttribute(description="HTML link rel attribute")
public void setRel(String rel) {
this.rel = rel;
}
@StrutsTagAttribute(description="HTML link sizes attribute")
public void setSizes(String sizes) {
this.sizes = sizes;
}
@StrutsTagAttribute(description="HTML link crossorigin attribute")
public void setCrossorigin(String crossorigin) {
this.crossorigin = crossorigin;
}
@StrutsTagAttribute(description="HTML link type attribute")
public void setType(String type) {
this.type = type;
}
@StrutsTagAttribute(description="HTML link as attribute")
public void setAs(String as) {
this.as = as;
}
@StrutsTagAttribute(description="HTML link media attribute")
public void setMedia(String media) {
this.media = media;
}
@StrutsTagAttribute(description="HTML link referrerpolicy attribute")
public void setReferrerpolicy(String referrerpolicy) {
this.referrerpolicy = referrerpolicy;
}
@Override
protected String getDefaultTemplate() {
return TEMPLATE;
}
@Override
protected void evaluateExtraParams() {
super.evaluateExtraParams();
if (href != null) {
addParameter("href", findString(href));
}
if (hreflang != null) {
addParameter("hreflang", findString(hreflang));
}
if (rel != null) {
addParameter("rel", findString(rel));
}
if (media != null) {
addParameter("media", findString(media));
}
if (referrerpolicy != null) {
addParameter("referrerpolicy", findString(referrerpolicy));
}
if (sizes != null) {
addParameter("sizes", findString(sizes));
}
if (crossorigin != null) {
addParameter("crossorigin", findString(crossorigin));
}
if (type != null) {
addParameter("type", findString(type));
}
if (as != null) {
addParameter("as", findString(as));
}
if (disabled != null) {
addParameter("disabled", findString(disabled));
}
if (title != null) {
addParameter("title", findString(title));
}
if (stack.getActionContext().getSession().containsKey("nonce")) {
String nonceValue = stack.getActionContext().getSession().get("nonce").toString();
addParameter("nonce", nonceValue);
}
}
}
@@ -0,0 +1,178 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.components;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.views.annotations.StrutsTag;
import org.apache.struts2.views.annotations.StrutsTagAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>
* Add nonce propagation feature to implement CSP in script tags
* </p>
*
* <p>
* The script tag allows the user to execute JavaScript. It also allows external resources to execute
* scripts which can be malicious. The s:script tag includes a nonce attribute that is being randomly
* generated with each request and only allows scripts with the valid nonce value to be executed.
* </p>
*
* <p><b>Examples</b></p>
*
* <pre>
*
* &lt;s:script ... /&gt;
*
* </pre>
*
*/
@StrutsTag(name="script",
tldTagClass="org.apache.struts2.views.jsp.ui.ScriptTag",
description="Script tag automatically adds nonces to script blocks - should be used in combination with Struts' CSP Interceptor.",
allowDynamicAttributes=true)
public class Script extends ClosingUIBean {
protected String async;
protected String charset;
protected String defer;
protected String src;
protected String type;
protected String referrerpolicy;
protected String nomodule;
protected String integrity;
protected String crossorigin;
private static final String TEMPLATE = "script-close";
private static final String OPEN_TEMPLATE = "script";
public Script(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
super(stack, request, response);
}
@Override
public String getDefaultOpenTemplate() {
return OPEN_TEMPLATE;
}
@Override
protected String getDefaultTemplate() {
return TEMPLATE;
}
@StrutsTagAttribute(description="HTML script async attribute")
public void setAsync(String async) {
this.async = async;
}
@StrutsTagAttribute(description="HTML script charset attribute")
public void setCharset(String charset) {
this.charset = charset;
}
@StrutsTagAttribute(description="HTML script defer attribute")
public void setDefer(String defer) {
this.defer = defer;
}
@StrutsTagAttribute(description="HTML script src attribute")
public void setSrc(String src) {
this.src = src;
}
@StrutsTagAttribute(description="HTML script type attribute")
public void setType(String type) {
this.type = type;
}
@StrutsTagAttribute(description="HTML script referrerpolicy attribute")
public void setReferrerpolicy(String referrerpolicy) {
this.referrerpolicy = referrerpolicy;
}
@StrutsTagAttribute(description="HTML script nomodule attribute")
public void setNomodule(String nomodule) {
this.nomodule = nomodule;
}
@StrutsTagAttribute(description="HTML script integrity attribute")
public void setIntegrity(String integrity) {
this.integrity = integrity;
}
@StrutsTagAttribute(description="HTML script crossorigin attribute")
public void setCrossorigin(String crossorigin) {
this.crossorigin = crossorigin;
}
@Override
public boolean usesBody() {
return true;
}
@Override
protected void evaluateExtraParams() {
super.evaluateExtraParams();
if (async != null) {
addParameter("async", findString(async));
}
if (charset != null) {
addParameter("charset", findString(charset));
}
if (defer != null) {
addParameter("defer", findString(defer));
}
if (src != null) {
addParameter("src", findString(src));
}
if (type != null) {
addParameter("type", findString(type));
}
if (referrerpolicy != null) {
addParameter("referrerpolicy", findString(referrerpolicy));
}
if (nomodule != null) {
addParameter("nomodule", findString(nomodule));
}
if (integrity != null) {
addParameter("integrity", findString(integrity));
}
if (crossorigin != null) {
addParameter("crossorigin", findString(crossorigin));
}
if (stack.getActionContext().getSession().containsKey("nonce")) {
String nonceValue = stack.getActionContext().getSession().get("nonce").toString();
addParameter("nonce", nonceValue);
}
}
}
@@ -0,0 +1,78 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.interceptor.csp;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import java.net.URI;
import java.util.Optional;
import javax.servlet.http.HttpServletResponse;
/**
* Interceptor that implements Content Security Policy on incoming requests used to protect against
* common XSS and data injection attacks. Uses {@link CspSettings} to add appropriate Content Security Policy header
* to the response. These headers determine what the browser will consider a policy violation and the browser's behavior
* when a violation occurs. A detailed explanation of CSP can be found <a href="https://csp.withgoogle.com/docs/index.html">here</a>.
*
* @see <a href="https://csp.withgoogle.com/docs/index.html">https://csp.withgoogle.com/docs/index.html/</a>
* @see CspSettings
* @see DefaultCspSettings
**/
public final class CspInterceptor extends AbstractInterceptor implements PreResultListener {
private final CspSettings settings = new DefaultCspSettings();
@Override
public String intercept(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener(this);
return invocation.invoke();
}
public void beforeResult(ActionInvocation invocation, String resultCode) {
HttpServletResponse response = invocation.getInvocationContext().getServletResponse();
settings.addCspHeaders(response);
}
public void setReportUri(String reportUri) {
Optional<URI> uri = buildUri(reportUri);
if (!uri.isPresent()) {
throw new IllegalArgumentException("Could not parse configured report URI for CSP interceptor: " + reportUri);
}
if (!uri.get().isAbsolute() && !reportUri.startsWith("/")) {
throw new IllegalArgumentException("Illegal configuration: report URI is not relative to the root. Please set a report URI that starts with /");
}
settings.setReportUri(reportUri);
}
private Optional<URI> buildUri(String reportUri) {
try {
return Optional.of(URI.create(reportUri));
} catch (IllegalArgumentException ignored) {
}
return Optional.empty();
}
public void setEnforcingMode(String value){
boolean enforcingMode = Boolean.parseBoolean(value);
settings.setEnforcingMode(enforcingMode);
}
}
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.interceptor.csp;
import javax.servlet.http.HttpServletResponse;
/**
* CspSettings interface used by the {@link CspInterceptor} to add the CSP header to the response.
* The default implementation can be found in {@link DefaultCspSettings}.
*
* @see DefaultCspSettings
*/
public interface CspSettings {
int NONCE_RANDOM_LENGTH = 18;
String CSP_ENFORCE_HEADER = "Content-Security-Policy";
String CSP_REPORT_HEADER = "Content-Security-Policy-Report-Only";
String OBJECT_SRC = "object-src";
String SCRIPT_SRC = "script-src";
String BASE_URI = "base-uri";
String REPORT_URI = "report-uri";
String NONE = "none";
String STRICT_DYNAMIC = "strict-dynamic";
String HTTP = "http:";
String HTTPS = "https:";
String CSP_REPORT_TYPE = "application/csp-report";
void addCspHeaders(HttpServletResponse response);
// sets the uri where csp violation reports will be sent
void setReportUri(String uri);
// sets CSP headers in enforcing mode when true, and report-only when false
void setEnforcingMode(boolean value);
}
@@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.interceptor.csp;
import static java.lang.String.format;
import com.opensymphony.xwork2.ActionContext;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletResponse;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.Map;
/**
* Default implementation of {@link CspSettings}.
* The default policy implements strict CSP with a nonce based approach and follows the guide: <a href="https://csp.withgoogle.com/docs/index.html">https://csp.withgoogle.com/docs/index.html/</a>
*
* @see CspSettings
* @see CspInterceptor
*/
public class DefaultCspSettings implements CspSettings {
private final SecureRandom sRand = new SecureRandom();
// this lazy supplier computes a policy format the first time it's called and caches the result
// to reduce string operations when attaching policies to HTTP responses
private final Supplier<String> lazyPolicyBuilder = new Supplier<String>() {
boolean hasBeenCalled;
String policyFormat;
@Override
public String get() {
if (!hasBeenCalled) {
StringBuilder policyFormatBuilder = new StringBuilder()
.append(OBJECT_SRC)
.append(format(" '%s'; ", NONE))
.append(SCRIPT_SRC)
.append(" 'nonce-%s' ") // nonce placeholder
.append(format("'%s' ", STRICT_DYNAMIC))
.append(format("%s %s; ", HTTP, HTTPS))
.append(BASE_URI)
.append(format(" '%s'; ", NONE));
if (reportUri != null) {
policyFormatBuilder
.append(REPORT_URI)
.append(format(" %s", reportUri));
}
policyFormat = policyFormatBuilder.toString();
}
return format(policyFormat, getNonceString());
}
};
private String reportUri;
// default to reporting mode
private String cspHeader = CSP_REPORT_HEADER;
public void addCspHeaders(HttpServletResponse response) {
associateNonceWithSession();
response.setHeader(cspHeader, lazyPolicyBuilder.get());
}
private String getNonceString() {
Map<String, Object> session = ActionContext.getContext().getSession();
return (String) session.get("nonce");
}
private void associateNonceWithSession() {
Map<String, Object> session = ActionContext.getContext().getSession();
String nonceValue = Base64.getUrlEncoder().encodeToString(getRandomBytes());
session.put("nonce", nonceValue);
}
private byte[] getRandomBytes() {
byte[] ret = new byte[NONCE_RANDOM_LENGTH];
sRand.nextBytes(ret);
return ret;
}
public void setEnforcingMode(boolean enforcingMode) {
if (enforcingMode) {
cspHeader = CSP_ENFORCE_HEADER;
}
}
public void setReportUri(String reportUri) {
this.reportUri = reportUri;
}
}
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.jsp.ui;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.components.Component;
import org.apache.struts2.components.Link;
import org.apache.struts2.components.Script;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @see Link
*/
public class LinkTag extends AbstractUITag {
protected String href;
protected String hreflang;
protected String rel;
protected String media;
protected String referrerpolicy;
protected String sizes;
protected String crossorigin;
protected String type;
protected String as;
@Override
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new Link(stack, req, res);
}
protected void populateParams() {
super.populateParams();
Link link = ((Link) component);
link.setHref(href);
link.setHreflang(hreflang);
link.setRel(rel);
link.setDisabled(disabled);
link.setMedia(media);
link.setReferrerpolicy(referrerpolicy);
link.setSizes(sizes);
link.setCrossorigin(crossorigin);
link.setType(type);
link.setAs(as);
link.setTitle(title);
}
public void setHref(String href) {
this.href = href;
}
public void setHreflang(String hreflang) {
this.hreflang = hreflang;
}
public void setRel(String rel) {
this.rel = rel;
}
public void setSizes(String sizes) {
this.sizes = sizes;
}
public void setCrossorigin(String crossorigin) {
this.crossorigin = crossorigin;
}
public void setType(String type) {
this.type = type;
}
public void setAs(String as) {
this.as = as;
}
public void setMedia(String media) {
this.media = media;
}
public void setReferrerpolicy(String referrerpolicy) {
this.referrerpolicy = referrerpolicy;
}
}
@@ -0,0 +1,98 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.jsp.ui;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.components.Component;
import org.apache.struts2.components.Form;
import org.apache.struts2.components.Script;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @see Script
*/
public class ScriptTag extends AbstractUITag {
protected String async;
protected String charset;
protected String defer;
protected String src;
protected String type;
protected String referrerpolicy;
protected String nomodule;
protected String integrity;
protected String crossorigin;
@Override
public Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
return new Script(stack, req, res);
}
protected void populateParams() {
super.populateParams();
Script script = ((Script) component);
script.setAsync(async);
script.setCharset(charset);
script.setDefer(defer);
script.setSrc(src);
script.setType(type);
script.setReferrerpolicy(referrerpolicy);
script.setNomodule(nomodule);
script.setIntegrity(integrity);
script.setCrossorigin(crossorigin);
}
public void setAsync(String async) {
this.async = async;
}
public void setCharset(String charset) {
this.charset = charset;
}
public void setSrc(String src) {
this.src = src;
}
public void setDefer(String defer) {
this.defer = defer;
}
public void setType(String type) {
this.type = type;
}
public void setReferrerpolicy(String referrerpolicy) {
this.referrerpolicy = referrerpolicy;
}
public void setNomodule(String nomodule) {
this.nomodule = nomodule;
}
public void setIntegrity(String integrity) {
this.integrity = integrity;
}
public void setCrossorigin(String crossorigin) {
this.crossorigin = crossorigin;
}
}
@@ -248,6 +248,7 @@
<interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
<interceptor name="coopInterceptor" class="org.apache.struts2.interceptor.CoopInterceptor"/>
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
<interceptor name="cspInterceptor" class="org.apache.struts2.interceptor.csp.CspInterceptor"/>
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
@@ -379,6 +380,9 @@
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="cspInterceptor">
<param name="enforcingMode">false</param>
</interceptor-ref>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
@@ -0,0 +1,65 @@
<#--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
<link nonce="${parameters.nonce}"<#rt/>
<#if parameters.href?has_content>
href="${parameters.href}"<#rt/>
</#if>
<#if parameters.hreflang?has_content>
hreflang="${parameters.hreflang}"<#rt/>
</#if>
<#if parameters.rel?has_content>
rel="${parameters.rel}"<#rt/>
</#if>
<#if parameters.disabled?has_content>
<#if parameters.disabled=="true">
disabled<#rt/>
</#if>
</#if>
<#if parameters.media?has_content>
media="${parameters.media}"<#rt/>
</#if>
<#if parameters.type?has_content>
type="${parameters.type}"<#rt/>
</#if>
<#if parameters.title?has_content>
title="${parameters.title}"<#rt/>
</#if>
<#if parameters.as?has_content>
as="${parameters.as}"<#rt/>
</#if>
<#if parameters.referrerpolicy?has_content>
referrerpolicy="${parameters.referrerpolicy}"<#rt/>
</#if>
<#if parameters.sizes?has_content>
sizes="${parameters.sizes}"<#rt/>
</#if>
<#if parameters.crossorigin?has_content>
crossorigin="${parameters.crossorigin}"<#rt/>
</#if>
<#if parameters.integrity?has_content>
integrity="${parameters.integrity}"<#rt/>
</#if>
<#if parameters.importance?has_content>
importance="${parameters.importance}"<#rt/>
</#if>
>
@@ -0,0 +1,21 @@
<#--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
</script>
@@ -0,0 +1,63 @@
<#--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
<#include "/${parameters.templateDir}/${parameters.expandTheme}/common-attributes.ftl" />
<#include "/${parameters.templateDir}/${parameters.expandTheme}/dynamic-attributes.ftl" />
<script <#rt/>
<#if parameters.nonce?has_content>
nonce="${parameters.nonce}"<#rt/>
</#if>
<#if parameters.async?has_content>
<#if parameters.async=="true">
async<#rt/>
</#if>
</#if>
<#if parameters.charset?has_content>
charset="${parameters.charset}"<#rt/>
</#if>
<#if parameters.defer?has_content>
<#if parameters.defer=="true">
defer<#rt/>
</#if>
</#if>
<#if parameters.src?has_content>
src="${parameters.src}"<#rt/>
</#if>
<#if parameters.type?has_content>
type="${parameters.type}"<#rt/>
</#if>
<#if parameters.name?has_content>
name="${parameters.name}"<#rt/>
</#if>
<#if parameters.referrerpolicy?has_content>
referrerpolicy="${parameters.referrerpolicy}"<#rt/>
</#if>
<#if parameters.nomodule?has_content>
<#if parameters.nomodule=="true">
nomodule<#rt/>
</#if>
</#if>
<#if parameters.integrity?has_content>
integrity="${parameters.integrity}"<#rt/>
</#if>
<#if parameters.crossorigin?has_content>
crossorigin="${parameters.crossorigin}"<#rt/>
</#if>
>
@@ -0,0 +1,21 @@
<#--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
<#include "/${parameters.templateDir}/simple/link.ftl" />
@@ -0,0 +1,21 @@
<#--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
-->
<#include "/${parameters.templateDir}/simple/script.ftl" />
@@ -0,0 +1,432 @@
<table width="100%">
<tr>
<td colspan="6"><h4>Dynamic Attributes Allowed:</h4> true</td>
</tr>
<tr>
<td colspan="6">&nbsp;</td>
</tr>
<tr>
<th align="left" valign="top"><h4>Name</h4></th>
<th align="left" valign="top"><h4>Required</h4></th>
<th align="left" valign="top"><h4>Default</h4></th>
<th align="left" valign="top"><h4>Evaluated</h4></th>
<th align="left" valign="top"><h4>Type</h4></th>
<th align="left" valign="top"><h4>Description</h4></th>
</tr>
<tr>
<td align="left" valign="top">accesskey</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html accesskey attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">as</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link as attribute</td>
</tr>
<tr>
<td align="left" valign="top">class</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css class to use for element - it's an alias of cssClass attribute.</td>
</tr>
<tr>
<td align="left" valign="top">crossorigin</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link crossorigin attribute</td>
</tr>
<tr>
<td align="left" valign="top">cssClass</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css class to use for element</td>
</tr>
<tr>
<td align="left" valign="top">cssErrorClass</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css error class to use for element</td>
</tr>
<tr>
<td align="left" valign="top">cssErrorStyle</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css error style definitions for element to use</td>
</tr>
<tr>
<td align="left" valign="top">cssStyle</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css style definitions for element to use</td>
</tr>
<tr>
<td align="left" valign="top">disabled</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html disabled attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">errorPosition</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Define error position of form element (top|bottom)</td>
</tr>
<tr>
<td align="left" valign="top">href</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link href attribute</td>
</tr>
<tr>
<td align="left" valign="top">hreflang</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link hreflang attribute</td>
</tr>
<tr>
<td align="left" valign="top">id</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML id attribute</td>
</tr>
<tr>
<td align="left" valign="top">javascriptTooltip</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">Use JavaScript to generate tooltips</td>
</tr>
<tr>
<td align="left" valign="top">key</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the key (name, value, label) for this particular component</td>
</tr>
<tr>
<td align="left" valign="top">label</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Label expression used for rendering an element specific label</td>
</tr>
<tr>
<td align="left" valign="top">labelSeparator</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">:</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">String that will be appended to the label</td>
</tr>
<tr>
<td align="left" valign="top">labelposition</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Define label position of form element (top/left)</td>
</tr>
<tr>
<td align="left" valign="top">media</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link media attribute</td>
</tr>
<tr>
<td align="left" valign="top">name</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The name to set for element</td>
</tr>
<tr>
<td align="left" valign="top">onblur</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top"> Set the html onblur attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onchange</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onchange attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onclick</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onclick attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">ondblclick</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html ondblclick attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onfocus</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onfocus attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onkeydown</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onkeydown attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onkeypress</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onkeypress attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onkeyup</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onkeyup attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmousedown</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmousedown attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmousemove</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmousemove attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmouseout</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmouseout attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmouseover</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmouseover attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmouseup</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmouseup attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onselect</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onselect attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">referrerpolicy</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link referrerpolicy attribute</td>
</tr>
<tr>
<td align="left" valign="top">rel</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link rel attribute</td>
</tr>
<tr>
<td align="left" valign="top">requiredLabel</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">If set to true, the rendered element will indicate that input is required</td>
</tr>
<tr>
<td align="left" valign="top">requiredPosition</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Define required position of required form element (left|right)</td>
</tr>
<tr>
<td align="left" valign="top">sizes</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link sizes attribute</td>
</tr>
<tr>
<td align="left" valign="top">style</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css style definitions for element to use - it's an alias of cssStyle attribute.</td>
</tr>
<tr>
<td align="left" valign="top">tabindex</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html tabindex attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">template</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The template (other than default) to use for rendering the element</td>
</tr>
<tr>
<td align="left" valign="top">templateDir</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The template directory.</td>
</tr>
<tr>
<td align="left" valign="top">theme</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The theme (other than default) to use for rendering the element</td>
</tr>
<tr>
<td align="left" valign="top">title</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html title attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">tooltip</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the tooltip of this particular component</td>
</tr>
<tr>
<td align="left" valign="top">tooltipConfig</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Deprecated. Use individual tooltip configuration attributes instead.</td>
</tr>
<tr>
<td align="left" valign="top">tooltipCssClass</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">StrutsTTClassic</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">CSS class applied to JavaScrip tooltips</td>
</tr>
<tr>
<td align="left" valign="top">tooltipDelay</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Classic</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Delay in milliseconds, before showing JavaScript tooltips </td>
</tr>
<tr>
<td align="left" valign="top">tooltipIconPath</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Icon path used for image that will have the tooltip</td>
</tr>
<tr>
<td align="left" valign="top">type</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML link type attribute</td>
</tr>
<tr>
<td align="left" valign="top">value</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Preset the value of input element.</td>
</tr>
</table>
@@ -0,0 +1 @@
Link tag automatically adds nonces to link elements - should be used in combination with Struts' CSP Interceptor.
@@ -0,0 +1,440 @@
<table width="100%">
<tr>
<td colspan="6"><h4>Dynamic Attributes Allowed:</h4> true</td>
</tr>
<tr>
<td colspan="6">&nbsp;</td>
</tr>
<tr>
<th align="left" valign="top"><h4>Name</h4></th>
<th align="left" valign="top"><h4>Required</h4></th>
<th align="left" valign="top"><h4>Default</h4></th>
<th align="left" valign="top"><h4>Evaluated</h4></th>
<th align="left" valign="top"><h4>Type</h4></th>
<th align="left" valign="top"><h4>Description</h4></th>
</tr>
<tr>
<td align="left" valign="top">accesskey</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html accesskey attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">async</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script async attribute</td>
</tr>
<tr>
<td align="left" valign="top">charset</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script charset attribute</td>
</tr>
<tr>
<td align="left" valign="top">class</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css class to use for element - it's an alias of cssClass attribute.</td>
</tr>
<tr>
<td align="left" valign="top">crossorigin</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script crossorigin attribute</td>
</tr>
<tr>
<td align="left" valign="top">cssClass</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css class to use for element</td>
</tr>
<tr>
<td align="left" valign="top">cssErrorClass</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css error class to use for element</td>
</tr>
<tr>
<td align="left" valign="top">cssErrorStyle</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css error style definitions for element to use</td>
</tr>
<tr>
<td align="left" valign="top">cssStyle</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css style definitions for element to use</td>
</tr>
<tr>
<td align="left" valign="top">defer</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script defer attribute</td>
</tr>
<tr>
<td align="left" valign="top">disabled</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html disabled attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">errorPosition</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Define error position of form element (top|bottom)</td>
</tr>
<tr>
<td align="left" valign="top">id</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML id attribute</td>
</tr>
<tr>
<td align="left" valign="top">integrity</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script integrity attribute</td>
</tr>
<tr>
<td align="left" valign="top">javascriptTooltip</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">Use JavaScript to generate tooltips</td>
</tr>
<tr>
<td align="left" valign="top">key</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the key (name, value, label) for this particular component</td>
</tr>
<tr>
<td align="left" valign="top">label</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Label expression used for rendering an element specific label</td>
</tr>
<tr>
<td align="left" valign="top">labelSeparator</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">:</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">String that will be appended to the label</td>
</tr>
<tr>
<td align="left" valign="top">labelposition</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Define label position of form element (top/left)</td>
</tr>
<tr>
<td align="left" valign="top">name</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The name to set for element</td>
</tr>
<tr>
<td align="left" valign="top">nomodule</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script nomodule attribute</td>
</tr>
<tr>
<td align="left" valign="top">onblur</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top"> Set the html onblur attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onchange</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onchange attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onclick</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onclick attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">ondblclick</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html ondblclick attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onfocus</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onfocus attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onkeydown</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onkeydown attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onkeypress</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onkeypress attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onkeyup</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onkeyup attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmousedown</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmousedown attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmousemove</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmousemove attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmouseout</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmouseout attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmouseover</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmouseover attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onmouseup</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onmouseup attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">onselect</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html onselect attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">openTemplate</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set template to use for opening the rendered html.</td>
</tr>
<tr>
<td align="left" valign="top">referrerpolicy</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script referrerpolicy attribute</td>
</tr>
<tr>
<td align="left" valign="top">requiredLabel</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Boolean</td>
<td align="left" valign="top">If set to true, the rendered element will indicate that input is required</td>
</tr>
<tr>
<td align="left" valign="top">requiredPosition</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Define required position of required form element (left|right)</td>
</tr>
<tr>
<td align="left" valign="top">src</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script src attribute</td>
</tr>
<tr>
<td align="left" valign="top">style</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The css style definitions for element to use - it's an alias of cssStyle attribute.</td>
</tr>
<tr>
<td align="left" valign="top">tabindex</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html tabindex attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">template</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The template (other than default) to use for rendering the element</td>
</tr>
<tr>
<td align="left" valign="top">templateDir</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The template directory.</td>
</tr>
<tr>
<td align="left" valign="top">theme</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">The theme (other than default) to use for rendering the element</td>
</tr>
<tr>
<td align="left" valign="top">title</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the html title attribute on rendered html element</td>
</tr>
<tr>
<td align="left" valign="top">tooltip</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Set the tooltip of this particular component</td>
</tr>
<tr>
<td align="left" valign="top">tooltipConfig</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Deprecated. Use individual tooltip configuration attributes instead.</td>
</tr>
<tr>
<td align="left" valign="top">tooltipCssClass</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">StrutsTTClassic</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">CSS class applied to JavaScrip tooltips</td>
</tr>
<tr>
<td align="left" valign="top">tooltipDelay</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">Classic</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Delay in milliseconds, before showing JavaScript tooltips </td>
</tr>
<tr>
<td align="left" valign="top">tooltipIconPath</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Icon path used for image that will have the tooltip</td>
</tr>
<tr>
<td align="left" valign="top">type</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">HTML script type attribute</td>
</tr>
<tr>
<td align="left" valign="top">value</td>
<td align="left" valign="top">false</td>
<td align="left" valign="top"></td>
<td align="left" valign="top">false</td>
<td align="left" valign="top">String</td>
<td align="left" valign="top">Preset the value of input element.</td>
</tr>
</table>
@@ -0,0 +1 @@
Script tag automatically adds nonces to script blocks - should be used in combination with Struts' CSP Interceptor.
@@ -0,0 +1,130 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.action;
import com.opensymphony.xwork2.XWorkTestCase;
import org.apache.struts2.interceptor.csp.CspSettings;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class CspReportActionTest extends XWorkTestCase {
public void testWhenMethodNotPost_thenReportNotProcessed() {
for (HttpMethod method : HttpMethod.values()) {
TestCspReportAction cspReportAction = new TestCspReportAction();
// only expect a report if the method is post
int expectedReports = method == HttpMethod.POST ? 1 : 0;
MockHttpServletRequest request = new MockHttpServletRequest(method.toString(), "/requestUri");
request.setContent("someSampleContent".getBytes());
request.setContentType(CspSettings.CSP_REPORT_TYPE);
cspReportAction.withServletRequest(request);
assertEquals(
"Unexpected behaviour with method " + method,
expectedReports,
cspReportAction.actualNumberOfReports
);
assertCorrectResponseStatusCode(cspReportAction);
}
}
public void testWhenNoContentLength_thenReportNotProcessed() {
TestCspReportAction cspReportAction = new TestCspReportAction();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/requestUri");
request.setContentType(CspSettings.CSP_REPORT_TYPE);
cspReportAction.withServletRequest(request);
assertEquals(
"Report request with empty body should not be processed",
0,
cspReportAction.actualNumberOfReports
);
assertCorrectResponseStatusCode(cspReportAction);
}
public void testWhenContentTypeNotCsp_thenReportNotProcessed() {
TestCspReportAction cspReportAction = new TestCspReportAction();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/requestUri");
request.setContent("someSampleContent".getBytes());
request.setContentType("application/json");
cspReportAction.withServletRequest(request);
assertEquals(
"Report request with wrong content type should not be processed",
0,
cspReportAction.actualNumberOfReports
);
assertCorrectResponseStatusCode(cspReportAction);
}
public void testWhenValidReportRequest_thenReportProcessed() {
TestCspReportAction cspReportAction = new TestCspReportAction();
String sampleReport = "someSampleContent";
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/requestUri");
request.setContent(sampleReport.getBytes());
request.setContentType(CspSettings.CSP_REPORT_TYPE);
cspReportAction.withServletRequest(request);
assertEquals(
"Valid report request was not processed",
1,
cspReportAction.actualNumberOfReports
);
assertEquals(
"Processed report body did not match",
sampleReport,
cspReportAction.actualReport
);
assertCorrectResponseStatusCode(cspReportAction);
}
private void assertCorrectResponseStatusCode(TestCspReportAction cspReportAction) {
MockHttpServletResponse response = new MockHttpServletResponse();
cspReportAction.withServletResponse(response);
assertEquals(
"Unexpected response status code: " + response.getStatus(),
204,
response.getStatus()
);
}
static class TestCspReportAction extends CspReportAction {
int actualNumberOfReports;
String actualReport;
@Override
void processReport(String jsonCspReport) {
actualNumberOfReports++;
actualReport = jsonCspReport;
}
}
}
@@ -0,0 +1,177 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.interceptor;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.mock.MockActionInvocation;
import org.apache.logging.log4j.util.Strings;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsInternalTestCase;
import org.apache.struts2.interceptor.csp.CspInterceptor;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import static org.apache.struts2.interceptor.csp.CspSettings.*;
public class CspInterceptorTest extends StrutsInternalTestCase {
private final CspInterceptor interceptor = new CspInterceptor();
private final MockActionInvocation mai = new MockActionInvocation();
private final MockHttpServletRequest request = new MockHttpServletRequest();
private final MockHttpServletResponse response = new MockHttpServletResponse();
private final Map<String, Object> session = new HashMap<>();
public void test_whenRequestReceived_thenNonceIsSetInSession_andCspHeaderContainsIt() throws Exception {
String reportUri = "/barfoo";
String reporting = "false";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(reporting);
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
checkHeader(reportUri, reporting);
}
public void test_whenNonceAlreadySetInSession_andRequestReceived_thenNewNonceIsSet() throws Exception {
String reportUri = "https://www.google.com/";
String enforcingMode = "true";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.put("nonce", "foo");
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertFalse("New nonce value couldn't be set", session.get("nonce").equals("foo"));
checkHeader(reportUri, enforcingMode);
}
public void testEnforcingCspHeadersSet() throws Exception {
String reportUri = "/csp-reports";
String enforcingMode = "true";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.put("nonce", "foo");
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertFalse("New nonce value couldn't be set", session.get("nonce").equals("foo"));
checkHeader(reportUri, enforcingMode);
}
public void testReportingCspHeadersSet() throws Exception {
String reportUri = "/csp-reports";
String enforcingMode = "false";
interceptor.setReportUri(reportUri);
interceptor.setEnforcingMode(enforcingMode);
session.put("nonce", "foo");
interceptor.intercept(mai);
assertTrue("Nonce key does not exist", session.containsKey("nonce"));
assertFalse("Nonce value is empty", Strings.isEmpty((String) session.get("nonce")));
assertFalse("New nonce value couldn't be set", session.get("nonce").equals("foo"));
checkHeader(reportUri, enforcingMode);
}
public void test_uriSetOnlyWhenSetIsCalled() throws Exception {
String enforcingMode = "false";
interceptor.setEnforcingMode(enforcingMode);
interceptor.intercept(mai);
checkHeader(null, enforcingMode);
// set report uri
String reportUri = "/some-uri";
interceptor.setReportUri(reportUri);
interceptor.intercept(mai);
checkHeader(reportUri, enforcingMode);
}
public void testCannotParseUri() throws Exception {
String enforcingMode = "false";
interceptor.setEnforcingMode(enforcingMode);
try{
interceptor.setReportUri("ww w. google.@com");
assert(false);
} catch (IllegalArgumentException e){
assert(true);
}
}
public void testCannotParseRelativeUri() throws Exception {
String enforcingMode = "false";
interceptor.setEnforcingMode(enforcingMode);
try{
interceptor.setReportUri("some-uri");
assert(false);
} catch (IllegalArgumentException e){
assert(true);
}
}
public void checkHeader(String reportUri, String enforcingMode){
String expectedCspHeader = "";
if (Strings.isEmpty(reportUri)) {
expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; ",
OBJECT_SRC, NONE,
SCRIPT_SRC, session.get("nonce"), STRICT_DYNAMIC, HTTP, HTTPS,
BASE_URI, NONE
);
} else {
expectedCspHeader = String.format("%s '%s'; %s 'nonce-%s' '%s' %s %s; %s '%s'; %s %s",
OBJECT_SRC, NONE,
SCRIPT_SRC, session.get("nonce"), STRICT_DYNAMIC, HTTP, HTTPS,
BASE_URI, NONE,
REPORT_URI, reportUri
);
}
String header = "";
if (enforcingMode.equals("true")){
header = response.getHeader(CSP_ENFORCE_HEADER);
} else {
header = response.getHeader(CSP_REPORT_HEADER);
}
assertFalse("No CSP header exists", Strings.isEmpty(header));
assertEquals("Response headers do not contain nonce header", expectedCspHeader, header);
}
@Override
protected void setUp() throws Exception {
super.setUp();
container.inject(interceptor);
ServletActionContext.setRequest(request);
ServletActionContext.setResponse(response);
ActionContext context = ServletActionContext.getActionContext().bind();
context.withSession(session);
mai.setInvocationContext(context);
}
}
@@ -0,0 +1,74 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.jsp.ui;
import org.apache.struts2.views.jsp.AbstractUITagTest;
import javax.servlet.jsp.JspException;
public class LinkTagTest extends AbstractUITagTest {
private static final String NONCE_VAL = "r4andom";
public void testLinkTagAttributes() {
LinkTag tag = new LinkTag();
tag.setHref("mysrc.js");
tag.setHreflang("test");
tag.setRel("module");
tag.setMedia("foo");
tag.setReferrerpolicy("test");
tag.setSizes("foo");
tag.setCrossorigin("same-origin");
tag.setType("anonymous");
tag.setAs("test");
tag.setDisabled("false");
tag.setTitle("test");
doLinkTest(tag);
String s = writer.toString();
assertTrue("Incorrect href attribute for link tag", s.contains("href=\"mysrc.js\""));
assertTrue("Incorrect hreflang attribute for link tag", s.contains("hreflang=\"test\""));
assertTrue("Incorrect rel attribute for link tag", s.contains("rel=\"module\""));
assertTrue("Incorrect media attribute for link tag", s.contains("media=\"foo\""));
assertTrue("Incorrect referrerpolicy attribute for link tag", s.contains("referrerpolicy=\"test\""));
assertTrue("Incorrect sizes attribute for link tag", s.contains("sizes=\"foo\""));
assertTrue("Incorrect crossorigin attribute for link tag", s.contains("crossorigin=\"same-origin\""));
assertTrue("Incorrect type attribute for link tag", s.contains("type=\"anonymous\""));
assertTrue("Incorrect as attribute for link tag", s.contains("as=\"test\""));
assertFalse("Non-existent disabled attribute for link tag", s.contains("disabled"));
assertTrue("Incorrect title attribute for link tag", s.contains("title=\"test\""));
assertTrue("Incorrect nonce attribute for link tag", s.contains("nonce=\"" + NONCE_VAL+"\""));
}
private void doLinkTest(LinkTag tag) {
//creating nonce value like the CspInterceptor does
stack.getActionContext().getSession().put("nonce", NONCE_VAL);
tag.setPageContext(pageContext);
try {
tag.doStartTag();
tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
}
}
}
@@ -0,0 +1,73 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.jsp.ui;
import org.apache.struts2.views.jsp.AbstractUITagTest;
import javax.servlet.jsp.JspException;
public class ScriptTagTest extends AbstractUITagTest {
private static final String NONCE_VAL = "r4andom";
public void testScriptTagAttributes() {
ScriptTag tag = new ScriptTag();
tag.setSrc("mysrc.js");
tag.setAsync("false");
tag.setType("module");
tag.setCharset("foo");
tag.setNomodule("true");
tag.setDefer("true");
tag.setReferrerpolicy("same-origin");
tag.setCrossorigin("anonymous");
tag.setIntegrity("test");
doScriptTest(tag);
String s = writer.toString();
assertTrue("Incorrect src attribute for script tag", s.contains("src=\"mysrc.js\""));
assertFalse("Non-existent async attribute for script tag", s.contains("async"));
assertTrue("Incorrect type attribute for script tag", s.contains("type=\"module\""));
assertTrue("Incorrect charset attribute for script tag", s.contains("charset=\"foo\""));
assertTrue("Non-existent nomodule attribute for script tag", s.contains("nomodule"));
assertTrue("Non-existent defer attribute for script tag", s.contains("defer"));
assertTrue("Incorrect referrerpolicy attribute for script tag", s.contains("referrerpolicy=\"same-origin\""));
assertTrue("Incorrect crossorigin attribute for script tag", s.contains("crossorigin=\"anonymous\""));
assertTrue("Incorrect integrity attribute for script tag", s.contains("integrity=\"test\""));
assertTrue("Incorrect nonce attribute for script tag", s.contains("nonce=\"" + NONCE_VAL+"\""));
}
private void doScriptTest(ScriptTag tag) {
//creating nonce value like the CspInterceptor does
stack.getActionContext().getSession().put("nonce", NONCE_VAL);
tag.setPageContext(pageContext);
try {
tag.doStartTag();
tag.doEndTag();
} catch (JspException e) {
e.printStackTrace();
fail();
}
}
}
@@ -0,0 +1,50 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.java.simple;
import org.apache.struts2.views.java.Attributes;
import org.apache.struts2.views.java.TagGenerator;
import java.io.IOException;
import java.util.Map;
public class LinkHandler extends AbstractTagHandler implements TagGenerator {
@Override
public void generate() throws IOException {
Map<String, Object> params = context.getParameters();
Attributes attrs = new Attributes();
attrs.add("nonce", (String) params.get("nonce"))
.addIfExists("href", params.get(("href")))
.addIfExists("hreflang", params.get("hreflang"))
.addIfExists("rel", params.get("rel"))
.addIfExists("media", params.get("media"))
.addIfExists("sizes", params.get("sizes"))
.addIfExists("crossorigin", params.get("crossorigin"))
.addIfExists("referrerpolicy", params.get("referrerpolicy"))
.addIfExists("type", params.get("type"))
.addIfExists("as", params.get("as"))
.addIfExists("disabled", params.get("disabled"))
.addIfExists("title", params.get("title"));
start("link", attrs);
end("link");
}
}
@@ -0,0 +1,61 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.java.simple;
import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.views.java.Attributes;
import org.apache.struts2.views.java.TagGenerator;
import java.io.IOException;
import java.util.Map;
public class ScriptHandler extends AbstractTagHandler implements TagGenerator {
@Override
public void generate() throws IOException {
Map<String, Object> params = context.getParameters();
Attributes attrs = new Attributes();
attrs.add("nonce", (String) params.get("nonce"))
.addIfExists("async", params.get("async"))
.addIfExists("charset", params.get("charset"))
.addIfExists("defer", params.get("defer"))
.addIfExists("src", params.get("src"))
.addIfExists("type", params.get("type"))
.addIfExists("name", params.get("name"))
.addIfExists("referrerpolicy", params.get("referrerpolicy"))
.addIfExists("nomodule", params.get("nomodule"))
.addIfExists("integrity", params.get("integrity"))
.addIfExists("crossorigin", params.get("crossorigin"));
start("script", attrs);
}
public static class CloseHandler extends AbstractTagHandler implements TagGenerator {
public void generate() throws IOException {
Map<String, Object> params = context.getParameters();
String body = (String) params.get("body");
if (StringUtils.isNotEmpty(body))
characters(body, false); // false means no HTML encoding
end("script");
}
}
}
@@ -50,6 +50,9 @@ public class SimpleTheme extends DefaultTheme {
put("textarea", new FactoryList(TextAreaHandler.class, ScriptingEventsHandler.class, CommonAttributesHandler.class, DynamicAttributesHandler.class));
put("radiomap", new FactoryList(RadioHandler.class, ScriptingEventsHandler.class, CommonAttributesHandler.class, DynamicAttributesHandler.class));
put("checkboxlist", new FactoryList(CheckboxListHandler.class, ScriptingEventsHandler.class, CommonAttributesHandler.class, DynamicAttributesHandler.class));
put("script", new FactoryList(ScriptHandler.class, CommonAttributesHandler.class, DynamicAttributesHandler.class));
put("script-close", new FactoryList(ScriptHandler.CloseHandler.class));
put("link", new FactoryList(LinkHandler.class, CommonAttributesHandler.class, DynamicAttributesHandler.class));
put("actionerror", new FactoryList(ActionErrorHandler.class));
put("token", new FactoryList(TokenHandler.class));
put("actionmessage", new FactoryList(ActionMessageHandler.class));
@@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.java.simple;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.components.Link;
import org.apache.struts2.components.UIBean;
import java.util.HashMap;
import java.util.Map;
public class LinkTest extends AbstractTest{
private Link tag;
private static final String NONCE_VAL = "r4andom";
public void testRenderScriptTag() {
tag.setHref("testhref");
tag.setHreflang("test");
tag.setRel("module");
tag.setMedia("foo");
tag.setReferrerpolicy("test");
tag.setSizes("foo");
tag.setCrossorigin("same-origin");
tag.setType("anonymous");
tag.setAs("test");
tag.setDisabled("disabled_");
tag.setTitle("test");
tag.evaluateParams();
map.putAll(tag.getParameters());
theme.renderTag(getTagName(), context);
String s = writer.getBuffer().toString();
assertTrue("Incorrect href attribute for link tag", s.contains("href=\"testhref\""));
assertTrue("Incorrect hreflang attribute for link tag", s.contains("hreflang=\"test\""));
assertTrue("Incorrect rel attribute for link tag", s.contains("rel=\"module\""));
assertTrue("Incorrect media attribute for link tag", s.contains("media=\"foo\""));
assertTrue("Incorrect referrerpolicy attribute for link tag", s.contains("referrerpolicy=\"test\""));
assertTrue("Incorrect sizes attribute for link tag", s.contains("sizes=\"foo\""));
assertTrue("Incorrect crossorigin attribute for link tag", s.contains("crossorigin=\"same-origin\""));
assertTrue("Incorrect type attribute for link tag", s.contains("type=\"anonymous\""));
assertTrue("Incorrect as attribute for link tag", s.contains("as=\"test\""));
assertTrue("Non-existent disabled attribute for link tag", s.contains("disabled=\"disabled_\""));
assertTrue("Incorrect title attribute for link tag", s.contains("title=\"test\""));
assertTrue("Incorrect nonce attribute for link tag", s.contains("nonce=\"" + NONCE_VAL+"\""));
}
@Override
protected UIBean getUIBean() throws Exception {
return tag;
}
@Override
protected String getTagName() {
return "link";
}
@Override
protected void setUp() throws Exception {
super.setUp();
ActionContext actionContext = stack.getActionContext();
Map<String, Object> session = new HashMap<>();
session.put("nonce", NONCE_VAL);
actionContext.withSession(session);
this.tag = new Link(stack, request, response);
}
}
@@ -0,0 +1,86 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.struts2.views.java.simple;
import com.opensymphony.xwork2.ActionContext;
import org.apache.struts2.components.Script;
import org.apache.struts2.components.UIBean;
import java.util.HashMap;
import java.util.Map;
public class ScriptTest extends AbstractTest {
private Script tag;
private static final String NONCE_VAL = "r4andom";
public void testRenderScriptTag() {
tag.setName("name_");
tag.setType("text/javascript");
tag.setSrc("mysrc");
tag.setAsync("false");
tag.setDefer("false");
tag.setCharset("test");
tag.setReferrerpolicy("foo");
tag.setNomodule("bar");
tag.setIntegrity("test");
tag.setCrossorigin("test");
tag.evaluateParams();
map.putAll(tag.getParameters());
theme.renderTag(getTagName(), context);
String output = writer.getBuffer().toString();
assertTrue("Script doesn't have nonce attribute", output.contains("nonce="));
assertTrue("Script doesn't have type attribute", output.contains("type="));
assertTrue("Script doesn't have src attribute", output.contains("src="));
assertTrue("Script doesn't have async attribute", output.contains("async"));
assertTrue("Script doesn't have defer attribute", output.contains("defer"));
assertTrue("Script doesn't have charset attribute", output.contains("charset="));
assertTrue("Script doesn't have referrerpolicy attribute", output.contains("referrerpolicy="));
assertTrue("Script doesn't have nomodule attribute", output.contains("nomodule"));
assertTrue("Script doesn't have integrity attribute", output.contains("integrity="));
assertTrue("Script doesn't have crossorigin attribute", output.contains("crossorigin="));
}
@Override
protected UIBean getUIBean() throws Exception {
return tag;
}
@Override
protected String getTagName() {
return "script";
}
@Override
protected void setUp() throws Exception {
super.setUp();
ActionContext actionContext = stack.getActionContext();
Map<String, Object> session = new HashMap<>();
session.put("nonce", NONCE_VAL);
actionContext.withSession(session);
this.tag = new Script(stack, request, response);
}
}