From 5cefefe1309e5e3ef96373633ad755bf20e7f16d Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:28:29 +0800 Subject: [PATCH] fix: restrict thumbnail generation to images in the attachment library (#7079) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core /milestone 2.20.x #### What this PR does / why we need it: 限制缩略图生成仅针对附件库中的图片,防止任意 URI 的生成行为带来的潜在攻击风险 先 merge #7077 后才能合并此 PR #### Does this PR introduce a user-facing change? ```release-note 限制缩略图生成仅针对附件库中的图片,防止任意 URI 的生成行为带来的潜在攻击风险 ``` --- .../halo/app/content/HtmlThumbnailSrcsetInjector.java | 2 +- .../halo/app/core/attachment/ThumbnailService.java | 11 +++++++++++ .../core/attachment/impl/ThumbnailServiceImpl.java | 7 +++++++ .../app/core/endpoint/theme/ThumbnailEndpoint.java | 2 +- .../app/theme/finders/impl/ThumbnailFinderImpl.java | 2 +- .../core/endpoint/theme/ThumbnailEndpointTest.java | 2 +- .../theme/finders/impl/ThumbnailFinderImplTest.java | 4 ++-- 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/run/halo/app/content/HtmlThumbnailSrcsetInjector.java b/application/src/main/java/run/halo/app/content/HtmlThumbnailSrcsetInjector.java index 39f6cedab8..1cffc0756c 100644 --- a/application/src/main/java/run/halo/app/content/HtmlThumbnailSrcsetInjector.java +++ b/application/src/main/java/run/halo/app/content/HtmlThumbnailSrcsetInjector.java @@ -65,7 +65,7 @@ static String buildSizesAttr() { */ public static Mono generateSrcset(URI src, ThumbnailService thumbnailService) { return Flux.fromArray(ThumbnailSize.values()) - .flatMap(size -> thumbnailService.generate(src, size) + .flatMap(size -> thumbnailService.get(src, size) .map(thumbnail -> thumbnail.toString() + " " + size.getWidth() + "w") ) .collect(StringBuilder::new, (builder, srcsetValue) -> { diff --git a/application/src/main/java/run/halo/app/core/attachment/ThumbnailService.java b/application/src/main/java/run/halo/app/core/attachment/ThumbnailService.java index dfa1680008..e6e5d8500e 100644 --- a/application/src/main/java/run/halo/app/core/attachment/ThumbnailService.java +++ b/application/src/main/java/run/halo/app/core/attachment/ThumbnailService.java @@ -18,5 +18,16 @@ public interface ThumbnailService { */ Mono generate(URI imageUri, ThumbnailSize size); + /** + *

Get thumbnail by the given image uri and size.

+ *

It depends on the {@link #generate(URI, ThumbnailSize)} method, currently the thumbnail + * generation is limited to the attachment service, that is, the thumbnail is strongly + * associated with the attachment.

+ * + * @return if thumbnail exists, return the thumbnail uri, otherwise return the original image + * uri + */ + Mono get(URI imageUri, ThumbnailSize size); + Mono delete(URI imageUri); } diff --git a/application/src/main/java/run/halo/app/core/attachment/impl/ThumbnailServiceImpl.java b/application/src/main/java/run/halo/app/core/attachment/impl/ThumbnailServiceImpl.java index 9c34ce64cc..b8c1508fbd 100644 --- a/application/src/main/java/run/halo/app/core/attachment/impl/ThumbnailServiceImpl.java +++ b/application/src/main/java/run/halo/app/core/attachment/impl/ThumbnailServiceImpl.java @@ -72,6 +72,13 @@ private Mono doGenerate(URI imageUri, ThumbnailSize size) { }); } + @Override + public Mono get(URI imageUri, ThumbnailSize size) { + return fetchThumbnail(imageUri, size) + .map(thumbnail -> URI.create(thumbnail.getSpec().getThumbnailUri())) + .defaultIfEmpty(imageUri); + } + @Override public Mono delete(URI imageUri) { Assert.notNull(imageUri, "Image uri must not be null"); diff --git a/application/src/main/java/run/halo/app/core/endpoint/theme/ThumbnailEndpoint.java b/application/src/main/java/run/halo/app/core/endpoint/theme/ThumbnailEndpoint.java index 012bdd249c..0edc18e7fb 100644 --- a/application/src/main/java/run/halo/app/core/endpoint/theme/ThumbnailEndpoint.java +++ b/application/src/main/java/run/halo/app/core/endpoint/theme/ThumbnailEndpoint.java @@ -66,7 +66,7 @@ public RouterFunction endpoint() { private Mono getThumbnailByUri(ServerRequest request) { var query = new ThumbnailQuery(request.queryParams()); - return thumbnailService.generate(query.getUri(), query.getSize()) + return thumbnailService.get(query.getUri(), query.getSize()) .filterWhen(uri -> isAccessible(request, uri)) .defaultIfEmpty(query.getUri()) .flatMap(uri -> ServerResponse.temporaryRedirect(uri).build()); diff --git a/application/src/main/java/run/halo/app/theme/finders/impl/ThumbnailFinderImpl.java b/application/src/main/java/run/halo/app/theme/finders/impl/ThumbnailFinderImpl.java index e8eac936b8..9ea14868ad 100644 --- a/application/src/main/java/run/halo/app/theme/finders/impl/ThumbnailFinderImpl.java +++ b/application/src/main/java/run/halo/app/theme/finders/impl/ThumbnailFinderImpl.java @@ -18,7 +18,7 @@ public class ThumbnailFinderImpl implements ThumbnailFinder { @Override public Mono gen(String uriStr, String size) { return Mono.fromSupplier(() -> URI.create(uriStr)) - .flatMap(uri -> thumbnailService.generate(uri, ThumbnailSize.fromName(size))) + .flatMap(uri -> thumbnailService.get(uri, ThumbnailSize.fromName(size))) .map(URI::toString) .onErrorResume(Throwable.class, e -> { log.debug("Failed to generate thumbnail for [{}], error: [{}]", uriStr, diff --git a/application/src/test/java/run/halo/app/core/endpoint/theme/ThumbnailEndpointTest.java b/application/src/test/java/run/halo/app/core/endpoint/theme/ThumbnailEndpointTest.java index 6d02dac6c7..5446b2ceb0 100644 --- a/application/src/test/java/run/halo/app/core/endpoint/theme/ThumbnailEndpointTest.java +++ b/application/src/test/java/run/halo/app/core/endpoint/theme/ThumbnailEndpointTest.java @@ -39,7 +39,7 @@ void setUp() { @Test void thumbnailUriNotAccessible() { - when(thumbnailService.generate(any(), any())) + when(thumbnailService.get(any(), any())) .thenReturn(Mono.just(URI.create("/thumbnail-not-found.png"))); webClient.get() .uri("/thumbnails/-/via-uri?size=l&uri=/myavatar.png") diff --git a/application/src/test/java/run/halo/app/theme/finders/impl/ThumbnailFinderImplTest.java b/application/src/test/java/run/halo/app/theme/finders/impl/ThumbnailFinderImplTest.java index 3fb8d6e3b9..b53e0fbeef 100644 --- a/application/src/test/java/run/halo/app/theme/finders/impl/ThumbnailFinderImplTest.java +++ b/application/src/test/java/run/halo/app/theme/finders/impl/ThumbnailFinderImplTest.java @@ -42,13 +42,13 @@ void shouldNotGenWhenUriIsInvalid() { @Test void shouldGenWhenUriIsValid() { - when(thumbnailService.generate(any(), any())) + when(thumbnailService.get(any(), any())) .thenReturn(Mono.just(URI.create("/test-thumb.jpg"))); thumbnailFinder.gen("/test.jpg", "l") .as(StepVerifier::create) .expectNext("/test-thumb.jpg") .verifyComplete(); - verify(thumbnailService).generate(any(), any()); + verify(thumbnailService).get(any(), any()); } }