From 4f6ce61de2ff53b89fae51afdd00c24a8cd5bb34 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Mon, 15 Jun 2020 16:15:26 +0100
Subject: [PATCH 1/8] Move EmailInjection query out of experimental
---
ql/src/{experimental => Security}/CWE-640/EmailBad.go | 0
ql/src/{experimental => Security}/CWE-640/EmailGood.go | 0
ql/src/{experimental => Security}/CWE-640/EmailInjection.qhelp | 0
ql/src/{experimental => Security}/CWE-640/EmailInjection.ql | 0
ql/src/{experimental => Security}/CWE-640/EmailInjection.qll | 0
.../CWE-640/EmailInjectionCustomizations.qll | 0
ql/test/experimental/CWE-640/EmailInjection.qlref | 1 -
.../Security}/CWE-640/EmailInjection.expected | 0
ql/test/query-tests/Security/CWE-640/EmailInjection.qlref | 1 +
ql/test/{experimental => query-tests/Security}/CWE-640/email.go | 0
ql/test/{experimental => query-tests/Security}/CWE-640/go.mod | 0
.../vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE | 0
.../vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go | 0
.../Security}/CWE-640/vendor/modules.txt | 0
14 files changed, 1 insertion(+), 1 deletion(-)
rename ql/src/{experimental => Security}/CWE-640/EmailBad.go (100%)
rename ql/src/{experimental => Security}/CWE-640/EmailGood.go (100%)
rename ql/src/{experimental => Security}/CWE-640/EmailInjection.qhelp (100%)
rename ql/src/{experimental => Security}/CWE-640/EmailInjection.ql (100%)
rename ql/src/{experimental => Security}/CWE-640/EmailInjection.qll (100%)
rename ql/src/{experimental => Security}/CWE-640/EmailInjectionCustomizations.qll (100%)
delete mode 100644 ql/test/experimental/CWE-640/EmailInjection.qlref
rename ql/test/{experimental => query-tests/Security}/CWE-640/EmailInjection.expected (100%)
create mode 100644 ql/test/query-tests/Security/CWE-640/EmailInjection.qlref
rename ql/test/{experimental => query-tests/Security}/CWE-640/email.go (100%)
rename ql/test/{experimental => query-tests/Security}/CWE-640/go.mod (100%)
rename ql/test/{experimental => query-tests/Security}/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE (100%)
rename ql/test/{experimental => query-tests/Security}/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go (100%)
rename ql/test/{experimental => query-tests/Security}/CWE-640/vendor/modules.txt (100%)
diff --git a/ql/src/experimental/CWE-640/EmailBad.go b/ql/src/Security/CWE-640/EmailBad.go
similarity index 100%
rename from ql/src/experimental/CWE-640/EmailBad.go
rename to ql/src/Security/CWE-640/EmailBad.go
diff --git a/ql/src/experimental/CWE-640/EmailGood.go b/ql/src/Security/CWE-640/EmailGood.go
similarity index 100%
rename from ql/src/experimental/CWE-640/EmailGood.go
rename to ql/src/Security/CWE-640/EmailGood.go
diff --git a/ql/src/experimental/CWE-640/EmailInjection.qhelp b/ql/src/Security/CWE-640/EmailInjection.qhelp
similarity index 100%
rename from ql/src/experimental/CWE-640/EmailInjection.qhelp
rename to ql/src/Security/CWE-640/EmailInjection.qhelp
diff --git a/ql/src/experimental/CWE-640/EmailInjection.ql b/ql/src/Security/CWE-640/EmailInjection.ql
similarity index 100%
rename from ql/src/experimental/CWE-640/EmailInjection.ql
rename to ql/src/Security/CWE-640/EmailInjection.ql
diff --git a/ql/src/experimental/CWE-640/EmailInjection.qll b/ql/src/Security/CWE-640/EmailInjection.qll
similarity index 100%
rename from ql/src/experimental/CWE-640/EmailInjection.qll
rename to ql/src/Security/CWE-640/EmailInjection.qll
diff --git a/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll b/ql/src/Security/CWE-640/EmailInjectionCustomizations.qll
similarity index 100%
rename from ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll
rename to ql/src/Security/CWE-640/EmailInjectionCustomizations.qll
diff --git a/ql/test/experimental/CWE-640/EmailInjection.qlref b/ql/test/experimental/CWE-640/EmailInjection.qlref
deleted file mode 100644
index a6d8abad1c9..00000000000
--- a/ql/test/experimental/CWE-640/EmailInjection.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/CWE-640/EmailInjection.ql
\ No newline at end of file
diff --git a/ql/test/experimental/CWE-640/EmailInjection.expected b/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
similarity index 100%
rename from ql/test/experimental/CWE-640/EmailInjection.expected
rename to ql/test/query-tests/Security/CWE-640/EmailInjection.expected
diff --git a/ql/test/query-tests/Security/CWE-640/EmailInjection.qlref b/ql/test/query-tests/Security/CWE-640/EmailInjection.qlref
new file mode 100644
index 00000000000..98862a2de38
--- /dev/null
+++ b/ql/test/query-tests/Security/CWE-640/EmailInjection.qlref
@@ -0,0 +1 @@
+Security/CWE-640/EmailInjection.ql
\ No newline at end of file
diff --git a/ql/test/experimental/CWE-640/email.go b/ql/test/query-tests/Security/CWE-640/email.go
similarity index 100%
rename from ql/test/experimental/CWE-640/email.go
rename to ql/test/query-tests/Security/CWE-640/email.go
diff --git a/ql/test/experimental/CWE-640/go.mod b/ql/test/query-tests/Security/CWE-640/go.mod
similarity index 100%
rename from ql/test/experimental/CWE-640/go.mod
rename to ql/test/query-tests/Security/CWE-640/go.mod
diff --git a/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE b/ql/test/query-tests/Security/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE
similarity index 100%
rename from ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE
rename to ql/test/query-tests/Security/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE
diff --git a/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go b/ql/test/query-tests/Security/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go
similarity index 100%
rename from ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go
rename to ql/test/query-tests/Security/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go
diff --git a/ql/test/experimental/CWE-640/vendor/modules.txt b/ql/test/query-tests/Security/CWE-640/vendor/modules.txt
similarity index 100%
rename from ql/test/experimental/CWE-640/vendor/modules.txt
rename to ql/test/query-tests/Security/CWE-640/vendor/modules.txt
From f27ecdabb896e3dd7a5798ac82583679ba40e071 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Mon, 15 Jun 2020 16:18:28 +0100
Subject: [PATCH 2/8] Set precision to high
---
ql/src/Security/CWE-640/EmailInjection.ql | 1 +
1 file changed, 1 insertion(+)
diff --git a/ql/src/Security/CWE-640/EmailInjection.ql b/ql/src/Security/CWE-640/EmailInjection.ql
index 7cf11fa1a3e..1ae4d751342 100644
--- a/ql/src/Security/CWE-640/EmailInjection.ql
+++ b/ql/src/Security/CWE-640/EmailInjection.ql
@@ -8,6 +8,7 @@
* @problem.severity error
* @tags security
* external/cwe/cwe-640
+ * @precision high
*/
import go
From 336eba1be496039ad068c4f680a146ab18f2dd69 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 16 Jun 2020 12:48:43 +0100
Subject: [PATCH 3/8] Add Hash.Write and similar as sanitizers
---
ql/src/Security/CWE-640/EmailInjection.qll | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/ql/src/Security/CWE-640/EmailInjection.qll b/ql/src/Security/CWE-640/EmailInjection.qll
index 4cf8b382c98..726836ed922 100644
--- a/ql/src/Security/CWE-640/EmailInjection.qll
+++ b/ql/src/Security/CWE-640/EmailInjection.qll
@@ -24,6 +24,19 @@ module EmailInjection {
override predicate isSource(DataFlow::Node source) { source instanceof Source }
+ override predicate isSanitizerOut(DataFlow::Node node) {
+ exists(DataFlow::CallNode call |
+ call.getTarget().hasQualifiedName("hash.Hash", "Write") and
+ (
+ call.getReceiver().getType().getName() = "Hash" or
+ call.getReceiver().getType().getName() = "Hash32" or
+ call.getReceiver().getType().getName() = "Hash64"
+ )
+ |
+ node = call.getArgument(0)
+ )
+ }
+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
}
}
From 1b49bcc3b38da4cafe242e43367369144b7aef99 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 16 Jun 2020 15:44:51 +0100
Subject: [PATCH 4/8] Put code snippets from qhelp in test folder
---
.../query-tests/Security/CWE-640/EmailBad.go | 13 +++
.../query-tests/Security/CWE-640/EmailGood.go | 13 +++
.../Security/CWE-640/EmailInjection.expected | 80 +++++++++----------
.../Security/CWE-640/{email.go => main.go} | 24 ++----
4 files changed, 74 insertions(+), 56 deletions(-)
create mode 100644 ql/test/query-tests/Security/CWE-640/EmailBad.go
create mode 100644 ql/test/query-tests/Security/CWE-640/EmailGood.go
rename ql/test/query-tests/Security/CWE-640/{email.go => main.go} (81%)
diff --git a/ql/test/query-tests/Security/CWE-640/EmailBad.go b/ql/test/query-tests/Security/CWE-640/EmailBad.go
new file mode 100644
index 00000000000..aab8467b340
--- /dev/null
+++ b/ql/test/query-tests/Security/CWE-640/EmailBad.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+ "net/http"
+ "net/smtp"
+)
+
+func mail(w http.ResponseWriter, r *http.Request) {
+ host := r.Header.Get("Host")
+ token := backend.getUserSecretResetToken(email)
+ body := "Click to reset password: " + host + "/" + token
+ smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body))
+}
diff --git a/ql/test/query-tests/Security/CWE-640/EmailGood.go b/ql/test/query-tests/Security/CWE-640/EmailGood.go
new file mode 100644
index 00000000000..5a85e543540
--- /dev/null
+++ b/ql/test/query-tests/Security/CWE-640/EmailGood.go
@@ -0,0 +1,13 @@
+package main
+
+import (
+ "net/http"
+ "net/smtp"
+)
+
+func mailGood(w http.ResponseWriter, r *http.Request) {
+ host := config["Host"]
+ token := backend.getUserSecretResetToken(email)
+ body := "Click to reset password: " + host + "/" + token
+ smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body))
+}
diff --git a/ql/test/query-tests/Security/CWE-640/EmailInjection.expected b/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
index 37a3af5ae75..9e4eb436872 100644
--- a/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
+++ b/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
@@ -1,43 +1,43 @@
edges
-| email.go:24:10:24:17 | selection of Header : Header | email.go:27:56:27:67 | type conversion |
-| email.go:34:21:34:31 | call to Referer : string | email.go:36:57:36:78 | type conversion |
-| email.go:42:21:42:31 | call to Referer : string | email.go:45:3:45:7 | definition of write |
-| email.go:51:21:51:31 | call to Referer : string | email.go:57:46:57:59 | untrustedInput |
-| email.go:51:21:51:31 | call to Referer : string | email.go:58:52:58:65 | untrustedInput |
-| email.go:63:21:63:31 | call to Referer : string | email.go:68:16:68:22 | content |
-| email.go:73:21:73:31 | call to Referer : string | email.go:81:50:81:56 | content |
-| email.go:73:21:73:31 | call to Referer : string | email.go:81:59:81:65 | content |
-| email.go:73:21:73:31 | call to Referer : string | email.go:82:16:82:22 | content |
-| email.go:87:21:87:31 | call to Referer : string | email.go:94:37:94:50 | untrustedInput |
-| email.go:87:21:87:31 | call to Referer : string | email.go:98:16:98:23 | content2 |
+| EmailBad.go:9:10:9:17 | selection of Header : Header | EmailBad.go:12:56:12:67 | type conversion |
+| main.go:26:21:26:31 | call to Referer : string | main.go:28:57:28:78 | type conversion |
+| main.go:34:21:34:31 | call to Referer : string | main.go:37:3:37:7 | definition of write |
+| main.go:43:21:43:31 | call to Referer : string | main.go:49:46:49:59 | untrustedInput |
+| main.go:43:21:43:31 | call to Referer : string | main.go:50:52:50:65 | untrustedInput |
+| main.go:55:21:55:31 | call to Referer : string | main.go:60:16:60:22 | content |
+| main.go:65:21:65:31 | call to Referer : string | main.go:73:50:73:56 | content |
+| main.go:65:21:65:31 | call to Referer : string | main.go:73:59:73:65 | content |
+| main.go:65:21:65:31 | call to Referer : string | main.go:74:16:74:22 | content |
+| main.go:79:21:79:31 | call to Referer : string | main.go:86:37:86:50 | untrustedInput |
+| main.go:79:21:79:31 | call to Referer : string | main.go:90:16:90:23 | content2 |
nodes
-| email.go:24:10:24:17 | selection of Header : Header | semmle.label | selection of Header : Header |
-| email.go:27:56:27:67 | type conversion | semmle.label | type conversion |
-| email.go:34:21:34:31 | call to Referer : string | semmle.label | call to Referer : string |
-| email.go:36:57:36:78 | type conversion | semmle.label | type conversion |
-| email.go:42:21:42:31 | call to Referer : string | semmle.label | call to Referer : string |
-| email.go:45:3:45:7 | definition of write | semmle.label | definition of write |
-| email.go:51:21:51:31 | call to Referer : string | semmle.label | call to Referer : string |
-| email.go:57:46:57:59 | untrustedInput | semmle.label | untrustedInput |
-| email.go:58:52:58:65 | untrustedInput | semmle.label | untrustedInput |
-| email.go:63:21:63:31 | call to Referer : string | semmle.label | call to Referer : string |
-| email.go:68:16:68:22 | content | semmle.label | content |
-| email.go:73:21:73:31 | call to Referer : string | semmle.label | call to Referer : string |
-| email.go:81:50:81:56 | content | semmle.label | content |
-| email.go:81:59:81:65 | content | semmle.label | content |
-| email.go:82:16:82:22 | content | semmle.label | content |
-| email.go:87:21:87:31 | call to Referer : string | semmle.label | call to Referer : string |
-| email.go:94:37:94:50 | untrustedInput | semmle.label | untrustedInput |
-| email.go:98:16:98:23 | content2 | semmle.label | content2 |
+| EmailBad.go:9:10:9:17 | selection of Header : Header | semmle.label | selection of Header : Header |
+| EmailBad.go:12:56:12:67 | type conversion | semmle.label | type conversion |
+| main.go:26:21:26:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:28:57:28:78 | type conversion | semmle.label | type conversion |
+| main.go:34:21:34:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:37:3:37:7 | definition of write | semmle.label | definition of write |
+| main.go:43:21:43:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:49:46:49:59 | untrustedInput | semmle.label | untrustedInput |
+| main.go:50:52:50:65 | untrustedInput | semmle.label | untrustedInput |
+| main.go:55:21:55:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:60:16:60:22 | content | semmle.label | content |
+| main.go:65:21:65:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:73:50:73:56 | content | semmle.label | content |
+| main.go:73:59:73:65 | content | semmle.label | content |
+| main.go:74:16:74:22 | content | semmle.label | content |
+| main.go:79:21:79:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:86:37:86:50 | untrustedInput | semmle.label | untrustedInput |
+| main.go:90:16:90:23 | content2 | semmle.label | content2 |
#select
-| email.go:27:56:27:67 | type conversion | email.go:24:10:24:17 | selection of Header : Header | email.go:27:56:27:67 | type conversion | Email content may contain $@. | email.go:24:10:24:17 | selection of Header | untrusted input |
-| email.go:36:57:36:78 | type conversion | email.go:34:21:34:31 | call to Referer : string | email.go:36:57:36:78 | type conversion | Email content may contain $@. | email.go:34:21:34:31 | call to Referer | untrusted input |
-| email.go:45:3:45:7 | definition of write | email.go:42:21:42:31 | call to Referer : string | email.go:45:3:45:7 | definition of write | Email content may contain $@. | email.go:42:21:42:31 | call to Referer | untrusted input |
-| email.go:57:46:57:59 | untrustedInput | email.go:51:21:51:31 | call to Referer : string | email.go:57:46:57:59 | untrustedInput | Email content may contain $@. | email.go:51:21:51:31 | call to Referer | untrusted input |
-| email.go:58:52:58:65 | untrustedInput | email.go:51:21:51:31 | call to Referer : string | email.go:58:52:58:65 | untrustedInput | Email content may contain $@. | email.go:51:21:51:31 | call to Referer | untrusted input |
-| email.go:68:16:68:22 | content | email.go:63:21:63:31 | call to Referer : string | email.go:68:16:68:22 | content | Email content may contain $@. | email.go:63:21:63:31 | call to Referer | untrusted input |
-| email.go:81:50:81:56 | content | email.go:73:21:73:31 | call to Referer : string | email.go:81:50:81:56 | content | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input |
-| email.go:81:59:81:65 | content | email.go:73:21:73:31 | call to Referer : string | email.go:81:59:81:65 | content | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input |
-| email.go:82:16:82:22 | content | email.go:73:21:73:31 | call to Referer : string | email.go:82:16:82:22 | content | Email content may contain $@. | email.go:73:21:73:31 | call to Referer | untrusted input |
-| email.go:94:37:94:50 | untrustedInput | email.go:87:21:87:31 | call to Referer : string | email.go:94:37:94:50 | untrustedInput | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input |
-| email.go:98:16:98:23 | content2 | email.go:87:21:87:31 | call to Referer : string | email.go:98:16:98:23 | content2 | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input |
+| EmailBad.go:12:56:12:67 | type conversion | EmailBad.go:9:10:9:17 | selection of Header : Header | EmailBad.go:12:56:12:67 | type conversion | Email content may contain $@. | EmailBad.go:9:10:9:17 | selection of Header | untrusted input |
+| main.go:28:57:28:78 | type conversion | main.go:26:21:26:31 | call to Referer : string | main.go:28:57:28:78 | type conversion | Email content may contain $@. | main.go:26:21:26:31 | call to Referer | untrusted input |
+| main.go:37:3:37:7 | definition of write | main.go:34:21:34:31 | call to Referer : string | main.go:37:3:37:7 | definition of write | Email content may contain $@. | main.go:34:21:34:31 | call to Referer | untrusted input |
+| main.go:49:46:49:59 | untrustedInput | main.go:43:21:43:31 | call to Referer : string | main.go:49:46:49:59 | untrustedInput | Email content may contain $@. | main.go:43:21:43:31 | call to Referer | untrusted input |
+| main.go:50:52:50:65 | untrustedInput | main.go:43:21:43:31 | call to Referer : string | main.go:50:52:50:65 | untrustedInput | Email content may contain $@. | main.go:43:21:43:31 | call to Referer | untrusted input |
+| main.go:60:16:60:22 | content | main.go:55:21:55:31 | call to Referer : string | main.go:60:16:60:22 | content | Email content may contain $@. | main.go:55:21:55:31 | call to Referer | untrusted input |
+| main.go:73:50:73:56 | content | main.go:65:21:65:31 | call to Referer : string | main.go:73:50:73:56 | content | Email content may contain $@. | main.go:65:21:65:31 | call to Referer | untrusted input |
+| main.go:73:59:73:65 | content | main.go:65:21:65:31 | call to Referer : string | main.go:73:59:73:65 | content | Email content may contain $@. | main.go:65:21:65:31 | call to Referer | untrusted input |
+| main.go:74:16:74:22 | content | main.go:65:21:65:31 | call to Referer : string | main.go:74:16:74:22 | content | Email content may contain $@. | main.go:65:21:65:31 | call to Referer | untrusted input |
+| main.go:86:37:86:50 | untrustedInput | main.go:79:21:79:31 | call to Referer : string | main.go:86:37:86:50 | untrustedInput | Email content may contain $@. | main.go:79:21:79:31 | call to Referer | untrusted input |
+| main.go:90:16:90:23 | content2 | main.go:79:21:79:31 | call to Referer : string | main.go:90:16:90:23 | content2 | Email content may contain $@. | main.go:79:21:79:31 | call to Referer | untrusted input |
diff --git a/ql/test/query-tests/Security/CWE-640/email.go b/ql/test/query-tests/Security/CWE-640/main.go
similarity index 81%
rename from ql/test/query-tests/Security/CWE-640/email.go
rename to ql/test/query-tests/Security/CWE-640/main.go
index 44a54b59fe7..2f57864ad06 100644
--- a/ql/test/query-tests/Security/CWE-640/email.go
+++ b/ql/test/query-tests/Security/CWE-640/main.go
@@ -11,23 +11,15 @@ import (
sendgrid "github.com/sendgrid/sendgrid-go/helpers/mail"
)
-// OK
-func mailGood(w http.ResponseWriter, r *http.Request) {
- host := config["Host"]
- token := backend.getUserSecretResetToken(email)
- body := "Click to reset password: " + host + "/" + token
- smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body))
-}
-
-// Not OK
-func mail(w http.ResponseWriter, r *http.Request) {
- host := r.Header.Get("Host")
- token := backend.getUserSecretResetToken(email)
- body := "Click to reset password: " + host + "/" + token
- smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(body))
-}
-
func main() {
+ var w http.ResponseWriter
+ var r *http.Request
+
+ // Not OK
+ mail(w, r)
+
+ // OK
+ mailGood(w, r)
// Not OK
http.HandleFunc("/ex0", func(w http.ResponseWriter, r *http.Request) {
From a3bc09473186bbc7a07f3570c0d0b14d0de408d8 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Tue, 16 Jun 2020 15:48:39 +0100
Subject: [PATCH 5/8] Add change note
---
change-notes/2020-06-16-email-injection.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 change-notes/2020-06-16-email-injection.md
diff --git a/change-notes/2020-06-16-email-injection.md b/change-notes/2020-06-16-email-injection.md
new file mode 100644
index 00000000000..63abd57f2d6
--- /dev/null
+++ b/change-notes/2020-06-16-email-injection.md
@@ -0,0 +1,2 @@
+lgtm,codescanning
+* The query "Email injection" (`go/email-injection`) has been moved out of the experimental folder. The query detects when untrusted input can be incorporated directly into an email.
From f926808c8aae56215cd8c7d1b17607ebf8985010 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 17 Jun 2020 10:06:35 +0100
Subject: [PATCH 6/8] Address review comments
---
ql/src/Security/CWE-640/EmailInjection.qll | 13 -------------
1 file changed, 13 deletions(-)
diff --git a/ql/src/Security/CWE-640/EmailInjection.qll b/ql/src/Security/CWE-640/EmailInjection.qll
index 726836ed922..4cf8b382c98 100644
--- a/ql/src/Security/CWE-640/EmailInjection.qll
+++ b/ql/src/Security/CWE-640/EmailInjection.qll
@@ -24,19 +24,6 @@ module EmailInjection {
override predicate isSource(DataFlow::Node source) { source instanceof Source }
- override predicate isSanitizerOut(DataFlow::Node node) {
- exists(DataFlow::CallNode call |
- call.getTarget().hasQualifiedName("hash.Hash", "Write") and
- (
- call.getReceiver().getType().getName() = "Hash" or
- call.getReceiver().getType().getName() = "Hash32" or
- call.getReceiver().getType().getName() = "Hash64"
- )
- |
- node = call.getArgument(0)
- )
- }
-
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
}
}
From 83697f62ac7a0212d7c508b62c6daf702aa2b0bc Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 17 Jun 2020 14:21:37 +0100
Subject: [PATCH 7/8] Address review comments on qhelp
---
ql/src/Security/CWE-640/EmailInjection.qhelp | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/ql/src/Security/CWE-640/EmailInjection.qhelp b/ql/src/Security/CWE-640/EmailInjection.qhelp
index f3ac1dc4bbe..1da839cedb8 100644
--- a/ql/src/Security/CWE-640/EmailInjection.qhelp
+++ b/ql/src/Security/CWE-640/EmailInjection.qhelp
@@ -4,7 +4,7 @@
Using untrusted input to construct an email can cause multiple security
vulnerabilities. For instance, inclusion of an untrusted input in an email body
- may allow an attacker to conduct Cross Site Scripting (XSS) attacks, while
+ may allow an attacker to conduct cross-site scripting (XSS) attacks, while
inclusion of an HTTP header may allow a full account compromise as shown in the
example below.
@@ -19,13 +19,13 @@
In the following example snippet, the host field is user controlled.
- A malicious user can send an HTTP request to the targeted web site,
- but with a Host header that refers to their own web site. This means the
+ A malicious user can send an HTTP request to the targeted website,
+ but with a Host header that refers to their own website. This means the
emails will be sent out to potential victims, originating from a server
- they trust, but with links leading to a malicious web site.
+ they trust, but with links leading to a malicious website.
- If the email contains a password reset link, and should the victim click
+ If the email contains a password reset link, and the victim clicks
the link, the secret reset token will be leaked to the attacker. Using the
leaked token, the attacker can then construct the real reset link and use it to
change the victim's password.
@@ -38,7 +38,7 @@
- OWASP
+ OWASP:
Content Spoofing
.
From 49abd0b9b1103c37f13c862aad659dfb2eb2c564 Mon Sep 17 00:00:00 2001
From: Owen Mansel-Chan
Date: Wed, 17 Jun 2020 14:33:26 +0100
Subject: [PATCH 8/8] Add test using hashing
---
.../Security/CWE-640/EmailInjection.expected | 72 +++++++++----------
ql/test/query-tests/Security/CWE-640/main.go | 16 +++++
2 files changed, 52 insertions(+), 36 deletions(-)
diff --git a/ql/test/query-tests/Security/CWE-640/EmailInjection.expected b/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
index 9e4eb436872..6e84a0466cd 100644
--- a/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
+++ b/ql/test/query-tests/Security/CWE-640/EmailInjection.expected
@@ -1,43 +1,43 @@
edges
| EmailBad.go:9:10:9:17 | selection of Header : Header | EmailBad.go:12:56:12:67 | type conversion |
-| main.go:26:21:26:31 | call to Referer : string | main.go:28:57:28:78 | type conversion |
-| main.go:34:21:34:31 | call to Referer : string | main.go:37:3:37:7 | definition of write |
-| main.go:43:21:43:31 | call to Referer : string | main.go:49:46:49:59 | untrustedInput |
-| main.go:43:21:43:31 | call to Referer : string | main.go:50:52:50:65 | untrustedInput |
-| main.go:55:21:55:31 | call to Referer : string | main.go:60:16:60:22 | content |
-| main.go:65:21:65:31 | call to Referer : string | main.go:73:50:73:56 | content |
-| main.go:65:21:65:31 | call to Referer : string | main.go:73:59:73:65 | content |
-| main.go:65:21:65:31 | call to Referer : string | main.go:74:16:74:22 | content |
-| main.go:79:21:79:31 | call to Referer : string | main.go:86:37:86:50 | untrustedInput |
-| main.go:79:21:79:31 | call to Referer : string | main.go:90:16:90:23 | content2 |
+| main.go:29:21:29:31 | call to Referer : string | main.go:31:57:31:78 | type conversion |
+| main.go:37:21:37:31 | call to Referer : string | main.go:40:3:40:7 | definition of write |
+| main.go:46:21:46:31 | call to Referer : string | main.go:52:46:52:59 | untrustedInput |
+| main.go:46:21:46:31 | call to Referer : string | main.go:53:52:53:65 | untrustedInput |
+| main.go:58:21:58:31 | call to Referer : string | main.go:63:16:63:22 | content |
+| main.go:68:21:68:31 | call to Referer : string | main.go:76:50:76:56 | content |
+| main.go:68:21:68:31 | call to Referer : string | main.go:76:59:76:65 | content |
+| main.go:68:21:68:31 | call to Referer : string | main.go:77:16:77:22 | content |
+| main.go:82:21:82:31 | call to Referer : string | main.go:89:37:89:50 | untrustedInput |
+| main.go:82:21:82:31 | call to Referer : string | main.go:93:16:93:23 | content2 |
nodes
| EmailBad.go:9:10:9:17 | selection of Header : Header | semmle.label | selection of Header : Header |
| EmailBad.go:12:56:12:67 | type conversion | semmle.label | type conversion |
-| main.go:26:21:26:31 | call to Referer : string | semmle.label | call to Referer : string |
-| main.go:28:57:28:78 | type conversion | semmle.label | type conversion |
-| main.go:34:21:34:31 | call to Referer : string | semmle.label | call to Referer : string |
-| main.go:37:3:37:7 | definition of write | semmle.label | definition of write |
-| main.go:43:21:43:31 | call to Referer : string | semmle.label | call to Referer : string |
-| main.go:49:46:49:59 | untrustedInput | semmle.label | untrustedInput |
-| main.go:50:52:50:65 | untrustedInput | semmle.label | untrustedInput |
-| main.go:55:21:55:31 | call to Referer : string | semmle.label | call to Referer : string |
-| main.go:60:16:60:22 | content | semmle.label | content |
-| main.go:65:21:65:31 | call to Referer : string | semmle.label | call to Referer : string |
-| main.go:73:50:73:56 | content | semmle.label | content |
-| main.go:73:59:73:65 | content | semmle.label | content |
-| main.go:74:16:74:22 | content | semmle.label | content |
-| main.go:79:21:79:31 | call to Referer : string | semmle.label | call to Referer : string |
-| main.go:86:37:86:50 | untrustedInput | semmle.label | untrustedInput |
-| main.go:90:16:90:23 | content2 | semmle.label | content2 |
+| main.go:29:21:29:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:31:57:31:78 | type conversion | semmle.label | type conversion |
+| main.go:37:21:37:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:40:3:40:7 | definition of write | semmle.label | definition of write |
+| main.go:46:21:46:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:52:46:52:59 | untrustedInput | semmle.label | untrustedInput |
+| main.go:53:52:53:65 | untrustedInput | semmle.label | untrustedInput |
+| main.go:58:21:58:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:63:16:63:22 | content | semmle.label | content |
+| main.go:68:21:68:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:76:50:76:56 | content | semmle.label | content |
+| main.go:76:59:76:65 | content | semmle.label | content |
+| main.go:77:16:77:22 | content | semmle.label | content |
+| main.go:82:21:82:31 | call to Referer : string | semmle.label | call to Referer : string |
+| main.go:89:37:89:50 | untrustedInput | semmle.label | untrustedInput |
+| main.go:93:16:93:23 | content2 | semmle.label | content2 |
#select
| EmailBad.go:12:56:12:67 | type conversion | EmailBad.go:9:10:9:17 | selection of Header : Header | EmailBad.go:12:56:12:67 | type conversion | Email content may contain $@. | EmailBad.go:9:10:9:17 | selection of Header | untrusted input |
-| main.go:28:57:28:78 | type conversion | main.go:26:21:26:31 | call to Referer : string | main.go:28:57:28:78 | type conversion | Email content may contain $@. | main.go:26:21:26:31 | call to Referer | untrusted input |
-| main.go:37:3:37:7 | definition of write | main.go:34:21:34:31 | call to Referer : string | main.go:37:3:37:7 | definition of write | Email content may contain $@. | main.go:34:21:34:31 | call to Referer | untrusted input |
-| main.go:49:46:49:59 | untrustedInput | main.go:43:21:43:31 | call to Referer : string | main.go:49:46:49:59 | untrustedInput | Email content may contain $@. | main.go:43:21:43:31 | call to Referer | untrusted input |
-| main.go:50:52:50:65 | untrustedInput | main.go:43:21:43:31 | call to Referer : string | main.go:50:52:50:65 | untrustedInput | Email content may contain $@. | main.go:43:21:43:31 | call to Referer | untrusted input |
-| main.go:60:16:60:22 | content | main.go:55:21:55:31 | call to Referer : string | main.go:60:16:60:22 | content | Email content may contain $@. | main.go:55:21:55:31 | call to Referer | untrusted input |
-| main.go:73:50:73:56 | content | main.go:65:21:65:31 | call to Referer : string | main.go:73:50:73:56 | content | Email content may contain $@. | main.go:65:21:65:31 | call to Referer | untrusted input |
-| main.go:73:59:73:65 | content | main.go:65:21:65:31 | call to Referer : string | main.go:73:59:73:65 | content | Email content may contain $@. | main.go:65:21:65:31 | call to Referer | untrusted input |
-| main.go:74:16:74:22 | content | main.go:65:21:65:31 | call to Referer : string | main.go:74:16:74:22 | content | Email content may contain $@. | main.go:65:21:65:31 | call to Referer | untrusted input |
-| main.go:86:37:86:50 | untrustedInput | main.go:79:21:79:31 | call to Referer : string | main.go:86:37:86:50 | untrustedInput | Email content may contain $@. | main.go:79:21:79:31 | call to Referer | untrusted input |
-| main.go:90:16:90:23 | content2 | main.go:79:21:79:31 | call to Referer : string | main.go:90:16:90:23 | content2 | Email content may contain $@. | main.go:79:21:79:31 | call to Referer | untrusted input |
+| main.go:31:57:31:78 | type conversion | main.go:29:21:29:31 | call to Referer : string | main.go:31:57:31:78 | type conversion | Email content may contain $@. | main.go:29:21:29:31 | call to Referer | untrusted input |
+| main.go:40:3:40:7 | definition of write | main.go:37:21:37:31 | call to Referer : string | main.go:40:3:40:7 | definition of write | Email content may contain $@. | main.go:37:21:37:31 | call to Referer | untrusted input |
+| main.go:52:46:52:59 | untrustedInput | main.go:46:21:46:31 | call to Referer : string | main.go:52:46:52:59 | untrustedInput | Email content may contain $@. | main.go:46:21:46:31 | call to Referer | untrusted input |
+| main.go:53:52:53:65 | untrustedInput | main.go:46:21:46:31 | call to Referer : string | main.go:53:52:53:65 | untrustedInput | Email content may contain $@. | main.go:46:21:46:31 | call to Referer | untrusted input |
+| main.go:63:16:63:22 | content | main.go:58:21:58:31 | call to Referer : string | main.go:63:16:63:22 | content | Email content may contain $@. | main.go:58:21:58:31 | call to Referer | untrusted input |
+| main.go:76:50:76:56 | content | main.go:68:21:68:31 | call to Referer : string | main.go:76:50:76:56 | content | Email content may contain $@. | main.go:68:21:68:31 | call to Referer | untrusted input |
+| main.go:76:59:76:65 | content | main.go:68:21:68:31 | call to Referer : string | main.go:76:59:76:65 | content | Email content may contain $@. | main.go:68:21:68:31 | call to Referer | untrusted input |
+| main.go:77:16:77:22 | content | main.go:68:21:68:31 | call to Referer : string | main.go:77:16:77:22 | content | Email content may contain $@. | main.go:68:21:68:31 | call to Referer | untrusted input |
+| main.go:89:37:89:50 | untrustedInput | main.go:82:21:82:31 | call to Referer : string | main.go:89:37:89:50 | untrustedInput | Email content may contain $@. | main.go:82:21:82:31 | call to Referer | untrusted input |
+| main.go:93:16:93:23 | content2 | main.go:82:21:82:31 | call to Referer : string | main.go:93:16:93:23 | content2 | Email content may contain $@. | main.go:82:21:82:31 | call to Referer | untrusted input |
diff --git a/ql/test/query-tests/Security/CWE-640/main.go b/ql/test/query-tests/Security/CWE-640/main.go
index 2f57864ad06..c6685475a8d 100644
--- a/ql/test/query-tests/Security/CWE-640/main.go
+++ b/ql/test/query-tests/Security/CWE-640/main.go
@@ -3,6 +3,9 @@ package main
//go:generate depstubber -vendor github.com/sendgrid/sendgrid-go/helpers/mail "" NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit
import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
"io"
"log"
"net/http"
@@ -90,6 +93,19 @@ func main() {
v.AddContent(content2)
})
+ // OK
+ http.HandleFunc("/ex6", func(w http.ResponseWriter, r *http.Request) {
+ untrustedInput := r.Referer()
+
+ sha256 := sha256.New
+ appsecret := "appid"
+ hash := hmac.New(sha256, []byte(appsecret))
+ hash.Write([]byte(untrustedInput))
+ signature := base64.StdEncoding.EncodeToString(hash.Sum(nil))
+
+ smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(signature))
+ })
+
log.Println(http.ListenAndServe(":80", nil))
}