diff --git a/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedUtils.java b/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedUtils.java index b63d6609..1ecdf067 100644 --- a/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedUtils.java +++ b/commafeed-server/src/main/java/com/commafeed/backend/feed/FeedUtils.java @@ -3,7 +3,6 @@ package com.commafeed.backend.feed; import java.util.List; import org.apache.commons.lang3.StringUtils; -import org.apache.hc.client5.http.utils.Base64; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; @@ -69,34 +68,8 @@ public class FeedUtils { if (StringUtils.isBlank(url)) { return url; } - return "rest/server/proxy?u=" + imageProxyEncoder(url); - } - public static String rot13(String msg) { - StringBuilder message = new StringBuilder(); - - for (char c : msg.toCharArray()) { - if (c >= 'a' && c <= 'm') { - c += 13; - } else if (c >= 'n' && c <= 'z') { - c -= 13; - } else if (c >= 'A' && c <= 'M') { - c += 13; - } else if (c >= 'N' && c <= 'Z') { - c -= 13; - } - message.append(c); - } - - return message.toString(); - } - - public static String imageProxyEncoder(String url) { - return Base64.encodeBase64String(rot13(url).getBytes()); - } - - public static String imageProxyDecoder(String code) { - return rot13(new String(Base64.decodeBase64(code))); + return "rest/server/proxy?u=" + ImageProxyUrl.encode(url); } public static void removeUnwantedFromSearch(List entries, List keywords) { diff --git a/commafeed-server/src/main/java/com/commafeed/backend/feed/ImageProxyUrl.java b/commafeed-server/src/main/java/com/commafeed/backend/feed/ImageProxyUrl.java new file mode 100644 index 00000000..682cf22e --- /dev/null +++ b/commafeed-server/src/main/java/com/commafeed/backend/feed/ImageProxyUrl.java @@ -0,0 +1,37 @@ +package com.commafeed.backend.feed; + +import org.apache.hc.client5.http.utils.Base64; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public class ImageProxyUrl { + + public static String encode(String url) { + return Base64.encodeBase64String(rot13(url).getBytes()); + } + + public static String decode(String code) { + return rot13(new String(Base64.decodeBase64(code))); + } + + private static String rot13(String msg) { + StringBuilder message = new StringBuilder(); + + for (char c : msg.toCharArray()) { + if (c >= 'a' && c <= 'm') { + c += 13; + } else if (c >= 'n' && c <= 'z') { + c -= 13; + } else if (c >= 'A' && c <= 'M') { + c += 13; + } else if (c >= 'N' && c <= 'Z') { + c -= 13; + } + message.append(c); + } + + return message.toString(); + } + +} diff --git a/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java b/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java index e59049cf..e2776f0e 100644 --- a/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java +++ b/commafeed-server/src/main/java/com/commafeed/frontend/resource/ServerREST.java @@ -17,7 +17,7 @@ import com.commafeed.CommaFeedConfiguration; import com.commafeed.CommaFeedVersion; import com.commafeed.backend.HttpGetter; import com.commafeed.backend.HttpGetter.HttpResult; -import com.commafeed.backend.feed.FeedUtils; +import com.commafeed.backend.feed.ImageProxyUrl; import com.commafeed.frontend.model.ServerInfo; import com.commafeed.security.Roles; @@ -77,7 +77,7 @@ public class ServerREST { return Response.status(Status.FORBIDDEN).build(); } - url = FeedUtils.imageProxyDecoder(url); + url = ImageProxyUrl.decode(url); try { HttpResult result = httpGetter.get(url); return Response.ok(result.getContent()).build(); diff --git a/commafeed-server/src/test/java/com/commafeed/backend/feed/ImageProxyUrlTest.java b/commafeed-server/src/test/java/com/commafeed/backend/feed/ImageProxyUrlTest.java new file mode 100644 index 00000000..1bf96dec --- /dev/null +++ b/commafeed-server/src/test/java/com/commafeed/backend/feed/ImageProxyUrlTest.java @@ -0,0 +1,57 @@ +package com.commafeed.backend.feed; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class ImageProxyUrlTest { + + @ParameterizedTest + @ValueSource( + strings = { + // simple URL + "http://example.com/image.jpg", + + // URL with query parameters + "https://example.com/image.png?width=100&height=200", + + // URL with special characters + "https://example.com/path/image.gif?param=value&other=test#fragment", + + // URL with non-ascii characters + "https://example.com/image-ñáéíóú.jpg", + + // blank URL + "", + + // URL with port number + "http://localhost:8080/images/photo.jpg", + + // long URL + "https://very-long-domain-name-example.com/very/long/path/to/image/file/with/many/segments/image.jpg?param1=value1¶m2=value2¶m3=value3", + + // URL with mixed case + "HTTPS://EXAMPLE.COM/Image.JPG", + + // URL with port number + "https://example123.com/image123.jpg?id=456789", }) + void testEncodingDecoding(String originalUrl) { + String encoded = ImageProxyUrl.encode(originalUrl); + String decoded = ImageProxyUrl.decode(encoded); + + Assertions.assertEquals(originalUrl, decoded); + } + + @Test + void encodeProducesDifferentResultForDifferentUrls() { + String url1 = "https://example.com/image1.jpg"; + String url2 = "https://example.com/image2.jpg"; + + String encoded1 = ImageProxyUrl.encode(url1); + String encoded2 = ImageProxyUrl.encode(url2); + + Assertions.assertNotEquals(encoded1, encoded2); + } + +} \ No newline at end of file