diff --git a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java index 958b3babce7c..ac9ea29a5dca 100644 --- a/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -88,7 +88,15 @@ else if (code == HttpURLConnection.HTTP_NOT_FOUND) { @Override public boolean isReadable() { try { - URL url = getURL(); + return checkReadable(getURL()); + } + catch (IOException ex) { + return false; + } + } + + boolean checkReadable(URL url) { + try { if (ResourceUtils.isFileURL(url)) { // Proceed with file system resolution File file = getFile(); diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java index 6374f2768b9d..f99adce06ce4 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -142,6 +142,18 @@ public boolean exists() { return (resolveURL() != null); } + /** + * This implementation checks for the resolution of a resource URL upfront, + * then proceeding with {@link AbstractFileResolvingResource}'s length check. + * @see java.lang.ClassLoader#getResource(String) + * @see java.lang.Class#getResource(String) + */ + @Override + public boolean isReadable() { + URL url = resolveURL(); + return (url != null && checkReadable(url)); + } + /** * Resolves a URL for the underlying class path resource. * @return the resolved URL, or {@code null} if not resolvable diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java index eb4109c22361..6d9dd2ecf330 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/PathResourceLookupFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -72,7 +72,7 @@ public Mono apply(ServerRequest request) { try { Resource resource = this.location.createRelative(path); - if (resource.exists() && resource.isReadable() && isResourceUnderLocation(resource)) { + if (resource.isReadable() && isResourceUnderLocation(resource)) { return Mono.just(resource); } else { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java index 0dfb73d0eafe..09cc636cca5a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/function/PathResourceLookupFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -71,7 +71,7 @@ public Optional apply(ServerRequest request) { try { Resource resource = this.location.createRelative(path); - if (resource.exists() && resource.isReadable() && isResourceUnderLocation(resource)) { + if (resource.isReadable() && isResourceUnderLocation(resource)) { return Optional.of(resource); } else { @@ -110,10 +110,7 @@ private boolean isInvalidPath(String path) { return true; } } - if (path.contains("..") && StringUtils.cleanPath(path).contains("../")) { - return true; - } - return false; + return path.contains("..") && StringUtils.cleanPath(path).contains("../"); } private boolean isResourceUnderLocation(Resource resource) throws IOException { @@ -144,10 +141,8 @@ else if (resource instanceof ClassPathResource) { if (!resourcePath.startsWith(locationPath)) { return false; } - if (resourcePath.contains("%") && StringUtils.uriDecode(resourcePath, StandardCharsets.UTF_8).contains("../")) { - return false; - } - return true; + return !resourcePath.contains("%") || + !StringUtils.uriDecode(resourcePath, StandardCharsets.UTF_8).contains("../"); }