Skip to content

Commit 3159d8e

Browse files
committed
Correlate SendGridMail declaration with its predicates
1 parent 67b672a commit 3159d8e

3 files changed

Lines changed: 67 additions & 37 deletions

File tree

python/ql/src/experimental/semmle/python/frameworks/Sendgrid.qll

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ private module Sendgrid {
5151
)
5252
}
5353

54-
private DataFlow::Node sendgridWrite(string attributeName) {
54+
private DataFlow::Node sendgridWrite(DataFlow::CallCfgNode mailCall, string attributeName) {
5555
exists(DataFlow::AttrWrite attrWrite |
56-
attrWrite.getObject().getALocalSource() = sendgridMailCall() and
56+
attrWrite.getObject().getALocalSource() = mailCall and
5757
attrWrite.getAttributeName() = attributeName and
5858
result = attrWrite.getValue()
5959
)
@@ -86,32 +86,39 @@ private module Sendgrid {
8686
private class SendGridMail extends DataFlow::CallCfgNode, EmailSender::Range {
8787
SendGridMail() { this.getFunction() = sendgridApiSendCall() }
8888

89+
DataFlow::CallCfgNode getMailCall() {
90+
exists(DataFlow::Node n |
91+
n in [this.getArg(0), this.getArgByName("request_body")] and
92+
result = [n, n.(DataFlow::MethodCallNode).getObject()].getALocalSource()
93+
)
94+
}
95+
8996
override DataFlow::Node getPlainTextBody() {
9097
result in [
91-
sendgridMailCall().getArg(3), sendgridMailCall().getArgByName("plain_text_content")
98+
this.getMailCall().getArg(3), this.getMailCall().getArgByName("plain_text_content")
9299
]
93100
or
94101
result in [
95-
sendgridContent(sendgridMailHelper().getMember("Content").getACall(), "text/plain"),
102+
sendgridContent([
103+
this.getMailCall().getArg(3), this.getMailCall().getArgByName("plain_text_content")
104+
].getALocalSource(), "text/plain"),
96105
sendgridContent(sendgridMailInstance().getMember("add_content").getACall(), "text/plain")
97106
]
98107
or
99-
result = sendgridWrite("plain_text_content")
108+
result = sendgridWrite(this.getMailCall(), "plain_text_content")
100109
}
101110

102111
override DataFlow::Node getHtmlBody() {
103-
result in [sendgridMailCall().getArg(4), sendgridMailCall().getArgByName("html_content")]
112+
result in [this.getMailCall().getArg(4), this.getMailCall().getArgByName("html_content")]
104113
or
105-
result = sendgridMailInstance().getMember("set_html").getACall().getArg(0)
114+
result = this.getMailCall().getAMethodCall("set_html").getArg(0)
106115
or
107-
result in [
108-
sendgridContent(sendgridMailHelper().getMember("Content").getACall(),
109-
["text/html", "text/x-amp-html"]),
110-
sendgridContent(sendgridMailInstance().getMember("add_content").getACall(),
111-
["text/html", "text/x-amp-html"])
112-
]
116+
result =
117+
sendgridContent([
118+
this.getMailCall().getArg(4), this.getMailCall().getArgByName("html_content")
119+
].getALocalSource(), ["text/html", "text/x-amp-html"])
113120
or
114-
result = sendgridWrite("html_content")
121+
result = sendgridWrite(this.getMailCall(), "html_content")
115122
or
116123
exists(KeyValuePair content, Dict generalDict, KeyValuePair typePair, KeyValuePair valuePair |
117124
content.getKey().(StrConst).getText() = "content" and
@@ -122,7 +129,9 @@ private module Sendgrid {
122129
valuePair.getKey().(StrConst).getText() = "value" and
123130
result.asExpr() = valuePair.getValue() and
124131
// correlate generalDict with previously set KeyValuePairs
125-
generalDict.getAnItem() in [typePair, valuePair]
132+
generalDict.getAnItem() in [typePair, valuePair] and
133+
[this.getArg(0), this.getArgByName("request_body")].getALocalSource().asExpr() =
134+
any(Dict d | d.getAnItem() = content)
126135
)
127136
or
128137
exists(KeyValuePair footer, Dict generalDict, KeyValuePair enablePair, KeyValuePair htmlPair |
@@ -135,38 +144,46 @@ private module Sendgrid {
135144
htmlPair.getKey().(StrConst).getText() = "html" and
136145
result.asExpr() = htmlPair.getValue() and
137146
// correlate generalDict with previously set KeyValuePairs
138-
generalDict.getAnItem() in [enablePair, htmlPair]
147+
generalDict.getAnItem() in [enablePair, htmlPair] and
148+
exists(KeyValuePair k |
149+
k.getKey() =
150+
[this.getArg(0), this.getArgByName("request_body")]
151+
.getALocalSource()
152+
.asExpr()
153+
.(Dict)
154+
.getAKey() and
155+
k.getValue() = any(Dict d | d.getAKey() = footer.getKey())
156+
)
139157
)
140158
}
141159

142160
override DataFlow::Node getTo() {
143-
result in [sendgridMailCall().getArg(1), sendgridMailCall().getArgByName("to_emails")]
161+
result in [this.getMailCall().getArg(1), this.getMailCall().getArgByName("to_emails")]
144162
or
145-
result = sendgridMailHelper().getMember("To").getACall().getArg(0)
163+
result = this.getMailCall().getAMethodCall("To").getArg(0)
146164
or
147165
result =
148-
sendgridMailInstance()
149-
.getMember(["to", "add_to", "cc", "add_cc", "bcc", "add_bcc"])
150-
.getACall()
166+
this.getMailCall()
167+
.getAMethodCall(["to", "add_to", "cc", "add_cc", "bcc", "add_bcc"])
151168
.getArg(0)
152169
}
153170

154171
override DataFlow::Node getFrom() {
155-
result in [sendgridMailCall().getArg(0), sendgridMailCall().getArgByName("from_email")]
172+
result in [this.getMailCall().getArg(0), this.getMailCall().getArgByName("from_email")]
156173
or
157-
result = sendgridMailHelper().getMember("Email").getACall().getArg(0)
174+
result = this.getMailCall().getAMethodCall("Email").getArg(0)
158175
or
159-
result = sendgridMailInstance().getMember(["from_email", "set_from"]).getACall().getArg(0)
176+
result = this.getMailCall().getAMethodCall(["from_email", "set_from"]).getArg(0)
160177
or
161-
result = sendgridWrite("from_email")
178+
result = sendgridWrite(this.getMailCall(), "from_email")
162179
}
163180

164181
override DataFlow::Node getSubject() {
165-
result in [sendgridMailCall().getArg(2), sendgridMailCall().getArgByName("subject")]
182+
result in [this.getMailCall().getArg(2), this.getMailCall().getArgByName("subject")]
166183
or
167-
result = sendgridMailInstance().getMember(["subject", "set_subject"]).getACall().getArg(0)
184+
result = this.getMailCall().getAMethodCall(["subject", "set_subject"]).getArg(0)
168185
or
169-
result = sendgridWrite("subject")
186+
result = sendgridWrite(this.getMailCall(), "subject")
170187
}
171188
}
172189
}

python/ql/test/experimental/query-tests/Security/CWE-079/ReflectedXSS.expected

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ edges
1212
| sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute |
1313
| sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute | sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript |
1414
| sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript | sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() |
15-
| sendgrid_mail.py:37:36:37:42 | ControlFlowNode for request | sendgrid_mail.py:37:36:37:47 | ControlFlowNode for Attribute |
16-
| sendgrid_mail.py:37:36:37:47 | ControlFlowNode for Attribute | sendgrid_mail.py:37:36:37:63 | ControlFlowNode for Subscript |
15+
| sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute |
16+
| sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute | sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript |
1717
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute |
1818
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request |
1919
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute |
@@ -55,9 +55,9 @@ nodes
5555
| sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
5656
| sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
5757
| sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
58-
| sendgrid_mail.py:37:36:37:42 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
59-
| sendgrid_mail.py:37:36:37:47 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
60-
| sendgrid_mail.py:37:36:37:63 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
58+
| sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
59+
| sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
60+
| sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
6161
| sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
6262
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
6363
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
@@ -89,7 +89,7 @@ subpaths
8989
| flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript | flask_mail.py:31:24:31:30 | ControlFlowNode for request | flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:31:24:31:30 | ControlFlowNode for request | a user-provided value |
9090
| sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript | sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | a user-provided value |
9191
| sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() | sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | a user-provided value |
92-
| sendgrid_mail.py:37:36:37:63 | ControlFlowNode for Subscript | sendgrid_mail.py:37:36:37:42 | ControlFlowNode for request | sendgrid_mail.py:37:36:37:63 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:37:36:37:42 | ControlFlowNode for request | a user-provided value |
92+
| sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | a user-provided value |
9393
| sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | a user-provided value |
9494
| sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | a user-provided value |
9595
| sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | a user-provided value |

python/ql/test/experimental/query-tests/Security/CWE-079/sendgrid_mail.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,24 @@ def send_post():
3434
from_email = Email("test@example.com")
3535
to_email = To("test@example.com")
3636
subject = "Sending with SendGrid is Fun"
37-
content = Content("text/html", request.args["html_content"])
37+
html_content = Content("text/html", request.args["html_content"])
38+
plain_content = Content("text/plain", request.args["plain_content"])
3839

39-
content = Content(MimeType.html, request.args["html_content"])
40+
mail = Mail(from_email, to_email, subject, plain_content, html_content)
4041

41-
mail = Mail(from_email, to_email, subject, content)
42+
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
43+
response = sg.client.mail.send.post(request_body=mail.get())
44+
45+
46+
@app.route("/send_post2")
47+
def send_post2():
48+
from_email = Email("test@example.com")
49+
to_email = To("test@example.com")
50+
subject = "Sending with SendGrid is Fun"
51+
html_content = Content(MimeType.html, request.args["html_content"])
52+
plain_content = Content(MimeType.text, request.args["plain_content"])
53+
54+
mail = Mail(from_email, to_email, subject, plain_content, html_content)
4255

4356
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
4457
response = sg.client.mail.send.post(request_body=mail.get())

0 commit comments

Comments
 (0)