Skip to content

Commit d33d0ac

Browse files
committed
Fix zip slip vulnerability
1 parent beeb4d0 commit d33d0ac

File tree

5 files changed

+74
-43
lines changed

5 files changed

+74
-43
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"prefer-stable": true,
33
"require": {
44
"ext-json": "*",
5-
"php": ">=7.0"
5+
"php": ">=7.0",
6+
"ext-zip": "*"
67
},
78
"require-dev": {
89
"yoast/phpunit-polyfills": "2.0.0"

src/WordPress/Blueprints/Runner/Step/UnzipStepRunner.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class UnzipStepRunner extends BaseStepRunner {
1616
* @return void
1717
*/
1818
public function run(
19-
$input,
20-
$progress_tracker
19+
UnzipStep $input,
20+
Tracker $progress_tracker
2121
) {
2222
$progress_tracker->set( 10, 'Unzipping...' );
2323

src/WordPress/Zip/functions.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ function zip_extract_to( $fp, $to_path ) {
1414
continue;
1515
}
1616

17+
if ( false !== strpos( $entry->path ,'..') ) {
18+
continue;
19+
}
20+
1721
$path = Path::canonicalize( $to_path . '/' . $entry->path );
1822
$parent = Path::getDirectory( $path );
1923
if ( ! is_dir( $parent ) ) {

tests/unit/steps/UnzipStepRunnerTest.php

Lines changed: 36 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@ class UnzipStepRunnerTest extends PHPUnitTestCase {
2525
private $runtime;
2626

2727
/**
28-
* @var UnzipStepRunner $step
28+
* @var UnzipStepRunner $step_runner
2929
*/
30-
private $step;
30+
private $step_runner;
3131

3232
/**
3333
* @var Filesystem
3434
*/
35-
private $file_system;
35+
private $filesystem;
3636

3737
/**
38-
* @var Stub
38+
* @var ResourceManager $resource_manager resource manager mock
3939
*/
4040
private $resource_manager;
4141

@@ -48,51 +48,47 @@ public function before() {
4848

4949
$this->resource_manager = $this->createMock( ResourceManager::class );
5050

51-
$this->step = new UnzipStepRunner();
52-
$this->step->setRuntime( $this->runtime );
53-
$this->step->setResourceManager( $this->resource_manager );
51+
$this->step_runner = new UnzipStepRunner();
52+
$this->step_runner->setRuntime( $this->runtime );
53+
$this->step_runner->setResourceManager( $this->resource_manager );
5454

55-
$this->file_system = new Filesystem();
55+
$this->filesystem = new Filesystem();
5656
}
5757

5858
/**
5959
* @after
6060
*/
6161
public function after() {
62-
$this->file_system->remove( $this->document_root );
62+
$this->filesystem->remove( $this->document_root );
6363
}
6464

65-
public function test(){
66-
$this->assertTrue(true); // Placeholder until true test are fixed.
65+
public function testUnzipFileWhenUsingAbsolutePath() {
66+
$zip = __DIR__ . '/resources/test_zip.zip';
67+
$this->resource_manager->method( 'getStream' )
68+
->willReturn( fopen( $zip, 'rb' ) );
69+
70+
$step = new UnzipStep();
71+
$step->setZipFile( $zip );
72+
$extracted_file_path = $this->runtime->resolvePath( 'dir/test_zip.txt' );
73+
$step->setExtractToPath( Path::getDirectory( $extracted_file_path ) );
74+
75+
$this->step_runner->run( $step, new Tracker() );
76+
77+
self::assertFileEquals( __DIR__ . '/resources/test_zip.txt', $extracted_file_path );
6778
}
6879

69-
// public function testUnzipFileWhenUsingAbsolutePath() {
70-
// $zip = __DIR__ . '/resources/test_zip.zip';
71-
// $this->resource_manager->method( 'getStream' )
72-
// ->willReturn( fopen( $zip, 'rb' ) );
73-
//
74-
// $input = new UnzipStep();
75-
// $input->setZipFile( $zip );
76-
// $extracted_file_path = $this->runtime->resolvePath( 'dir/test_zip.txt' );
77-
// $input->setExtractToPath( Path::getDirectory( $extracted_file_path ) );
78-
//
79-
// $this->step->run( $input, new Tracker() );
80-
//
81-
// $this->assertFileEquals( __DIR__ . '/resources/test_zip.txt', $extracted_file_path );
82-
// }
83-
//
84-
// public function testUnzipFileWhenUsingRelativePath() {
85-
// $zip = __DIR__ . '/resources/test_zip.zip';
86-
// $this->resource_manager->method( 'getStream' )
87-
// ->willReturn( fopen( $zip, 'rb' ) );
88-
//
89-
// $input = new UnzipStep();
90-
// $input->setZipFile( $zip );
91-
// $input->setExtractToPath( 'dir' );
92-
//
93-
// $this->step->run( $input, new Tracker() );
94-
//
95-
// $extracted_file_path = $this->runtime->resolvePath( 'dir/test_zip.txt' );
96-
// $this->assertFileEquals( __DIR__ . '/resources/test_zip.txt', $extracted_file_path );
97-
// }
80+
public function testUnzipFileWhenUsingRelativePath() {
81+
$zip = __DIR__ . '/resources/test_zip.zip';
82+
$this->resource_manager->method( 'getStream' )
83+
->willReturn( fopen( $zip, 'rb' ) );
84+
85+
$input = new UnzipStep();
86+
$input->setZipFile( $zip );
87+
$input->setExtractToPath( 'dir' );
88+
89+
$this->step_runner->run( $input, new Tracker() );
90+
91+
$extracted_file_path = $this->runtime->resolvePath( 'dir/test_zip.txt' );
92+
self::assertFileEquals( __DIR__ . '/resources/test_zip.txt', $extracted_file_path );
93+
}
9894
}

tests/unit/zip/ZipFunctionsTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace unit\zip;
4+
5+
use PHPUnitTestCase;
6+
use Symfony\Component\Filesystem\Filesystem;
7+
use Symfony\Component\Filesystem\Path;
8+
use ZipArchive;
9+
use function WordPress\Zip\zip_extract_to;
10+
11+
class ZipFunctionsTest extends PHPUnitTestCase {
12+
public function testImmuneToZipSlip() {
13+
$filesystem = new Filesystem();
14+
15+
$filename = __DIR__ . 'tmp/zip-slip-test.zip';
16+
$filesystem->mkdir( dirname( $filename ) );
17+
18+
$zip = new ZipArchive();
19+
$zip->open( $filename, ZipArchive::CREATE );
20+
$zip->addFromString( "../../../../../../../../tmp/zip-slip-test.txt" . time(), "zip slip test" );
21+
$zip->close();
22+
23+
zip_extract_to( fopen( $filename, 'rb' ), dirname( $filename ) );
24+
25+
$slipped_dir = Path::canonicalize(__DIR__ . "../../../../../../../../tmp");
26+
self::assertDirectoryDoesNotExist( $slipped_dir );
27+
28+
$filesystem->remove( dirname( $filename ) );
29+
}
30+
}

0 commit comments

Comments
 (0)