From c8eefb7c5cbb526acb0c7f4da60c7df97fe27439 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 15 Jul 2025 14:47:17 +0100 Subject: [PATCH 1/3] Golang: Mark filepath.IsLocal as a tainted-path sanitizer guard --- .../go/security/TaintedPathCustomizations.qll | 16 ++++++++++++++++ .../query-tests/Security/CWE-022/TaintedPath.go | 6 ++++++ 2 files changed, 22 insertions(+) diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index df601ce1eb84..77badabfd40a 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("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/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) + } } From ac72f8523a6a8c10725d05fb798c0f8c376b4e30 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 15 Jul 2025 14:51:19 +0100 Subject: [PATCH 2/3] Change note --- go/ql/src/change-notes/2025-07-15-islocal-sanitizer.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 go/ql/src/change-notes/2025-07-15-islocal-sanitizer.md 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. From b71f9ae240536df5924a6fddf3d5b242ccd47307 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Tue, 15 Jul 2025 16:37:30 +0100 Subject: [PATCH 3/3] Fix function qname --- go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index 77badabfd40a..39db60de4830 100644 --- a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll +++ b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll @@ -250,7 +250,7 @@ module TaintedPath { class IsLocalCheck extends SanitizerGuard, DataFlow::CallNode { IsLocalCheck() { exists(Function f | - f.hasQualifiedName("filepath", "IsLocal") and + f.hasQualifiedName("path/filepath", "IsLocal") and this = f.getACall() ) }