Skip to content

Added allowed url property to URLContextClassLoaderFactory - #6527

Open
dlmarion wants to merge 9 commits into
apache:mainfrom
dlmarion:url-ctx-factory-allowed-prop
Open

dlmarion wants to merge 9 commits into
apache:mainfrom
dlmarion:url-ctx-factory-allowed-prop

Conversation

@dlmarion

@dlmarion dlmarion commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@dlmarion dlmarion added this to the 4.0.0 milestone Sep 8, 2026
@dlmarion dlmarion self-assigned this Sep 8, 2026
@dlmarion dlmarion changed the title Url ctx factory allowed prop Added allowed url property to URLContextClassLoaderFactory Sep 8, 2026
@dlmarion
dlmarion marked this pull request as ready for review September 17, 2026 19:56
@dlmarion

Copy link
Copy Markdown
Contributor Author

Full IT build passed

@dlmarion
dlmarion requested a review from ctubbsii September 23, 2026 11:05
@ctubbsii ctubbsii modified the milestones: 4.0.0-alpha-1, 4.0.0 Sep 24, 2026

@phrocker phrocker left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of questions. I'm not sure my assertions are correct. I ran a quick test but may be I misunderstood my test?

@Override
public ClassLoader getClassLoader(String context) {
public void init(ContextClassLoaderEnvironment env) {
String urlPatternProperty = env.getConfiguration().get(URL_PATTERN_PROPERTY);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The factory normalizes the context URL, so file:///opt/ctx/a.jar becomes file:/opt/ctx/a.jar, but the pattern is matched exactly as configured. An admin who sets file:///opt/ctx/.* gets:
user pattern matches: false -- and then contexts are denied, is this expected?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes added in 2e8e0b3 should address this.

try {
return URI.create(p).toURL();
URL url = new URL(p);
checkArgument(urlPattern.matcher(url.toExternalForm()).matches(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't normalize the path does it? Should the URL be normalized to any URL containing .. to avoid path traversal?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes added in 2e8e0b3 should address this.

@dlmarion
dlmarion requested a review from phrocker October 1, 2026 13:10
Assisted-by: OpenAI GPT-6 Luna

@ctubbsii ctubbsii left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made reference to normalization using URI in my other comments, but that doesn't handle URL-encoded things like /%2E%2E/

However, this would:

new URL(url, Paths.get(URLDecoder.decode(url.getPath())).normalize().toString().replace("\\","/"));

This context classloader factory used to be a test. It was only moved to be the default implementation in 3.0 after the VFS stuff was stripped out. However, I think that it should not be the default. I think it's fine if we went back to using this for tests only. The default implementation should be a dummy/pass-through factory that just returns the system classloader for every context. If a user wants different behavior, that's when they set it to a different factory.

Comment on lines +74 to +75
urlPattern =
Pattern.compile(urlPatternProperty.replaceAll(":///", ":/").replaceAll("://", ":/"));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should modify the user's configuration here. The pattern should be expected to validate against any normalized URL. If the user configures a pattern that isn't normalized, we shouldn't guess what they intended, and should just accept the pattern as-is, rejecting if needed.

Comment on lines +84 to +85
// Match and return the actual filesystem target, not the possibly traversing URL spelling.
// toRealPath also resolves symbolic links, so an allowed path cannot escape via a symlink.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be considered an escape. This prevents legitimate use cases for file system organization/management. If the pattern matches, it means we trust whatever is at that location... even if it's a symlink. This is no different than trusting an HTTP location that is a proxy for another location, or any other URL-redirect mechanism. Whether or not the resource at the given URL is the actual resource, or a proxy, is entirely dependent on the URL resolver. We don't need to complicate it... we can just trust the location or not, as it is specified.

If we really want to avoid path traversal bugs, then we can address that with normalization:

    url.toURI().normalize().toURL();

Comment on lines +88 to +94
} else if ("hdfs".equalsIgnoreCase(uri.getScheme())) {
// Match and return the actual filesystem target, not the possibly traversing URL spelling.
// resolvePath also resolves symbolic links, so an allowed path cannot escape via a symlink.
// This may subsequently fail if the HdfsURLStreamHandlerProvider is not on the classpath
org.apache.hadoop.fs.Path resolved =
FileSystem.get(uri, new Configuration()).resolvePath(new org.apache.hadoop.fs.Path(uri));
url = resolved.toUri().toURL();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment here.

Also, there are some weird quirks with how some versions of Hadoop resolve paths as URIs (apache/hadoop#8307). I'm not sure this would handle path traversal avoidance through normalization better than just using URI.normalize.

The comment also makes reference to a specific URLStreamHandlerProvider, but any URLStreamHandlerProvider for hdfs would work for converting it to a URL.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants