From 9f68c9d4e465a04b8dfcbb7725c00aafd7cbe682 Mon Sep 17 00:00:00 2001 From: Andrian Sevastyanov Date: Wed, 28 May 2025 14:19:38 -0600 Subject: [PATCH 1/3] Treat all links to directories as problematic --- .../imageinspector/linux/FileOperations.java | 24 +--- .../linux/FileOperationsTest.java | 130 +++++++++++++++++- 2 files changed, 132 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java b/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java index c690dada..9bb4a338 100644 --- a/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java +++ b/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java @@ -191,12 +191,11 @@ public boolean createNewFile(final File newFile) throws IOException { } public void pruneProblematicSymLinksRecursively(final File dir) throws IOException { - logger.trace(String.format("pruneDanglingSymLinksRecursively: %s", dir.getAbsolutePath())); for (File dirEntry : dir.listFiles()) { - if (mustPrune(dir, dirEntry)) { + if (mustPrune(dirEntry)) { final boolean deleteSucceeded = Files.deleteIfExists(dirEntry.toPath()); if (!deleteSucceeded) { - logger.warn(String.format("Delete of dangling or circular symlink %s failed", dirEntry.getAbsolutePath())); + logger.warn("Delete of dangling or circular symlink {} failed", dirEntry.getAbsolutePath()); } } else if (dirEntry.isDirectory()) { pruneProblematicSymLinksRecursively(dirEntry); @@ -204,24 +203,15 @@ public void pruneProblematicSymLinksRecursively(final File dir) throws IOExcepti } } - private boolean mustPrune(File dir, File dirEntry) throws IOException { - Path dirEntryAsPath = dirEntry.toPath(); + private boolean mustPrune(File entry) { + Path dirEntryAsPath = entry.toPath(); if (!Files.isSymbolicLink(dirEntryAsPath)) { return false; } - final Path symLinkTargetPath = Files.readSymbolicLink(dirEntryAsPath); - final File symLinkTargetFile = new File(dir, symLinkTargetPath.toString()); - Path symLinkTargetPathAdjusted = symLinkTargetFile.toPath(); - logger.trace(String.format("Found symlink %s -> %s [link value: %s]", dirEntry.getAbsolutePath(), symLinkTargetFile.getAbsolutePath(), symLinkTargetPath)); - logger.trace(String.format("Checking to see if %s starts with %s", dirEntryAsPath.normalize().toFile().getAbsolutePath(), symLinkTargetPathAdjusted.normalize().toFile().getAbsolutePath())); - if (dirEntryAsPath.normalize().startsWith(symLinkTargetPathAdjusted.normalize())) { - logger.debug(String.format("symlink %s lives under its target %s; this is a circular symlink that will/must be deleted", dirEntry.getAbsolutePath(), symLinkTargetFile.getAbsolutePath())); - return true; - } - if (!symLinkTargetFile.exists()) { - logger.debug(String.format("Symlink target %s does not exist; %s is a dangling symlink that will/must be deleted", symLinkTargetFile.getAbsolutePath(), dirEntry.getAbsolutePath())); + + if (entry.isDirectory()) { return true; } - return false; + return !entry.exists(); } } diff --git a/src/test/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperationsTest.java b/src/test/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperationsTest.java index 03c77a0f..e9817826 100644 --- a/src/test/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperationsTest.java +++ b/src/test/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperationsTest.java @@ -6,13 +6,39 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeEach; public class FileOperationsTest { + private static final File TEST_DIR = new File("test"); + + @BeforeEach + public void setUp() throws IOException { + deleteDirectory(TEST_DIR); + TEST_DIR.mkdirs(); + } + + private void deleteDirectory(File directory) { + if (directory.exists()) { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteDirectory(file); + } else { + file.delete(); + } + } + } + directory.delete(); + } + } private static FileOperations fileOperations; @BeforeAll @@ -26,9 +52,9 @@ public static void tearDownAfterAll() { @Test public void testMoveFile() throws IOException { - final File fileToMove = new File("test/fileToMove.txt"); + final File fileToMove = new File(TEST_DIR, "fileToMove.txt"); fileToMove.createNewFile(); - File imageDir = new File("test/output"); + File imageDir = new File(TEST_DIR, "output"); if (!imageDir.exists()) { imageDir.mkdirs(); } @@ -41,7 +67,7 @@ public void testMoveFile() throws IOException { @Test public void testDeleteFilesOnly() throws IOException { - final File fileToDelete = new File("test/dirWithFileToDelete/fileToDelete.txt"); + final File fileToDelete = new File(TEST_DIR, "dirWithFileToDelete/fileToDelete.txt"); fileToDelete.getParentFile().mkdir(); fileToDelete.createNewFile(); @@ -54,7 +80,7 @@ public void testDeleteFilesOnly() throws IOException { @Test public void testGetFileOwnerGroupPermsMsgs() throws IOException { - final File fileToLog = new File("test/dirWithFileToDelete/fileToDelete.txt"); + final File fileToLog = new File(TEST_DIR, "dirWithFileToDelete/fileToDelete.txt"); fileToLog.getParentFile().mkdir(); fileToLog.createNewFile(); @@ -69,7 +95,7 @@ public void testGetFileOwnerGroupPermsMsgs() throws IOException { @Test public void testDeleteDirPersistently() throws IOException, InterruptedException { - final File fileToDelete = new File("test/dirWithFileToDelete/fileToDelete.txt"); + final File fileToDelete = new File(TEST_DIR, "dirWithFileToDelete/fileToDelete.txt"); fileToDelete.getParentFile().mkdir(); fileToDelete.createNewFile(); @@ -87,4 +113,98 @@ public void testCreateTempDirectory() throws IOException { assertEquals(0, tempDir.listFiles().length); tempDir.deleteOnExit(); } + + @Test + public void testPruneProblematicSymLinksRecursively_danglingLink() throws IOException { + File testDir = new File(TEST_DIR, "symlinks"); + testDir.mkdirs(); + File danglingLink = new File(testDir, "danglingLink"); + Files.createSymbolicLink(danglingLink.toPath(), new File("nonexistent").toPath()); + + fileOperations.pruneProblematicSymLinksRecursively(testDir); + + assertFalse(danglingLink.exists()); + } + + @Test + public void testPruneProblematicSymLinksRecursively_circularLink() throws IOException { + File testDir = new File(TEST_DIR, "symlinks"); + testDir.mkdirs(); + File circularLink = new File(testDir, "circularLink"); + Files.createSymbolicLink(circularLink.toPath(), Path.of(testDir.getAbsolutePath())); + + fileOperations.pruneProblematicSymLinksRecursively(testDir); + + assertFalse(circularLink.exists()); + } + + @Test + public void testPruneProblematicSymLinksRecursively_validLink() throws IOException { + File testDir = new File(TEST_DIR, "symlinks"); + testDir.mkdirs(); + File targetFile = new File(testDir, "targetFile"); + targetFile.createNewFile(); + File validLink = new File(testDir, "validLink"); + Files.createSymbolicLink(validLink.toPath(), Path.of("targetFile")); + + assertTrue(targetFile.exists()); + assertTrue(validLink.exists()); + } + + @Test + public void testPruneProblematicSymLinksRecursively_nestedDirectories() throws IOException { + File testDir = new File(TEST_DIR, "symlinks/nested"); + testDir.mkdirs(); + + File normalFile = new File(testDir, "normalFile"); + normalFile.createNewFile(); + + File danglingLink = new File(testDir, "danglingLink"); + Files.createSymbolicLink(danglingLink.toPath(), new File("nonexistent").toPath()); + + fileOperations.pruneProblematicSymLinksRecursively(new File(TEST_DIR, "symlinks")); + + assertFalse(danglingLink.exists()); + assertTrue(normalFile.exists()); + } + + @Test + public void testPruneProblematicSymLinksRecursively_symlinkToDir_absolutePath() throws IOException { + File testDir = new File(TEST_DIR, "symlinks/nested"); + testDir.mkdirs(); + + File normalFile = new File(testDir, "normalFile"); + normalFile.createNewFile(); + + File linkToDir = new File(testDir.getParent(), "linkToDir"); + Files.createSymbolicLink(linkToDir.toPath(), Path.of(testDir.getAbsolutePath())); + + assertTrue(linkToDir.exists()); + + fileOperations.pruneProblematicSymLinksRecursively(new File(TEST_DIR, "symlinks")); + + assertFalse(linkToDir.exists()); + assertTrue(testDir.exists()); + assertTrue(normalFile.exists()); + } + + @Test + public void testPruneProblematicSymLinksRecursively_symlinkToDir_relativePath() throws IOException { + File testDir = new File(TEST_DIR, "symlinks/nested"); + testDir.mkdirs(); + + File normalFile = new File(testDir, "normalFile"); + normalFile.createNewFile(); + + File linkToDir = new File(testDir.getParent(), "linkToDir"); + Files.createSymbolicLink(linkToDir.toPath(), Path.of("nested")); + + assertTrue(linkToDir.exists()); + + fileOperations.pruneProblematicSymLinksRecursively(new File(TEST_DIR, "symlinks")); + + assertFalse(linkToDir.exists()); + assertTrue(testDir.exists()); + assertTrue(normalFile.exists()); + } } From 921ffb1bd3f8b50e23004f377dccc4121f9f8809 Mon Sep 17 00:00:00 2001 From: Andrian Sevastyanov Date: Wed, 28 May 2025 14:24:29 -0600 Subject: [PATCH 2/3] Special snapshot version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ac7217ca..c7c5668d 100644 --- a/build.gradle +++ b/build.gradle @@ -24,7 +24,7 @@ project.ext.moduleName = 'com.blackduck.integration.hub-imageinspector-lib' project.ext.javaUseAutoModuleName = 'true' project.ext.junitShowStandardStreams = 'true' -version = '15.0.3-SNAPSHOT' +version = '15.0.3-IDETECT-4714-prune-dir-links-SNAPSHOT' description = 'A library for creating Black Duck Input Output (BDIO) representing the packages installed in a Linux Docker image' apply plugin: "io.spring.dependency-management" From e2c2b7ccddbf059bf9506db03d20131795db45d4 Mon Sep 17 00:00:00 2001 From: Andrian Sevastyanov Date: Wed, 28 May 2025 14:26:52 -0600 Subject: [PATCH 3/3] Update warning --- .../blackduck/imageinspector/linux/FileOperations.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java b/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java index 9bb4a338..2700012a 100644 --- a/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java +++ b/src/main/java/com/blackduck/integration/blackduck/imageinspector/linux/FileOperations.java @@ -195,7 +195,7 @@ public void pruneProblematicSymLinksRecursively(final File dir) throws IOExcepti if (mustPrune(dirEntry)) { final boolean deleteSucceeded = Files.deleteIfExists(dirEntry.toPath()); if (!deleteSucceeded) { - logger.warn("Delete of dangling or circular symlink {} failed", dirEntry.getAbsolutePath()); + logger.warn("Delete of problematic symlink {} failed", dirEntry.getAbsolutePath()); } } else if (dirEntry.isDirectory()) { pruneProblematicSymLinksRecursively(dirEntry);