Просмотр исходного кода

ZF-8734
- Detection of upwards directory traversal added in PHP < 5.2.8
- Target is required in decompress()

git-svn-id: http://framework.zend.com/svn/framework/standard/trunk@20126 44c647ce-9c0f-0410-b52a-842ac1e357ba

ralph 16 лет назад
Родитель
Сommit
57627b7fdc

+ 26 - 4
library/Zend/Filter/Compress/Zip.php

@@ -46,7 +46,7 @@ class Zend_Filter_Compress_Zip extends Zend_Filter_Compress_CompressAbstract
      */
     protected $_options = array(
         'archive' => null,
-        'target'  => '.',
+        'target'  => null,
     );
 
     /**
@@ -214,12 +214,16 @@ class Zend_Filter_Compress_Zip extends Zend_Filter_Compress_CompressAbstract
         $res = $zip->open($archive);
 
         $target = $this->getTarget();
-        if (!is_dir($target)) {
+
+        if (!empty($target) && !is_dir($target)) {
             $target = dirname($target);
         }
+        
+        if (!empty($target)) {
+            $target = rtrim($target, '/\\') . DIRECTORY_SEPARATOR;
+        }
 
-        $target = $target . DIRECTORY_SEPARATOR;
-        if (empty($target)) {
+        if (empty($target) || !is_dir($target)) {
             require_once 'Zend/Filter/Exception.php';
             throw new Zend_Filter_Exception('No target for ZIP decompression set');
         }
@@ -229,6 +233,24 @@ class Zend_Filter_Compress_Zip extends Zend_Filter_Compress_CompressAbstract
             throw new Zend_Filter_Exception($this->_errorString($res));
         }
 
+        if (version_compare(PHP_VERSION, '5.2.8', '<')) {
+            for ($i = 0; $i < $zip->numFiles; $i++) {
+                $statIndex = $zip->statIndex($i);
+                $currName = $statIndex['name'];
+                if (($currName{0} == '/') ||
+                    (substr($currName, 0, 2) == '..') ||
+                    (substr($currName, 0, 4) == './..')
+                    )
+                {
+                    require_once 'Zend/Filter/Exception.php';
+                    throw new Zend_Filter_Exception('Upward directory traversal was detected inside ' . $archive
+                        . ' please use PHP 5.2.8 or greater to take advantage of path resolution features of '
+                        . 'the zip extension in this decompress() method.'
+                        );
+                }
+            }
+        }    
+        
         $res = @$zip->extractTo($target);
         if ($res !== true) {
             require_once 'Zend/Filter/Exception.php';

+ 51 - 2
tests/Zend/Filter/Compress/ZipTest.php

@@ -159,7 +159,7 @@ class Zend_Filter_Compress_ZipTest extends PHPUnit_Framework_TestCase
     public function testZipGetSetOptions()
     {
         $filter = new Zend_Filter_Compress_Zip();
-        $this->assertEquals(array('archive' => null, 'target' => '.'), $filter->getOptions());
+        $this->assertEquals(array('archive' => null, 'target' => null), $filter->getOptions());
 
         $this->assertEquals(null, $filter->getOptions('archive'));
 
@@ -193,7 +193,7 @@ class Zend_Filter_Compress_ZipTest extends PHPUnit_Framework_TestCase
     public function testZipGetSetTarget()
     {
         $filter = new Zend_Filter_Compress_Zip();
-        $this->assertEquals('.', $filter->getTarget());
+        $this->assertNull($filter->getTarget());
         $filter->setTarget('Testfile.txt');
         $this->assertEquals('Testfile.txt', $filter->getTarget());
         $this->assertEquals('Testfile.txt', $filter->getOptions('target'));
@@ -301,6 +301,55 @@ class Zend_Filter_Compress_ZipTest extends PHPUnit_Framework_TestCase
         $filter = new Zend_Filter_Compress_Zip();
         $this->assertEquals('Zip', $filter->toString());
     }
+
+    /**
+     * @group
+     * @expectedException Zend_Filter_Exception
+     */
+    public function testDecompressWillThrowExceptionWhenDecompressingWithNoTarget()
+    {
+        $filter  = new Zend_Filter_Compress_Zip(
+            array(
+                'archive' => dirname(__FILE__) . '/../_files/compressed.zip',
+                'target'  => dirname(__FILE__) . '/../_files/_compress'
+            )
+        );
+
+        $content = $filter->compress('compress me');
+        $this->assertEquals(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '_files'
+                            . DIRECTORY_SEPARATOR . 'compressed.zip', $content);
+
+        $filter  = new Zend_Filter_Compress_Zip(
+            array(
+                'archive' => dirname(__FILE__) . '/../_files/compressed.zip'
+            )
+        );
+        $content = $filter->decompress($content);
+        $this->assertEquals(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR, $content);
+        $content = file_get_contents(dirname(__FILE__) . '/../_files/zip.tmp');
+        $this->assertEquals('compress me', $content);
+    }
+    
+    /**
+     * @group RS
+     * @expectedException Zend_Filter_Exception
+     */
+    public function testDecompressWillThrowExceptionWhenDetectingUpwardDirectoryTraversal()
+    {
+        if (version_compare(PHP_VERSION, '5.2.8', '>=')) {
+            $this->markTestSkipped('This test is to run on PHP less than 5.2.8');
+            return;
+        }
+        
+        $filter  = new Zend_Filter_Compress_Zip(
+            array(
+                'archive' => dirname(__FILE__) . '/../_files/compressed.zip',
+                'target'  => dirname(__FILE__) . '/../_files/evil.zip'
+                )
+            );
+        
+        $filter->decompress(dirname(__FILE__) . '/../_files/evil.zip');
+    }
 }
 
 if (PHPUnit_MAIN_METHOD == 'Zend_Filter_Compress_ZipTest::main') {

BIN
tests/Zend/Filter/_files/evil.zip