-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #6114 - hot deploy of symlink results in symlink name #6160
Issue #6114 - hot deploy of symlink results in symlink name #6160
Conversation
+ Using getAbsolutePath not canonical (too many corner cases with it to be reliable) Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
This PR also addresses a disabled testcase in Issue #5684 |
Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm questioning the need for this change.
The javadoc for File.getCanonicalPath says:
A canonical pathname is both absolute and unique. The precise definition of canonical form is system-dependent. This method first converts this pathname to absolute form if necessary, as if by invoking the getAbsolutePath() method, and then maps it to its unique form in a system-dependent way. This typically involves removing redundant names such as "." and ".." from the pathname, resolving symbolic links (on UNIX platforms), and converting drive letters to a standard case (on Microsoft Windows platforms).
Therefore a canonical path is an absolute path.
There are 2 major reasons. Canonical path requires filesystem permissions for all paths necessary to create the canonical path and it follows symlinks. Example:
Where The use of getCanonicalPath above will result in either an IOException or a more specific FileSystemException. Absolute path does not require extra filesystem permissions, and does not follow links, it merely resolves them (as a directory or a file in place). This gets even more complicated on filesystems that are different from the default, such as network shares, virtual filesystems (git/gitlab/github/perforce/docker to name a few), and even alternate filesystem implementations (seen in increasing frequency in data centers for example) An alternative would be The Scanner operating on getCanonicalPath breaks the ability to have symlinks in /webapps/ directory and deploy as the name on the symlink, it forces the name to be the canonical name (what we don't want). In short, getCanonicalPath is a bad API for ..
|
I'm cautious about making this change at all. It seems more secure to be both absolute and canonical for the majority of operating systems. If some operating systems have different behaviours with canonicalization what is the risk? That a webapp won't be deployed? That a webapp will be redeployed multiple times for every scan? BTW the Scanner has always worked on canonical paths. That being the case, how was it possible for the usecase with multiple symlinks in the |
The testcase for this PR isn't new, it was created back when we underwent the Scanner > PathWatcher > Scanner changes a few years ago. getCanonicalPath (and getCanonicalFile) has a history with this project were we remove it when it becomes a problem (either reported, or discovered), it's been done in the WebAppClassloader already, it's been done in the Resource layer already, it's been done in jetty-start already. Do we have usages of getCanonicalPath still around? Yes, but the vast majority is our own test cases (where we don't have to worry about the variety of environments it runs on). The knowledge of the canonical path in the Scanner has no value, so why break legit usage by insisting on it. |
I'm also cautious of this change. The removal of getCanonicalPath has already caused one significant CVE. Most importantly, the handling must be the same in the Scanner as it is in WebAppProvider, but the Scanner is used elsewhere, so changing it may break other things. Thus I think we need to do is create an interface like public interface Normalizer
{
String normalize(File f) throws IOException;
} Which can optionally be passed into the constructor of Scanner. If not passed, then it is initialised to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see previous comment
The Normalizer would then need to be OS and FileSystem specific. There would just be one with getCanonicalPath for users on Linux with ext3 or ext4. |
@joakime, it may not even be that. It might be a normalizer with getCanonicalPath for any other direct usages of the Scanner and a normalizer with getAbsolutePath for using the scanner in the DeployerProvider. It's not about different OS's. It's about not changing the Scanner just so it syncs with the Deployer. By having it pluggable the deployer can ensure that the same impl is used and not be vulnerable if the Scanner is subsequently changed. It is about non fragile code. |
@joakime when we resolve this issue, can you also take a look at 2 |
This will need to be reworked, in light of the changes in #6317 |
Signed-off-by: Joakim Erdfelt joakim.erdfelt@gmail.com