Skip to content

Commit 8ae29b1

Browse files
committed
Tighten resource path validation
1 parent 7296c36 commit 8ae29b1

4 files changed

Lines changed: 65 additions & 0 deletions

File tree

javaagent-bootstrap/src/main/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoader.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.security.PermissionCollection;
2323
import java.security.Permissions;
2424
import java.security.cert.Certificate;
25+
import java.util.Collections;
2526
import java.util.Enumeration;
2627
import java.util.function.Function;
2728
import java.util.jar.JarEntry;
@@ -358,7 +359,12 @@ private AgentJarResource findVersionedAgentJarResource(
358359
}
359360

360361
@Override
362+
@Nullable
361363
public URL getResource(String resourceName) {
364+
if (!isNormalizedResourcePath(resourceName)) {
365+
return null;
366+
}
367+
362368
URL bootstrapResource = bootstrapProxy.getResource(resourceName);
363369
if (null == bootstrapResource) {
364370
return super.getResource(resourceName);
@@ -368,7 +374,12 @@ public URL getResource(String resourceName) {
368374
}
369375

370376
@Override
377+
@Nullable
371378
public URL findResource(String name) {
379+
if (!isNormalizedResourcePath(name)) {
380+
return null;
381+
}
382+
372383
URL url = findJarResource(name);
373384
if (url != null) {
374385
return url;
@@ -400,6 +411,10 @@ private URL getAgentJarResourceUrl(@Nullable AgentJarResource jarResource) {
400411

401412
@Override
402413
public Enumeration<URL> findResources(String name) throws IOException {
414+
if (!isNormalizedResourcePath(name)) {
415+
return Collections.enumeration(Collections.<URL>emptyList());
416+
}
417+
403418
// find resources from agent initializer jar
404419
Enumeration<URL> delegate = super.findResources(name);
405420
// agent jar can have only one resource for given name
@@ -452,6 +467,10 @@ public BootstrapClassLoaderProxy(Function<String, URL> getResourceFunction) {
452467
@Override
453468
@Nullable
454469
public URL getResource(String resourceName) {
470+
if (!isNormalizedResourcePath(resourceName)) {
471+
return null;
472+
}
473+
455474
// find resource from boot loader
456475
URL url = super.getResource(resourceName);
457476
if (url != null) {

javaagent-bootstrap/src/test/java/io/opentelemetry/javaagent/bootstrap/AgentClassLoaderTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,10 @@ void rejectsNonNormalizedResourceNames() throws Exception {
127127

128128
try (AgentClassLoader loader = new AgentClassLoader(new File(testJarLocation.toURI()))) {
129129
assertThat(loader.getResource("../test.txt")).isNull();
130+
assertThat(loader.getBootstrapProxy().getResource("../test.txt")).isNull();
130131
assertThat(loader.getResource("/test.txt")).isNull();
131132
assertThat(loader.findResource("META-INF/../MANIFEST.MF")).isNull();
133+
assertThat(loader.findResources("META-INF/../MANIFEST.MF").hasMoreElements()).isFalse();
132134
}
133135
}
134136
}

muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollector.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,37 @@ private static boolean isKnownSpiResourcePath(String resource) {
126126
return prefixSeparator > 0 && isSpiFile(resource.substring(prefixSeparator + 1));
127127
}
128128

129+
private static boolean isNormalizedResourcePath(String resourceName) {
130+
if (resourceName.isEmpty()
131+
|| resourceName.startsWith("/")
132+
|| resourceName.startsWith("\\")) {
133+
return false;
134+
}
135+
136+
int segmentStart = 0;
137+
for (int i = 0; i <= resourceName.length(); i++) {
138+
if (i == resourceName.length() || resourceName.charAt(i) == '/') {
139+
if (!isValidResourceSegment(resourceName, segmentStart, i)) {
140+
return false;
141+
}
142+
segmentStart = i + 1;
143+
} else if (resourceName.charAt(i) == '\\') {
144+
return false;
145+
}
146+
}
147+
148+
return true;
149+
}
150+
151+
private static boolean isValidResourceSegment(String resourceName, int start, int end) {
152+
int length = end - start;
153+
return length > 0
154+
&& !(length == 1 && resourceName.charAt(start) == '.')
155+
&& !(length == 2
156+
&& resourceName.charAt(start) == '.'
157+
&& resourceName.charAt(start + 1) == '.');
158+
}
159+
129160
/**
130161
* Traverse a graph of classes starting from {@code adviceClassName} and collect all references to
131162
* both internal (instrumentation) and external classes.
@@ -186,6 +217,10 @@ private InputStream getClassFileStream(String className) throws IOException {
186217
}
187218

188219
private InputStream getResourceStream(String resource) throws IOException {
220+
if (!isNormalizedResourcePath(resource)) {
221+
throw new IllegalArgumentException("Unexpected resource path " + resource);
222+
}
223+
189224
URLConnection connection =
190225
Preconditions.checkNotNull(
191226
resourceLoader.getResource(resource), "Couldn't find resource %s", resource)

muzzle/src/test/java/io/opentelemetry/javaagent/tooling/muzzle/ReferenceCollectorTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,15 @@ public void shouldCreateReferencesForHelperClasses() {
189189
assertHelperInterfaceMethod(helperClass, false);
190190
}
191191

192+
@Test
193+
public void shouldRejectNonNormalizedClassResourcePath() {
194+
ReferenceCollector collector = new ReferenceCollector(s -> false);
195+
196+
assertThatIllegalArgumentException()
197+
.isThrownBy(() -> collector.collectReferencesFromAdvice("../Bad"))
198+
.withMessageContaining("Unexpected resource path");
199+
}
200+
192201
@Test
193202
public void shouldCollectFieldDeclarationReferences() {
194203
ReferenceCollector collector =

0 commit comments

Comments
 (0)