-
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
MultiPart Part.write(String fileName) - Write method used unexpected path #1337
Comments
Seems like we are following the spec. javax.servlet.http.Part.write(String filename); http://docs.oracle.com/javaee/7/api/javax/servlet/http/Part.html#write-java.lang.String-
|
@joakime Yes, that's true, but please see this issue for Spring: https://jira.spring.io/browse/SPR-15257 |
@hrabosch I added some more details about this at that spring issue. Spring has its implementation wrong, seems it was relying on the forked/non-standard javadoc from Tomcat about how the |
I'll add some checks to see if the path given is absolute or not. If absolute we will throw an exception, as that is incorrect according to the spec. |
I agree, I don't think it is against the spec to pass a URI starting with /. The only question is what is that relative to? The spec is pretty clear, but if other containers have been more flexible then we need to raise it with the EG. |
The issue has been raised with the servlet EG, just waiting on some considered, official response. |
The EG engaged in some discussion on this, however, the outcome is not entirely clear. I think a public draft is coming in the near future, so it may be prudent to wait to see the exact wording in the javadoc. |
I believe Servlet 4.0 (in Jetty 10.x) will change this behavior. |
While I can't find the EG discussion, I was able to find the issue tracking the decision at javaee/servlet-spec#172 The updated /**
* A convenience method to write this uploaded item to disk.
*
* <p>This method is not guaranteed to succeed if called more than once for
* the same part. This allows a particular implementation to use, for
* example, file renaming, where possible, rather than copying all of the
* underlying data, thus gaining a significant performance benefit.
*
* @param fileName The location into which the uploaded part should
be stored. The value may be a file name or a path. The actual
location of the file in the filesystem is relative to {@link
javax.servlet.MultipartConfigElement#getLocation()}. Absolute
paths are used as provided and are relative to
<code>getLocation()</code>. Note: that this is a system
dependent string and URI notation may not be acceptable on all
systems. For portability, this string should be generated with
the File or Path APIs.
*
* @throws IOException if an error occurs.
*/
public void write(String fileName) throws IOException; Perhaps internally we should use the updated
That seems to honor the new javadoc language correctly, and put the onus on developers to use the updated |
Found EG discussion archive about this. http://download.oracle.com/javaee-archive/servlet-spec.java.net/jsr369-experts/2017/03/0558.html |
As you can see by the discussion, the final wording did not address the difference in implementation behaviour of tomcat: as the javadoc currently reads, an "absolute" path must be interpreted relative to the configured multipart location (ie write to location + path) , whereas tomcat will really treat it as absolute (ie write to just path). As the wording says that even absolute paths must be interpreted relative to multipart location, it is not clear what should be done if the path uses .. to write outside of the multipart location - throwing an exception was discussed in the EG, but not specified in the javadoc. |
This entire reply is just my opinion. There's 2 parts of the updated javadoc I'd like to focus on ... Part 1: The question about "relative to" and "absolute" vs "relative"
To me, "is relative to" is equal to Note: Absolute vs Relative depends entirely on the OS and FileSystem used. The problem with testing for "absolute" You cannot assume that a import java.nio.file.FileSystems;
import java.nio.file.Path;
public class AbsoluteTest
{
public static void main(String[] args)
{
isAbsolute("/is/it/abs");
isAbsolute("\\is\\it\\abs");
isAbsolute("\\\\ceres\\joakim\\abs");
isAbsolute("D:\\is\\it\\abs");
}
private static void isAbsolute(String fileName)
{
// FIXME: This is wrong, we cannot assume default FileSystem
Path path = FileSystems.getDefault().getPath(fileName);
System.out.printf("path [%s].isAbsolute=%b%n", fileName, path.isAbsolute());
}
} results in ...
You might be tempted to use Part 2: The question about portability and system independence of API
To me, this pushes the responsibility for "sane" usage of this API on to the user of the API, and away from the implementation. This could also be equivalent of using This section also takes the API away from being consistent and compatible on all environments to being environment specific. Some examples / observations on windows ... import java.io.File;
import java.nio.file.Path;
public class PathFun
{
public static void main(String[] args)
{
testPath(new File(System.getProperty("user.dir")).toPath().toAbsolutePath()); // PWD/CWD directory
testPath(new File("D:\\database").toPath()); // On drive D
testPath(new File("C:\\projects").toPath()); // On drive C
testPath(new File("\\\\ceres\\joakim\\archive").toPath()); // A network reference
}
public static void testPath(Path path)
{
System.out.println("path = " + path);
// on windows, none of these strings are absolute
System.out.println(" p1 = " + path.resolve("/is/this/abs")); // starts with '/'
System.out.println(" p2 = " + path.resolve("\\is\\this\\abs")); // starts with '\\'
System.out.println(" p3 = " + path.resolve("is/this/abs")); // does not start with '/' or '\\'
System.out.println(" p4 = " + path.resolve("is\\this\\abs")); // does not start with '/' or '\\'
// but these are
System.out.println(" p5 = " + path.resolve("D:\\alt"));
System.out.println(" p6 = " + path.resolve("\\\\ceres\\public\\alt"));
}
} The output (on windows) ...
In conclusion: I think we should move our implementation to use a The argument made in the mailing list that the security issue with |
@janbartel @gregw want me to prepare a PR for this? |
@joakime I think we should update along the lines you say... but perhaps we need a compliance mode to maintain our existing behaviour? |
@joakime so long as the result is that all filenames will be resolved against the configured getLocation(), and we throw an error if you try and write outside of that. |
The spec javadoc on this is still a mess. I've raised another issue at jakarta to try and get some clarity: jakartaee/servlet#274 |
Raised PR at jakarta: jakartaee/servlet#276 |
Signed-off-by: Jan Bartel <[email protected]>
Signed-off-by: Jan Bartel <[email protected]>
+ We cannot determine if a String represents an absolute or relative path simply by using the String itself. We must use a Path and resolve against it, allowing the runtime JVM, its OS, its FileSystems, and its FileStore implementations to make that call. We do this by using a resolved java.nio.file.Path and asking it to resolve a new path from a provided String. This satisfies the FileStore and FileSystem requirements, and in the end pushes the call on what is relative or absolute on the FileSystem implementation. + Switched entirely to Path from File as it improves behaviors on all runtime JVMs + Using FileStore for permissions changing as File.setReadable() is broken on Windows. + Moved _location calculation to Constructor. + Eliminated .deleteOnExit() as it doesn't work on Windows (at all) and is only performed at Runtime exit on OSX. Signed-off-by: Joakim Erdfelt <[email protected]>
Signed-off-by: Joakim Erdfelt <[email protected]>
Signed-off-by: Jan Bartel <[email protected]>
Signed-off-by: Jan Bartel <[email protected]>
…5119) * Issue #1337 Use either absolute or relative multipart file location Signed-off-by: Jan Bartel <[email protected]>
Hi,
In MultiPartInputStreamParser class is method
public void write(String fileName) throws IOException
where original JavaDoc says:From Spring Framework is it called by class StandardMultipartHttpServletRequest in transferTo method which looks like:
And in my case, there is absolutePath.
And in Jetty implementation of write method is:
Result is that there is duplicated path because _tmpDir contains path already path to tmp files.
There should be checking if variable fileName is absolute, shouldn't be?
The text was updated successfully, but these errors were encountered: