Skip to content

Commit 7ddbdfa

Browse files
wesmclaude
andcommitted
Reject Windows drive-relative and UNC paths in ValidateOutputPath
filepath.IsAbs misses drive-relative paths (C:foo, C:..\evil) and UNC paths (\\server\share) on Windows. Add a filepath.VolumeName check to catch these. Add Windows-only test cases for C:\, C:relative, C:..\, and UNC paths. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 269aa5b commit 7ddbdfa

File tree

2 files changed

+32
-0
lines changed

2 files changed

+32
-0
lines changed

internal/export/attachments.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,11 @@ func ValidateOutputPath(outputPath string) error {
207207
if filepath.IsAbs(cleaned) {
208208
return fmt.Errorf("output path %q is absolute; use a relative path", outputPath)
209209
}
210+
// Reject Windows drive-relative (C:foo) and UNC (\\server\share) paths,
211+
// which filepath.IsAbs does not catch.
212+
if filepath.VolumeName(cleaned) != "" {
213+
return fmt.Errorf("output path %q contains a drive or UNC prefix; use a relative path", outputPath)
214+
}
210215
if cleaned == ".." || strings.HasPrefix(cleaned, ".."+string(filepath.Separator)) {
211216
return fmt.Errorf("output path %q escapes the working directory", outputPath)
212217
}

internal/export/attachments_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,33 @@ func TestValidateOutputPath(t *testing.T) {
416416
{"traversal via subdir", "foo/../../evil.txt", true},
417417
}
418418

419+
// Windows drive and UNC paths — only meaningful on Windows where
420+
// filepath.VolumeName returns non-empty for these forms.
421+
if runtime.GOOS == "windows" {
422+
tests = append(tests,
423+
struct {
424+
name string
425+
path string
426+
wantErr bool
427+
}{"windows absolute", `C:\tmp\file.pdf`, true},
428+
struct {
429+
name string
430+
path string
431+
wantErr bool
432+
}{"windows drive-relative", `C:tmp\file.pdf`, true},
433+
struct {
434+
name string
435+
path string
436+
wantErr bool
437+
}{"windows drive-relative traversal", `C:..\evil`, true},
438+
struct {
439+
name string
440+
path string
441+
wantErr bool
442+
}{"windows UNC path", `\\server\share\file.pdf`, true},
443+
)
444+
}
445+
419446
for _, tt := range tests {
420447
t.Run(tt.name, func(t *testing.T) {
421448
err := ValidateOutputPath(tt.path)

0 commit comments

Comments
 (0)