From 909a199214066e2b9702d068711a13b6c62a3213 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 31 Aug 2026 17:08:27 +0100 Subject: [PATCH] docs: warn that getFromResource does not filter its resource name Motivation: `getFromResource` passes the resource name to the class loader as given. Its only guard is the trailing-slash check that stops directory resources being served; unlike `getFromResourceDirectory`, which routes the request path through `safeJoinPaths` and rejects `..` and separator characters, it applies no traversal filtering at all. An application that builds the name from request input, for example `getFromResource(s"public/$name")`, can therefore be made to resolve a resource outside the intended prefix against a directory-backed class loader. The scaladoc did not say so, and the contrast with the sibling directive makes that easy to miss. Modification: Document the behaviour on both the Scala and the Java DSL entry points, pointing at `getFromResourceDirectory` as the filtering alternative. Result: The trust boundary of the directive is stated where a caller reads it. Tests: Not run - docs only References: None - documents that getFromResource does not filter its resource name --- .../server/directives/FileAndResourceDirectives.scala | 4 ++++ .../server/directives/FileAndResourceDirectives.scala | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/http/src/main/scala/org/apache/pekko/http/javadsl/server/directives/FileAndResourceDirectives.scala b/http/src/main/scala/org/apache/pekko/http/javadsl/server/directives/FileAndResourceDirectives.scala index a7b61e1f7..17c7de185 100644 --- a/http/src/main/scala/org/apache/pekko/http/javadsl/server/directives/FileAndResourceDirectives.scala +++ b/http/src/main/scala/org/apache/pekko/http/javadsl/server/directives/FileAndResourceDirectives.scala @@ -50,6 +50,10 @@ abstract class FileAndResourceDirectives extends ExecutionDirectives { * Completes GET requests with the content of the given resource loaded from the default ClassLoader, * using the default content type resolver. * If the resource cannot be found or read the Route rejects the request. + * + * The path is passed to the class loader as given: unlike `getFromResourceDirectory` this directive does not + * reject `..` or separator characters, so a path built from request input can resolve to a resource outside the + * intended prefix. Validate any request-derived part of the path, or use `getFromResourceDirectory` instead. */ def getFromResource(path: String): Route = RouteAdapter { D.getFromResource(path) diff --git a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/FileAndResourceDirectives.scala b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/FileAndResourceDirectives.scala index 19a6c9955..c97e50bcf 100644 --- a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/FileAndResourceDirectives.scala +++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/FileAndResourceDirectives.scala @@ -94,6 +94,10 @@ trait FileAndResourceDirectives { * Completes GET requests with the content of the given class-path resource. * If the resource cannot be found or read the Route rejects the request. * + * The resource name is passed to the class loader as given: unlike `getFromResourceDirectory` this directive does + * not reject `..` or separator characters, so a name built from request input can resolve to a resource outside the + * intended prefix. Validate any request-derived part of the name, or use `getFromResourceDirectory` instead. + * * @group fileandresource */ def getFromResource(resourceName: String)(implicit resolver: ContentTypeResolver): Route = @@ -103,6 +107,8 @@ trait FileAndResourceDirectives { * Completes GET requests with the content of the given resource. * If the resource is a directory or cannot be found or read the Route rejects the request. * + * The resource name is not checked for path traversal, see `getFromResource(resourceName)` above. + * * @group fileandresource */ def getFromResource(