1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2017, 2019, Chris Fraire <cfraire@me.com>. 22 * Copyright (c) 2008, 2018, Oracle and/or its affiliates. All rights reserved. 23 */ 24 package org.opengrok.indexer.util; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertTrue; 28 29 import java.io.File; 30 import java.io.IOException; 31 import java.nio.file.Files; 32 import java.nio.file.Path; 33 import java.nio.file.Paths; 34 import java.util.ArrayList; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Set; 38 import org.junit.After; 39 import org.junit.Assert; 40 import org.junit.Ignore; 41 import org.junit.Rule; 42 import org.junit.Test; 43 import org.opengrok.indexer.condition.ConditionalRun; 44 import org.opengrok.indexer.condition.ConditionalRunRule; 45 import org.opengrok.indexer.condition.UnixPresent; 46 47 /** 48 * Represents a container for tests of {@link PathUtils}. 49 */ 50 public class PathUtilsTest { 51 52 private final List<File> tempDirs = new ArrayList<>(); 53 54 @Rule 55 public ConditionalRunRule rule = new ConditionalRunRule(); 56 57 @After tearDown()58 public void tearDown() { 59 try { 60 tempDirs.forEach((tempDir) -> { 61 try { 62 IOUtils.removeRecursive(tempDir.toPath()); 63 } catch (IOException e) { 64 // ignore 65 } 66 }); 67 } finally { 68 tempDirs.clear(); 69 } 70 } 71 72 @Test shouldHandleSameInputs()73 public void shouldHandleSameInputs() throws IOException { 74 final String USR_BIN = Paths.get("/usr/bin").toString(); 75 String rel = PathUtils.getRelativeToCanonical(USR_BIN, USR_BIN); 76 Assert.assertEquals(USR_BIN + " rel to itself", "", rel); 77 } 78 79 @Test shouldHandleEffectivelySameInputs()80 public void shouldHandleEffectivelySameInputs() throws IOException { 81 String USR_BIN = Paths.get(Paths.get("/usr/bin").toUri()).toString(); 82 String rel = PathUtils.getRelativeToCanonical(USR_BIN + File.separator, USR_BIN); 83 Assert.assertEquals(USR_BIN + " rel to ~itself", "", rel); 84 } 85 86 @Test 87 @ConditionalRun(UnixPresent.class) shouldHandleLinksOfArbitraryDepthWithValidation()88 public void shouldHandleLinksOfArbitraryDepthWithValidation() 89 throws IOException, ForbiddenSymlinkException { 90 // Create real directories 91 File sourceRoot = createTemporaryDirectory("srcroot"); 92 assertTrue("sourceRoot.isDirectory()", sourceRoot.isDirectory()); 93 94 File realDir1 = createTemporaryDirectory("realdir1"); 95 assertTrue("realDir1.isDirectory()", realDir1.isDirectory()); 96 File realDir1b = new File(realDir1, "b"); 97 realDir1b.mkdir(); 98 assertTrue("realDir1b.isDirectory()", realDir1b.isDirectory()); 99 100 File realDir2 = createTemporaryDirectory("realdir2"); 101 assertTrue("realDir2.isDirectory()", realDir2.isDirectory()); 102 103 // Create symlink #1 underneath source root. 104 final String SYMLINK1 = "symlink1"; 105 File symlink1 = new File(sourceRoot, SYMLINK1); 106 Files.createSymbolicLink(Paths.get(symlink1.getPath()), 107 Paths.get(realDir1.getPath())); 108 assertTrue("symlink1.exists()", symlink1.exists()); 109 110 // Create symlink #2 underneath realdir1/b. 111 final String SYMLINK2 = "symlink2"; 112 File symlink2 = new File(realDir1b, SYMLINK2); 113 Files.createSymbolicLink(Paths.get(symlink2.getPath()), 114 Paths.get(realDir2.getPath())); 115 assertTrue("symlink2.exists()", symlink2.exists()); 116 117 // Assert symbolic path 118 Path sympath = Paths.get(sourceRoot.getPath(), SYMLINK1, "b", 119 SYMLINK2); 120 assertTrue("2-link path exists", Files.exists(sympath)); 121 122 // Test v. realDir1 canonical 123 String realDir1Canon = realDir1.getCanonicalPath(); 124 String rel = PathUtils.getRelativeToCanonical(sympath.toString(), 125 realDir1Canon); 126 assertEquals("because links aren't validated", "b/" + SYMLINK2, rel); 127 128 // Test v. realDir1 canonical with validation and no allowed links 129 Set<String> allowedSymLinks = new HashSet<>(); 130 ForbiddenSymlinkException expex = null; 131 try { 132 PathUtils.getRelativeToCanonical(sympath.toString(), realDir1Canon, 133 allowedSymLinks, null); 134 } catch (ForbiddenSymlinkException e) { 135 expex = e; 136 } 137 Assert.assertNotNull("because no links allowed, arg1 is returned fully", 138 expex); 139 140 // Test v. realDir1 canonical with validation and an allowed link 141 allowedSymLinks.add(symlink2.getPath()); 142 rel = PathUtils.getRelativeToCanonical(sympath.toString(), 143 realDir1Canon, allowedSymLinks, null); 144 assertEquals("because link is OKed", "b/" + SYMLINK2, rel); 145 } 146 147 @Test 148 @ConditionalRun(UnixPresent.class) shouldHandleLinksToCanonicalChildrenOfAllowedLinks()149 public void shouldHandleLinksToCanonicalChildrenOfAllowedLinks() 150 throws IOException, ForbiddenSymlinkException { 151 // Create real directories 152 File sourceRoot = createTemporaryDirectory("srcroot"); 153 assertTrue(sourceRoot + " should be a dir", sourceRoot.isDirectory()); 154 155 File realDir1 = createTemporaryDirectory("realdir1"); 156 assertTrue(realDir1 + " should be a dir", realDir1.isDirectory()); 157 158 File realDir1b = new File(realDir1, "b"); 159 assertTrue(realDir1b + " should be created", realDir1b.mkdir()); 160 161 // Create symlink #1 to realdir1/ in source root. 162 final String SYMLINK1 = "symlink1"; 163 File symlink1 = new File(sourceRoot, SYMLINK1); 164 Files.createSymbolicLink(symlink1.toPath(), realDir1.toPath()); 165 assertTrue(symlink1 + " should exist", symlink1.exists()); 166 167 // Create symlink #2 to realdir1/b in source root. 168 final String SYMLINK2 = "symlink2"; 169 File symlink2 = new File(realDir1b, SYMLINK2); 170 Files.createSymbolicLink(symlink2.toPath(), realDir1b.toPath()); 171 assertTrue(symlink2 + " should exist", symlink2.exists()); 172 173 // Test symlink2 v. realDir1 canonical with validation and an allowed symlink1 174 Set<String> allowedSymLinks = new HashSet<>(); 175 allowedSymLinks.add(symlink1.getPath()); 176 177 String realDir1Canon = realDir1.getCanonicalPath(); 178 String rel = PathUtils.getRelativeToCanonical(symlink2.toString(), 179 realDir1Canon, allowedSymLinks, null); 180 assertEquals("symlink2 should be allowed implicitly as a canonical child of symlink1", 181 "b", rel); 182 } 183 184 @Ignore("macOS has /var symlink, and I also made a second link, `myhome'.") 185 @Test shouldResolvePrivateVarOnMacOS()186 public void shouldResolvePrivateVarOnMacOS() throws IOException { 187 final String MY_VAR_FOLDERS = 188 "/var/folders/58/546k9lk08xl56t0059bln0_h0000gp/T/tilde/Documents"; 189 final String EXPECTED_REL = MY_VAR_FOLDERS.substring("/var/".length()); 190 String rel = PathUtils.getRelativeToCanonical(MY_VAR_FOLDERS, 191 "/private/var"); 192 Assert.assertEquals("/var/run rel to /private/var", EXPECTED_REL, rel); 193 } 194 195 @Ignore("For ad-hoc testing with \\ in paths.") 196 @Test mightResolveBackslashesToo()197 public void mightResolveBackslashesToo() throws IOException { 198 final String MY_VAR_FOLDERS = 199 "\\var\\folders\\58\\546k9lk08xl56t0059bln0_h0000gp\\T"; 200 final String EXPECTED_REL = MY_VAR_FOLDERS.substring("/var/".length()). 201 replace('\\', '/'); 202 String rel = PathUtils.getRelativeToCanonical(MY_VAR_FOLDERS, 203 "/private/var"); 204 Assert.assertEquals("/var/run rel to /private/var", EXPECTED_REL, rel); 205 } 206 createTemporaryDirectory(String name)207 private File createTemporaryDirectory(String name) throws IOException { 208 File f = Files.createTempDirectory(name).toFile(); 209 tempDirs.add(f); 210 return f; 211 } 212 } 213