diff --git a/FirebaseStorage/Sources/Internal/StoragePath.swift b/FirebaseStorage/Sources/Internal/StoragePath.swift index d27149af40e..84e12dd893b 100644 --- a/FirebaseStorage/Sources/Internal/StoragePath.swift +++ b/FirebaseStorage/Sources/Internal/StoragePath.swift @@ -58,8 +58,10 @@ class StoragePath: NSCopying, Equatable { let bucketObject = aURIString.dropFirst("gs://".count) if bucketObject.contains("/") { let splitStringArray = bucketObject.split(separator: "/", maxSplits: 1).map(String.init) - let object = splitStringArray.count == 2 ? splitStringArray[1] : nil - return StoragePath(with: splitStringArray[0], object: object) + if let bucketName = splitStringArray.first { + let object = splitStringArray.dropFirst().first + return StoragePath(with: bucketName, object: object) + } } else if bucketObject.count > 0 { return StoragePath(with: String(bucketObject)) } @@ -91,6 +93,13 @@ class StoragePath: NSCopying, Equatable { guard pathComponents.count > 4 else { return StoragePath(with: bucketName) } + guard pathComponents[4] == "o" else { + throw StoragePathError.storagePathError("Internal error: URL must be in the form of " + + "http[s]:///v0/b//o/[?token=signed_url_params]") + } + guard pathComponents.count > 5 else { + return StoragePath(with: bucketName) + } // Construct object name var objectName = pathComponents[5] for i in 6 ..< pathComponents.count { diff --git a/FirebaseStorage/Tests/Unit/StoragePathTests.swift b/FirebaseStorage/Tests/Unit/StoragePathTests.swift index 3acc075d99e..d06dbdbb65c 100644 --- a/FirebaseStorage/Tests/Unit/StoragePathTests.swift +++ b/FirebaseStorage/Tests/Unit/StoragePathTests.swift @@ -84,14 +84,39 @@ class StoragePathTests: XCTestCase { XCTAssertEqual(path.object, "#hashtag/no/token") } + func testHTTPURLObjectMarkerWithoutObject() throws { + // "/o" object marker present but no object path; must not index past the + // path components. + let httpURL = "http://firebasestorage.googleapis.com/v0/b/bucket/o" + let path = try StoragePath.path(string: httpURL) + XCTAssertEqual(path.bucket, "bucket") + XCTAssertNil(path.object) + } + func testGSURIThrowsOnNoBucket() { XCTAssertThrowsError(try StoragePath.path(string: "gs://")) } + func testGSURIThrowsOnOnlySlashes() { + XCTAssertThrowsError(try StoragePath.path(string: "gs:///")) + } + func testHTTPURLThrowsOnNoBucket() { XCTAssertThrowsError(try StoragePath.path(string: "http://firebasestorage.googleapis.com/")) } + func testHTTPURLThrowsOnMissingObjectMarker() { + // A path segment in the "/o" marker position that isn't "o" is not a valid + // Storage URL and must throw rather than be parsed as an object. + let httpURL = "http://firebasestorage.googleapis.com/v0/b/bucket/x/path/to/object" + XCTAssertThrowsError(try StoragePath.path(string: httpURL)) + + // Same invalid marker but with no object path (exactly five components) must + // also throw rather than be parsed as a bucket-only path. + let shortHTTPURL = "http://firebasestorage.googleapis.com/v0/b/bucket/x" + XCTAssertThrowsError(try StoragePath.path(string: shortHTTPURL)) + } + func testThrowsOnInvalidScheme() { let ftpURL = "ftp://firebasestorage.googleapis.com/v0/b/bucket/o/path/to/object" XCTAssertThrowsError(try StoragePath.path(string: ftpURL))