Add Sendgrid modeling

This commit is contained in:
jorgectf
2021-06-23 20:53:17 +02:00
parent bf1eb7238e
commit 9563faf918
4 changed files with 171 additions and 16 deletions

View File

@@ -6,3 +6,4 @@ private import experimental.semmle.python.frameworks.Stdlib
private import experimental.semmle.python.frameworks.LDAP
private import experimental.semmle.python.frameworks.Flask
private import experimental.semmle.python.frameworks.Django
private import experimental.semmle.python.frameworks.Sendgrid

View File

@@ -0,0 +1,137 @@
/**
* Provides classes modeling security-relevant aspects of the `sendgrid` PyPI package.
* See https://github.com/sendgrid/sendgrid-python.
*/
private import python
private import semmle.python.dataflow.new.DataFlow
private import experimental.semmle.python.Concepts
private import semmle.python.ApiGraphs
private module Sendgrid {
private API::Node sendgrid() { result = API::moduleImport("sendgrid") }
private API::Node sendgridMailHelper() {
result = sendgrid().getMember("helpers").getMember("mail")
}
private API::Node sendgridMailInstance() { result = sendgridMailHelper().getMember("Mail") }
private DataFlow::CallCfgNode sendgridMailCall() { result = sendgridMailInstance().getACall() }
/** Gets a reference to a `SendGridAPIClient` instance. */
private DataFlow::LocalSourceNode sendgridApiClient(DataFlow::TypeTracker t) {
t.start() and
result.(DataFlow::AttrRead).getObject*().getALocalSource() =
sendgrid().getMember("SendGridAPIClient").getReturn().getAUse()
or
exists(DataFlow::TypeTracker t2 | result = sendgridApiClient(t2).track(t2, t))
}
/** Gets a reference to a `SendGridAPIClient` instance use. */
private DataFlow::Node sendgridApiClient() {
sendgridApiClient(DataFlow::TypeTracker::end()).flowsTo(result)
}
/** Gets a reference to a `SendGridAPIClient` instance call with `send` or `post`. */
private DataFlow::Node sendgridApiSendCall() {
result = sendgridApiClient() and
result.(DataFlow::AttrRead).getAttributeName() in ["send", "post"]
}
/**
* https://github.com/sendgrid/sendgrid-python
* https://github.com/sendgrid/sendgrid-python/blob/cf0924c35c37bbec8e5ca39e963a55f54f0eec11/sendgrid/helpers/mail/mail.py#L20
*/
private class SendGridMail extends DataFlow::CallCfgNode, EmailSender {
SendGridMail() { this.getFunction() = sendgridApiSendCall() }
override DataFlow::Node getPlainTextBody() {
result in [
sendgridMailCall().getArg(3), sendgridMailCall().getArgByName("plain_text_content")
]
or
exists(DataFlow::CallCfgNode contentCall, StrConst mime |
contentCall = sendgridMailHelper().getMember("Content").getACall() and
mime.getText() = "text/plain" and
DataFlow::exprNode(mime).(DataFlow::LocalSourceNode).flowsTo(contentCall.getArg(0)) and
result = contentCall.getArg(1)
)
or
exists(DataFlow::CallCfgNode addContentCall, StrConst mime |
addContentCall = sendgridMailInstance().getMember("add_content").getACall() and
mime.getText() = "text/plain" and
DataFlow::exprNode(mime).(DataFlow::LocalSourceNode).flowsTo(addContentCall.getArg(1)) and
result = addContentCall.getArg(0)
)
or
exists(DataFlow::AttrWrite bodyWrite |
bodyWrite.getObject().getALocalSource() = sendgridMailCall() and
bodyWrite.getAttributeName() = "plain_text_content" and
result = bodyWrite.getValue()
)
}
override DataFlow::Node getHtmlBody() {
result in [sendgridMailCall().getArg(4), sendgridMailCall().getArgByName("html_content")]
or
exists(DataFlow::CallCfgNode contentCall, StrConst mime |
contentCall = sendgridMailHelper().getMember("Content").getACall() and
mime.getText() = "text/html" and
DataFlow::exprNode(mime).(DataFlow::LocalSourceNode).flowsTo(contentCall.getArg(0)) and
result = contentCall.getArg(1)
)
or
exists(DataFlow::CallCfgNode addContentCall, StrConst mime |
addContentCall = sendgridMailInstance().getMember("add_content").getACall() and
mime.getText() = "text/html" and
DataFlow::exprNode(mime).(DataFlow::LocalSourceNode).flowsTo(addContentCall.getArg(1)) and
result = addContentCall.getArg(0)
)
or
exists(DataFlow::AttrWrite htmlWrite |
htmlWrite.getObject().getALocalSource() = sendgridMailCall() and
htmlWrite.getAttributeName() = "html_content" and
result = htmlWrite.getValue()
)
}
override DataFlow::Node getTo() {
result in [sendgridMailCall().getArg(1), sendgridMailCall().getArgByName("to_emails")]
or
result = sendgridMailHelper().getMember("To").getACall().getArg(0)
or
result =
sendgridMailInstance()
.getMember(["to", "add_to", "cc", "add_cc", "bcc", "add_bcc"])
.getACall()
.getArg(0)
}
override DataFlow::Node getFrom() {
result in [sendgridMailCall().getArg(0), sendgridMailCall().getArgByName("from_email")]
or
result = sendgridMailHelper().getMember("Email").getACall().getArg(0)
or
result = sendgridMailInstance().getMember("from_email").getACall().getArg(0)
or
exists(DataFlow::AttrWrite fromWrite |
fromWrite.getObject().getALocalSource() = sendgridMailCall() and
fromWrite.getAttributeName() = "from_email" and
result = fromWrite.getValue()
)
}
override DataFlow::Node getSubject() {
result in [sendgridMailCall().getArg(2), sendgridMailCall().getArgByName("subject")]
or
result = sendgridMailInstance().getMember("subject").getACall().getArg(0)
or
exists(DataFlow::AttrWrite subjectWrite |
subjectWrite.getObject().getALocalSource() = sendgridMailCall() and
subjectWrite.getAttributeName() = "subject" and
result = subjectWrite.getValue()
)
}
}
}

