Skip to content

Commit

Permalink
Sanitize path traversal characters from file names before unarchiving…
Browse files Browse the repository at this point in the history
… them
  • Loading branch information
EthanArbuckle committed May 18, 2018
1 parent 3ad651c commit 317ca7c
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 0 deletions.
10 changes: 10 additions & 0 deletions ObjectiveCExample/ObjectiveCExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
37FF0CB81F853459006E4361 /* ProgressDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 37FF0CB71F853459006E4361 /* ProgressDelegate.m */; };
37FF0CB91F853459006E4361 /* ProgressDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 37FF0CB71F853459006E4361 /* ProgressDelegate.m */; };
37FF0CBA1F853459006E4361 /* ProgressDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 37FF0CB71F853459006E4361 /* ProgressDelegate.m */; };
5FBB318220AF99BC003BA18F /* PathTraversal.zip in Resources */ = {isa = PBXBuildFile; fileRef = 5FBB318120AF99BC003BA18F /* PathTraversal.zip */; };
5FBB318320AF99C9003BA18F /* PathTraversal.zip in Resources */ = {isa = PBXBuildFile; fileRef = 5FBB318120AF99BC003BA18F /* PathTraversal.zip */; };
5FBB318420AF99CF003BA18F /* PathTraversal.zip in Resources */ = {isa = PBXBuildFile; fileRef = 5FBB318120AF99BC003BA18F /* PathTraversal.zip */; };
5FBB318520AF99D4003BA18F /* PathTraversal.zip in Resources */ = {isa = PBXBuildFile; fileRef = 5FBB318120AF99BC003BA18F /* PathTraversal.zip */; };
6BFA1E5841DFEC4E21BA7543 /* Pods_core_ObjectiveCExampleTests_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 38A85B006A1C84E475375AE1 /* Pods_core_ObjectiveCExampleTests_macOS.framework */; };
8DFE19EF1BDA9FF300709011 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DFE19EE1BDA9FF300709011 /* main.m */; };
8DFE19F21BDA9FF300709011 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8DFE19F11BDA9FF300709011 /* AppDelegate.m */; };
Expand Down Expand Up @@ -97,6 +101,7 @@
48AB7DF053F85FCC6AFB0C26 /* Pods-core-ObjectiveCExampleTests_macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-core-ObjectiveCExampleTests_macOS.debug.xcconfig"; path = "Pods/Target Support Files/Pods-core-ObjectiveCExampleTests_macOS/Pods-core-ObjectiveCExampleTests_macOS.debug.xcconfig"; sourceTree = "<group>"; };
49974600DB985CD98FC3AD39 /* Pods_core_ObjectiveCExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_core_ObjectiveCExample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
51F30FF220ECFD8DC810E21A /* Pods-core-ObjectiveCExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-core-ObjectiveCExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-core-ObjectiveCExample/Pods-core-ObjectiveCExample.debug.xcconfig"; sourceTree = "<group>"; };
5FBB318120AF99BC003BA18F /* PathTraversal.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = PathTraversal.zip; sourceTree = "<group>"; };
8DFE19EA1BDA9FF300709011 /* ObjectiveCExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ObjectiveCExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
8DFE19EE1BDA9FF300709011 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
8DFE19F01BDA9FF300709011 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -255,6 +260,7 @@
8DFE1A151BDAA10100709011 /* Fixtures */ = {
isa = PBXGroup;
children = (
5FBB318120AF99BC003BA18F /* PathTraversal.zip */,
3754B1311F88961800A58AA0 /* Empty.zip */,
8DFE1A161BDAA10100709011 /* hello.zip */,
8DFE1A171BDAA10100709011 /* IncorrectHeaders.zip */,
Expand Down Expand Up @@ -423,6 +429,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5FBB318420AF99CF003BA18F /* PathTraversal.zip in Resources */,
3773ADC31F7F4D1C009A4B2D /* 3.m4a in Resources */,
3773ADC21F7F4D1C009A4B2D /* 2.m4a in Resources */,
3773ADC41F7F4D1C009A4B2D /* 4.m4a in Resources */,
Expand All @@ -447,6 +454,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5FBB318520AF99D4003BA18F /* PathTraversal.zip in Resources */,
3793E6E71F7F6052000B1A19 /* 6.m4a in Resources */,
3793E6E91F7F6059000B1A19 /* hello.zip in Resources */,
3793E6F01F7F605C000B1A19 /* PermissionsTestApp.app in Resources */,
Expand All @@ -471,6 +479,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5FBB318220AF99BC003BA18F /* PathTraversal.zip in Resources */,
8DFE19FD1BDA9FF300709011 /* LaunchScreen.storyboard in Resources */,
8DFE19FA1BDA9FF300709011 /* Assets.xcassets in Resources */,
8DFE19F81BDA9FF300709011 /* Main.storyboard in Resources */,
Expand All @@ -482,6 +491,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5FBB318320AF99C9003BA18F /* PathTraversal.zip in Resources */,
8DFE1A321BDAA10100709011 /* 3.m4a in Resources */,
8DFE1A311BDAA10100709011 /* 2.m4a in Resources */,
8DFE1A331BDAA10100709011 /* 4.m4a in Resources */,
Expand Down
Binary file not shown.
17 changes: 17 additions & 0 deletions ObjectiveCExample/ObjectiveCExampleTests/SSZipArchiveTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,23 @@ -(void)testShouldProvidePathOfUnzippedFileInDelegateCallback {
XCTAssertEqualObjects(collector.files[1], [outputPath stringByAppendingString:@"/Readme.markdown"]);
}

- (void)testUnzippingFileWithPathTraversalName {

// This zip archive contains a file titled '../../../../../../../../../../..//tmp/test.txt'. SSZipArchive should
// ignore the path traversing and write the file to "tmp/test.txt"
NSString *zipPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"PathTraversal" ofType:@"zip"];
NSString *outputPath = [self _cachesPath:@"Traversal"];

id<SSZipArchiveDelegate> delegate = [ProgressDelegate new];
BOOL success = [SSZipArchive unzipFileAtPath:zipPath toDestination:outputPath delegate:delegate];
XCTAssertTrue(success);

NSString *expectedFile = [outputPath stringByAppendingPathComponent:@"tmp/test.txt"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:expectedFile];

XCTAssertTrue(fileExists, @"Path traversal characters should not be followed when unarchiving a file");
}

#pragma mark - Private

- (NSString *)_cachesPath:(NSString *)directory {
Expand Down
6 changes: 6 additions & 0 deletions SSZipArchive/SSZipArchive.m
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,12 @@ + (BOOL)unzipFileAtPath:(NSString *)path
strPath = [strPath stringByReplacingOccurrencesOfString:@"\\" withString:@"/"];
}

// Sanitize path traversal characters if they're present in the file name to prevent directory backtracking. Ignoring these characters mimicks the default behavior of the Unarchiving tool on macOS.
if ([strPath rangeOfString:@"../"].location != NSNotFound) {
// "../../../../../../../../../../../tmp/test.txt" -> "tmp/test.txt"
strPath = [[[NSURL URLWithString:strPath] standardizedURL] absoluteString];
}

NSString *fullPath = [destination stringByAppendingPathComponent:strPath];
NSError *err = nil;
NSDictionary *directoryAttr;
Expand Down

0 comments on commit 317ca7c

Please sign in to comment.