From 3cf399f7f8d76966e9e69e395f11f3fe444dfbe0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 25 May 2026 10:05:13 +0000 Subject: [PATCH] test(ensemble): harden upload path guard and view-group index coverage Add edge-case assertions for uploadPathContainsParentSegment, verify UploadUtils.uploadFiles rejects traversal paths before I/O, and cover safeViewGroupPayloadIndex when payload length is non-positive. Co-authored-by: Sharjeel Yunus --- .../safe_view_group_payload_index_test.dart | 5 +++ .../test/upload_path_security_test.dart | 38 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/modules/ensemble/test/safe_view_group_payload_index_test.dart b/modules/ensemble/test/safe_view_group_payload_index_test.dart index f129b0bfa..8652a01f3 100644 --- a/modules/ensemble/test/safe_view_group_payload_index_test.dart +++ b/modules/ensemble/test/safe_view_group_payload_index_test.dart @@ -15,5 +15,10 @@ void main() { expect(safeViewGroupPayloadIndex(0, 0), 0); expect(safeViewGroupPayloadIndex(5, 0), 0); }); + + test('returns 0 when payload length is invalid', () { + expect(safeViewGroupPayloadIndex(0, -1), 0); + expect(safeViewGroupPayloadIndex(10, -3), 0); + }); }); } diff --git a/modules/ensemble/test/upload_path_security_test.dart b/modules/ensemble/test/upload_path_security_test.dart index e5f4546bb..8e629f953 100644 --- a/modules/ensemble/test/upload_path_security_test.dart +++ b/modules/ensemble/test/upload_path_security_test.dart @@ -1,3 +1,4 @@ +import 'package:ensemble/framework/data_context.dart' show File; import 'package:ensemble/util/upload_utils.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -17,5 +18,42 @@ void main() { expect(uploadPathContainsParentSegment('..'), true); expect(uploadPathContainsParentSegment('../etc/passwd'), true); }); + + test('treats only a full path segment ".." as traversal', () { + expect(uploadPathContainsParentSegment('photo..jpg'), false); + expect(uploadPathContainsParentSegment('.../x'), false); + expect(uploadPathContainsParentSegment(r'a\..\b'), true); + }); + + test('normalises backslashes before splitting', () { + expect(uploadPathContainsParentSegment(r'var\..\evil'), true); + }); + }); + + group('UploadUtils.uploadFiles path guard', () { + test('throws before any network I/O when path contains ".."', () async { + final files = [ + File(null, null, null, '/tmp/../outside.jpg', null), + ]; + + await expectLater( + UploadUtils.uploadFiles( + taskId: 't1', + method: 'POST', + url: 'http://127.0.0.1:9/should-not-connect', + headers: const {}, + fields: const {}, + files: files, + fieldName: 'file', + ), + throwsA( + isA().having( + (e) => e.message, + 'message', + contains('..'), + ), + ), + ); + }); }); }