This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/karaf.git
The following commit(s) were added to refs/heads/main by this push:
new 61d8201712 Restrict WebConsole plugin getResource() to /res/ paths
(#2892)
61d8201712 is described below
commit 61d82017122f0b502c8c6235f03a4d01979f0d1d
Author: JB Onofré <[email protected]>
AuthorDate: Thu Sep 17 09:37:38 2026 +0200
Restrict WebConsole plugin getResource() to /res/ paths (#2892)
* Restrict WebConsole plugin getResource() to /res/ paths
FeaturesPlugin, GogoPlugin, HttpPlugin and InstancePlugin override
getResource() and pass the request path straight to
classLoader.getResource() with no restriction, unlike Felix's own
AbstractServlet.getResource() which only spools paths starting with
/res/. Restore that check in all four plugins so only the plugins'
own static resources can be served.
* Normalize path and fix dead null-check in WebConsole getResource()
FeaturesPlugin, GogoPlugin, HttpPlugin and InstancePlugin checked
`path == null` after already calling `path.substring(...)` on it,
so the check could never run (a null path throws NPE from substring
first). Move the null guard before the substring call so it is
actually effective (GogoPlugin, FeaturesPlugin, InstancePlugin had
this dead check; HttpPlugin gets the same guard for consistency).
Also close a residual path-traversal gap in the /res/ prefix check
added earlier in this branch: a request like
/res/../forbidden_directory/secret could still satisfy
`startsWith("/res/")` before being handed to classLoader.getResource().
Normalize the path with URI.normalize() before the prefix check, so
".." segments are resolved first and traversal attempts no longer
start with /res/ after normalization; a path that fails to parse as
a URI is rejected rather than passed through.
Addresses PR review feedback from CptBartender and holgerfriedrich.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
---------
Co-authored-by: Claude Sonnet 5 <[email protected]>
---
.../org/apache/karaf/webconsole/features/FeaturesPlugin.java | 10 +++++++++-
.../java/org/apache/karaf/webconsole/gogo/GogoPlugin.java | 11 ++++++++++-
.../java/org/apache/karaf/webconsole/http/HttpPlugin.java | 11 ++++++++++-
.../org/apache/karaf/webconsole/instance/InstancePlugin.java | 11 ++++++++++-
4 files changed, 39 insertions(+), 4 deletions(-)
diff --git
a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
index 503ed7bf03..e2a6aae6f7 100644
---
a/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
+++
b/webconsole/features/src/main/java/org/apache/karaf/webconsole/features/FeaturesPlugin.java
@@ -131,8 +131,16 @@ public class FeaturesPlugin extends AbstractServlet {
@Override
protected URL getResource(String path) {
+ if (path == null) {
+ return null;
+ }
path = path.substring(NAME.length() + 1);
- if (path == null || path.isEmpty()) {
+ try {
+ path = URI.create(path).normalize().getPath();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ if (path.isEmpty() || !path.startsWith("/res/")) {
return null;
}
URL url = this.classLoader.getResource(path);
diff --git
a/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
b/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
index 9c7b5dc182..0c1a1300c9 100644
---
a/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
+++
b/webconsole/gogo/src/main/java/org/apache/karaf/webconsole/gogo/GogoPlugin.java
@@ -31,6 +31,7 @@ import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
+import java.net.URI;
import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessController;
@@ -96,8 +97,16 @@ public class GogoPlugin extends AbstractServlet {
@Override
protected URL getResource(String path) {
+ if (path == null) {
+ return null;
+ }
path = path.substring(NAME.length() + 1);
- if (path == null || path.isEmpty()) {
+ try {
+ path = URI.create(path).normalize().getPath();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ if (path.isEmpty() || !path.startsWith("/res/")) {
return null;
}
URL url = this.getClass().getClassLoader().getResource(path);
diff --git
a/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
b/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
index 2ce2b03a4e..e384cc571c 100644
---
a/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
+++
b/webconsole/http/src/main/java/org/apache/karaf/webconsole/http/HttpPlugin.java
@@ -19,6 +19,7 @@ package org.apache.karaf.webconsole.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
@@ -97,8 +98,16 @@ public class HttpPlugin extends AbstractServlet {
@Override
protected URL getResource(String path) {
+ if (path == null) {
+ return null;
+ }
path = path.substring(NAME.length() + 1);
- if (path.isEmpty()) {
+ try {
+ path = URI.create(path).normalize().getPath();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ if (path.isEmpty() || !path.startsWith("/res/")) {
return null;
}
URL url = this.classLoader.getResource(path);
diff --git
a/webconsole/instance/src/main/java/org/apache/karaf/webconsole/instance/InstancePlugin.java
b/webconsole/instance/src/main/java/org/apache/karaf/webconsole/instance/InstancePlugin.java
index c8b1ff61f5..a4f292861d 100644
---
a/webconsole/instance/src/main/java/org/apache/karaf/webconsole/instance/InstancePlugin.java
+++
b/webconsole/instance/src/main/java/org/apache/karaf/webconsole/instance/InstancePlugin.java
@@ -19,6 +19,7 @@ package org.apache.karaf.webconsole.instance;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
+import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@@ -156,8 +157,16 @@ public class InstancePlugin extends AbstractServlet {
@Override
protected URL getResource(String path) {
+ if (path == null) {
+ return null;
+ }
path = path.substring(NAME.length() + 1);
- if (path == null || path.isEmpty()) {
+ try {
+ path = URI.create(path).normalize().getPath();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ if (path.isEmpty() || !path.startsWith("/res/")) {
return null;
}
URL url = this.classLoader.getResource(path);