From c651cc35ccb66a9f940f9e52ac6fcd8618dd0de6 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 24 Sep 2025 12:06:19 +0100 Subject: [PATCH 1/2] Documentation updates by copilot --- go/ql/src/Security/CWE-022/TaintedPath.qhelp | 21 +++++ .../Security/CWE-022/TaintedPathSanitizers.go | 85 +++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 go/ql/src/Security/CWE-022/TaintedPathSanitizers.go diff --git a/go/ql/src/Security/CWE-022/TaintedPath.qhelp b/go/ql/src/Security/CWE-022/TaintedPath.qhelp index 94edec4e4f4b..ab9b3be41158 100644 --- a/go/ql/src/Security/CWE-022/TaintedPath.qhelp +++ b/go/ql/src/Security/CWE-022/TaintedPath.qhelp @@ -38,6 +38,20 @@ result in the string "../" if only "../" sequences are removed. Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that the user input matches one of these patterns.

+ +

+Sanitizer functions from the Go standard library: +

+

+The Go standard library provides several functions that can help sanitize paths: +

+ @@ -71,6 +85,13 @@ that the resulting path is still within it.

+Using Go standard library sanitizers: +

+

+You can also use Go's built-in path validation functions for additional safety: +

+ +

Note that /home/user is just an example, you should replace it with the actual safe directory in your application. Also, while in this example the path of the safe directory is absolute, this may not always be the case, and you may need to resolve it diff --git a/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go b/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go new file mode 100644 index 000000000000..685e755a4e3b --- /dev/null +++ b/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go @@ -0,0 +1,85 @@ +package main + +import ( + "net/http" + "os" + "path/filepath" + "strings" +) + +func handleFileWithSanitizers(w http.ResponseWriter, r *http.Request) { + userPath := r.URL.Query().Get("file") + safeDir := "/home/user/documents" + + // Method 1: Using filepath.IsLocal to validate the path is local + if !filepath.IsLocal(userPath) { + http.Error(w, "Invalid path: must be local", http.StatusBadRequest) + return + } + + // Method 2: Using strings.Contains to check for path traversal sequences + if strings.Contains(userPath, "..") { + http.Error(w, "Invalid path: contains path traversal", http.StatusBadRequest) + return + } + + // Method 3: Using filepath.Rel to ensure path is within safe directory + relPath, err := filepath.Rel(safeDir, filepath.Join(safeDir, userPath)) + if err != nil || strings.HasPrefix(relPath, "..") { + http.Error(w, "Invalid path: outside safe directory", http.StatusBadRequest) + return + } + + // Method 4: Using filepath.Clean with absolute prefix for normalization + cleanPath := filepath.Clean("/" + userPath) + if strings.Contains(cleanPath, "..") { + http.Error(w, "Invalid path after normalization", http.StatusBadRequest) + return + } + + // Method 5: Using strings.HasPrefix for additional validation + finalPath := filepath.Join(safeDir, userPath) + if !strings.HasPrefix(finalPath, safeDir) { + http.Error(w, "Invalid path: must be within safe directory", http.StatusBadRequest) + return + } + + // Safe to open file + file, err := os.Open(finalPath) + if err != nil { + http.Error(w, "File not found", http.StatusNotFound) + return + } + defer file.Close() + + // Process file... +} + +// Example using mime/multipart filename sanitization +func handleUpload(w http.ResponseWriter, r *http.Request) { + err := r.ParseMultipartForm(32 << 20) // 32MB max + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + file, header, err := r.FormFile("upload") + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + defer file.Close() + + // The Filename field is automatically sanitized by mime/multipart + // using filepath.Base, making it safe from path traversal + filename := header.Filename + + // Additional validation can still be useful + if strings.Contains(filename, "..") || strings.ContainsAny(filename, "/\\") { + http.Error(w, "Invalid filename", http.StatusBadRequest) + return + } + + // Safe to use filename + _ = filename +} From aa991e255ac47777f12b4d264a2e85116bb3c35d Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Wed, 24 Sep 2025 12:08:08 +0100 Subject: [PATCH 2/2] Remove unhelpful example --- .../Security/CWE-022/TaintedPathSanitizers.go | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go b/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go index 685e755a4e3b..6c2696911a08 100644 --- a/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go +++ b/go/ql/src/Security/CWE-022/TaintedPathSanitizers.go @@ -54,32 +54,3 @@ func handleFileWithSanitizers(w http.ResponseWriter, r *http.Request) { // Process file... } - -// Example using mime/multipart filename sanitization -func handleUpload(w http.ResponseWriter, r *http.Request) { - err := r.ParseMultipartForm(32 << 20) // 32MB max - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - file, header, err := r.FormFile("upload") - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - defer file.Close() - - // The Filename field is automatically sanitized by mime/multipart - // using filepath.Base, making it safe from path traversal - filename := header.Filename - - // Additional validation can still be useful - if strings.Contains(filename, "..") || strings.ContainsAny(filename, "/\\") { - http.Error(w, "Invalid filename", http.StatusBadRequest) - return - } - - // Safe to use filename - _ = filename -}