Skip to content

Commit

Permalink
[SITES-15031] [Regression] Encoded URL is decoded unintentionally and… (
Browse files Browse the repository at this point in the history
#2564)

[SITES-15031] [Regression] Encoded URL is decoded unintentionally and the URL doesn't work

---------

Co-authored-by: cristian cioriia <cioriia@adobe.com>
  • Loading branch information
cristic83 and cioriia committed Aug 3, 2023
1 parent 2ed3f44 commit 68e429d
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.wcm.core.components.internal.link;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import org.apache.commons.lang3.StringUtils;
Expand Down Expand Up @@ -146,11 +148,13 @@ public boolean accepts(@NotNull String path, @NotNull SlingHttpServletRequest re
public @NotNull String map(@NotNull String path, @NotNull SlingHttpServletRequest request) {
ResourceResolver resourceResolver = request.getResourceResolver();
String mappedPath;
Map<String, String> placeholders = new HashMap<>();
String maskedPath = LinkUtil.mask(path, placeholders);
try {
if (vanityConfig == VanityConfig.MAPPING || vanityConfig == VanityConfig.ALWAYS) {
mappedPath = StringUtils.defaultString(resourceResolver.map(request, getPathOrVanityUrl(path, resourceResolver)));
mappedPath = LinkUtil.unmask(StringUtils.defaultString(resourceResolver.map(request, getPathOrVanityUrl(maskedPath, resourceResolver))), placeholders);
} else {
mappedPath = StringUtils.defaultString(resourceResolver.map(request, path));
mappedPath = LinkUtil.unmask(StringUtils.defaultString(resourceResolver.map(request, maskedPath)), placeholders);
}
} catch (Exception e) {
mappedPath = path;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import java.util.stream.Collectors;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -41,7 +43,21 @@ public class LinkUtil {

private final static Logger LOG = LoggerFactory.getLogger(LinkUtil.class);

private final static List<Pattern> PATTERNS = Collections.singletonList(Pattern.compile("(<%[=@].*?%>)"));
//RFC 3986 section 2.2 Reserved Characters
private static final String[] RESERVED_CHARACTERS_ENCODED = {
"%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27", "%28","%29",
"%2A", "%2B", "%2C", "%2F", "%3A", "%3B", "%3D", "%3F", "%40", "%5B", "%5D",
"%2a", "%2b", "%2c", "%2f", "%3a", "%3b", "%3d", "%3f", "%5b", "%5d"
};
private final static List<Pattern> PATTERNS = new ArrayList<>();

static {
PATTERNS.add(Pattern.compile("(<%[=@].*?%>)"));
PATTERNS.addAll(Arrays.stream(RESERVED_CHARACTERS_ENCODED)
.map(encoded -> Pattern.compile("(" + encoded + ")") )
.collect(Collectors.toList()));
}


/**
* Decodes and encoded or escaped URL taking care to not break Adobe Campaign expressions
Expand Down Expand Up @@ -78,8 +94,10 @@ public static String escape(final String path, final String queryString, final S
final String maskedQueryString = mask(queryString, placeholders);
String escaped;
URI parsed;
final String maskedPath = LinkUtil.mask(path, placeholders);

try {
parsed = new URI(path, false);
parsed = new URI(maskedPath, false);
} catch (URIException e) {
parsed = null;
LOG.error(e.getMessage(), e);
Expand All @@ -104,7 +122,7 @@ public static String escape(final String path, final String queryString, final S
.toString();
}
}

} catch (Exception e) {
LOG.error(e.getMessage(), e);
StringBuilder sb = new StringBuilder(path);
Expand Down Expand Up @@ -132,7 +150,7 @@ public static String escape(final String path, final String queryString, final S
* @return the masked {@link String}
* @see LinkUtil#unmask(String, Map)
*/
private static String mask(final String original, final Map<String, String> placeholders) {
static String mask(final String original, final Map<String, String> placeholders) {
if (original == null) {
return null;
}
Expand All @@ -159,7 +177,7 @@ private static String mask(final String original, final Map<String, String> plac
* @param placeholders the {@link Map} of placeholders to replace
* @return the unmasked {@link String}
*/
private static String unmask(final String masked, final Map<String, String> placeholders) {
static String unmask(final String masked, final Map<String, String> placeholders) {
if (masked == null) {
return null;
}
Expand Down Expand Up @@ -208,5 +226,5 @@ private static String replaceEncodedCharacters(final String str) {
.replace("%28", "(")
.replace("%29", ")")
.replace("%2C", ",");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Copyright 2023 Adobe
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
package com.adobe.cq.wcm.core.components.internal.link;

import java.io.UnsupportedEncodingException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class LinkUtilTest {

@Test
void decode_whenReservedCharactersAreEncoded_thenReservedCharactersAreNotAffectedByTheDecode() throws UnsupportedEncodingException {
String encodedPath = "https://api01-platform.stream.co.jp/apiservice/plt3/Mzc3Mg%3d%3d%23Mjkx%23280%23168%230%233FE320DBC400%23OzEwOzEwOzEw%23";
String decodedPath = LinkUtil.decode(encodedPath);
assertEquals(encodedPath, decodedPath);
}

@Test
void decode_whenReservedCharactersAreNotEncoded_thenReservedCharactersAreNotAffectedBTheDecode() throws UnsupportedEncodingException {
String encodedPath = "http://google.com/?q=hello";
String decodedPath = LinkUtil.decode(encodedPath);
assertEquals(encodedPath, decodedPath);
}

@Test
void decode_whenUnreservedCharactersAreEncoded_thenUnreservedCharactersAreDecodedInTheDecodedString() throws UnsupportedEncodingException {
String encodedPath = "http://google.com/?q=hello%7Eworld";
String decodedPath = LinkUtil.decode(encodedPath);
assertEquals("http://google.com/?q=hello~world", decodedPath);
}

@Test
void decode_whenUnreservedCharactersAreNotEncoded_thenUnreservedCharactersAreNotAffectedByTheDecode() throws UnsupportedEncodingException {
String encodedPath = "http://google.com/?q=hello-world";
String decodedPath = LinkUtil.decode(encodedPath);
assertEquals(encodedPath, decodedPath);
}

@Test
void decode_whenCampaignPatternIsPresentInTheString_thenCampaignPatternIsNotAffectedByTheDecode() throws UnsupportedEncodingException {
String encodedPath = "/content/path/to/page.html?recipient=<%= recipient.id %>";
String decodedPath = LinkUtil.decode(encodedPath);
assertEquals(encodedPath, decodedPath);
}
}

0 comments on commit 68e429d

Please sign in to comment.