From 4e4cd52f6300da905256df3c5a532a018ff756b1 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Fri, 22 Mar 2024 11:44:06 +0000
Subject: [PATCH 1/3] Go: Update query help for `go/path-injection` to include
example fixes.
---
go/ql/src/Security/CWE-022/TaintedPath.qhelp | 51 +++++++++++++++----
go/ql/src/Security/CWE-022/TaintedPathGood.go | 20 ++++++++
.../src/Security/CWE-022/TaintedPathGood2.go | 23 +++++++++
3 files changed, 84 insertions(+), 10 deletions(-)
create mode 100644 go/ql/src/Security/CWE-022/TaintedPathGood.go
create mode 100644 go/ql/src/Security/CWE-022/TaintedPathGood2.go
diff --git a/go/ql/src/Security/CWE-022/TaintedPath.qhelp b/go/ql/src/Security/CWE-022/TaintedPath.qhelp
index 061ffbe34712..3a3948c425dc 100644
--- a/go/ql/src/Security/CWE-022/TaintedPath.qhelp
+++ b/go/ql/src/Security/CWE-022/TaintedPath.qhelp
@@ -9,23 +9,35 @@ Accessing files using paths constructed from user-controlled data can allow an a
unexpected resources. This can result in sensitive information being revealed or deleted, or an
attacker being able to influence behavior by modifying unexpected files.
+
+Paths that are naively constructed from data controlled by a user may be absolute paths,
+or may contain unexpected special characters such as "..". Such a path could point anywhere
+on the file system.
+
-Validate user input before using it to construct a file path, either using an off-the-shelf library
-or by performing custom validation.
+Validate user input before using it to construct a file path.
+
+
+Common validation methods include checking that the normalized path is relative and
+does not contain any ".." components, or checking that the path is contained within a safe folder. The method you should use depends on how the path is used in the application, and whether the path should be a single path component.
+
+
+If the path should be a single path component (such as a file name), you can check for the
+existence of any path separators ("/" or "\"), or ".." sequences in the input, and reject
+the input if any are found.
+
+
+Note that removing "../" sequences is not sufficient, since the input could still
+contain a path separator followed by "..". For example, the input ".../...//" would still
+result in the string "../" if only "../" sequences are removed.
-Ideally, follow these rules:
+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.
-
-- Do not allow more than a single "." character.
-- Do not allow directory separators such as "/" or "\" (depending on the file system).
-- Do not rely on simply replacing problematic sequences such as "../". For example, after
-applying this filter to ".../...//", the resulting string would still be "../".
-- Use an allowlist of known good patterns.
-
@@ -43,6 +55,25 @@ password file. This file would then be sent back to the user, giving them access
information.
+
+If the input should only be a file name, you can check that it doesn't contain any
+path separators or ".." sequences.
+
+
+
+Note that this approach is only suitable if the input is expected to be a single file name.
+
+
+If the input can be a path with multiple components, we can make it safe by verifying
+that the path is within a specific directory that is considered safe.
+This can be done by resolving the input with respect to that directory, and then checking
+that the resulting path is still within it.
+
+
+
+Note that /home/user
is just an example, you should replace it with the actual
+safe directory in your application.
+
diff --git a/go/ql/src/Security/CWE-022/TaintedPathGood.go b/go/ql/src/Security/CWE-022/TaintedPathGood.go
new file mode 100644
index 000000000000..50c1359a9030
--- /dev/null
+++ b/go/ql/src/Security/CWE-022/TaintedPathGood.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "strings"
+)
+
+func handler(w http.ResponseWriter, r *http.Request) {
+ path := r.URL.Query()["path"][0]
+
+ // GOOD: ensure that the filename has no path separators or parent directory references
+ if strings.Contains(path, "/") || strings.Contains(path, "\\") || strings.Contains(path, "..") {
+ http.Error(w, "Invalid file name", http.StatusBadRequest)
+ return
+ }
+ data, _ := ioutil.ReadFile(filepath.Join("/home/user/", path))
+ w.Write(data)
+}
diff --git a/go/ql/src/Security/CWE-022/TaintedPathGood2.go b/go/ql/src/Security/CWE-022/TaintedPathGood2.go
new file mode 100644
index 000000000000..0136adf11dcb
--- /dev/null
+++ b/go/ql/src/Security/CWE-022/TaintedPathGood2.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "strings"
+)
+
+const safeDir = "/home/user/"
+
+func handler(w http.ResponseWriter, r *http.Request) {
+ path := r.URL.Query()["path"][0]
+
+ // GOOD: ensure that the resolved path is within the safe directory
+ absPath, err := filepath.Abs(filepath.Join(safeDir, path))
+ if err != nil || !strings.HasPrefix(absPath, safeDir) {
+ http.Error(w, "Invalid file name", http.StatusBadRequest)
+ return
+ }
+ data, _ := ioutil.ReadFile(absPath)
+ w.Write(data)
+}
From bc9396e0e66b5bd7dc689aeea6058108701b1d5c Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Fri, 22 Mar 2024 13:19:36 +0000
Subject: [PATCH 2/3] Address suggestions from review.
---
go/ql/src/Security/CWE-022/TaintedPath.qhelp | 4 +++-
go/ql/src/Security/CWE-022/TaintedPathGood.go | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/go/ql/src/Security/CWE-022/TaintedPath.qhelp b/go/ql/src/Security/CWE-022/TaintedPath.qhelp
index 3a3948c425dc..3b54e80cd97c 100644
--- a/go/ql/src/Security/CWE-022/TaintedPath.qhelp
+++ b/go/ql/src/Security/CWE-022/TaintedPath.qhelp
@@ -72,7 +72,9 @@ that the resulting path is still within it.
Note that /home/user
is just an example, you should replace it with the actual
-safe directory in your application.
+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
+first before checking the input.
diff --git a/go/ql/src/Security/CWE-022/TaintedPathGood.go b/go/ql/src/Security/CWE-022/TaintedPathGood.go
index 50c1359a9030..8a46c76b26a0 100644
--- a/go/ql/src/Security/CWE-022/TaintedPathGood.go
+++ b/go/ql/src/Security/CWE-022/TaintedPathGood.go
@@ -11,6 +11,7 @@ func handler(w http.ResponseWriter, r *http.Request) {
path := r.URL.Query()["path"][0]
// GOOD: ensure that the filename has no path separators or parent directory references
+ // (Note that this is only suitable if `path` is expected to have a single component!)
if strings.Contains(path, "/") || strings.Contains(path, "\\") || strings.Contains(path, "..") {
http.Error(w, "Invalid file name", http.StatusBadRequest)
return
From 034ed1722781b0bd219855af192a793d48077d1c Mon Sep 17 00:00:00 2001
From: Max Schaefer <54907921+max-schaefer@users.noreply.github.com>
Date: Fri, 22 Mar 2024 15:24:29 +0000
Subject: [PATCH 3/3] Apply suggestions from code review
Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
---
go/ql/src/Security/CWE-022/TaintedPath.qhelp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/go/ql/src/Security/CWE-022/TaintedPath.qhelp b/go/ql/src/Security/CWE-022/TaintedPath.qhelp
index 3b54e80cd97c..94edec4e4f4b 100644
--- a/go/ql/src/Security/CWE-022/TaintedPath.qhelp
+++ b/go/ql/src/Security/CWE-022/TaintedPath.qhelp
@@ -64,9 +64,9 @@ path separators or ".." sequences.
Note that this approach is only suitable if the input is expected to be a single file name.
-If the input can be a path with multiple components, we can make it safe by verifying
+If the input can be a path with multiple components, you can make it safe by verifying
that the path is within a specific directory that is considered safe.
-This can be done by resolving the input with respect to that directory, and then checking
+You can do this by resolving the input with respect to that directory, and then checking
that the resulting path is still within it.