From b50aec59124416b7315a49220cfc3999223414cc Mon Sep 17 00:00:00 2001 From: dotasek Date: Fri, 20 Jan 2023 16:56:57 -0500 Subject: [PATCH] Advisory fix 1 (#1089) * Update cache and comparison downloads to use https * Zip Slip tests and fix * Zip Slip tests 2 and fix * Add missing tempDir child in ScannerTest * Add win format zip test * Add tests to r4b * Add tests and fixes for slips in tgz processing * Update fhir-test-cases version --- .../misc/NpmPackageVersionConverter.java | 69 ++++++++------ .../misc/NpmPackageVersionConverterTests.java | 37 +++++++ .../npmPackageVersionConverter/tgz-evil.tgz | Bin 0 -> 390 bytes .../npmPackageVersionConverter/tgz-normal.tgz | Bin 0 -> 394 bytes .../TerminologyCacheManager.java | 7 +- .../TerminologyCacheManagerTests.java | 77 +++++++++++++++ .../terminologyCacheManager/zip-normal.zip | Bin 0 -> 588 bytes .../terminologyCacheManager/zip-slip-2.zip | Bin 0 -> 190 bytes .../terminologyCacheManager/zip-slip-win.zip | Bin 0 -> 137 bytes .../terminologyCacheManager/zip-slip.zip | Bin 0 -> 172 bytes .../TerminologyCacheManager.java | 6 +- .../TerminologyCacheManagerTests.java | 78 +++++++++++++++ .../terminologyCacheManager/zip-normal.zip | Bin 0 -> 588 bytes .../terminologyCacheManager/zip-slip-2.zip | Bin 0 -> 190 bytes .../terminologyCacheManager/zip-slip-win.zip | Bin 0 -> 137 bytes .../terminologyCacheManager/zip-slip.zip | Bin 0 -> 172 bytes .../hl7/fhir/utilities/npm/NpmPackage.java | 6 +- .../fhir/utilities/npm/NpmPackageTests.java | 35 +++++++ .../src/test/resources/npm/tar/tgz-evil.tgz | Bin 0 -> 390 bytes .../src/test/resources/npm/tar/tgz-normal.tgz | Bin 0 -> 394 bytes .../java/org/hl7/fhir/validation/Scanner.java | 13 ++- .../org/hl7/fhir/validation/ScannerTest.java | 90 ++++++++++++++++++ .../src/test/resources/scanner/zip-normal.zip | Bin 0 -> 588 bytes .../src/test/resources/scanner/zip-slip-2.zip | Bin 0 -> 190 bytes .../test/resources/scanner/zip-slip-win.zip | Bin 0 -> 137 bytes .../src/test/resources/scanner/zip-slip.zip | Bin 0 -> 172 bytes 26 files changed, 376 insertions(+), 42 deletions(-) create mode 100644 org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverterTests.java create mode 100644 org.hl7.fhir.convertors/src/test/resources/misc/npmPackageVersionConverter/tgz-evil.tgz create mode 100644 org.hl7.fhir.convertors/src/test/resources/misc/npmPackageVersionConverter/tgz-normal.tgz create mode 100644 org.hl7.fhir.r4b/src/test/java/org/hl7/fhir/r4b/terminologies/TerminologyCacheManagerTests.java create mode 100644 org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-normal.zip create mode 100644 org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip-2.zip create mode 100644 org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip-win.zip create mode 100644 org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip.zip create mode 100644 org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/terminologies/TerminologyCacheManagerTests.java create mode 100644 org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-normal.zip create mode 100644 org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip-2.zip create mode 100644 org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip-win.zip create mode 100644 org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip.zip create mode 100644 org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/npm/NpmPackageTests.java create mode 100644 org.hl7.fhir.utilities/src/test/resources/npm/tar/tgz-evil.tgz create mode 100644 org.hl7.fhir.utilities/src/test/resources/npm/tar/tgz-normal.tgz create mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ScannerTest.java create mode 100644 org.hl7.fhir.validation/src/test/resources/scanner/zip-normal.zip create mode 100644 org.hl7.fhir.validation/src/test/resources/scanner/zip-slip-2.zip create mode 100644 org.hl7.fhir.validation/src/test/resources/scanner/zip-slip-win.zip create mode 100644 org.hl7.fhir.validation/src/test/resources/scanner/zip-slip.zip diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java index 2c9a4f102..8ba46ea1b 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java @@ -1,9 +1,6 @@ package org.hl7.fhir.convertors.misc; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileInputStream; -import java.io.IOException; +import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; @@ -39,6 +36,7 @@ import org.hl7.fhir.utilities.json.parser.JsonParser; import org.hl7.fhir.utilities.npm.NpmPackageIndexBuilder; import com.google.common.base.Charsets; +import org.jetbrains.annotations.NotNull; public class NpmPackageVersionConverter { @@ -76,33 +74,7 @@ public class NpmPackageVersionConverter { } public void execute() throws IOException { - GzipCompressorInputStream gzipIn; - try { - gzipIn = new GzipCompressorInputStream(new FileInputStream(source)); - } catch (Exception e) { - throw new IOException("Error reading " + source + ": " + e.getMessage(), e); - } - Map content = new HashMap<>(); - - try (TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn)) { - TarArchiveEntry entry; - - while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) { - String n = entry.getName(); - if (!entry.isDirectory()) { - int count; - byte[] data = new byte[BUFFER_SIZE]; - ByteArrayOutputStream fos = new ByteArrayOutputStream(); - try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) { - while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) { - dest.write(data, 0, count); - } - } - fos.close(); - content.put(n, fos.toByteArray()); - } - } - } + Map content = loadContentMap(new FileInputStream(source)); Map output = new HashMap<>(); output.put("package/package.json", convertPackage(content.get("package/package.json"))); @@ -180,6 +152,41 @@ public class NpmPackageVersionConverter { TextFile.bytesToFile(b, dest); } + @NotNull + protected Map loadContentMap(InputStream inputStream) throws IOException { + GzipCompressorInputStream gzipIn; + try { + gzipIn = new GzipCompressorInputStream(inputStream); + } catch (Exception e) { + throw new IOException("Error reading " + source + ": " + e.getMessage(), e); + } + Map content = new HashMap<>(); + + try (TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn)) { + TarArchiveEntry entry; + + while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) { + String n = entry.getName(); + if (n.contains("..")) { + throw new RuntimeException("Entry with an illegal name: " + n); + } + if (!entry.isDirectory()) { + int count; + byte[] data = new byte[BUFFER_SIZE]; + ByteArrayOutputStream fos = new ByteArrayOutputStream(); + try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) { + while ((count = tarIn.read(data, 0, BUFFER_SIZE)) != -1) { + dest.write(data, 0, count); + } + } + fos.close(); + content.put(n, fos.toByteArray()); + } + } + } + return content; + } + private byte[] convertPackage(byte[] cnt) throws IOException { JsonObject json = JsonParser.parseObject(cnt); currentVersion = json.getJsonArray("fhirVersions").get(0).asString(); diff --git a/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverterTests.java b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverterTests.java new file mode 100644 index 000000000..3d7d84fa4 --- /dev/null +++ b/org.hl7.fhir.convertors/src/test/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverterTests.java @@ -0,0 +1,37 @@ +package org.hl7.fhir.convertors.misc; + +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.tests.ResourceLoaderTests; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class NpmPackageVersionConverterTests implements ResourceLoaderTests { + @org.junit.jupiter.api.Test + + public void testNormalTgz() throws IOException { + InputStream tgzStream = getResourceAsInputStream("misc", "npmPackageVersionConverter", "tgz-normal.tgz"); + NpmPackageVersionConverter converter = new NpmPackageVersionConverter(null, null, "r5", null); + Map contents = converter.loadContentMap(tgzStream); + String actual = new String(contents.get("depth1/test.txt"), StandardCharsets.UTF_8); + assertEquals("dummy file content", actual); + } + + @Test + public void testEvilTgz() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + InputStream tgzStream = getResourceAsInputStream("misc", "npmPackageVersionConverter", "tgz-evil.tgz"); + NpmPackageVersionConverter converter = new NpmPackageVersionConverter(null, null, "r5", null); + converter.loadContentMap(tgzStream); + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal name: ../evil.txt", thrown.getMessage()); + } +} diff --git a/org.hl7.fhir.convertors/src/test/resources/misc/npmPackageVersionConverter/tgz-evil.tgz b/org.hl7.fhir.convertors/src/test/resources/misc/npmPackageVersionConverter/tgz-evil.tgz new file mode 100644 index 0000000000000000000000000000000000000000..966df0aadce6f40cc327cb33956ad65f5435180e GIT binary patch literal 390 zcmV;10eSu(iwFQQ!O3F)1MO2iPs1<}W#(6$GJyn%(|jOR2!WUwm|#N;ZsNA4i5=OO zmI(E~V>@YSqzaZ6ft0tL{O;@7zN=`qpfeU!t@You6ekA;Ata4su=XlVoF$|7?2yJO zgmILPqL@TU2qZL2asuSgLiVB7NR^S2(s>eUwZJRD4n!`MoYXp>VA;V{S{D3#PS| zdjn_o4(19wM>G@zeg3QMldzIEu{v#|eps k{r?Tv&xa{57}$Yk;&@XqV8DO@1O5^|0h`JGlK>0=0M+2X+W-In literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.convertors/src/test/resources/misc/npmPackageVersionConverter/tgz-normal.tgz b/org.hl7.fhir.convertors/src/test/resources/misc/npmPackageVersionConverter/tgz-normal.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c34096a8703d3186909fc2d6f4e8133d7248d56d GIT binary patch literal 394 zcmV;50d@W#iwFQs!O3F)1MO5hPs1<}?#!>SGJyn%^JtJNgg{ITOt2vaH@P;Zi5=OO zmI(E~V<%~85Q2sBQobQ4fA{(v-8qF4Gv7nd=;9NO_5n(197V+1(|Fui8novYK|uT{ zjDs)?{qdMk^P&ET(7mX6gGwV)MoP+-Jar{94U6~UOxSGpL7ny{WNZHj(yAzGGUGXr zL<$74pAi`FOHs09!RFv~)_v4ce07h{_CJo|ZvP`1g`wGh!^Fe>e+Qlqh=C(m0nUUt z&2wVR{w-q8Na>c~ { + InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_ZIP); + TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage()); + } + + @Test + public void testSlip2Zip() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_2_ZIP); + TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: child/../../evil.txt", thrown.getMessage()); + } + + @Test + public void testSlipZipWin() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_WIN_ZIP); + TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage()); + } +} diff --git a/org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-normal.zip b/org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-normal.zip new file mode 100644 index 0000000000000000000000000000000000000000..8c5f279069abdc7a340932d1d15b3c37d03b89fa GIT binary patch literal 588 zcmWIWW@Zs#0D(y{#$jLvl;CDiV5rJ0(9O#)%1zAC4-MgEV4qreGI=);gXq!gRun@!eCESgNY6_i21N!L-rIX*kE8u<4mmP6B~#D-mGjOvzUQU Mk&%Hx6o?rZ07Man4*&oF literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip-2.zip b/org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip-2.zip new file mode 100644 index 0000000000000000000000000000000000000000..dc41ceb311060fb165869007e6d8dc925a331c72 GIT binary patch literal 190 zcmWIWW@h1H00H%M<1jD-N{BGXFeGPW=A`KB>48vcS!Rx2NkvI$2qyz`Rm;g_5H79Y zW?*Fb%E-XLA_CMG;LXS+$BfH73Aph~8bM6#rn5p!$1s_d4Wx$=2%~_sABe*M04bgy AeEh*n6;%t=*9&d)1J%_|A; jW@Hj!z^xak9SpWLf+!@D0=!w-KthZ_XbGgPKpX}DXpR_s literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip.zip b/org.hl7.fhir.r4b/src/test/resources/terminologyCacheManager/zip-slip.zip new file mode 100644 index 0000000000000000000000000000000000000000..99bab1361e8ef58b99ed244c683f75e13fbdfd60 GIT binary patch literal 172 zcmWIWW@h1H00H%M<1jD-N^mpCFzD&&r { + InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_ZIP); + TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage()); + } + + @Test + public void testSlip2Zip() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_2_ZIP); + TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: child/../../evil.txt", thrown.getMessage()); + } + + @Test + public void testSlipZipWin() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + InputStream slipInputStream = getResourceAsInputStream( "terminologyCacheManager", ZIP_SLIP_WIN_ZIP); + TerminologyCacheManager.unzip( slipInputStream, tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage()); + } +} diff --git a/org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-normal.zip b/org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-normal.zip new file mode 100644 index 0000000000000000000000000000000000000000..8c5f279069abdc7a340932d1d15b3c37d03b89fa GIT binary patch literal 588 zcmWIWW@Zs#0D(y{#$jLvl;CDiV5rJ0(9O#)%1zAC4-MgEV4qreGI=);gXq!gRun@!eCESgNY6_i21N!L-rIX*kE8u<4mmP6B~#D-mGjOvzUQU Mk&%Hx6o?rZ07Man4*&oF literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip-2.zip b/org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip-2.zip new file mode 100644 index 0000000000000000000000000000000000000000..dc41ceb311060fb165869007e6d8dc925a331c72 GIT binary patch literal 190 zcmWIWW@h1H00H%M<1jD-N{BGXFeGPW=A`KB>48vcS!Rx2NkvI$2qyz`Rm;g_5H79Y zW?*Fb%E-XLA_CMG;LXS+$BfH73Aph~8bM6#rn5p!$1s_d4Wx$=2%~_sABe*M04bgy AeEh*n6;%t=*9&d)1J%_|A; jW@Hj!z^xak9SpWLf+!@D0=!w-KthZ_XbGgPKpX}DXpR_s literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip.zip b/org.hl7.fhir.r5/src/test/resources/terminologyCacheManager/zip-slip.zip new file mode 100644 index 0000000000000000000000000000000000000000..99bab1361e8ef58b99ed244c683f75e13fbdfd60 GIT binary patch literal 172 zcmWIWW@h1H00H%M<1jD-N^mpCFzD&&r { + InputStream tgzStream = getResourceAsInputStream("npm", "tar", "tgz-evil.tgz"); + NpmPackage npmPackage = NpmPackage.fromPackage(tgzStream); + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal name: ../evil.txt", thrown.getMessage()); + } +} diff --git a/org.hl7.fhir.utilities/src/test/resources/npm/tar/tgz-evil.tgz b/org.hl7.fhir.utilities/src/test/resources/npm/tar/tgz-evil.tgz new file mode 100644 index 0000000000000000000000000000000000000000..966df0aadce6f40cc327cb33956ad65f5435180e GIT binary patch literal 390 zcmV;10eSu(iwFQQ!O3F)1MO2iPs1<}W#(6$GJyn%(|jOR2!WUwm|#N;ZsNA4i5=OO zmI(E~V>@YSqzaZ6ft0tL{O;@7zN=`qpfeU!t@You6ekA;Ata4su=XlVoF$|7?2yJO zgmILPqL@TU2qZL2asuSgLiVB7NR^S2(s>eUwZJRD4n!`MoYXp>VA;V{S{D3#PS| zdjn_o4(19wM>G@zeg3QMldzIEu{v#|eps k{r?Tv&xa{57}$Yk;&@XqV8DO@1O5^|0h`JGlK>0=0M+2X+W-In literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.utilities/src/test/resources/npm/tar/tgz-normal.tgz b/org.hl7.fhir.utilities/src/test/resources/npm/tar/tgz-normal.tgz new file mode 100644 index 0000000000000000000000000000000000000000..c34096a8703d3186909fc2d6f4e8133d7248d56d GIT binary patch literal 394 zcmV;50d@W#iwFQs!O3F)1MO5hPs1<}?#!>SGJyn%^JtJNgg{ITOt2vaH@P;Zi5=OO zmI(E~V<%~85Q2sBQobQ4fA{(v-8qF4Gv7nd=;9NO_5n(197V+1(|Fui8novYK|uT{ zjDs)?{qdMk^P&ET(7mX6gGwV)MoP+-Jar{94U6~UOxSGpL7ny{WNZHj(yAzGGUGXr zL<$74pAi`FOHs09!RFv~)_v4ce07h{_CJo|ZvP`1g`wGh!^Fe>e+Qlqh=C(m0nUUt z&2wVR{w-q8Na>c~ items) throws IOException, FHIRException, EOperationOutcome { String f = Utilities.path(folder, "comparison.zip"); - download("http://fhir.org/archive/comparison.zip", f); + download("https://fhir.org/archive/comparison.zip", f); unzip(f, folder); for (int i = 0; i < items.size(); i++) { @@ -342,13 +342,18 @@ public class Scanner { // iterates over entries in the zip file while (entry != null) { String filePath = destDirectory + File.separator + entry.getName(); + + final File zipEntryFile = new File(destDirectory, entry.getName()); + if (!zipEntryFile.toPath().normalize().startsWith(destDirectory)) { + throw new RuntimeException("Entry with an illegal path: " + entry.getName()); + } + if (!entry.isDirectory()) { - // if the entry is a file, extracts it + // if the entry is a file, extract it extractFile(zipIn, filePath); } else { // if the entry is a directory, make the directory - File dir = new File(filePath); - dir.mkdir(); + zipEntryFile.mkdir(); } zipIn.closeEntry(); entry = zipIn.getNextEntry(); diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ScannerTest.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ScannerTest.java new file mode 100644 index 000000000..aa427bf84 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/ScannerTest.java @@ -0,0 +1,90 @@ +package org.hl7.fhir.validation; + +import org.hl7.fhir.utilities.tests.ResourceLoaderTests; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class ScannerTest implements ResourceLoaderTests { + + public static final String ZIP_NORMAL_ZIP = "zip-normal.zip"; + public static final String ZIP_SLIP_ZIP = "zip-slip.zip"; + public static final String ZIP_SLIP_2_ZIP = "zip-slip-2.zip"; + + public static final String ZIP_SLIP_WIN_ZIP = "zip-slip-win.zip"; + + Path tempDir; + Path zipNormalPath; + Path zipSlipPath; + + Path zipSlip2Path; + + Path zipSlipWinPath; + + @BeforeAll + public void beforeAll() throws IOException { + tempDir = Files.createTempDirectory("scanner-zip"); + tempDir.resolve("child").toFile().mkdir(); + zipNormalPath = tempDir.resolve(ZIP_NORMAL_ZIP); + zipSlipPath = tempDir.resolve(ZIP_SLIP_ZIP); + zipSlip2Path = tempDir.resolve(ZIP_SLIP_2_ZIP); + zipSlipWinPath = tempDir.resolve(ZIP_SLIP_WIN_ZIP); + + copyResourceToFile(zipNormalPath, "scanner", ZIP_NORMAL_ZIP); + copyResourceToFile(zipSlipPath, "scanner", ZIP_SLIP_ZIP); + copyResourceToFile(zipSlip2Path, "scanner", ZIP_SLIP_2_ZIP); + copyResourceToFile(zipSlipWinPath, "scanner", ZIP_SLIP_WIN_ZIP); + } + + @Test + public void testNormalZip() throws IOException { + Scanner scanner = new Scanner(null,null,null,null); + scanner.unzip(zipNormalPath.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath()); + + Path expectedFilePath = tempDir.resolve("zip-normal").resolve("depth1").resolve("test.txt"); + String actualContent = Files.readString(expectedFilePath); + assertEquals("dummy file content", actualContent); + } + + @Test + public void testSlipZip() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + Scanner scanner = new Scanner(null,null,null,null); + scanner.unzip(zipSlipPath.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage()); + } + + @Test + public void testSlipZip2() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + Scanner scanner = new Scanner(null,null,null,null); + scanner.unzip(zipSlip2Path.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: child/../../evil.txt", thrown.getMessage()); + } + + @Test + public void testSlipZipWin() throws IOException { + RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> { + Scanner scanner = new Scanner(null,null,null,null); + scanner.unzip(zipSlipWinPath.toFile().getAbsolutePath(), tempDir.toFile().getAbsolutePath()); + //Code under test + }); + assertNotNull(thrown); + assertEquals("Entry with an illegal path: ../evil.txt", thrown.getMessage()); + } +} diff --git a/org.hl7.fhir.validation/src/test/resources/scanner/zip-normal.zip b/org.hl7.fhir.validation/src/test/resources/scanner/zip-normal.zip new file mode 100644 index 0000000000000000000000000000000000000000..8c5f279069abdc7a340932d1d15b3c37d03b89fa GIT binary patch literal 588 zcmWIWW@Zs#0D(y{#$jLvl;CDiV5rJ0(9O#)%1zAC4-MgEV4qreGI=);gXq!gRun@!eCESgNY6_i21N!L-rIX*kE8u<4mmP6B~#D-mGjOvzUQU Mk&%Hx6o?rZ07Man4*&oF literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.validation/src/test/resources/scanner/zip-slip-2.zip b/org.hl7.fhir.validation/src/test/resources/scanner/zip-slip-2.zip new file mode 100644 index 0000000000000000000000000000000000000000..dc41ceb311060fb165869007e6d8dc925a331c72 GIT binary patch literal 190 zcmWIWW@h1H00H%M<1jD-N{BGXFeGPW=A`KB>48vcS!Rx2NkvI$2qyz`Rm;g_5H79Y zW?*Fb%E-XLA_CMG;LXS+$BfH73Aph~8bM6#rn5p!$1s_d4Wx$=2%~_sABe*M04bgy AeEh*n6;%t=*9&d)1J%_|A; jW@Hj!z^xak9SpWLf+!@D0=!w-KthZ_XbGgPKpX}DXpR_s literal 0 HcmV?d00001 diff --git a/org.hl7.fhir.validation/src/test/resources/scanner/zip-slip.zip b/org.hl7.fhir.validation/src/test/resources/scanner/zip-slip.zip new file mode 100644 index 0000000000000000000000000000000000000000..99bab1361e8ef58b99ed244c683f75e13fbdfd60 GIT binary patch literal 172 zcmWIWW@h1H00H%M<1jD-N^mpCFzD&&r