View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  
18  package org.apache.commons.text.lookup;
19  
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  
25  import java.io.IOException;
26  import java.nio.charset.StandardCharsets;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.Paths;
30  
31  import org.apache.commons.lang3.StringUtils;
32  import org.apache.commons.text.StringSubstitutor;
33  import org.junit.jupiter.api.Test;
34  import org.junit.jupiter.api.io.TempDir;
35  
36  /**
37   * Tests {@link FileStringLookup}.
38   */
39  public class FileStringLookupTest {
40  
41      private static final Path DOCUEMENT_PATH = Paths.get("src/test/resources/org/apache/commons/text/document.properties");
42      private static final Path CURRENT_PATH = Paths.get(StringUtils.EMPTY);
43  
44      public static String readDocumentFixtureString() throws IOException {
45          return new String(Files.readAllBytes(DOCUEMENT_PATH), StandardCharsets.UTF_8);
46      }
47  
48      public static void testFence(final String expectedString, final FileStringLookup fileStringLookup) {
49          assertEquals(expectedString, fileStringLookup.apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
50          assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:/src/test/resources/org/apache/commons/text/document.properties"));
51          assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:../src/test/resources/org/apache/commons/text/document.properties"));
52      }
53  
54      public static void testFence(final StringSubstitutor stringSubstitutor) throws IOException {
55          assertEquals(readDocumentFixtureString(), stringSubstitutor.replace("${file:UTF-8:" + DOCUEMENT_PATH + "}"));
56          assertThrows(IllegalArgumentException.class, () -> stringSubstitutor.replace("${file:UTF-8:/foo.txt}"));
57          assertThrows(IllegalArgumentException.class, () -> stringSubstitutor.replace("${file:UTF-8:../foo.txt}"));
58      }
59  
60      @Test
61      void testDefaultInstanceBadCharsetName() {
62          assertThrows(IllegalArgumentException.class,
63                  () -> FileStringLookup.INSTANCE.apply("BAD_CHARSET_NAME:src/test/resources/org/apache/commons/text/document.properties"));
64      }
65  
66      @Test
67      void testDefaultInstanceBadDocumentPath() {
68          assertThrows(IllegalArgumentException.class, () -> FileStringLookup.INSTANCE.apply("BAD_CHARSET_NAME:src/test/resources/DOCUMENT_NOT_FOUND.TXT"));
69      }
70  
71      @Test
72      void testDefaultInstanceMissingFilePart() {
73          assertThrows(IllegalArgumentException.class, () -> FileStringLookup.INSTANCE.apply(StandardCharsets.UTF_8.name()));
74      }
75  
76      @Test
77      void testDefaultInstanceNull() {
78          assertNull(FileStringLookup.INSTANCE.apply(null));
79      }
80  
81      @Test
82      void testDefaultInstanceOne() throws Exception {
83          final String expectedString = readDocumentFixtureString();
84          assertEquals(expectedString, FileStringLookup.INSTANCE.apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
85      }
86  
87      @Test
88      void testDefaultInstanceToString() {
89          // does not blow up and gives some kind of string.
90          assertFalse(FileStringLookup.INSTANCE.toString().isEmpty());
91      }
92  
93      @Test
94      void testFenceBadDirOne() throws Exception {
95          final FileStringLookup fileStringLookup = new FileStringLookup(Paths.get("dir does not exist at all"));
96          assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
97          assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:/src/test/resources/org/apache/commons/text/document.properties"));
98          assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:../src/test/resources/org/apache/commons/text/document.properties"));
99      }
100 
101     @Test
102     void testFenceBadDirPlusGoodOne() throws Exception {
103         final String expectedString = readDocumentFixtureString();
104         final FileStringLookup fileStringLookup = new FileStringLookup(Paths.get("dir does not exist at all"), CURRENT_PATH);
105         testFence(expectedString, fileStringLookup);
106     }
107 
108     @Test
109     void testFenceCurrentDirOne() throws Exception {
110         final String expectedString = readDocumentFixtureString();
111         final FileStringLookup fileStringLookup = new FileStringLookup(CURRENT_PATH);
112         testFence(expectedString, fileStringLookup);
113     }
114 
115     @Test
116     void testFenceCurrentDirPlusOne() throws Exception {
117         final String expectedString = readDocumentFixtureString();
118         final FileStringLookup fileStringLookup = new FileStringLookup(Paths.get("target"), CURRENT_PATH);
119         testFence(expectedString, fileStringLookup);
120     }
121 
122     @Test
123     void testFenceEmptyOne() throws Exception {
124         final String expectedString = readDocumentFixtureString();
125         assertEquals(expectedString, new FileStringLookup().apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
126     }
127 
128     @Test
129     void testFenceNullOne() throws Exception {
130         final String expectedString = readDocumentFixtureString();
131         assertEquals(expectedString,
132                 new FileStringLookup((Path[]) null).apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
133     }
134 
135     @Test
136     void testFenceRelativeParentTraversal(@TempDir final Path tempDir) throws Exception {
137         // A real, readable file that lives outside the fence but is reachable from the working
138         // directory through leading ".." segments. The fence must reject it; if the leading ".."
139         // survives unresolved, the prefix check passes and the file is read, escaping the fence.
140         final Path secret = Files.write(tempDir.resolve("secret.txt"), "secret".getBytes(StandardCharsets.UTF_8));
141         final Path relativeEscape = CURRENT_PATH.toAbsolutePath().relativize(secret);
142         final FileStringLookup fileStringLookup = new FileStringLookup(CURRENT_PATH);
143         assertThrows(IllegalArgumentException.class, () -> fileStringLookup.apply("UTF-8:" + relativeEscape));
144     }
145 
146     @Test
147     void testFenceRootWithParentSegment() throws Exception {
148         // A fence root that itself carries an unresolved ".." segment must be normalized when the
149         // fence is built. Otherwise the component-wise prefix check never matches and a file that
150         // really is inside the fence is wrongly rejected. Here "target/.." resolves to the working
151         // directory, so the in-fence document must still be readable.
152         final String expectedString = readDocumentFixtureString();
153         final FileStringLookup fileStringLookup = new FileStringLookup(Paths.get("target/.."));
154         assertEquals(expectedString, fileStringLookup.apply("UTF-8:src/test/resources/org/apache/commons/text/document.properties"));
155     }
156 
157     @Test
158     void testInterpolatorReplace() throws IOException {
159         final StringSubstitutor stringSubstitutor = StringSubstitutor.createInterpolator();
160         assertEquals(readDocumentFixtureString(), stringSubstitutor.replace("${file:UTF-8:" + DOCUEMENT_PATH + "}"));
161         final InterpolatorStringLookup stringLookup = (InterpolatorStringLookup) stringSubstitutor.getStringLookup();
162         stringLookup.getStringLookupMap().replace(StringLookupFactory.KEY_FILE, StringLookupFactory.INSTANCE.fileStringLookup(CURRENT_PATH));
163         testFence(stringSubstitutor);
164     }
165 }