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 {
continue;
}
final File destFile = _fileSystem.file(_fileSystem.path.join(
final File destFile = _fileSystem.file(
_fileSystem.path.canonicalize(
_fileSystem.path.join(
targetDirectory.path,
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,
archiveFile.name,
));
);
if (!destinationFileCanonicalPath.startsWith(targetDirectoryCanonicalPath)) {
throw StateError(
'Tried to extract the file $destinationFileCanonicalPath outside of the '
'target directory $targetDirectoryCanonicalPath',
);
}
if (!destFile.parent.existsSync()) {
destFile.parent.createSync(recursive: true);
}
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:archive/archive.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
......@@ -496,6 +497,33 @@ void main() {
);
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', () {
......
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