diff --git a/composer.json b/composer.json index cb0b96e9..7ba219f7 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,8 @@ "php": ">=7.0" }, "require-dev": { - "yoast/phpunit-polyfills": "2.0.0" + "yoast/phpunit-polyfills": "2.0.0", + "ext-zip": "*" }, "config": { "optimize-autoloader": true, diff --git a/src/WordPress/Zip/ZipException.php b/src/WordPress/Zip/ZipException.php new file mode 100644 index 00000000..0eb7a07e --- /dev/null +++ b/src/WordPress/Zip/ZipException.php @@ -0,0 +1,7 @@ + using relative path to access otherwise inaccessible files + if ( false !== strpos( $entry->path ,'..') ) { + throw new ZipException( "Relative paths in zips are not allowed." ); + } + + // prevent zip with symlinks -> using a symbolic link to access otherwise inaccessible files + if ( is_link( $entry->path ) ) { + throw new ZipException( "Semantic links in zips are not allowed." ); + } + $path = Path::canonicalize( $to_path . '/' . $entry->path ); $parent = Path::getDirectory( $path ); if ( ! is_dir( $parent ) ) { - if(is_file($parent)) { - unlink($parent); + if( is_file( $parent ) ) { + unlink( $parent ); } mkdir( $parent, 0777, true ); } diff --git a/tests/unit/zip/ZipFunctionsTest.php b/tests/unit/zip/ZipFunctionsTest.php new file mode 100644 index 00000000..3cc4cff9 --- /dev/null +++ b/tests/unit/zip/ZipFunctionsTest.php @@ -0,0 +1,64 @@ +filesystem = new Filesystem(); + $this->filesystem->mkdir( self::TMP_DIRECTORY ); + } + + /** + * @after + */ + public function after(){ + $this->filesystem->remove( self::TMP_DIRECTORY ); + } + + public function testThrowsExceptionWhenZipContainsFilesWithRelativePaths() { + $filename = self::TMP_DIRECTORY . '/zip-slip-test.zip'; + + $zip = new ZipArchive(); + $zip->open( $filename, ZipArchive::CREATE ); + $zip->addFromString( "../../evilfile.txt","zip-slip-test" ); + $zip->close(); + + self::expectException( ZipException::class ); + self::expectExceptionMessage( "Relative paths in zips are not allowed." ); + zip_extract_to( fopen( $filename, 'rb' ), dirname( $filename ) ); + } + + public function testThrowsExceptionWhenZipContainsFilesWithSymlinks() { + $filename = self::TMP_DIRECTORY . '/zip-symlink-test.zip'; + + $zip = new ZipArchive(); + $zip->open( $filename, ZipArchive::CREATE ); + $symlink = symlink( self::TMP_DIRECTORY, 'evillink.txt' ); + $zip->addFile( $symlink,"zip-symlink-test" ); + $zip->close(); + + self::expectException( ZipException::class ); + self::expectExceptionMessage( "Semantic links in zips are not allowed." ); + zip_extract_to( fopen( $filename, 'rb' ), dirname( $filename ) ); + } +}