From 442e3aa7e831b8d99a1a1984b389db58480500fe Mon Sep 17 00:00:00 2001 From: JohnNiang Date: Sat, 12 Oct 2024 15:42:42 +0800 Subject: [PATCH] Fix the problem of being able to configure invalid external URL Signed-off-by: JohnNiang --- .../app/infra/properties/HaloProperties.java | 20 ++++++++- .../run/halo/app/PathPrefixPredicateTest.java | 7 +++ .../infra/properties/HaloPropertiesTest.java | 45 +++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 application/src/test/java/run/halo/app/infra/properties/HaloPropertiesTest.java diff --git a/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java b/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java index 2a296dffb8..e89bc2b0c1 100644 --- a/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java +++ b/application/src/main/java/run/halo/app/infra/properties/HaloProperties.java @@ -2,6 +2,7 @@ import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Path; import java.util.HashSet; @@ -69,9 +70,26 @@ public boolean supports(Class clazz) { @Override public void validate(Object target, Errors errors) { var props = (HaloProperties) target; - if (props.isUseAbsolutePermalink() && props.getExternalUrl() == null) { + var externalUrl = props.getExternalUrl(); + if (props.isUseAbsolutePermalink() && externalUrl == null) { errors.rejectValue("externalUrl", "external-url.required.when-using-absolute-permalink", "External URL is required when property `use-absolute-permalink` is set to true."); } + // check if the external URL is a http or https URL and is not an opaque URL. + if (externalUrl != null && !isValidExternalUrl(externalUrl)) { + errors.rejectValue("externalUrl", "external-url.invalid-format", + "External URL must be a http or https URL."); + } + } + + private boolean isValidExternalUrl(URL externalUrl) { + try { + var uri = externalUrl.toURI(); + return !uri.isOpaque() + && uri.getAuthority() != null + && Set.of("http", "https").contains(uri.getScheme()); + } catch (URISyntaxException e) { + return false; + } } } diff --git a/application/src/test/java/run/halo/app/PathPrefixPredicateTest.java b/application/src/test/java/run/halo/app/PathPrefixPredicateTest.java index 1447b4dc59..6ca99b356f 100644 --- a/application/src/test/java/run/halo/app/PathPrefixPredicateTest.java +++ b/application/src/test/java/run/halo/app/PathPrefixPredicateTest.java @@ -2,6 +2,7 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.net.URI; import org.junit.jupiter.api.Test; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -34,4 +35,10 @@ class TestController { } + + @Test + void urlTest() { + URI uri = URI.create("https:///path"); + System.out.println(uri); + } } diff --git a/application/src/test/java/run/halo/app/infra/properties/HaloPropertiesTest.java b/application/src/test/java/run/halo/app/infra/properties/HaloPropertiesTest.java new file mode 100644 index 0000000000..1501c50c45 --- /dev/null +++ b/application/src/test/java/run/halo/app/infra/properties/HaloPropertiesTest.java @@ -0,0 +1,45 @@ +package run.halo.app.infra.properties; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.validation.SimpleErrors; + +class HaloPropertiesTest { + + static Stream validateTest() throws MalformedURLException { + return Stream.of( + Arguments.of(true, new URL("http://localhost:8080"), true), + Arguments.of(false, new URL("http://localhost:8080"), true), + Arguments.of(true, new URL("https://localhost:8080"), true), + Arguments.of(false, new URL("https://localhost:8080"), true), + Arguments.of(true, new URL("ftp://localhost:8080"), false), + Arguments.of(false, new URL("ftp://localhost:8080"), false), + Arguments.of(true, new URL("http:www/halo/run"), false), + Arguments.of(false, new URL("http:www/halo.run"), false), + Arguments.of(true, new URL("https:www/halo/run"), false), + Arguments.of(false, new URL("https:www/halo/run"), false), + Arguments.of(true, new URL("https:///path"), false), + Arguments.of(false, new URL("https:///path"), false), + Arguments.of(true, new URL("http:///path"), false), + Arguments.of(false, new URL("http:///path"), false), + Arguments.of(true, null, false), + Arguments.of(false, null, true) + ); + } + + @ParameterizedTest + @MethodSource + void validateTest(boolean useAbsolutePermalink, URL externalUrl, boolean valid) { + var properties = new HaloProperties(); + properties.setUseAbsolutePermalink(useAbsolutePermalink); + properties.setExternalUrl(externalUrl); + var errors = new SimpleErrors(properties); + properties.validate(properties, errors); + Assertions.assertEquals(valid, !errors.hasErrors()); + } +} \ No newline at end of file