Unverified Commit 610b41e8 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

[flutter_tools] add validation of paths of contained files to os_utils _unpackArchive() (#96565)

parent 9cdad24d
...@@ -539,10 +539,32 @@ class _WindowsUtils extends OperatingSystemUtils { ...@@ -539,10 +539,32 @@ class _WindowsUtils extends OperatingSystemUtils {
continue; continue;
} }
final File destFile = _fileSystem.file(_fileSystem.path.join( final File destFile = _fileSystem.file(
_fileSystem.path.canonicalize(
_fileSystem.path.join(
targetDirectory.path, targetDirectory.path,
archiveFile.name, archiveFile.name,
)); ),
),
);
// Validate that the destFile is within the targetDirectory we want to
// extract to.
//
// See https://snyk.io/research/zip-slip-vulnerability for more context.
final String destinationFileCanonicalPath = _fileSystem.path.canonicalize(
destFile.path,
);
final String targetDirectoryCanonicalPath = _fileSystem.path.canonicalize(
targetDirectory.path,
);
if (!destinationFileCanonicalPath.startsWith(targetDirectoryCanonicalPath)) {
throw StateError(
'Tried to extract the file $destinationFileCanonicalPath outside of the '
'target directory $targetDirectoryCanonicalPath',
);
}
if (!destFile.parent.existsSync()) { if (!destFile.parent.existsSync()) {
destFile.parent.createSync(recursive: true); destFile.parent.createSync(recursive: true);
} }
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:archive/archive.dart';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart'; import 'package:file_testing/file_testing.dart';
...@@ -496,6 +497,33 @@ void main() { ...@@ -496,6 +497,33 @@ void main() {
); );
expect(utils.name, 'Pretty Name'); expect(utils.name, 'Pretty Name');
}); });
// See https://snyk.io/research/zip-slip-vulnerability for more context
testWithoutContext('Windows validates paths when unzipping', () {
// on POSIX systems we use the `unzip` binary, which will fail to extract
// files with paths outside the target directory
final OperatingSystemUtils utils = createOSUtils(FakePlatform(operatingSystem: 'windows'));
final MemoryFileSystem fs = MemoryFileSystem.test();
final File fakeZipFile = fs.file('archive.zip');
final Directory targetDirectory = fs.directory('output')..createSync(recursive: true);
const String content = 'hello, world!';
final Archive archive = Archive()..addFile(
// This file would be extracted outside of the target extraction dir
ArchiveFile(r'..\..\..\Target File.txt', content.length, content.codeUnits),
);
final List<int> zipData = ZipEncoder().encode(archive)!;
fakeZipFile.writeAsBytesSync(zipData);
expect(
() => utils.unzip(fakeZipFile, targetDirectory),
throwsA(
isA<StateError>().having(
(StateError error) => error.message,
'correct error message',
contains('Tried to extract the file '),
),
),
);
});
}); });
testWithoutContext('If unzip fails, include stderr in exception text', () { testWithoutContext('If unzip fails, include stderr in exception text', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment