mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Add Sendgrid modeling
This commit is contained in:
@@ -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
|
||||
|
||||
137
python/ql/src/experimental/semmle/python/frameworks/Sendgrid.qll
Normal file
137
python/ql/src/experimental/semmle/python/frameworks/Sendgrid.qll
Normal 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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
@@ -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())
|
||||
Reference in New Issue
Block a user