1919
2020import static org .junit .jupiter .api .Assertions .assertEquals ;
2121import static org .junit .jupiter .api .Assertions .assertNotNull ;
22+ import static org .junit .jupiter .api .Assertions .assertNull ;
2223import static org .junit .jupiter .api .Assertions .assertSame ;
2324import static org .junit .jupiter .api .Assertions .assertThrows ;
2425import static org .junit .jupiter .api .Assertions .assertTrue ;
2829import java .nio .file .Path ;
2930import java .nio .file .Paths ;
3031
32+ import org .apache .commons .lang3 .StringUtils ;
3133import org .junit .jupiter .api .Test ;
3234import org .junit .jupiter .api .io .TempDir ;
3335import org .junit .jupiter .params .ParameterizedTest ;
@@ -42,16 +44,29 @@ private Path createDirectory(final Path tempDir, final String other) throws IOEx
4244 return Files .createDirectory (tempDir .resolve (other ));
4345 }
4446
47+ private Path getRelPathToTop () {
48+ final Path startPath = PathUtils .current ().toAbsolutePath ();
49+ final Path parent = startPath ;
50+ final int nameCount = parent .getNameCount ();
51+ final String relName = StringUtils .repeat ("../" , nameCount );
52+ final Path relPath = Paths .get (relName );
53+ // sanity checks
54+ final Path rootPath = relPath .toAbsolutePath ().normalize ();
55+ assertNull (rootPath .getFileName ());
56+ assertEquals (startPath .getRoot (), rootPath );
57+ return relPath ;
58+ }
59+
4560 @ Test
4661 void testAbsolutePath (@ TempDir final Path fenceRootPath ) throws Exception {
4762 // tempDir is the fence
48- final Path childTest = fenceRootPath .resolve ("child/file.txt" );
63+ final Path resolved = fenceRootPath .resolve ("child/file.txt" );
4964 final PathFence fence = PathFence .builder ().setRoots (fenceRootPath ).get ();
5065 // getPath with an absolute string should be allowed
51- final Path childOk = fence .apply (childTest .toString ());
52- assertEquals (childTest .toAbsolutePath ().normalize (), childOk .toAbsolutePath ().normalize ());
66+ final Path childOk = fence .apply (resolved .toString ());
67+ assertEquals (resolved .toAbsolutePath ().normalize (), childOk .toAbsolutePath ().normalize ());
5368 // check with a Path instance should return the same instance when allowed
54- assertSame (childTest , fence .apply (childTest ));
69+ assertSame (resolved , fence .apply (resolved ));
5570 }
5671
5772 @ ParameterizedTest
@@ -64,6 +79,18 @@ void testEmpty(final String test) {
6479 assertSame (path , fence .apply (path ));
6580 }
6681
82+ @ ParameterizedTest
83+ @ ValueSource (strings = { "/a/b" , "/a/b/c" , "/a/b/c/d" , "a" , "a/b" , "a/b/c" , "a/b/c/d" })
84+ public void testEscapeAttempt (final Path fenceRootPath ) {
85+ final Path resolved = fenceRootPath .resolve ("../../etc/passwd" );
86+ final Path relative = Paths .get ("../../etc/passwd" );
87+ final PathFence fence = PathFence .builder ().setRoots (fenceRootPath ).get ();
88+ assertThrows (IllegalArgumentException .class , () -> fence .apply (resolved ));
89+ assertThrows (IllegalArgumentException .class , () -> fence .apply (relative ));
90+ assertThrows (IllegalArgumentException .class , () -> fence .apply (resolved .toString ()));
91+ assertThrows (IllegalArgumentException .class , () -> fence .apply (relative .toString ()));
92+ }
93+
6794 @ Test
6895 void testMultipleFencePaths (@ TempDir final Path tempDir ) throws Exception {
6996 // The fence is inside tempDir
@@ -81,10 +108,9 @@ void testMultipleFencePaths(@TempDir final Path tempDir) throws Exception {
81108 @ Test
82109 void testNormalization (@ TempDir final Path tempDir ) throws Exception {
83110 final Path fenceRootPath = createDirectory (tempDir , "root-one" );
84- final Path weird = fenceRootPath .resolve ("subdir/../other.txt" );
111+ final Path resolved = fenceRootPath .resolve ("subdir/../other.txt" );
85112 final PathFence fence = PathFence .builder ().setRoots (fenceRootPath ).get ();
86- // Path contains '..' but after normalization it's still inside the base
87- assertSame (weird , fence .apply (weird ));
113+ assertSame (resolved , fence .apply (resolved ));
88114 }
89115
90116 @ Test
@@ -98,4 +124,21 @@ void testOutsideFenceThrows(@TempDir final Path tempDir) throws Exception {
98124 assertTrue (msg .contains ("not in the fence" ), () -> "Expected message to mention fence: " + msg );
99125 assertTrue (msg .contains (other .toAbsolutePath ().toString ()), () -> "Expected message to contain the path: " + msg );
100126 }
127+
128+ @ Test
129+ void testResolveRelative () throws Exception {
130+ final PathFence fence = PathFence .builder ().setRoots (Paths .get ("/foo/bar" )).get ();
131+ final Path relPathTop = getRelPathToTop ();
132+ final Path relPath = relPathTop .resolve ("foo/bar" );
133+ assertSame (relPath , fence .apply (relPath ));
134+ }
135+
136+ @ Test
137+ void testResolveRelativeRoot () throws Exception {
138+ final Path relPathTop = getRelPathToTop ();
139+ final PathFence fence = PathFence .builder ().setRoots (relPathTop .resolve ("foo/bar" )).get ();
140+ final Path relPath = relPathTop .resolve ("foo/bar" );
141+ assertSame (relPath , fence .apply (relPath ));
142+ }
143+
101144}
0 commit comments