View File

@@ -0,0 +1,33 @@
# https://www.twilio.com/blog/how-to-send-emails-in-python-with-sendgrid
from flask import request, Flask
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, Email, To, Content, MimeType
app = Flask(__name__)
@app.route("/send")
def send():
message = Mail(
from_email='from_email@example.com',
to_emails='to@example.com',
subject='Sending with Twilio SendGrid is Fun',
html_content=request.args["html_content"])
sg = SendGridAPIClient('SENDGRID_API_KEY')
sg.send(message)
@app.route("/send_post")
def send_post():
from_email = Email("test@example.com")
to_email = To("test@example.com")
subject = "Sending with SendGrid is Fun"
content = Content("text/html", request.args["html_content"])
# https://github.com/sendgrid/sendgrid-python/blob/cf0924c35c37bbec8e5ca39e963a55f54f0eec11/sendgrid/helpers/mail/mime_type.py#L1
content = Content(MimeType.html, request.args["html_content"])
mail = Mail(from_email, to_email, subject, content)
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
response = sg.client.mail.send.post(request_body=mail.get())

View File

@@ -1,16 +0,0 @@
# This tests that the developer doesn't pass content via the Content class initializer.
# source:https://github.com/sendgrid/sendgrid-python
import sendgrid
import os
from sendgrid.helpers.mail import *
sg = sendgrid.SendGridAPIClient(api_key=os.environ.get('SENDGRID_API_KEY'))
from_email = Email("test@example.com")
to_email = To("test@example.com")
subject = "Sending with SendGrid is Fun"
content = Content("text/html", "and <b>easy</b> to do anywhere, even with Python") # Content can also take the MimeType.html as the first arg here. Need to create a separate example for this.
mail = Mail(from_email, to_email, subject, content)
response = sg.client.mail.send.post(request_body=mail.get())