This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch 5.9.x-javax in repository https://gitbox.apache.org/repos/asf/tapestry-5.git
commit 3f1ff58749e1172155e0ebb84c4e036e8ecc7d90 Author: Ben Weidig <[email protected]> AuthorDate: Wed Oct 7 08:26:59 2026 +0200 Improving context asset handling --- .../assets/ContextAssetRequestHandler.java | 52 ++++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java index fdb2f402c..b0dba55c4 100644 --- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java +++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/ContextAssetRequestHandler.java @@ -1,4 +1,4 @@ -// Copyright 2010, 2013 The Apache Software Foundation +// Copyright 2010, 2013, 2026 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,7 +18,10 @@ import org.apache.tapestry5.commons.Resource; import org.apache.tapestry5.http.services.Request; import org.apache.tapestry5.http.services.Response; import org.apache.tapestry5.internal.services.ResourceStreamer; +import org.apache.tapestry5.services.ContextAssetProtectionRule; import org.apache.tapestry5.services.assets.AssetRequestHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.regex.Pattern; @@ -31,28 +34,69 @@ import java.util.regex.Pattern; */ public class ContextAssetRequestHandler implements AssetRequestHandler { + private final static Logger LOGGER = LoggerFactory.getLogger(ContextAssetRequestHandler.class); + private final ResourceStreamer resourceStreamer; private final Resource rootContextResource; + private final ContextAssetProtectionRule contextAssetProtectionRule; + private final Pattern illegal = Pattern.compile("^([\\\\/]*((web|meta)-inf.*)|(.*\\.tml$))", Pattern.CASE_INSENSITIVE); - public ContextAssetRequestHandler(ResourceStreamer resourceStreamer, Resource rootContextResource) + public ContextAssetRequestHandler(ResourceStreamer resourceStreamer, Resource rootContextResource, + ContextAssetProtectionRule contextAssetProtectionRule) { this.resourceStreamer = resourceStreamer; this.rootContextResource = rootContextResource; + this.contextAssetProtectionRule = contextAssetProtectionRule; } public boolean handleAssetRequest(Request request, Response response, String extraPath) throws IOException { ChecksumPath path = new ChecksumPath(resourceStreamer, null, extraPath); - if (illegal.matcher(path.resourcePath).matches()) + final Resource resource; + + try + { + resource = rootContextResource.forFile(path.resourcePath); + } + catch (IllegalStateException ex) + { + // Fail closed: treat it as blocked rather than surfacing it as a server error. + if (LOGGER.isWarnEnabled()) + { + LOGGER.warn("Blocked request for context asset '" + path.resourcePath + + "': " + ex.getMessage()); + } + return false; + } + + final String resourcePath = resource.getPath(); + + if (illegal.matcher(resourcePath).matches()) + { + if (LOGGER.isWarnEnabled()) + { + LOGGER.warn("Blocked request for context asset '" + resourcePath + + "': the path resolves to a protected location (WEB-INF, META-INF or a template)."); + } + return false; + } + + // TAP5-2835: Allow custom protection rules mirroring ClasspathAssetProtectionRule + if (contextAssetProtectionRule.block(resourcePath)) { + if (LOGGER.isWarnEnabled()) + { + LOGGER.warn("Blocked request for context asset '" + resourcePath + + "'. Contribute a new ContextAssetProtectionRule if you need this asset to be publicly accessible."); + } return false; } - return path.stream(rootContextResource.forFile(path.resourcePath)); + return path.stream(resource); } }
