Skip to content

fix(docker): Windows paths not being converted into linux while being run in WSL#366

Merged
junaid2005p merged 14 commits intomainfrom
docker-path-fix
Apr 13, 2026
Merged

fix(docker): Windows paths not being converted into linux while being run in WSL#366
junaid2005p merged 14 commits intomainfrom
docker-path-fix

Conversation

@junaid2005p
Copy link
Copy Markdown
Collaborator

@junaid2005p junaid2005p commented Apr 13, 2026

Greptile Summary

This PR fixes Windows absolute paths (drive letters and UNC) sent by WSL clients not being mapped to the server-side Linux download directory. It introduces IsWindowsAbsPath, MapWindowsPathToDefaultDir, and mapClientWindowsPath with segment-name matching and .. traversal rejection, and adds corresponding tests. The change is correct and addresses the previously raised silent-fallthrough concern.

The PR bundles several unrelated improvements — a Docker multi-stage build refactor with a non-root user, updated compose.yml, and a new CI tag-based workflow trigger — alongside the core path fix; these should ideally be separate PRs per the project's contribution guidelines.

Confidence Score: 5/5

Safe to merge — all remaining findings are P2 style/test suggestions with no blocking correctness or security issues.

The core path-mapping logic is correct, traversal attacks are rejected in MapWindowsPathToDefaultDir, and the previously raised silent-fallthrough concern is addressed by the new fallback mechanism. All open comments are non-blocking P2 improvements.

docker/compose.yml — the user field may silently default to 1000:1000 in most environments.

Important Files Changed

Filename Overview
internal/utils/path.go Adds IsWindowsAbsPath and MapWindowsPathToDefaultDir with sound logic; POSIX // negative test case missing from companion test file.
cmd/root_downloads.go Adds mapClientWindowsPath and shouldFallbackUnmappedWindowsPath; correctly falls back to baseDir for unmapped Windows paths; missing end-to-end test for explicit non-RelativeToDefaultDir unmapped path on Linux.
cmd/http_handler_test.go Refactors path resolution tests to use pre-computed expectedOutputPath; adds Windows path cases and TestShouldFallbackUnmappedWindowsPath; lacks explicit unmapped Windows path test without RelativeToDefaultDir.
internal/utils/path_test.go Adds TestIsWindowsAbsPath and TestMapWindowsPathToDefaultDir with traversal, case-insensitive, and no-match cases; missing //server/share negative case.
docker/Dockerfile Clean multi-stage build with non-root surge user, proper volume declarations, and TARGETOS/TARGETARCH build args; jq correctly scoped to downloader stage.
docker/compose.yml Adds user field for non-root execution but $UID is not exported by bash by default so the value silently defaults to 1000 in most environments.
.github/workflows/build-push-images.yml Adds tag-based trigger, automatic version extraction from tag name, and a latest-tag step gated on clean semver tags; logic is correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Client sends DownloadRequest] --> B{IsWindowsAbsPath?}
    B -- No --> C{RelativeToDefaultDir?}
    B -- Yes --> D[mapClientWindowsPath]
    C -- Yes --> E[Join baseDir + reqPath]
    C -- No --> F[EnsureAbsPath reqPath]
    D --> G{MapWindowsPathToDefaultDir matches?}
    G -- Yes --> H[Return baseDir/suffix]
    G -- No --> I{shouldFallbackUnmappedWindowsPath?}
    I -- true --> J[Return filepath.Clean baseDir]
    I -- false --> K[Return empty, fall through]
    H --> L[resolveOutputDir returns path]
    J --> L
    E --> L
    F --> L
    K --> C
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: docker/compose.yml
Line: 12

Comment:
**`${UID}` is not exported by bash by default**

`$UID` is a bash read-only special variable that is NOT exported to child processes. When Docker Compose is launched normally, it won't see the host user's UID from the environment — `${UID:-1000}` will always resolve to `1000`, defeating the purpose of the mapping. Users need to explicitly export it or set it before running compose:

```bash
UID=$(id -u) GID=$(id -g) docker compose up
```

Or add to a `.env` file alongside `compose.yml`:
```
UID=1000
GID=1000
```

Consider adding a comment or README note explaining this requirement, or using a wrapper script that pre-sets the variables.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: internal/utils/path_test.go
Line: 51-63

Comment:
**Missing negative test for POSIX `//` double-slash path**

The implementation explicitly notes that `//path` is POSIX-legal and must NOT be treated as a Windows UNC path (only `\\` backslash-backslash is UNC). There's no test case confirming `//server/share` returns `false`, which is the critical distinction the code comment calls out.

```suggestion
	tests := []struct {
		path string
		want bool
	}{
		{path: `C:\Users\me\Downloads`, want: true},
		{path: `C:/Users/me/Downloads`, want: true},
		{path: `\\server\share\file.zip`, want: true},
		{path: `/tmp/downloads`, want: false},
		{path: `downloads/subdir`, want: false},
		{path: `//server/share`, want: false},
	}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: cmd/http_handler_test.go
Line: 155-165

Comment:
**No test for explicit unmapped Windows path on Linux without `RelativeToDefaultDir`**

The "Unmatched Windows Path Falls Back To Default Dir" case only covers the `RelativeToDefaultDir: true` branch. However, `shouldFallbackUnmappedWindowsPath(false, "linux")` also returns `true`, meaning an explicit Windows path that doesn't match the server's download folder name silently falls back to the default dir on Linux. Consider adding a case with `Path: "E:/Torrents/complete"` and no `RelativeToDefaultDir` flag to cover this branch end-to-end.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (8): Last reviewed commit: "fix(utils): reject parent traversal segm..." | Re-trigger Greptile

Context used:

  • Rule used - What: Flag commits that bundle unnecessary changes... (source)

Comment thread docker/Dockerfile Outdated
Comment thread docker/Dockerfile
FROM alpine:3.21 AS downloader

RUN apk add --no-cache ca-certificates curl jq
RUN apk add --no-cache ca-certificates curl jq tar
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 jq installed but no longer used

jq was needed to parse the GitHub API response when auto-fetching the latest version. Since version auto-detection was removed, jq is no longer used anywhere in this stage and can be dropped to keep the image lean.

Suggested change
RUN apk add --no-cache ca-certificates curl jq tar
RUN apk add --no-cache ca-certificates curl tar
Prompt To Fix With AI
This is a comment left during a code review.
Path: docker/Dockerfile
Line: 3

Comment:
**`jq` installed but no longer used**

`jq` was needed to parse the GitHub API response when auto-fetching the latest version. Since version auto-detection was removed, `jq` is no longer used anywhere in this stage and can be dropped to keep the image lean.

```suggestion
RUN apk add --no-cache ca-certificates curl tar
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread internal/utils/path.go Outdated
Comment thread internal/utils/path_test.go
Comment thread cmd/http_handler_test.go Outdated
Comment thread cmd/root_downloads.go Outdated
@junaid2005p junaid2005p merged commit 8f3fff5 into main Apr 13, 2026
21 checks passed
@junaid2005p junaid2005p deleted the docker-path-fix branch April 13, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants