Skip to content

Commit 9e81891

Browse files
committed
fix: Reject URL path segments containing "%2e%2e"
1 parent f53fbae commit 9e81891

2 files changed

Lines changed: 11 additions & 7 deletions

File tree

github/github.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -884,14 +884,12 @@ func (c *Client) NewFormRequest(ctx context.Context, urlStr string, body io.Read
884884
}
885885

886886
// checkURLPathTraversal returns ErrPathForbidden if urlStr contains ".." as a
887-
// path segment (e.g. "a/../b"), preventing path traversal attacks. It does not
888-
// match ".." embedded within a segment (e.g. "file..txt"). The check is
889-
// performed only on the path portion of the URL, ignoring any query string or
890-
// fragment.
887+
// path segment (e.g. "a/../b"), preventing path traversal attacks. Percent-
888+
// encoded equivalents such as "%2e%2e" are also rejected because url.Parse
889+
// decodes them to ".." before the check runs. It does not match ".." embedded
890+
// within a segment (e.g. "file..txt"). The check is performed only on the path
891+
// portion of the URL, ignoring any query string or fragment.
891892
func checkURLPathTraversal(urlStr string) error {
892-
if !strings.Contains(urlStr, "..") {
893-
return nil
894-
}
895893
u, err := url.Parse(urlStr)
896894
if err != nil {
897895
return err

github/github_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,6 +1680,12 @@ func TestCheckURLPathTraversal(t *testing.T) {
16801680
// URL with userinfo.
16811681
{"https://user:pass@api.github.com/repos/../admin", ErrPathForbidden},
16821682
{"https://user:pass@api.github.com/repos/o/r", nil},
1683+
// Percent-encoded dots (%2e%2e) — url.Parse decodes them to ".." in Path.
1684+
{"repos/%2e%2e/admin", ErrPathForbidden},
1685+
{"repos/%2E%2E/admin", ErrPathForbidden},
1686+
{"repos/x/%2e%2e/%2e%2e/%2e%2e/admin", ErrPathForbidden},
1687+
{"x/%2e%2e/%2e%2e/%2e%2e/admin/users", ErrPathForbidden},
1688+
{"repos/o/r/contents/file%2e%2etxt", nil},
16831689
}
16841690
for _, tt := range tests {
16851691
err := checkURLPathTraversal(tt.input)

0 commit comments

Comments
 (0)