Skip to content

Commit 8c85096

Browse files
authored
chore(storage): update storage path validation to include empty/white spaces (#3587)
1 parent 540acc2 commit 8c85096

File tree

8 files changed

+231
-2
lines changed

8 files changed

+231
-2
lines changed

AmplifyPlugins/Storage/Sources/AWSS3StoragePlugin/Support/Internal/StoragePath+Extensions.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension StoragePath {
2020
nil
2121
)
2222
}
23-
let path = resolve(identityId)
23+
let path = resolve(identityId).trimmingCharacters(in: .whitespaces)
2424
try validate(path)
2525
return path
2626
} else if self is StringStoragePath {
@@ -30,7 +30,7 @@ extension StoragePath {
3030
nil
3131
)
3232
}
33-
let path = resolve(input)
33+
let path = resolve(input).trimmingCharacters(in: .whitespaces)
3434
try validate(path)
3535
return path
3636
} else {
@@ -41,6 +41,12 @@ extension StoragePath {
4141
}
4242

4343
func validate(_ path: String) throws {
44+
guard !path.isEmpty else {
45+
let errorDescription = "Invalid StoragePath specified."
46+
let recoverySuggestion = "Please specify a valid StoragePath"
47+
throw StorageError.validation("path", errorDescription, recoverySuggestion, nil)
48+
}
49+
4450
if path.hasPrefix("/") {
4551
let errorDescription = "Invalid StoragePath specified."
4652
let recoverySuggestion = "Please specify a valid StoragePath that does not contain the prefix / "

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Operation/AWSS3StorageDownloadFileOperationTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,38 @@ class AWSS3StorageDownloadFileOperationTests: AWSS3StorageOperationTestBase {
214214
XCTAssertTrue(operation.isFinished)
215215
}
216216

217+
/// Given: Storage Download File Operation
218+
/// When: The operation is executed with a request that has an invalid StringStoragePath
219+
/// Then: The operation will fail with a validation error
220+
func testDownloadFileOperationEmptyStoragePathValidationError() {
221+
let path = StringStoragePath(resolve: { _ in return " " })
222+
let request = StorageDownloadFileRequest(path: path,
223+
local: testURL,
224+
options: StorageDownloadFileRequest.Options())
225+
226+
let failedInvoked = expectation(description: "failed was invoked on operation")
227+
let operation = AWSS3StorageDownloadFileOperation(request,
228+
storageConfiguration: testStorageConfiguration,
229+
storageService: mockStorageService,
230+
authService: mockAuthService,
231+
progressListener: nil) { result in
232+
switch result {
233+
case .failure(let error):
234+
guard case .validation = error else {
235+
XCTFail("Should have failed with validation error")
236+
return
237+
}
238+
failedInvoked.fulfill()
239+
default:
240+
XCTFail("Should have received failed event")
241+
}
242+
}
243+
244+
operation.start()
245+
waitForExpectations(timeout: 1)
246+
XCTAssertTrue(operation.isFinished)
247+
}
248+
217249
/// Given: Storage Download File Operation
218250
/// When: The operation is executed with a request that has an invalid IdentityIDStoragePath
219251
/// Then: The operation will fail with a validation error

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Operation/AWSS3StorageGetDataOperationTests.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,36 @@ class AWSS3StorageDownloadDataOperationTests: AWSS3StorageOperationTestBase {
204204
XCTAssertTrue(operation.isFinished)
205205
}
206206

207+
/// Given: Storage Download Data Operation
208+
/// When: The operation is executed with a request that has an invalid StringStoragePath
209+
/// Then: The operation will fail with a validation error
210+
func testDownloadDataOperationEmptyStoragePathValidationError() {
211+
let path = StringStoragePath(resolve: { _ in return " " })
212+
let request = StorageDownloadDataRequest(path: path, options: StorageDownloadDataRequest.Options())
213+
let failedInvoked = expectation(description: "failed was invoked on operation")
214+
let operation = AWSS3StorageDownloadDataOperation(request,
215+
storageConfiguration: testStorageConfiguration,
216+
storageService: mockStorageService,
217+
authService: mockAuthService,
218+
progressListener: nil
219+
) { event in
220+
switch event {
221+
case .failure(let error):
222+
guard case .validation = error else {
223+
XCTFail("Should have failed with validation error")
224+
return
225+
}
226+
failedInvoked.fulfill()
227+
default:
228+
XCTFail("Should have received failed event")
229+
}
230+
}
231+
232+
operation.start()
233+
waitForExpectations(timeout: 1)
234+
XCTAssertTrue(operation.isFinished)
235+
}
236+
207237
/// Given: Storage Download Data Operation
208238
/// When: The operation is executed with a request that has an invalid IdentityIDStoragePath
209239
/// Then: The operation will fail with a validation error

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Operation/AWSS3StoragePutDataOperationTests.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,37 @@ class AWSS3StorageUploadDataOperationTests: AWSS3StorageOperationTestBase {
250250
XCTAssertTrue(operation.isFinished)
251251
}
252252

253+
/// Given: Storage Upload Data Operation
254+
/// When: The operation is executed with a request that has an invalid StringStoragePath
255+
/// Then: The operation will fail with a validation error
256+
func testUploadDataOperationEmptyStoragePathValidationError() {
257+
let path = StringStoragePath(resolve: { _ in return " " })
258+
let failedInvoked = expectation(description: "failed was invoked on operation")
259+
let options = StorageUploadDataRequest.Options(accessLevel: .protected)
260+
let request = StorageUploadDataRequest(path: path, data: testData, options: options)
261+
let operation = AWSS3StorageUploadDataOperation(request,
262+
storageConfiguration: testStorageConfiguration,
263+
storageService: mockStorageService,
264+
authService: mockAuthService,
265+
progressListener: nil
266+
) { result in
267+
switch result {
268+
case .failure(let error):
269+
guard case .validation = error else {
270+
XCTFail("Should have failed with validation error")
271+
return
272+
}
273+
failedInvoked.fulfill()
274+
default:
275+
XCTFail("Should have received failed event")
276+
}
277+
}
278+
279+
operation.start()
280+
waitForExpectations(timeout: 1)
281+
XCTAssertTrue(operation.isFinished)
282+
}
283+
253284
/// Given: Storage Upload Data Operation
254285
/// When: The operation is executed with a request that has an invalid IdentityIDStoragePath
255286
/// Then: The operation will fail with a validation error

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Operation/AWSS3StorageUploadFileOperationTests.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,52 @@ class AWSS3StorageUploadFileOperationTests: AWSS3StorageOperationTestBase {
301301
XCTAssertTrue(operation.isFinished)
302302
}
303303

304+
/// Given: Storage Upload File Operation
305+
/// When: The operation is executed with a request that has an invalid StringStoragePath
306+
/// Then: The operation will fail with a validation error
307+
func testUploadFileOperationEmptyStoragePathValidationError() {
308+
let path = StringStoragePath(resolve: { _ in return " " })
309+
mockAuthService.identityId = testIdentityId
310+
let task = StorageTransferTask(transferType: .upload(onEvent: { _ in }), bucket: "bucket", key: "key")
311+
mockStorageService.storageServiceUploadEvents = [
312+
StorageEvent.initiated(StorageTaskReference(task)),
313+
StorageEvent.inProcess(Progress()),
314+
StorageEvent.completedVoid]
315+
316+
let filePath = NSTemporaryDirectory() + UUID().uuidString + ".tmp"
317+
let fileURL = URL(fileURLWithPath: filePath)
318+
FileManager.default.createFile(atPath: filePath, contents: testData, attributes: nil)
319+
let expectedUploadSource = UploadSource.local(fileURL)
320+
let metadata = ["mykey": "Value"]
321+
322+
let options = StorageUploadFileRequest.Options(accessLevel: .protected,
323+
metadata: metadata,
324+
contentType: testContentType)
325+
let request = StorageUploadFileRequest(path: path, local: fileURL, options: options)
326+
327+
let failedInvoked = expectation(description: "failed was invoked on operation")
328+
let operation = AWSS3StorageUploadFileOperation(request,
329+
storageConfiguration: testStorageConfiguration,
330+
storageService: mockStorageService,
331+
authService: mockAuthService,
332+
progressListener: nil) { result in
333+
switch result {
334+
case .failure(let error):
335+
guard case .validation = error else {
336+
XCTFail("Should have failed with validation error")
337+
return
338+
}
339+
failedInvoked.fulfill()
340+
default:
341+
XCTFail("Should have received failed event")
342+
}
343+
}
344+
345+
operation.start()
346+
waitForExpectations(timeout: 1)
347+
XCTAssertTrue(operation.isFinished)
348+
}
349+
304350
/// Given: Storage Upload File Operation
305351
/// When: The operation is executed with a request that has an invalid IdentityIDStoragePath
306352
/// Then: The operation will fail with a validation error

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Tasks/AWSS3StorageGetURLTaskTests.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,36 @@ class AWSS3StorageGetURLTaskTests: XCTestCase {
103103
}
104104
}
105105

106+
/// - Given: A configured Storage GetURL Task with invalid path
107+
/// - When: AWSS3StorageGetURLTask value is invoked
108+
/// - Then: A storage validation error should be returned
109+
func testGetURLTaskWithInvalidEmptyPath() async throws {
110+
let emptyPath = " "
111+
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory())
112+
113+
let serviceMock = MockAWSS3StorageService()
114+
serviceMock.getPreSignedURLHandler = { path, _, _ in
115+
XCTAssertEqual(emptyPath, path)
116+
return tempURL
117+
}
118+
119+
let request = StorageGetURLRequest(
120+
path: StringStoragePath.fromString(emptyPath), options: .init())
121+
let task = AWSS3StorageGetURLTask(
122+
request,
123+
storageBehaviour: serviceMock)
124+
do {
125+
_ = try await task.value
126+
XCTFail("Task should throw an exception")
127+
}
128+
catch {
129+
guard let storageError = error as? StorageError,
130+
case .validation(let field, _, _, _) = storageError else {
131+
XCTFail("Should throw a storage validation error, instead threw \(error)")
132+
return
133+
}
134+
135+
XCTAssertEqual(field, "path", "Field in error should be `path`")
136+
}
137+
}
106138
}

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Tasks/AWSS3StorageListObjectsTaskTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,30 @@ class AWSS3StorageListObjectsTaskTests: XCTestCase {
103103
}
104104
}
105105

