diff --git a/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java b/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java index d6095c4b4..f8f078da3 100644 --- a/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java +++ b/google-http-client/src/main/java/com/google/api/client/http/UriTemplate.java @@ -320,12 +320,30 @@ public static String expand( } private static String getSimpleValue(String name, String value, CompositeOutput compositeOutput) { + validatePathSegments(name, value, compositeOutput); if (compositeOutput.requiresVarAssignment()) { return String.format("%s=%s", name, compositeOutput.getEncodedValue(value)); } return compositeOutput.getEncodedValue(value); } + private static void validatePathSegments( + String name, String value, CompositeOutput compositeOutput) { + if (compositeOutput == CompositeOutput.SIMPLE) { + if (".".equals(value) || "..".equals(value)) { + throw new IllegalArgumentException(String.format("Invalid value %s for %s.", value, name)); + } + } else if (compositeOutput == CompositeOutput.PLUS) { + for (String segment : Splitter.on('/').split(value)) { + if (".".equals(segment) || "..".equals(segment)) { + throw new IllegalArgumentException( + String.format( + "Value for %s must not contain segments that are exactly . or ..", name)); + } + } + } + } + /** * Expand the template of a composite list property. Eg: If d := ["red", "green", "blue"] then * {/d*} is expanded to "/red/green/blue" diff --git a/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java b/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java index 9fc90ba85..061e42010 100644 --- a/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java +++ b/google-http-client/src/test/java/com/google/api/client/http/UriTemplateTest.java @@ -15,6 +15,7 @@ package com.google.api.client.http; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import com.google.api.client.util.Value; @@ -433,4 +434,54 @@ public void testExpandTemplates_reservedExpansion_mapWithMixedEncodedInput() { assertEquals( "#key1,val1%2F,key2,val2%2F,key3,val%20", UriTemplate.expand("{#keys}", variables, false)); } + + @Test + public void testExpandTemplates_rejectsDotSegmentsInSimpleExpansion() { + Map variables = Maps.newLinkedHashMap(); + variables.put("webhook", ".."); + IllegalArgumentException dotDotEx = + assertThrows( + IllegalArgumentException.class, + () -> UriTemplate.expand("v3/webhooks/{webhook}", variables, false)); + assertEquals("Invalid value .. for webhook.", dotDotEx.getMessage()); + + variables.put("webhook", "."); + IllegalArgumentException dotEx = + assertThrows( + IllegalArgumentException.class, + () -> UriTemplate.expand("v3/webhooks/{webhook}", variables, false)); + assertEquals("Invalid value . for webhook.", dotEx.getMessage()); + } + + @Test + public void testExpandTemplates_rejectsDotSegmentsInReservedExpansion() { + for (String invalidValue : + Arrays.asList( + "projects/p/locations/l/agents/a/sessions/..", + "projects/p/locations/l/agents/a/sessions/.", + "projects/sys-prod-123/databases/default/documents/doc-1/../../default", + "../escape-db", + "projects/sys-prod-123/databases/default/documents/doc-1/./child")) { + Map variables = Maps.newLinkedHashMap(); + variables.put("name", invalidValue); + IllegalArgumentException ex = + assertThrows( + IllegalArgumentException.class, + () -> UriTemplate.expand("v1/{+name}", variables, false)); + assertEquals( + "Value for name must not contain segments that are exactly . or ..", ex.getMessage()); + } + } + + @Test + public void testExpandTemplates_allowsSafeDotsInSimpleAndReservedExpansion() { + Map variables = Maps.newLinkedHashMap(); + variables.put("name", "projects/sys-prod-123/databases/default/documents/my-file..txt"); + assertEquals( + "v1/projects/sys-prod-123/databases/default/documents/my-file..txt", + UriTemplate.expand("v1/{+name}", variables, false)); + + variables.put("id", ".well-known"); + assertEquals("v1/.well-known", UriTemplate.expand("v1/{id}", variables, false)); + } }