fix: path traversal bypass via backslash separators on Windows in code_search - #530
fix: path traversal bypass via backslash separators on Windows in code_search#530Sunil56224972 wants to merge 2 commits into
Conversation
hasTraversalPathComponent() only splits on '/' to detect '..' traversal. On Windows, backslash is a valid separator, so '..\\..' bypasses the check. Fix: normalize via filepath.ToSlash + filepath.Clean before splitting.
|
🔍 OpenCodeReview found 1 issue(s) in this PR.
|
| func hasTraversalPathComponent(pathspec string) bool { | ||
| for _, part := range strings.Split(pathspec, "/") { | ||
| // Normalize backslash separators (Windows) so "..\..\.." is caught too. | ||
| normalized := filepath.ToSlash(filepath.Clean(pathspec)) |
There was a problem hiding this comment.
Security Issue: filepath.Clean resolves .. components by collapsing them with parent directories (e.g., "a/../b" becomes "b"). This means pathspecs that previously would have been rejected (because they contained literal .. segments) may now pass the check after being cleaned.
The stated goal is to also catch Windows-style backslash traversals like "..\\..\\..". Instead of using filepath.Clean (which semantically resolves paths), you should only normalize the separator characters so that .. segments remain intact for detection.
Consider replacing filepath.Clean with just filepath.ToSlash, or alternatively manually replace backslashes before splitting.
Suggestion:
| normalized := filepath.ToSlash(filepath.Clean(pathspec)) | |
| normalized := filepath.ToSlash(pathspec) |
lizhengfeng101
left a comment
There was a problem hiding this comment.
Review
Thanks for the contribution! The identified gap is real — hasTraversalPathComponent only splits on /, so backslash-separated paths like ..\..\..\etc bypass the check on Windows where \ is a valid path separator.
That said, the practical exploitability is very low: git grep only searches tracked files (or untracked files within the worktree with --untracked), and modern git rejects pathspecs that resolve above the repository root. So this is more of a defense-in-depth hardening than a directly exploitable vulnerability.
Issue with filepath.Clean
The current fix uses filepath.ToSlash(filepath.Clean(pathspec)), which introduces platform-dependent behavior:
- On Linux:
filepath.Clean("pkg\\..\\internal")→"pkg\\..\\internal"(backslash is a literal character, not a separator) → thenToSlashconverts it →"pkg/../internal"→..detected ✓ - On Windows:
filepath.Clean("pkg\\..\\internal")→"internal"(resolves the..) →..gone → not detected ✗
This means the test case "backslash middle parent" would fail on Windows, and the behavior diverges across platforms.
Suggested simpler approach
A plain string replacement achieves the goal without platform-dependent semantics:
func hasTraversalPathComponent(pathspec string) bool {
normalized := strings.ReplaceAll(pathspec, "\\", "/")
for _, part := range strings.Split(normalized, "/") {
if part == ".." {
return true
}
}
return false
}Advantages:
- Identical behavior on all platforms
- Preserves the original strict policy (any
..component is rejected) - No new
path/filepathimport needed - Easier to audit — no need to reason about
filepath.Cleansemantics - All test cases pass consistently everywhere
|
Great catch on the I've adopted your suggested approach: func hasTraversalPathComponent(pathspec string) bool {
normalized := strings.ReplaceAll(pathspec, "\\", "/")
for _, part := range strings.Split(normalized, "/") {
if part == ".." {
return true
}
}
return false
}Advantages (as you noted):
Also rebuilt both files from upstream to fix encoding. |
Summary
Security Fix:
hasTraversalPathComponent()ininternal/tool/code_search.goonly splits on'/'to detect..path traversal components infile_patterns. On Windows (an explicitly supported platform), backslash\is a valid path separator, so patterns like..\..\etc\passwdbypass the traversal check entirely.Root Cause
The function splits the pathspec on
'/'only. When a backslash-separated path like..\..\..\etcis provided,strings.Split(s, "/")produces a single component..\..\..\etcwhich does NOT equal.., so the traversal is not detected.Impact
An LLM (potentially via prompt injection in reviewed code) could craft
file_patternsarguments tocode_searchusing backslash path separators to search files outside the repository root when OCR runs on Windows.Fix
Normalize the pathspec via
filepath.ToSlash()(converts\to/) andfilepath.Clean()(collapses redundant separators and..segments) before splitting to check for traversal components.Testing
Added
TestCodeSearchProvider_Execute_RejectsBackslashTraversalwith test cases for:..\..\\etcpkg\..\internalpkg\../..\secretAll existing tests continue to pass.