106+
/// - Given: A configured Storage ListObjects Task with invalid path
107+
/// - When: AWSS3StorageListObjectsTask value is invoked
108+
/// - Then: A storage validation error should be returned
109+
func testListObjectsTaskWithInvalidEmptyPath() async throws {
110+
let serviceMock = MockAWSS3StorageService()
111+
112+
let request = StorageListRequest(
113+
path: StringStoragePath.fromString(" "), options: .init())
114+
let task = AWSS3StorageListObjectsTask(
115+
request,
116+
storageConfiguration: AWSS3StoragePluginConfiguration(),
117+
storageBehaviour: serviceMock)
118+
do {
119+
_ = try await task.value
120+
XCTFail("Task should throw an exception")
121+
}
122+
catch {
123+
guard let storageError = error as? StorageError,
124+
case .validation(let field, _, _, _) = storageError else {
125+
XCTFail("Should throw a storage validation error, instead threw \(error)")
126+
return
127+
}
128+
129+
XCTAssertEqual(field, "path", "Field in error should be `path`")
130+
}
131+
}
106132
}

AmplifyPlugins/Storage/Tests/AWSS3StoragePluginTests/Tasks/AWSS3StorageRemoveTaskTests.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,30 @@ class AWSS3StorageRemoveTaskTests: XCTestCase {
9494
}
9595
}
9696

97+
/// - Given: A configured Storage Remove Task with invalid path
98+
/// - When: AWSS3StorageRemoveTask value is invoked
99+
/// - Then: A storage validation error should be returned
100+
func testRemoveTaskWithInvalidEmptyPath() async throws {
101+
let serviceMock = MockAWSS3StorageService()
102+
103+
let request = StorageRemoveRequest(
104+
path: StringStoragePath.fromString(" "), options: .init())
105+
let task = AWSS3StorageRemoveTask(
106+
request,
107+
storageConfiguration: AWSS3StoragePluginConfiguration(),
108+
storageBehaviour: serviceMock)
109+
do {
110+
_ = try await task.value
111+
XCTFail("Task should throw an exception")
112+
}
113+
catch {
114+
guard let storageError = error as? StorageError,
115+
case .validation(let field, _, _, _) = storageError else {
116+
XCTFail("Should throw a storage validation error, instead threw \(error)")
117+
return
118+
}
119+
120+
XCTAssertEqual(field, "path", "Field in error should be `path`")
121+
}
122+
}
97123
}

0 commit comments

Comments
 (0)