You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pa...@apache.org on 2020/08/07 20:43:30 UTC
[wicket] 01/04: WICKET-6786: code reformatting
This is an automated email from the ASF dual-hosted git repository.
papegaaij pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git
commit 5d9d4b598ec070cb863d67124d276e74cd3cdc87
Author: Emond Papegaaij <em...@topicus.nl>
AuthorDate: Fri Aug 7 12:13:32 2020 +0200
WICKET-6786: code reformatting
---
.../http/CsrfPreventionRequestCycleListener.java | 4 +-
.../http/DefaultResourceIsolationPolicy.java | 6 +-
.../http/FetchMetadataRequestCycleListener.java | 212 +++++-----
.../http/OriginBasedResourceIsolationPolicy.java | 446 +++++++++++----------
.../FetchMetadataRequestCycleListenerTest.java | 307 +++++++-------
5 files changed, 497 insertions(+), 478 deletions(-)
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
index db39cc5..f4d4040 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/CsrfPreventionRequestCycleListener.java
@@ -579,8 +579,8 @@ public class CsrfPreventionRequestCycleListener extends OriginBasedResourceIsola
{
onAborted(request, origin, page);
log.info(
- "Possible CSRF attack, request URL: {}, Origin: {}, action: aborted with error {} {}",
- request.getRequestURL(), origin, errorCode, errorMessage);
+ "Possible CSRF attack, request URL: {}, Origin: {}, action: aborted with error {} {}",
+ request.getRequestURL(), origin, errorCode, errorMessage);
throw new AbortWithHttpErrorCodeException(errorCode, errorMessage);
}
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/DefaultResourceIsolationPolicy.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/DefaultResourceIsolationPolicy.java
index de9d155..b1d7cf7 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/DefaultResourceIsolationPolicy.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/DefaultResourceIsolationPolicy.java
@@ -34,11 +34,11 @@ public class DefaultResourceIsolationPolicy implements ResourceIsolationPolicy
{
@Override
- public boolean isRequestAllowed(HttpServletRequest request,
- IRequestablePage targetPage)
+ public boolean isRequestAllowed(HttpServletRequest request, IRequestablePage targetPage)
{
// request made by a legacy browser with no support for Fetch Metadata
- if (!hasFetchMetadataHeaders(request)) {
+ if (!hasFetchMetadataHeaders(request))
+ {
return true;
}
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListener.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListener.java
index 1d1f88d..ea9a8ef 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListener.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListener.java
@@ -44,117 +44,121 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * The Fetch Metadata Request Cycle Listener is Wicket's implementation of Fetch Metadata.
- * This adds a layer of protection for modern browsers that prevents Cross-Site Request Forgery
- * attacks.
+ * The Fetch Metadata Request Cycle Listener is Wicket's implementation of Fetch Metadata. This adds
+ * a layer of protection for modern browsers that prevents Cross-Site Request Forgery attacks.
*
* This request listener uses the {@link DefaultResourceIsolationPolicy} by default and can be
* customized with additional Resource Isolation Policies.
*
- * This listener can be configured to add exempted URL paths that are intended to be used cross-site.
+ * This listener can be configured to add exempted URL paths that are intended to be used
+ * cross-site.
*
- * Learn more about Fetch Metadata and resource isolation
- * at <a href="https://web.dev/fetch-metadata/">https://web.dev/fetch-metadata/</a>
+ * Learn more about Fetch Metadata and resource isolation at
+ * <a href="https://web.dev/fetch-metadata/">https://web.dev/fetch-metadata/</a>
*
* @author Santiago Diaz - saldiaz@google.com
* @author Ecenaz Jen Ozmen - ecenazo@google.com
*/
-public class FetchMetadataRequestCycleListener implements IRequestCycleListener {
-
- private static final Logger log = LoggerFactory
- .getLogger(FetchMetadataRequestCycleListener.class);
- public static final int ERROR_CODE = 403;
- public static final String ERROR_MESSAGE = "Forbidden";
- public static final String VARY_HEADER_VALUE = SEC_FETCH_DEST_HEADER + ", "
- + SEC_FETCH_SITE_HEADER + ", " + SEC_FETCH_MODE_HEADER;
-
- private final Set<String> exemptedPaths = new HashSet<>();
- private final List<ResourceIsolationPolicy> resourceIsolationPolicies = new ArrayList<>();
-
- public FetchMetadataRequestCycleListener(ResourceIsolationPolicy... additionalPolicies) {
- this.resourceIsolationPolicies.addAll(
- asList(
- new DefaultResourceIsolationPolicy(),
- new OriginBasedResourceIsolationPolicy()
- )
- );
-
- this.resourceIsolationPolicies.addAll(asList(additionalPolicies));
- }
-
- public void addExemptedPaths(String... exemptions) {
- Arrays.stream(exemptions)
- .filter(e -> !Strings.isEmpty(e))
- .forEach(exemptedPaths::add);
- }
-
- @Override
- public void onBeginRequest(RequestCycle cycle)
- {
- HttpServletRequest containerRequest = (HttpServletRequest)cycle.getRequest()
- .getContainerRequest();
-
- log.debug("Processing request to: {}", containerRequest.getPathInfo());
- }
-
- @Override
- public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
- {
- handler = unwrap(handler);
- IPageRequestHandler pageRequestHandler = getPageRequestHandler(handler);
- if (pageRequestHandler == null) {
- return;
- }
-
- IRequestablePage targetedPage = pageRequestHandler.getPage();
- HttpServletRequest containerRequest = (HttpServletRequest)cycle.getRequest()
- .getContainerRequest();
-
- String pathInfo = containerRequest.getPathInfo();
- if (exemptedPaths.contains(pathInfo)) {
- if (log.isDebugEnabled()) {
- log.debug("Allowing request to {} because it matches an exempted path",
- new Object[]{pathInfo});
- }
- return;
- }
-
- for (ResourceIsolationPolicy resourceIsolationPolicy : resourceIsolationPolicies) {
- if (!resourceIsolationPolicy.isRequestAllowed(containerRequest, targetedPage)) {
- log.debug("Isolation policy {} has rejected a request to {}",
- Classes.simpleName(resourceIsolationPolicy.getClass()), pathInfo);
- throw new AbortWithHttpErrorCodeException(ERROR_CODE, ERROR_MESSAGE);
- }
- }
- }
-
- @Override
- public void onEndRequest(RequestCycle cycle)
- {
- // set vary headers to avoid caching responses processed by Fetch Metadata
- // caching these responses may return 403 responses to legitimate requests
- // or defeat the protection
- if (cycle.getResponse() instanceof WebResponse)
- {
- WebResponse webResponse = (WebResponse)cycle.getResponse();
- if (webResponse.isHeaderSupported())
- {
- webResponse.addHeader(VARY_HEADER, VARY_HEADER_VALUE);
- }
- }
- }
-
- private static IRequestHandler unwrap(IRequestHandler handler) {
- while (handler instanceof IRequestHandlerDelegate) {
- handler = ((IRequestHandlerDelegate)handler).getDelegateHandler();
- }
- return handler;
- }
-
- private IPageRequestHandler getPageRequestHandler(IRequestHandler handler)
- {
- boolean isPageRequestHandler = handler instanceof IPageRequestHandler &&
- !(handler instanceof RenderPageRequestHandler);
- return isPageRequestHandler ? (IPageRequestHandler) handler : null;
- }
+public class FetchMetadataRequestCycleListener implements IRequestCycleListener
+{
+
+ private static final Logger log = LoggerFactory
+ .getLogger(FetchMetadataRequestCycleListener.class);
+ public static final int ERROR_CODE = 403;
+ public static final String ERROR_MESSAGE = "Forbidden";
+ public static final String VARY_HEADER_VALUE = SEC_FETCH_DEST_HEADER + ", "
+ + SEC_FETCH_SITE_HEADER + ", " + SEC_FETCH_MODE_HEADER;
+
+ private final Set<String> exemptedPaths = new HashSet<>();
+ private final List<ResourceIsolationPolicy> resourceIsolationPolicies = new ArrayList<>();
+
+ public FetchMetadataRequestCycleListener(ResourceIsolationPolicy... additionalPolicies)
+ {
+ this.resourceIsolationPolicies.addAll(
+ asList(new DefaultResourceIsolationPolicy(), new OriginBasedResourceIsolationPolicy()));
+
+ this.resourceIsolationPolicies.addAll(asList(additionalPolicies));
+ }
+
+ public void addExemptedPaths(String... exemptions)
+ {
+ Arrays.stream(exemptions).filter(e -> !Strings.isEmpty(e)).forEach(exemptedPaths::add);
+ }
+
+ @Override
+ public void onBeginRequest(RequestCycle cycle)
+ {
+ HttpServletRequest containerRequest = (HttpServletRequest)cycle.getRequest()
+ .getContainerRequest();
+
+ log.debug("Processing request to: {}", containerRequest.getPathInfo());
+ }
+
+ @Override
+ public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler)
+ {
+ handler = unwrap(handler);
+ IPageRequestHandler pageRequestHandler = getPageRequestHandler(handler);
+ if (pageRequestHandler == null)
+ {
+ return;
+ }
+
+ IRequestablePage targetedPage = pageRequestHandler.getPage();
+ HttpServletRequest containerRequest = (HttpServletRequest)cycle.getRequest()
+ .getContainerRequest();
+
+ String pathInfo = containerRequest.getPathInfo();
+ if (exemptedPaths.contains(pathInfo))
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Allowing request to {} because it matches an exempted path",
+ new Object[] { pathInfo });
+ }
+ return;
+ }
+
+ for (ResourceIsolationPolicy resourceIsolationPolicy : resourceIsolationPolicies)
+ {
+ if (!resourceIsolationPolicy.isRequestAllowed(containerRequest, targetedPage))
+ {
+ log.debug("Isolation policy {} has rejected a request to {}",
+ Classes.simpleName(resourceIsolationPolicy.getClass()), pathInfo);
+ throw new AbortWithHttpErrorCodeException(ERROR_CODE, ERROR_MESSAGE);
+ }
+ }
+ }
+
+ @Override
+ public void onEndRequest(RequestCycle cycle)
+ {
+ // set vary headers to avoid caching responses processed by Fetch Metadata
+ // caching these responses may return 403 responses to legitimate requests
+ // or defeat the protection
+ if (cycle.getResponse() instanceof WebResponse)
+ {
+ WebResponse webResponse = (WebResponse)cycle.getResponse();
+ if (webResponse.isHeaderSupported())
+ {
+ webResponse.addHeader(VARY_HEADER, VARY_HEADER_VALUE);
+ }
+ }
+ }
+
+ private static IRequestHandler unwrap(IRequestHandler handler)
+ {
+ while (handler instanceof IRequestHandlerDelegate)
+ {
+ handler = ((IRequestHandlerDelegate)handler).getDelegateHandler();
+ }
+ return handler;
+ }
+
+ private IPageRequestHandler getPageRequestHandler(IRequestHandler handler)
+ {
+ boolean isPageRequestHandler = handler instanceof IPageRequestHandler
+ && !(handler instanceof RenderPageRequestHandler);
+ return isPageRequestHandler ? (IPageRequestHandler)handler : null;
+ }
}
diff --git a/wicket-core/src/main/java/org/apache/wicket/protocol/http/OriginBasedResourceIsolationPolicy.java b/wicket-core/src/main/java/org/apache/wicket/protocol/http/OriginBasedResourceIsolationPolicy.java
index e8fd195..bf07276 100644
--- a/wicket-core/src/main/java/org/apache/wicket/protocol/http/OriginBasedResourceIsolationPolicy.java
+++ b/wicket-core/src/main/java/org/apache/wicket/protocol/http/OriginBasedResourceIsolationPolicy.java
@@ -29,251 +29,255 @@ import org.apache.wicket.util.string.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class OriginBasedResourceIsolationPolicy implements ResourceIsolationPolicy {
- private static final Logger log = LoggerFactory
- .getLogger(OriginBasedResourceIsolationPolicy.class);
+public class OriginBasedResourceIsolationPolicy implements ResourceIsolationPolicy
+{
+ private static final Logger log = LoggerFactory
+ .getLogger(OriginBasedResourceIsolationPolicy.class);
- /**
- * A white list of accepted origins (host names/domain names) presented as
- * <domainname>.<TLD>. The domain part can contain subdomains.
- */
- private Collection<String> acceptedOrigins = new ArrayList<>();
+ /**
+ * A white list of accepted origins (host names/domain names) presented as
+ * <domainname>.<TLD>. The domain part can contain subdomains.
+ */
+ private Collection<String> acceptedOrigins = new ArrayList<>();
- /**
- * Adds an origin (host name/domain name) to the white list. An origin is in the form of
- * <domainname>.<TLD>, and can contain a subdomain. Every Origin header that matches
- * a domain from the whitelist is accepted and not checked any further for CSRF issues.
- *
- * E.g. when {@code example.com} is in the white list, this allows requests from (i.e. with an
- * {@code Origin:} header containing) {@code example.com} and {@code blabla.example.com} but
- * rejects requests from {@code blablaexample.com} and {@code example2.com}.
- *
- * @param acceptedOrigin
- * the acceptable origin
- * @return this
- */
- public OriginBasedResourceIsolationPolicy addAcceptedOrigin(String acceptedOrigin)
- {
- Checks.notNull("acceptedOrigin", acceptedOrigin);
+ /**
+ * Adds an origin (host name/domain name) to the white list. An origin is in the form of
+ * <domainname>.<TLD>, and can contain a subdomain. Every Origin header that matches
+ * a domain from the whitelist is accepted and not checked any further for CSRF issues.
+ *
+ * E.g. when {@code example.com} is in the white list, this allows requests from (i.e. with an
+ * {@code Origin:} header containing) {@code example.com} and {@code blabla.example.com} but
+ * rejects requests from {@code blablaexample.com} and {@code example2.com}.
+ *
+ * @param acceptedOrigin
+ * the acceptable origin
+ * @return this
+ */
+ public OriginBasedResourceIsolationPolicy addAcceptedOrigin(String acceptedOrigin)
+ {
+ Checks.notNull("acceptedOrigin", acceptedOrigin);
- // strip any leading dot characters
- final int len = acceptedOrigin.length();
- int i = 0;
- while (i < len && acceptedOrigin.charAt(i) == '.')
- {
- i++;
- }
- acceptedOrigins.add(acceptedOrigin.substring(i));
- return this;
- }
+ // strip any leading dot characters
+ final int len = acceptedOrigin.length();
+ int i = 0;
+ while (i < len && acceptedOrigin.charAt(i) == '.')
+ {
+ i++;
+ }
+ acceptedOrigins.add(acceptedOrigin.substring(i));
+ return this;
+ }
- /**
- * This origin-based listener can be used in combination with the {@link FetchMetadataRequestCycleListener}
- * to add support for legacy browsers that don't send Sec-Fetch-* headers yet.
- * @return whether the request is allowed based on its origin
- */
- @Override
- public boolean isRequestAllowed(HttpServletRequest request, IRequestablePage targetPage) {
- String sourceUri = getSourceUri(request);
+ /**
+ * This origin-based listener can be used in combination with the
+ * {@link FetchMetadataRequestCycleListener} to add support for legacy browsers that don't send
+ * Sec-Fetch-* headers yet.
+ *
+ * @return whether the request is allowed based on its origin
+ */
+ @Override
+ public boolean isRequestAllowed(HttpServletRequest request, IRequestablePage targetPage)
+ {
+ String sourceUri = getSourceUri(request);
- if (sourceUri == null || sourceUri.isEmpty())
- {
- log.debug("Source URI not present in request to {}", request.getPathInfo());
- return true;
- }
- sourceUri = sourceUri.toLowerCase(Locale.ROOT);
+ if (sourceUri == null || sourceUri.isEmpty())
+ {
+ log.debug("Source URI not present in request to {}", request.getPathInfo());
+ return true;
+ }
+ sourceUri = sourceUri.toLowerCase(Locale.ROOT);
- // if the origin is a know and trusted origin, don't check any further but allow the request
- if (isWhitelistedHost(sourceUri))
- {
- return true;
- }
+ // if the origin is a know and trusted origin, don't check any further but allow the request
+ if (isWhitelistedHost(sourceUri))
+ {
+ return true;
+ }
- // check if the origin HTTP header matches the request URI
- if (!isLocalOrigin(request, sourceUri))
- {
- log.debug("Source URI conflicts with request origin");
- return false;
- }
+ // check if the origin HTTP header matches the request URI
+ if (!isLocalOrigin(request, sourceUri))
+ {
+ log.debug("Source URI conflicts with request origin");
+ return false;
+ }
- return true;
- }
+ return true;
+ }
- /**
- * Checks whether the {@code Origin} HTTP header of the request matches where the request came
- * from.
- *
- * @param containerRequest
- * the current container request
- * @param originHeader
- * the contents of the {@code Origin} HTTP header
- * @return {@code true} when the origin of the request matches the {@code Origin} HTTP header
- */
- protected boolean isLocalOrigin(HttpServletRequest containerRequest, String originHeader)
- {
- // Make comparable strings from Origin and Location
- String origin = normalizeUri(originHeader);
- if (origin == null)
- return false;
+ /**
+ * Checks whether the {@code Origin} HTTP header of the request matches where the request came
+ * from.
+ *
+ * @param containerRequest
+ * the current container request
+ * @param originHeader
+ * the contents of the {@code Origin} HTTP header
+ * @return {@code true} when the origin of the request matches the {@code Origin} HTTP header
+ */
+ protected boolean isLocalOrigin(HttpServletRequest containerRequest, String originHeader)
+ {
+ // Make comparable strings from Origin and Location
+ String origin = normalizeUri(originHeader);
+ if (origin == null)
+ return false;
- String request = getTargetUriFromRequest(containerRequest);
- if (request == null)
- return false;
+ String request = getTargetUriFromRequest(containerRequest);
+ if (request == null)
+ return false;
- return origin.equalsIgnoreCase(request);
- }
+ return origin.equalsIgnoreCase(request);
+ }
- /**
- * Creates a RFC-6454 comparable URI from the {@code request} requested resource.
- *
- * @param request
- * the incoming request
- * @return only the scheme://host[:port] part, or {@code null} when the origin string is not
- * compliant
- */
- protected final String getTargetUriFromRequest(HttpServletRequest request)
- {
- // Build scheme://host:port from request
- StringBuilder target = new StringBuilder();
- String scheme = request.getScheme();
- if (scheme == null)
- {
- return null;
- }
- else
- {
- scheme = scheme.toLowerCase(Locale.ROOT);
- }
- target.append(scheme);
- target.append("://");
+ /**
+ * Creates a RFC-6454 comparable URI from the {@code request} requested resource.
+ *
+ * @param request
+ * the incoming request
+ * @return only the scheme://host[:port] part, or {@code null} when the origin string is not
+ * compliant
+ */
+ protected final String getTargetUriFromRequest(HttpServletRequest request)
+ {
+ // Build scheme://host:port from request
+ StringBuilder target = new StringBuilder();
+ String scheme = request.getScheme();
+ if (scheme == null)
+ {
+ return null;
+ }
+ else
+ {
+ scheme = scheme.toLowerCase(Locale.ROOT);
+ }
+ target.append(scheme);
+ target.append("://");
- String host = request.getServerName();
- if (host == null)
- {
- return null;
- }
- target.append(host);
+ String host = request.getServerName();
+ if (host == null)
+ {
+ return null;
+ }
+ target.append(host);
- int port = request.getServerPort();
- if ("http".equals(scheme) && port != 80 || "https".equals(scheme) && port != 443)
- {
- target.append(':');
- target.append(port);
- }
+ int port = request.getServerPort();
+ if ("http".equals(scheme) && port != 80 || "https".equals(scheme) && port != 443)
+ {
+ target.append(':');
+ target.append(port);
+ }
- return target.toString();
- }
+ return target.toString();
+ }
- /**
- * Resolves the source URI from the request headers ({@code Origin} or {@code Referer}).
- *
- * @param containerRequest
- * the current container request
- * @return the normalized source URI.
- */
- private String getSourceUri(HttpServletRequest containerRequest)
- {
- String sourceUri = containerRequest.getHeader(WebRequest.HEADER_ORIGIN);
- if (Strings.isEmpty(sourceUri))
- {
- sourceUri = containerRequest.getHeader(WebRequest.HEADER_REFERER);
- }
- return normalizeUri(sourceUri);
- }
+ /**
+ * Resolves the source URI from the request headers ({@code Origin} or {@code Referer}).
+ *
+ * @param containerRequest
+ * the current container request
+ * @return the normalized source URI.
+ */
+ private String getSourceUri(HttpServletRequest containerRequest)
+ {
+ String sourceUri = containerRequest.getHeader(WebRequest.HEADER_ORIGIN);
+ if (Strings.isEmpty(sourceUri))
+ {
+ sourceUri = containerRequest.getHeader(WebRequest.HEADER_REFERER);
+ }
+ return normalizeUri(sourceUri);
+ }
- /**
- * Creates a RFC-6454 comparable URI from the {@code uri} string.
- *
- * @param uri
- * the contents of the Origin or Referer HTTP header
- * @return only the scheme://host[:port] part, or {@code null} when the URI string is not
- * compliant
- */
- protected final String normalizeUri(String uri)
- {
- // the request comes from a privacy sensitive context, flag as non-local origin. If
- // alternative action is required, an implementor can override any of the onAborted,
- // onSuppressed or onAllowed and implement such needed action.
+ /**
+ * Creates a RFC-6454 comparable URI from the {@code uri} string.
+ *
+ * @param uri
+ * the contents of the Origin or Referer HTTP header
+ * @return only the scheme://host[:port] part, or {@code null} when the URI string is not
+ * compliant
+ */
+ protected final String normalizeUri(String uri)
+ {
+ // the request comes from a privacy sensitive context, flag as non-local origin. If
+ // alternative action is required, an implementor can override any of the onAborted,
+ // onSuppressed or onAllowed and implement such needed action.
- if (Strings.isEmpty(uri) || "null".equals(uri))
- return null;
+ if (Strings.isEmpty(uri) || "null".equals(uri))
+ return null;
- StringBuilder target = new StringBuilder();
+ StringBuilder target = new StringBuilder();
- try
- {
- URI originUri = new URI(uri);
- String scheme = originUri.getScheme();
- if (scheme == null)
- {
- return null;
- }
- else
- {
- scheme = scheme.toLowerCase(Locale.ROOT);
- }
+ try
+ {
+ URI originUri = new URI(uri);
+ String scheme = originUri.getScheme();
+ if (scheme == null)
+ {
+ return null;
+ }
+ else
+ {
+ scheme = scheme.toLowerCase(Locale.ROOT);
+ }
- target.append(scheme);
- target.append("://");
+ target.append(scheme);
+ target.append("://");
- String host = originUri.getHost();
- if (host == null)
- {
- return null;
- }
- target.append(host);
+ String host = originUri.getHost();
+ if (host == null)
+ {
+ return null;
+ }
+ target.append(host);
- int port = originUri.getPort();
- boolean portIsSpecified = port != -1;
- boolean isAlternateHttpPort = "http".equals(scheme) && port != 80;
- boolean isAlternateHttpsPort = "https".equals(scheme) && port != 443;
+ int port = originUri.getPort();
+ boolean portIsSpecified = port != -1;
+ boolean isAlternateHttpPort = "http".equals(scheme) && port != 80;
+ boolean isAlternateHttpsPort = "https".equals(scheme) && port != 443;
- if (portIsSpecified && (isAlternateHttpPort || isAlternateHttpsPort))
- {
- target.append(':');
- target.append(port);
- }
- return target.toString();
- }
- catch (URISyntaxException e)
- {
- log.debug("Invalid URI provided: {}, marked conflicting", uri);
- return null;
- }
- }
+ if (portIsSpecified && (isAlternateHttpPort || isAlternateHttpsPort))
+ {
+ target.append(':');
+ target.append(port);
+ }
+ return target.toString();
+ }
+ catch (URISyntaxException e)
+ {
+ log.debug("Invalid URI provided: {}, marked conflicting", uri);
+ return null;
+ }
+ }
- /**
- * Checks whether the domain part of the {@code sourceUri} ({@code Origin} or {@code Referer}
- * header) is whitelisted.
- *
- * @param sourceUri
- * the contents of the {@code Origin} or {@code Referer} HTTP header
- * @return {@code true} when the source domain was whitelisted
- */
- protected boolean isWhitelistedHost(final String sourceUri)
- {
- try
- {
- final String sourceHost = new URI(sourceUri).getHost();
- if (Strings.isEmpty(sourceHost))
- return false;
- for (String whitelistedOrigin : acceptedOrigins)
- {
- if (sourceHost.equalsIgnoreCase(whitelistedOrigin) ||
- sourceHost.endsWith("." + whitelistedOrigin))
- {
- log.trace("Origin {} matched whitelisted origin {}, request accepted",
- sourceUri, whitelistedOrigin);
- return true;
- }
- }
- }
- catch (URISyntaxException e)
- {
- log.debug("Origin: {} not parseable as an URI. Whitelisted-origin check skipped.",
- sourceUri);
- }
+ /**
+ * Checks whether the domain part of the {@code sourceUri} ({@code Origin} or {@code Referer}
+ * header) is whitelisted.
+ *
+ * @param sourceUri
+ * the contents of the {@code Origin} or {@code Referer} HTTP header
+ * @return {@code true} when the source domain was whitelisted
+ */
+ protected boolean isWhitelistedHost(final String sourceUri)
+ {
+ try
+ {
+ final String sourceHost = new URI(sourceUri).getHost();
+ if (Strings.isEmpty(sourceHost))
+ return false;
+ for (String whitelistedOrigin : acceptedOrigins)
+ {
+ if (sourceHost.equalsIgnoreCase(whitelistedOrigin)
+ || sourceHost.endsWith("." + whitelistedOrigin))
+ {
+ log.trace("Origin {} matched whitelisted origin {}, request accepted",
+ sourceUri, whitelistedOrigin);
+ return true;
+ }
+ }
+ }
+ catch (URISyntaxException e)
+ {
+ log.debug("Origin: {} not parseable as an URI. Whitelisted-origin check skipped.",
+ sourceUri);
+ }
- return false;
- }
+ return false;
+ }
}
diff --git a/wicket-core/src/test/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListenerTest.java b/wicket-core/src/test/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListenerTest.java
index 487ed12..f2c290c 100644
--- a/wicket-core/src/test/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListenerTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/protocol/http/FetchMetadataRequestCycleListenerTest.java
@@ -32,152 +32,163 @@ import org.apache.wicket.util.tester.WicketTestCase;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-public class FetchMetadataRequestCycleListenerTest extends WicketTestCase {
-
- private FetchMetadataRequestCycleListener fetchMetadataListener;
-
- @BeforeEach
- void before() {
- withCustomListener(new FetchMetadataRequestCycleListener());
- }
-
- void withCustomListener(FetchMetadataRequestCycleListener fetchMetadataListener) {
- WebApplication application = tester.getApplication();
-
- this.fetchMetadataListener = fetchMetadataListener;
- application.getRequestCycleListeners().add(fetchMetadataListener);
-
- tester.startPage(FirstPage.class);
- tester.assertRenderedPage(FirstPage.class);
- }
-
- /**
- * Tests whether a request with Sec-Fetch-Site = cross-site is aborted
- */
- @Test
- void crossSiteFMAborted() {
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
-
- assertRequestAborted();
- }
-
- /**
- * Tests whether embed requests are aborted by fetch metadata checks
- */
- @Test
- void destEmbedFMAborted() {
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
- tester.addRequestHeader(SEC_FETCH_DEST_HEADER, DEST_EMBED);
-
- assertRequestAborted();
- }
-
- /**
- * Tests whether object requests (sec-fetch-dest :"object" ) are aborted by FM checks
- */
- @Test
- void destObjectAborted() {
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
- tester.addRequestHeader(SEC_FETCH_DEST_HEADER, DEST_OBJECT);
-
- assertRequestAborted();
- }
-
- /**
- * Tests whether a top level navigation request is allowed by FM checks
- */
- @Test
- void topLevelNavigationAllowedFM() {
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, SAME_ORIGIN);
- tester.addRequestHeader(SEC_FETCH_MODE_HEADER, MODE_NAVIGATE);
-
- assertRequestAccepted();
- }
-
- /**
- * Tests that requests rejected by fetch metadata have the Vary header set
- */
- @Test
- void varyHeaderSetWhenFetchMetadataRejectsRequest() {
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
- assertRequestAborted();
-
- String vary = tester.getLastResponse().getHeader("Vary");
-
- if (vary == null) {
- throw new AssertionError("Vary header should not be null");
- }
-
- if (!vary.contains(SEC_FETCH_DEST_HEADER) || !vary.contains(SEC_FETCH_MODE_HEADER)
- || !vary.contains(SEC_FETCH_SITE_HEADER)) {
- throw new AssertionError("Unexpected vary header: " + vary);
- }
- }
-
- /**
- * Tests that requests accepted by fetch metadata have the Vary header set
- */
- @Test
- void varyHeaderSetWhenFetchMetadataAcceptsRequest() {
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, SAME_SITE);
- assertRequestAccepted();
-
- String vary = tester.getLastResponse().getHeader(VARY_HEADER);
- if (vary == null) {
- throw new AssertionError("Vary header should not be null");
- }
-
- if (!vary.contains(SEC_FETCH_DEST_HEADER) || !vary.contains(SEC_FETCH_MODE_HEADER)
- || !vary.contains(SEC_FETCH_SITE_HEADER)) {
- throw new AssertionError("Unexpected vary header: " + vary);
- }
- }
-
- @Test
- void whenAtLeastOnePolicyRejectsRequest_thenRequestRejected() {
- fetchMetadataListener = new FetchMetadataRequestCycleListener(
- (request, page) -> true,
- (request, page) -> true,
- (request, page) -> false,
- (request, page) -> true
- );
-
- withCustomListener(fetchMetadataListener);
- assertRequestAborted();
- }
-
- @Test
- void whenAllPoliciesAcceptRequest_thenRequestAccepted() {
- fetchMetadataListener = new FetchMetadataRequestCycleListener(
- (request, page) -> true,
- (request, page) -> true,
- (request, page) -> true,
- (request, page) -> true
- );
-
- withCustomListener(fetchMetadataListener);
- assertRequestAccepted();
- }
-
- @Test
- void whenCrossOriginRequestToExempted_thenRequestAccepted() {
- fetchMetadataListener.addExemptedPaths("/wicket/bookmarkable/org.apache.wicket.protocol.http.FirstPage");
- withCustomListener(fetchMetadataListener);
-
- tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
- assertRequestAccepted();
- }
-
- private void assertRequestAborted() {
- tester.clickLink("link");
- assertEquals(tester.getLastResponse().getStatus(),
- FetchMetadataRequestCycleListener.ERROR_CODE);
- assertEquals(tester.getLastResponse().getErrorMessage(),
- FetchMetadataRequestCycleListener.ERROR_MESSAGE);
- }
-
- private void assertRequestAccepted() {
- tester.clickLink("link");
- tester.assertRenderedPage(SecondPage.class);
- }
+public class FetchMetadataRequestCycleListenerTest extends WicketTestCase
+{
+
+ private FetchMetadataRequestCycleListener fetchMetadataListener;
+
+ @BeforeEach
+ void before()
+ {
+ withCustomListener(new FetchMetadataRequestCycleListener());
+ }
+
+ void withCustomListener(FetchMetadataRequestCycleListener fetchMetadataListener)
+ {
+ WebApplication application = tester.getApplication();
+
+ this.fetchMetadataListener = fetchMetadataListener;
+ application.getRequestCycleListeners().add(fetchMetadataListener);
+
+ tester.startPage(FirstPage.class);
+ tester.assertRenderedPage(FirstPage.class);
+ }
+
+ /**
+ * Tests whether a request with Sec-Fetch-Site = cross-site is aborted
+ */
+ @Test
+ void crossSiteFMAborted()
+ {
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
+
+ assertRequestAborted();
+ }
+
+ /**
+ * Tests whether embed requests are aborted by fetch metadata checks
+ */
+ @Test
+ void destEmbedFMAborted()
+ {
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
+ tester.addRequestHeader(SEC_FETCH_DEST_HEADER, DEST_EMBED);
+
+ assertRequestAborted();
+ }
+
+ /**
+ * Tests whether object requests (sec-fetch-dest :"object" ) are aborted by FM checks
+ */
+ @Test
+ void destObjectAborted()
+ {
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
+ tester.addRequestHeader(SEC_FETCH_DEST_HEADER, DEST_OBJECT);
+
+ assertRequestAborted();
+ }
+
+ /**
+ * Tests whether a top level navigation request is allowed by FM checks
+ */
+ @Test
+ void topLevelNavigationAllowedFM()
+ {
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, SAME_ORIGIN);
+ tester.addRequestHeader(SEC_FETCH_MODE_HEADER, MODE_NAVIGATE);
+
+ assertRequestAccepted();
+ }
+
+ /**
+ * Tests that requests rejected by fetch metadata have the Vary header set
+ */
+ @Test
+ void varyHeaderSetWhenFetchMetadataRejectsRequest()
+ {
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
+ assertRequestAborted();
+
+ String vary = tester.getLastResponse().getHeader("Vary");
+
+ if (vary == null)
+ {
+ throw new AssertionError("Vary header should not be null");
+ }
+
+ if (!vary.contains(SEC_FETCH_DEST_HEADER) || !vary.contains(SEC_FETCH_MODE_HEADER)
+ || !vary.contains(SEC_FETCH_SITE_HEADER))
+ {
+ throw new AssertionError("Unexpected vary header: " + vary);
+ }
+ }
+
+ /**
+ * Tests that requests accepted by fetch metadata have the Vary header set
+ */
+ @Test
+ void varyHeaderSetWhenFetchMetadataAcceptsRequest()
+ {
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, SAME_SITE);
+ assertRequestAccepted();
+
+ String vary = tester.getLastResponse().getHeader(VARY_HEADER);
+ if (vary == null)
+ {
+ throw new AssertionError("Vary header should not be null");
+ }
+
+ if (!vary.contains(SEC_FETCH_DEST_HEADER) || !vary.contains(SEC_FETCH_MODE_HEADER)
+ || !vary.contains(SEC_FETCH_SITE_HEADER))
+ {
+ throw new AssertionError("Unexpected vary header: " + vary);
+ }
+ }
+
+ @Test
+ void whenAtLeastOnePolicyRejectsRequest_thenRequestRejected()
+ {
+ fetchMetadataListener = new FetchMetadataRequestCycleListener((request, page) -> true,
+ (request, page) -> true, (request, page) -> false, (request, page) -> true);
+
+ withCustomListener(fetchMetadataListener);
+ assertRequestAborted();
+ }
+
+ @Test
+ void whenAllPoliciesAcceptRequest_thenRequestAccepted()
+ {
+ fetchMetadataListener = new FetchMetadataRequestCycleListener((request, page) -> true,
+ (request, page) -> true, (request, page) -> true, (request, page) -> true);
+
+ withCustomListener(fetchMetadataListener);
+ assertRequestAccepted();
+ }
+
+ @Test
+ void whenCrossOriginRequestToExempted_thenRequestAccepted()
+ {
+ fetchMetadataListener
+ .addExemptedPaths("/wicket/bookmarkable/org.apache.wicket.protocol.http.FirstPage");
+ withCustomListener(fetchMetadataListener);
+
+ tester.addRequestHeader(SEC_FETCH_SITE_HEADER, CROSS_SITE);
+ assertRequestAccepted();
+ }
+
+ private void assertRequestAborted()
+ {
+ tester.clickLink("link");
+ assertEquals(tester.getLastResponse().getStatus(),
+ FetchMetadataRequestCycleListener.ERROR_CODE);
+ assertEquals(tester.getLastResponse().getErrorMessage(),
+ FetchMetadataRequestCycleListener.ERROR_MESSAGE);
+ }
+
+ private void assertRequestAccepted()
+ {
+ tester.clickLink("link");
+ tester.assertRenderedPage(SecondPage.class);
+ }
}