1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package libcore.tzdata.update; 17 18import junit.framework.TestCase; 19 20import java.io.ByteArrayInputStream; 21import java.io.ByteArrayOutputStream; 22import java.io.File; 23import java.io.FilterInputStream; 24import java.io.IOException; 25import java.io.InputStream; 26import java.util.ArrayList; 27import java.util.Collections; 28import java.util.List; 29import java.util.zip.ZipEntry; 30import java.util.zip.ZipOutputStream; 31import libcore.io.IoUtils; 32 33/** 34 * Tests for {@link ConfigBundle}. 35 */ 36public class ConfigBundleTest extends TestCase { 37 38 private final List<File> testFiles = new ArrayList<>(); 39 40 @Override 41 public void tearDown() throws Exception { 42 // Delete files / directories in reverse order. 43 Collections.reverse(testFiles); 44 for (File tempFile : testFiles) { 45 tempFile.delete(); 46 } 47 super.tearDown(); 48 } 49 50 public void testExtractZipSafely_goodZip() throws Exception { 51 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 52 try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) { 53 addZipEntry(zipOutputStream, "/leadingSlash"); 54 addZipEntry(zipOutputStream, "absolute"); 55 addZipEntry(zipOutputStream, "subDir/../file"); 56 addZipEntry(zipOutputStream, "subDir/subDir/subDir/file"); 57 addZipEntry(zipOutputStream, "subDir/subDir2/"); // Directory entry 58 addZipEntry(zipOutputStream, "subDir/../subDir3/"); // Directory entry 59 } 60 File dir = createTempDir(); 61 File targetDir = new File(dir, "target"); 62 TestInputStream inputStream = 63 new TestInputStream(new ByteArrayInputStream(baos.toByteArray())); 64 ConfigBundle.extractZipSafely(inputStream, targetDir, true /* makeWorldReadable */); 65 inputStream.assertClosed(); 66 assertFilesExist( 67 new File(targetDir, "leadingSlash"), 68 new File(targetDir, "absolute"), 69 new File(targetDir, "file"), 70 new File(targetDir, "subDir/subDir/subDir/file")); 71 assertDirsExist( 72 new File(targetDir, "subDir/subDir2"), 73 new File(targetDir, "subDir3")); 74 } 75 76 public void testExtractZipSafely_badZip_fileOutsideTarget() throws Exception { 77 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 78 try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) { 79 addZipEntry(zipOutputStream, "../one"); 80 } 81 doExtractZipFails(baos); 82 } 83 84 public void testExtractZipSafely_badZip_dirOutsideTarget() throws Exception { 85 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 86 try (ZipOutputStream zipOutputStream = new ZipOutputStream(baos)) { 87 addZipEntry(zipOutputStream, "../one/"); 88 } 89 doExtractZipFails(baos); 90 } 91 92 private void doExtractZipFails(ByteArrayOutputStream baos) { 93 File dir = createTempDir(); 94 File targetDir = new File(dir, "target"); 95 TestInputStream inputStream = new TestInputStream( 96 new ByteArrayInputStream(baos.toByteArray())); 97 try { 98 ConfigBundle.extractZipSafely(inputStream, targetDir, true /* makeWorldReadable */); 99 fail(); 100 } catch (IOException expected) { 101 } 102 inputStream.assertClosed(); 103 } 104 105 private static void addZipEntry(ZipOutputStream zipOutputStream, String name) 106 throws IOException { 107 ZipEntry zipEntry = new ZipEntry(name); 108 zipOutputStream.putNextEntry(zipEntry); 109 if (!zipEntry.isDirectory()) { 110 zipOutputStream.write('a'); 111 } 112 } 113 114 private File createTempDir() { 115 final String tempPrefix = getClass().getSimpleName(); 116 File tempDir = IoUtils.createTemporaryDirectory(tempPrefix); 117 testFiles.add(tempDir); 118 return tempDir; 119 } 120 121 private static void assertFilesExist(File... files) { 122 for (File f : files) { 123 assertTrue(f + " file expected to exist", f.exists() && f.isFile()); 124 } 125 } 126 127 private static void assertDirsExist(File... dirs) { 128 for (File dir : dirs) { 129 assertTrue(dir + " directory expected to exist", dir.exists() && dir.isDirectory()); 130 } 131 } 132 133 private static class TestInputStream extends FilterInputStream { 134 135 private boolean closed; 136 137 public TestInputStream(InputStream in) { 138 super(in); 139 } 140 141 @Override 142 public void close() throws IOException { 143 closed = true; 144 super.close(); 145 } 146 147 public void assertClosed() { 148 assertTrue(closed); 149 } 150 } 151} 152