diff --git a/platform/util/src/com/intellij/util/io/Decompressor.java b/platform/util/src/com/intellij/util/io/Decompressor.java index fad1c186ed4d..8ffe200b3082 100644 --- a/platform/util/src/com/intellij/util/io/Decompressor.java +++ b/platform/util/src/com/intellij/util/io/Decompressor.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.util.io; import com.intellij.openapi.util.Condition; @@ -17,6 +17,8 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Enumeration; import java.util.List; @@ -296,12 +298,17 @@ public abstract class Decompressor { break; case SYMLINK: - if (StringUtil.isEmpty(entry.linkTarget) || - !FileUtil.isAncestor(outputDir, new File(FileUtil.toCanonicalPath(outputFile.getParent() + '/' + entry.linkTarget)), true)) { + if (StringUtil.isEmpty(entry.linkTarget)) { throw new IOException("Invalid symlink entry: " + entry.name + " -> " + entry.linkTarget); } - FileUtil.createParentDirs(outputFile); - Files.createSymbolicLink(outputFile.toPath(), Paths.get(entry.linkTarget)); + try { + Path outputTarget = Paths.get(entry.linkTarget); + FileUtil.createParentDirs(outputFile); + Files.createSymbolicLink(outputFile.toPath(), outputTarget); + } + catch (InvalidPathException e) { + throw new IOException("Invalid symlink entry: " + entry.name + " -> " + entry.linkTarget, e); + } break; } diff --git a/platform/util/testSrc/com/intellij/util/io/DecompressorTest.kt b/platform/util/testSrc/com/intellij/util/io/DecompressorTest.kt index 6132e8c63ec9..91e648ff508c 100644 --- a/platform/util/testSrc/com/intellij/util/io/DecompressorTest.kt +++ b/platform/util/testSrc/com/intellij/util/io/DecompressorTest.kt @@ -197,24 +197,6 @@ class DecompressorTest { assertThat(File(dir, "links/ok").toPath()).isSymbolicLink().hasSameBinaryContentAs(File(dir, "f").toPath()) } - @Test fun tarRogueSymlinks() { - assumeSymLinkCreationIsSupported() - - val tar = tempDir.newFile("test.tar") - TarArchiveOutputStream(FileOutputStream(tar)).use { writeEntry(it, "rogue", link = "../f") } - val dir = tempDir.newDirectory("unpacked") - testNoTraversal(Decompressor.Tar(tar).withSymlinks(), dir, File(dir, "rogue")) - } - - @Test fun zipRogueSymlinks() { - assumeSymLinkCreationIsSupported() - - val zip = tempDir.newFile("test.zip") - ZipArchiveOutputStream(FileOutputStream(zip)).use { writeEntry(it, "rogue", link = "../f") } - val dir = tempDir.newDirectory("unpacked") - testNoTraversal(Decompressor.Zip(zip).withUnixPermissionsAndSymlinks(), dir, File(dir, "rogue")) - } - @Test fun prefixPathsFilesInZip() { val zip = tempDir.newFile("test.zip") ZipOutputStream(FileOutputStream(zip)).use { @@ -362,17 +344,6 @@ class DecompressorTest { assertThat(File(dir, "links/ok").toPath()).isSymbolicLink().hasSameBinaryContentAs(File(dir, "f").toPath()) } - @Test fun prefixPathRogueSymlinks() { - assumeSymLinkCreationIsSupported() - - val tar = tempDir.newFile("test.tar") - TarArchiveOutputStream(FileOutputStream(tar)).use { - writeEntry(it, "a/b/c/rogue", link = "../f") - } - val dir = tempDir.newDirectory("unpacked") - testNoTraversal(Decompressor.Tar(tar).removePrefixPath("a/b/c"), dir, File(dir, "rogue")) - } - @Test fun prefixPathSkipsTooShortPaths() { val tar = tempDir.newFile("test.tar") TarArchiveOutputStream(FileOutputStream(tar)).use {