Copilot commented on code in PR #25628:
URL: https://github.com/apache/pulsar/pull/25628#discussion_r3170421833
##########
pulsar-package-management/core/src/main/java/org/apache/pulsar/packages/management/core/common/PackageName.java:
##########
@@ -136,7 +137,13 @@ public String toString() {
}
public String toRestPath() {
- return String.format("%s/%s/%s/%s/%s", type, tenant, namespace, name,
version);
+ // Use Guava's urlPathSegmentEscaper to safely encode each segment and
prevents Path Traversal (CWE-22)
+ return String.format("%s/%s/%s/%s/%s",
+ type.toString(),
+ UrlEscapers.urlPathSegmentEscaper().escape(tenant),
+ UrlEscapers.urlPathSegmentEscaper().escape(namespace),
+ UrlEscapers.urlPathSegmentEscaper().escape(name),
+ UrlEscapers.urlPathSegmentEscaper().escape(version));
Review Comment:
`toRestPath()` calls `UrlEscapers.urlPathSegmentEscaper()` repeatedly.
Consider storing the escaper in a local variable or a `private static final`
field to avoid repeated lookups/allocations and make the intent clearer (encode
each segment with the same escaper).
##########
pulsar-package-management/core/src/test/java/org/apache/pulsar/packages/management/core/common/PackageNameTest.java:
##########
@@ -116,4 +116,22 @@ public void testPackageNameErrors() {
PackageName name = PackageName.get("function://public/default/test");
Assert.assertEquals("function://public/default/test@latest",
name.toString());
}
+
+ @Test
+ public void testPathTraversalBypassConstructor() throws Exception {
+ // Create a normal, valid package to bypass the splitter
+ PackageName packageName =
PackageName.get("function://tenant-a/ns/name@v1");
+
+ java.lang.reflect.Field tenantField =
PackageName.class.getDeclaredField("tenant");
+ tenantField.setAccessible(true);
+ tenantField.set(packageName, "tenant-a/../../system-tenant");
+ // Define what the SAFE, patched output should look like
+ String expectedSafePath =
"function/tenant-a%2F..%2F..%2Fsystem-tenant/ns/name/v1";
+
+ // Trigger the vulnerable method
+ String actualPath = packageName.toRestPath();
+ // The Trap: This will fail, because actualPath will still contain
unencoded slashes
+ Assert.assertEquals(actualPath, expectedSafePath,
+ "VULNERABILITY REPLICATED: The toRestPath method failed to
encode path traversal characters!");
Review Comment:
The inline comments and assertion failure message read as if the test is
meant to demonstrate that the vulnerability still exists (“The Trap: This will
fail…”, “VULNERABILITY REPLICATED…”). Since the assertion expects the
safe/encoded output, please update these to describe the intended security
property being verified to avoid confusion when reading test output.
```suggestion
// Create a valid package instance, then mutate an internal field to
verify REST path encoding.
PackageName packageName =
PackageName.get("function://tenant-a/ns/name@v1");
java.lang.reflect.Field tenantField =
PackageName.class.getDeclaredField("tenant");
tenantField.setAccessible(true);
tenantField.set(packageName, "tenant-a/../../system-tenant");
// Verify that path separators in package components are
percent-encoded in the generated REST path.
String expectedSafePath =
"function/tenant-a%2F..%2F..%2Fsystem-tenant/ns/name/v1";
// Invoke the method under test and confirm it preserves
path-segment boundaries safely.
String actualPath = packageName.toRestPath();
// This assertion verifies that traversal characters are encoded
rather than emitted as raw slashes.
Assert.assertEquals(actualPath, expectedSafePath,
"The toRestPath method must encode path traversal characters
in package components.");
```
##########
pulsar-package-management/core/src/test/java/org/apache/pulsar/packages/management/core/common/PackageNameTest.java:
##########
@@ -116,4 +116,22 @@ public void testPackageNameErrors() {
PackageName name = PackageName.get("function://public/default/test");
Assert.assertEquals("function://public/default/test@latest",
name.toString());
}
+
+ @Test
+ public void testPathTraversalBypassConstructor() throws Exception {
+ // Create a normal, valid package to bypass the splitter
+ PackageName packageName =
PackageName.get("function://tenant-a/ns/name@v1");
+
+ java.lang.reflect.Field tenantField =
PackageName.class.getDeclaredField("tenant");
+ tenantField.setAccessible(true);
+ tenantField.set(packageName, "tenant-a/../../system-tenant");
Review Comment:
This test mutates a `PackageName` instance obtained from
`PackageName.get(...)`, which is backed by a static `LoadingCache`. Modifying
the cached instance via reflection can leak state into other tests (or later
lookups of the same key) and cause flaky failures. Prefer constructing a
non-cached instance (e.g., via reflective access to the private constructor) or
otherwise ensure the mutated instance cannot be reused from the cache.
##########
pulsar-package-management/core/src/main/java/org/apache/pulsar/packages/management/core/common/PackageName.java:
##########
@@ -136,7 +137,13 @@ public String toString() {
}
public String toRestPath() {
- return String.format("%s/%s/%s/%s/%s", type, tenant, namespace, name,
version);
+ // Use Guava's urlPathSegmentEscaper to safely encode each segment and
prevents Path Traversal (CWE-22)
Review Comment:
Comment grammar: “...encode each segment and prevents...” should be
“...encode each segment and prevent...” (or rephrase). Also consider
capitalizing consistently (e.g., `URL`/`RFC 3986`) if you keep the comment.
```suggestion
// Use Guava's URL path segment escaper to safely encode each
segment and prevent path traversal (CWE-22).
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]