Java file canonicalPath with tailing '.. ' leads to inconsistent behavior
Asked Answered
M

1

6

I'm currently investigating in some pathTraversal related security mechanisms and came across a weird behavior of java.io.File.getCanonicalPath(). I thought CanonicalPath will always represent the true unique path of the abstract underlying File. However if the file name consists a of two dots followed by a space the CanonicalPath does not seem to represent the correct path anymore.

Here is the example:

File root = new File("c:/git/");
String relative = ".. /.. \\";
File concatFile = new File (root.getCanonicalPath(), relative);

System.out.println("ConcatFileAbsolute: '" + concatFile.getAbsolutePath() + "'");
System.out.println("ConcatFileCanonical: '" + concatFile.getCanonicalPath() + "'");

File canonFile = new File(concatFile.getCanonicalPath());
System.out.println("\ncanonFileCanonical: '" + canonFile.getCanonicalPath() + "'");
System.out.println("canonFileAbsolute: '" + canonFile.getAbsolutePath() + "'");
System.out.println("canonFileName: '" + canonFile.getName() + "'\n");

for (File file : canonFile.listFiles()) {
    System.out.println("canon: '" + file.getCanonicalPath() + "' - absolute: '" + file.getAbsolutePath()+ "'");
}

Console Output:

ConcatFileAbsolute: 'C:\git\.. \.. '
ConcatFileCanonical: 'C:\git\.. \'

canonFileCanonical: 'C:\git\'
canonFileAbsolute: 'C:\git\.. '
canonFileName: '.. '

canon: 'C:\git\.. \$Recycle.Bin' - absolute: 'C:\git\.. \$Recycle.Bin'
canon: 'C:\git\.. \.m2' - absolute: 'C:\git\.. \.m2'
canon: 'C:\git\.. \boot' - absolute: 'C:\git\.. \boot'
...other content of C:/ 

As you can see although the canonicalPath of canonFile clearly indicates it's location in C:\git\, but .listFiles lists all files in C:.

This only happens if I use the new File(String, String) constructor. If I change the third line to File concatFile = new File (root.getCanonicalPath() + relative); then the output is as expected:

ConcatFileAbsolute: 'C:\git.. \.. '
ConcatFileCanonical: 'C:\git\' 
- The following output then lists files under C:\git\

I don't know if this behavior is similar on unix-like systems.

Can somebody clarify this behavior? Is this intended?

Thanks upfront!

Maxfield answered 22/12, 2016 at 15:46 Comment(1)
If you want to add something to your question, edit the question, code in comments is largely unreadable.Sou
F
3

Congrats! You found a possible bug (or feature?) of WinAPI.

More specifically, your question can be reduced to one line of code:

Why does new File("c:\\temp\\.. ").getCanonicalPath() returns c:\temp\ instead of c:\?

Short answer: because of tailing space after .. (yep, you've mentioned above it).

Long answer: if we will look inside Java implementation of underlying class, we will found that for Windows it is WinNTFileSystem class, where native method canonicalize0("c:\\temp\\.. ") returns this broken value. Why? To test it I've written simple VBS code to test Win API:

Dim folderName
folderName = "c:\temp\.. "

Dim fso
Set fso = CreateObject("Scripting.FileSystemObject")

Dim fullpath
fullpath = fso.GetAbsolutePathName(folderName)

WScript.Echo "fullpath:    " & fullpath

It gives exactly same result:

fullpath:    C:\temp
Friulian answered 22/12, 2016 at 16:29 Comment(2)
Is there any kind of error reporting on that call? .. looks like a filename, the API might be just deciding to toss it as an invalid one. Either way not returning `c:` doesn't seem completely unreasonable.Sou
Thank you very much. This explains it at least to OS level! I looked up the implementation of java.io.File (construtor as well as getCanonicalPath) and you are right. The fs-specific implementation of canonicalize and resolve seem to be the problem.Maxfield

© 2022 - 2024 — McMap. All rights reserved.