Skip to content

Commit 4fb0244

Browse files
committed
Enfoce single version removal in content versioning
Prior to this commit, content based version strategies would remove all instances of the version string in the request path when trying to resolve the original resource with the chain. This can cause issues in rare cases where there is a collision between the content version and some other version string in the request path. Because this strategy is based on the contents of the file itself, we should only remove the last instance of the version string and then attempt to resolve the original file. Fixes gh-36698
1 parent 0725bf4 commit 4fb0244

6 files changed

Lines changed: 34 additions & 2 deletions

File tree

spring-webflux/src/main/java/org/springframework/web/reactive/resource/AbstractFileNameVersionStrategy.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ public abstract class AbstractFileNameVersionStrategy implements VersionStrategy
5454

5555
@Override
5656
public String removeVersion(String requestPath, String version) {
57-
return StringUtils.delete(requestPath, "-" + version);
57+
String versionString = "-" + version;
58+
int index = requestPath.lastIndexOf(versionString);
59+
if (index != -1) {
60+
return requestPath.substring(0, index) + requestPath.substring(index + versionString.length());
61+
}
62+
return requestPath;
5863
}
5964

6065
@Override

spring-webflux/src/main/java/org/springframework/web/reactive/resource/VersionResourceResolver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ private Mono<Resource> resolveVersionedResource(@Nullable ServerWebExchange exch
180180
}
181181

182182
String simplePath = versionStrategy.removeVersion(requestPath, candidate);
183+
if (ResourceHandlerUtils.shouldIgnoreInputPath(simplePath)) {
184+
return Mono.empty();
185+
}
183186
return chain.resolveResource(exchange, simplePath, locations)
184187
.filterWhen(resource -> versionStrategy.getResourceVersion(resource)
185188
.map(actual -> {

spring-webflux/src/test/java/org/springframework/web/reactive/resource/ContentBasedVersionStrategyTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ void removeVersion() {
6262
assertThat(this.strategy.removeVersion(String.format(path, "-", hash), hash)).isEqualTo(String.format(path, "", ""));
6363
}
6464

65+
@Test
66+
void removeVersionOnlyOnce() {
67+
String hash = "sha";
68+
String path = "font-awesome/css%s%s/font-awesome.min%s%s.css";
69+
70+
assertThat(this.strategy.removeVersion(String.format(path, "-", hash, "-", hash), hash)).isEqualTo(String.format(path, "-", hash, "", ""));
71+
}
72+
6573
@Test
6674
void getResourceVersion() throws Exception {
6775
Resource expected = new ClassPathResource("test/bar.css", getClass());

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/AbstractVersionStrategy.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,12 @@ protected static class FileNameVersionPathStrategy implements VersionPathStrateg
134134

135135
@Override
136136
public String removeVersion(String requestPath, String version) {
137-
return StringUtils.delete(requestPath, "-" + version);
137+
String versionString = "-" + version;
138+
int index = requestPath.lastIndexOf(versionString);
139+
if (index != -1) {
140+
return requestPath.substring(0, index) + requestPath.substring(index + versionString.length());
141+
}
142+
return requestPath;
138143
}
139144

140145
@Override

spring-webmvc/src/main/java/org/springframework/web/servlet/resource/VersionResourceResolver.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ public VersionResourceResolver addVersionStrategy(VersionStrategy strategy, Stri
177177
}
178178

179179
String simplePath = versionStrategy.removeVersion(requestPath, candidateVersion);
180+
if (ResourceHandlerUtils.shouldIgnoreInputPath(simplePath)) {
181+
return null;
182+
}
180183
Resource baseResource = chain.resolveResource(request, simplePath, locations);
181184
if (baseResource == null) {
182185
return null;

spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ContentBasedVersionStrategyTests.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ void removeVersion() {
6363
assertThat(this.versionStrategy.removeVersion(String.format(file, "-", hash), hash)).isEqualTo(String.format(file, "", ""));
6464
}
6565

66+
@Test
67+
void removeVersionOnlyOnce() {
68+
String hash = "sha";
69+
String file = "font-awesome/css%s%s/font-awesome.min%s%s.css";
70+
71+
assertThat(this.versionStrategy.removeVersion(String.format(file, "-", hash, "-", hash), hash)).isEqualTo(String.format(file, "-", hash, "", ""));
72+
}
73+
6674
@Test
6775
void getResourceVersion() throws IOException {
6876
Resource expected = new ClassPathResource("test/bar.css", getClass());

0 commit comments

Comments
 (0)