From 8ef957cd53acf9ae24f3f75e8080bb8e30675946 Mon Sep 17 00:00:00 2001 From: kami Date: Sun, 12 Jul 2026 12:38:27 +0400 Subject: [PATCH] fix(sandbox): unique backup names to prevent same-basename collision (#64) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backups were named workingDir/${fileName}.bak — two affected paths with the same filename in different dirs overwrote each other's backup, so failure restore wrote the wrong content. Name by full-path hash. Latent today (single-path tools) but a correctness landmine for any multi-path tool. --- .../correx/infrastructure/tools/SandboxedToolExecutor.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/infrastructure/tools/src/main/kotlin/com/correx/infrastructure/tools/SandboxedToolExecutor.kt b/infrastructure/tools/src/main/kotlin/com/correx/infrastructure/tools/SandboxedToolExecutor.kt index 5ef90181..1abaf99b 100644 --- a/infrastructure/tools/src/main/kotlin/com/correx/infrastructure/tools/SandboxedToolExecutor.kt +++ b/infrastructure/tools/src/main/kotlin/com/correx/infrastructure/tools/SandboxedToolExecutor.kt @@ -119,7 +119,12 @@ class SandboxedToolExecutor( for (original in affectedPaths) { // A1: skip files that don't yet exist (new-file tools have nothing to back up) if (!Files.exists(original)) continue - val backupPath = workingDir.resolve("${original.fileName}.bak") + // Name the backup by the full source path, not just the basename — two affected paths + // sharing a filename in different dirs would otherwise overwrite each other's backup and + // restore the wrong content on failure. Hash keeps the name filesystem-safe and unique. + val stem = original.fileName.toString() + val hash = Integer.toHexString(original.toAbsolutePath().normalize().toString().hashCode()) + val backupPath = workingDir.resolve("$stem.$hash.bak") Files.copy(original, backupPath, StandardCopyOption.REPLACE_EXISTING) put(original, backupPath) }