aboutsummaryrefslogtreecommitdiff
path: root/hotline/file_transfer_test.go
diff options
context:
space:
mode:
authorJeff Halter <868228+jhalter@users.noreply.github.com>2026-06-01 10:56:43 -0700
committerJeff Halter <868228+jhalter@users.noreply.github.com>2026-06-01 10:56:43 -0700
commit30db5839a93936f8b69d0d5d005bbe03197722c1 (patch)
tree81ee122b064d5800bf924f7b1c5c2d56543a341c /hotline/file_transfer_test.go
parentc42c103ddb66028a085fb452102f73f8b27dcdcb (diff)
Normalize folder upload item paths and consolidate path handling
Anchor the path returned by folderUpload.FormattedPath relative to the upload root so item paths are resolved consistently, matching the behavior of ReadPath. Resolve each destination path once per item in UploadFolderHandler and use path.Join in place of manual string concatenation. Add test coverage for FormattedPath normalization.
Diffstat (limited to 'hotline/file_transfer_test.go')
-rw-r--r--hotline/file_transfer_test.go62
1 files changed, 62 insertions, 0 deletions
diff --git a/hotline/file_transfer_test.go b/hotline/file_transfer_test.go
index a5c4afe..d427352 100644
--- a/hotline/file_transfer_test.go
+++ b/hotline/file_transfer_test.go
@@ -255,6 +255,68 @@ func Test_folderUpload_FormattedPath(t *testing.T) {
},
want: "test@$%&",
},
+ {
+ // Traversal attempt: leading ".." segments must be collapsed and cannot escape the upload root.
+ name: "traversal with parent dir segments",
+ pathItemCount: [2]byte{0x00, 0x04},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x02, // segment length
+ 0x2e, 0x2e, // ".."
+ 0x00, 0x00, // path separator
+ 0x02, // segment length
+ 0x2e, 0x2e, // ".."
+ 0x00, 0x00, // path separator
+ 0x03, // segment length
+ 0x65, 0x74, 0x63, // "etc"
+ 0x00, 0x00, // path separator
+ 0x06, // segment length
+ 0x70, 0x61, 0x73, 0x73, 0x77, 0x64, // "passwd"
+ },
+ want: "etc/passwd",
+ },
+ {
+ // Interior ".." segments resolve away without escaping.
+ name: "traversal with interior parent dir segments",
+ pathItemCount: [2]byte{0x00, 0x04},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x03, // segment length
+ 0x66, 0x6f, 0x6f, // "foo"
+ 0x00, 0x00, // path separator
+ 0x02, // segment length
+ 0x2e, 0x2e, // ".."
+ 0x00, 0x00, // path separator
+ 0x02, // segment length
+ 0x2e, 0x2e, // ".."
+ 0x00, 0x00, // path separator
+ 0x03, // segment length
+ 0x62, 0x61, 0x72, // "bar"
+ },
+ want: "bar",
+ },
+ {
+ // A lone ".." segment collapses to the upload root (empty relative path).
+ name: "single parent dir segment",
+ pathItemCount: [2]byte{0x00, 0x01},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x02, // segment length
+ 0x2e, 0x2e, // ".."
+ },
+ want: "",
+ },
+ {
+ // A single segment whose raw bytes embed separators and "..".
+ name: "segment containing embedded separators",
+ pathItemCount: [2]byte{0x00, 0x01},
+ fileNamePath: []byte{
+ 0x00, 0x00, // path separator
+ 0x09, // segment length
+ 0x2e, 0x2e, 0x2f, 0x2e, 0x2e, 0x2f, 0x65, 0x74, 0x63, // "../../etc" (9 bytes)
+ },
+ want: "etc",
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {