diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index df601ce1eb84..39db60de4830 100644 --- a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll +++ b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll @@ -243,4 +243,20 @@ module TaintedPath { override predicate checks(Expr e, boolean branch) { regexpFunctionChecksExpr(this, e, branch) } } + + /** + * A call of the form `filepath.IsLocal(path)` considered as a sanitizer guard for `path`. + */ + class IsLocalCheck extends SanitizerGuard, DataFlow::CallNode { + IsLocalCheck() { + exists(Function f | + f.hasQualifiedName("path/filepath", "IsLocal") and + this = f.getACall() + ) + } + + override predicate checks(Expr e, boolean branch) { + e = this.getArgument(0).asExpr() and branch = true + } + } } diff --git a/go/ql/src/change-notes/2025-07-15-islocal-sanitizer.md b/go/ql/src/change-notes/2025-07-15-islocal-sanitizer.md new file mode 100644 index 000000000000..35f04aacb582 --- /dev/null +++ b/go/ql/src/change-notes/2025-07-15-islocal-sanitizer.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* `filepath.IsLocal` is now recognised as a sanitizer against path-traversal and related vulnerabilities. diff --git a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go index e6a1c49f4c5b..82c0592a5166 100644 --- a/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go +++ b/go/ql/test/query-tests/Security/CWE-022/TaintedPath.go @@ -93,4 +93,10 @@ func handler(w http.ResponseWriter, r *http.Request) { } data, _ = ioutil.ReadFile(part.FileName()) + + // GOOD: An attempt has been made to prevent path traversal + if filepath.IsLocal(tainted_path) { + data, _ = ioutil.ReadFile(tainted_path) + w.Write(data) + } }