gnodet-bot commented on code in PR #290:
URL: https://github.com/apache/maven-filtering/pull/290#discussion_r4093415077


##########
src/main/java/org/apache/maven/shared/filtering/DefaultMavenResourcesFiltering.java:
##########
@@ -476,26 +478,38 @@ private String 
getRelativeOutputDirectory(MavenResourcesExecution execution) {
      */
     private String filterFileName(String name, List<FilterWrapper> wrappers) 
throws MavenFilteringException {
 
-        Reader reader = new StringReader(name);
-        for (FilterWrapper wrapper : wrappers) {
-            reader = wrapper.getReader(reader);
-        }
-
-        try (StringWriter writer = new StringWriter()) {
-            char[] buffer = new char[BUFFER_LENGTH];
-            int nRead;
-            while ((nRead = reader.read(buffer, 0, buffer.length)) >= 0) {
-                writer.write(buffer, 0, nRead);
+        StringBuilder sb = new StringBuilder();
+        Path path = Path.of(name);
+        Iterator<Path> iterator = path.iterator();
+        while (iterator.hasNext()) {
+            String component = iterator.next().toString();
+            Reader reader = new StringReader(component);
+            for (FilterWrapper wrapper : wrappers) {
+                reader = wrapper.getReader(reader);
             }

Review Comment:
   ⚠️ **Resource leak: `reader` chain is never closed.** 
`FilterWrapper.getReader()` can return any `Reader` implementation — including 
readers backed by file I/O (e.g. when filter properties files are loaded 
lazily). The outermost reader is never closed; if any wrapper holds a resource, 
it leaks silently on every path component, every file.
   
   The fix is to put the whole reader chain inside a try-with-resources block 
alongside the `StringWriter`:
   
   ```suggestion
               Reader reader = new StringReader(component);
               for (FilterWrapper wrapper : wrappers) {
                   reader = wrapper.getReader(reader);
               }
   ```
   
   Replace the `try (StringWriter writer = ...)` with `try (StringWriter writer 
= new StringWriter(); Reader reader = ...)` — but since the reader chain is 
built dynamically (each wrapper wraps the previous), you need an 
effectively-final holder or a separate try block:
   
   ```java
               Reader reader = new StringReader(component);
               for (FilterWrapper wrapper : wrappers) {
                   reader = wrapper.getReader(reader);
               }
               try (Reader closeable = reader; StringWriter writer = new 
StringWriter()) {
   ```
   
   This ensures the outermost reader (and transitively its wrapped chain) is 
closed on every exit, including exception paths.



##########
src/test/java/org/apache/maven/shared/filtering/DefaultMavenResourcesFilteringTest.java:
##########
@@ -943,7 +944,50 @@ public void testFilterFileName() throws Exception {
 
         List<Path> files = list(targetPathFile);
         assertEquals(1, files.size());
-        assertEquals("1.0.txt", filename(files.get(0)));
+        assertEquals("subfolder", filename(files.get(0)));
+        assertTrue(Files.isDirectory(files.get(0)));
+
+        List<Path> subfolderFiles = list(files.get(0));
+        assertEquals(1, subfolderFiles.size());
+        assertEquals("1.0.txt", filename(subfolderFiles.get(0)));
+    }
+
+    @Test
+    public void testFilterFileNameWithFileSeparatorAsEscape() throws Exception 
{
+
+        String unitFilesDir = getBasedir() + 
"/src/test/units-files/maven-filename-filtering";
+
+        Resource resource = new Resource();
+        resource.setDirectory(unitFilesDir);
+        resource.setFiltering(true);
+        resource.addInclude("**/${pom.version}*");
+        resource.setTargetPath("testTargetPath");
+
+        MavenResourcesExecution mavenResourcesExecution = new 
MavenResourcesExecution(
+                Collections.singletonList(resource),
+                outputDirectory,
+                mavenProject,
+                "UTF-8",
+                Collections.<String>emptyList(),
+                Collections.<String>emptyList(),
+                new StubSession());
+        mavenResourcesExecution.setFilterFilenames(true);
+
+        // more likely to occur on windows, where the file
+        // separator is the same as the common escape string "\"
+        
mavenResourcesExecution.setEscapeString(FileSystems.getDefault().getSeparator());

Review Comment:
   💡 **Test coverage gap: this test doesn't exercise the Windows-specific 
failure mode.** On Linux, `FileSystems.getDefault().getSeparator()` returns 
`/`, which is also the path delimiter — but since `/` doesn't appear in the 
filter expression `${pom.version}`, the escape string has no observable effect 
here. The test passes on Linux for the same reason the unfixed code would have: 
the escape string never collides with anything in the filename component.
   
   The actual bug (issue #289) is that on Windows, the escape string `\` 
appears inside `${pom.version}` expressions and causes premature termination of 
placeholder resolution. To truly guard against regression, the test should set 
a hardcoded `"\\"` escape string and verify the property still expands:
   
   ```java
           mavenResourcesExecution.setEscapeString("\\\\"); // simulate Windows 
escape character
   ```
   
   Or add an `@EnabledOnOs(OS.WINDOWS)` sibling test. As-is this test provides 
confidence on Linux but leaves the Windows path untested in CI.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to