Skip to content

Commit dd47ae3

Browse files
authored
Disallow unwise characters (#1131)
Resolves #1130 This prohibits unwise characters in paths and also removes them from Slug headers
1 parent 7e1eb49 commit dd47ae3

File tree

8 files changed

+43
-7
lines changed

8 files changed

+43
-7
lines changed

core/common/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies {
2121

2222
implementation "commons-codec:commons-codec:$commonsCodecVersion"
2323
implementation "jakarta.xml.bind:jakarta.xml.bind-api:$jaxbApiVersion"
24+
implementation "org.apache.commons:commons-lang3:$commonsLangVersion"
2425
implementation "org.eclipse.microprofile.config:microprofile-config-api"
2526
implementation "org.slf4j:slf4j-api:$slf4jVersion"
2627

core/common/src/main/java/org/trellisldp/common/HttpConstants.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ public final class HttpConstants {
130130
/** The Memento link parameter indicating the ending range of a TimeMap. */
131131
public static final String UNTIL = "until";
132132

133+
/** A collection of "unwise" characters according to RFC 3987. */
134+
public static final String UNWISE_CHARACTERS = "<>{}`^\\%\"|";
135+
133136
/** The implied or default set of IRIs used with a Prefer header. */
134137
public static final Set<IRI> DEFAULT_REPRESENTATION = Set.of(PreferContainment, PreferMembership,
135138
PreferUserManaged, PreferServerManaged);

core/common/src/main/java/org/trellisldp/common/Slug.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.commons.codec.DecoderException;
2222
import org.apache.commons.codec.net.URLCodec;
23+
import org.apache.commons.lang3.StringUtils;
2324
import org.slf4j.Logger;
2425

2526
/**
@@ -80,8 +81,9 @@ private static String decodeSlug(final String value) {
8081
}
8182

8283
private static String cleanSlugString(final String value) {
83-
// Remove any fragment URIs and query parameters
84-
// Then trim the string and replace any remaining whitespace or slash characters with underscores
85-
return value.split("#")[0].split("\\?")[0].trim().replaceAll("[\\s/]+", "_");
84+
// Remove any fragment URIs, query parameters and whitespace
85+
final String base = StringUtils.deleteWhitespace(value.split("#")[0].split("\\?")[0]);
86+
// Remove any "unwise" characters plus '/'
87+
return StringUtils.replaceChars(base, HttpConstants.UNWISE_CHARACTERS + "/", "");
8688
}
8789
}

core/common/src/test/java/org/trellisldp/common/SlugTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
class SlugTest {
2828

2929
private static final String SLUG_VALUE = "slugValue";
30-
private static final String SLUG_UNDERSCORE_VALUE = "slug_value";
30+
private static final String SLUG_UNDERSCORE_VALUE = "slugvalue";
3131
private static final String CHECK_SLUG_VALUE = "Check slug value";
3232

3333
@Test
@@ -61,6 +61,12 @@ void testQueryParam() {
6161
assertEquals(SLUG_VALUE, slug.getValue(), CHECK_SLUG_VALUE);
6262
}
6363

64+
@Test
65+
void testUnwiseCharacters() {
66+
final Slug slug = Slug.valueOf("a|b^c\"d{e}f\\g`h^i<j>k");
67+
assertEquals("abcdefghijk", slug.getValue(), CHECK_SLUG_VALUE);
68+
}
69+
6470
@Test
6571
void testBadInput() {
6672
assertNull(Slug.valueOf("An invalid % value"), "Check invalid input");

core/http/src/main/java/org/trellisldp/http/TrellisHttpFilter.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import javax.ws.rs.core.Link;
4545
import javax.ws.rs.ext.Provider;
4646

47+
import org.apache.commons.lang3.StringUtils;
4748
import org.apache.commons.rdf.api.IRI;
4849
import org.trellisldp.common.AcceptDatetime;
4950
import org.trellisldp.common.LdpResource;
@@ -85,6 +86,8 @@ public void setExtensions(final Map<String, IRI> extensions) {
8586

8687
@Override
8788
public void filter(final ContainerRequestContext ctx) {
89+
// Validate path
90+
validatePath(ctx);
8891
// Validate headers
8992
validateAcceptDatetime(ctx);
9093
validateRange(ctx);
@@ -103,6 +106,13 @@ public void filter(final ContainerRequestContext ctx) {
103106
}
104107
}
105108

109+
private void validatePath(final ContainerRequestContext ctx) {
110+
final String path = ctx.getUriInfo().getPath();
111+
if (StringUtils.containsAny(path, UNWISE_CHARACTERS)) {
112+
ctx.abortWith(status(BAD_REQUEST).build());
113+
}
114+
}
115+
106116
private void validateAcceptDatetime(final ContainerRequestContext ctx) {
107117
final String acceptDatetime = ctx.getHeaderString(ACCEPT_DATETIME);
108118
if (acceptDatetime != null && AcceptDatetime.valueOf(acceptDatetime) == null) {

core/http/src/test/java/org/trellisldp/http/AbstractTrellisHttpResourceTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ abstract class AbstractTrellisHttpResourceTest extends BaseTrellisHttpResourceTe
155155
private static final String VAL_VERSION = "version";
156156
private static final String VERSION_PARAM = "?version=1496262729";
157157
private static final String PATH_REL_CHILD = "/child";
158-
private static final String PATH_REL_GRANDCHILD = "/child_grandchild";
159-
private static final String GRANDCHILD_SUFFIX = "_grandchild";
158+
private static final String PATH_REL_GRANDCHILD = "/childgrandchild";
159+
private static final String GRANDCHILD_SUFFIX = "grandchild";
160160
private static final String WEAK_PREFIX = "W/\"";
161161
private static final String PREFER_PREFIX = "return=representation; include=\"";
162162

core/http/src/test/java/org/trellisldp/http/TrellisHttpFilterTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,18 @@ void testTestRootSlash() {
5454
filter.filter(mockContext);
5555
verify(mockContext, never().description("Trailing slash should trigger a redirect!")).abortWith(any());
5656
}
57+
58+
@Test
59+
void testUnwisePath() {
60+
when(mockContext.getUriInfo()).thenReturn(mockUriInfo);
61+
when(mockUriInfo.getPath()).thenReturn("/foo/bar/one|two");
62+
when(mockUriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<>());
63+
64+
final TrellisHttpFilter filter = new TrellisHttpFilter();
65+
filter.setMutatingMethods(emptyList());
66+
filter.setExtensions(emptyMap());
67+
68+
filter.filter(mockContext);
69+
verify(mockContext).abortWith(any());
70+
}
5771
}

core/http/src/test/resources/logback-test.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
</encoder>
88
</appender>
99

10-
<logger name="org.trellisldp" additivity="false" level="DEBUG">
10+
<logger name="org.trellisldp" additivity="false" level="INFO">
1111
<appender-ref ref="STDOUT"/>
1212
</logger>
1313
<root additivity="false" level="WARN">

0 commit comments

Comments
 (0)