Description of the issue
The Go go/path-injection query treats !strings.Contains(path, "..") as a complete path-injection sanitizer. This means that when Contains returns false, taint is fully blocked - no additional sanitizer is required.
I'm wondering why this is modeled as a complete barrier, because checking for the absence of ".." does not prevent absolute-path attacks. For example, /etc/passwd does not contain "..", passes the guard, and can read files outside any intended directory.
The query help seems to agree - it says this approach is "only suitable if the input is expected to be a single file name", and also warns that user-controlled paths "may be absolute paths". But DotDotCheck doesn't distinguish between single-component inputs and multi-component paths.
The existing test case at TaintedPath.go line 31 marks this pattern as GOOD with the comment "This can only read inside the provided safe path", but tainted_path = "/etc/passwd" would still pass the check and escape any safe path.
Affected sanitizer
DotDotCheck in TaintedPathCustomizations.qll (lines 106-122) matches strings.Contains(p, "..") and declares the false branch as a complete sanitizer guard. Through SanitizerGuardAsSanitizer, this becomes a full isBarrier node.
Steps to reproduce
func handler(w http.ResponseWriter, r *http.Request) {
p := r.URL.Query().Get("file")
if !strings.Contains(p, "..") {
data, _ := ioutil.ReadFile(p) // 0 alerts with DotDotCheck enabled
w.Write(data)
}
}
Question
Given that !strings.Contains(p, "..") only blocks one specific attack vector and not absolute paths, would it be more appropriate to model it as a non-barrier (or at least not a complete one), similar to how filepath.Base is documented as "not a sanitizer for path traversal" in mime.multipart.model.yml?
Environment
- CodeQL CLI 2.25.6
- CodeQL repository commit:
f6f45d1536312f53eed079868e344a5906bf3d72
- Go 1.22.12 on Linux/amd64
Description of the issue
The Go
go/path-injectionquery treats!strings.Contains(path, "..")as a complete path-injection sanitizer. This means that whenContainsreturnsfalse, taint is fully blocked - no additional sanitizer is required.I'm wondering why this is modeled as a complete barrier, because checking for the absence of
".."does not prevent absolute-path attacks. For example,/etc/passwddoes not contain"..", passes the guard, and can read files outside any intended directory.The query help seems to agree - it says this approach is "only suitable if the input is expected to be a single file name", and also warns that user-controlled paths "may be absolute paths". But
DotDotCheckdoesn't distinguish between single-component inputs and multi-component paths.The existing test case at
TaintedPath.goline 31 marks this pattern asGOODwith the comment "This can only read inside the provided safe path", buttainted_path = "/etc/passwd"would still pass the check and escape any safe path.Affected sanitizer
DotDotCheckinTaintedPathCustomizations.qll(lines 106-122) matchesstrings.Contains(p, "..")and declares thefalsebranch as a complete sanitizer guard. ThroughSanitizerGuardAsSanitizer, this becomes a fullisBarriernode.Steps to reproduce
Question
Given that
!strings.Contains(p, "..")only blocks one specific attack vector and not absolute paths, would it be more appropriate to model it as a non-barrier (or at least not a complete one), similar to howfilepath.Baseis documented as "not a sanitizer for path traversal" inmime.multipart.model.yml?Environment
f6f45d1536312f53eed079868e344a5906bf3d72