зеркало из https://github.com/github/codeql.git
Merge pull request #7127 from jty-team/jty/python/emailInjection
Python: CWE-079 - Add Email injection query
This commit is contained in:
Коммит
699761889d
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* @name Reflected server-side cross-site scripting
|
||||||
|
* @description Writing user input directly to a web page
|
||||||
|
* allows for a cross-site scripting vulnerability.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity error
|
||||||
|
* @security-severity 2.9
|
||||||
|
* @sub-severity high
|
||||||
|
* @id py/reflective-xss
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-079
|
||||||
|
* external/cwe/cwe-116
|
||||||
|
*/
|
||||||
|
|
||||||
|
// determine precision above
|
||||||
|
import python
|
||||||
|
import experimental.semmle.python.security.dataflow.ReflectedXSS
|
||||||
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
|
from ReflectedXssConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||||
|
where config.hasFlowPath(source, sink)
|
||||||
|
select sink.getNode(), source, sink, "Cross-site scripting vulnerability due to $@.",
|
||||||
|
source.getNode(), "a user-provided value"
|
|
@ -602,3 +602,77 @@ class JwtDecoding extends DataFlow::Node instanceof JwtDecoding::Range {
|
||||||
|
|
||||||
/** DEPRECATED: Alias for JwtDecoding */
|
/** DEPRECATED: Alias for JwtDecoding */
|
||||||
deprecated class JWTDecoding = JwtDecoding;
|
deprecated class JWTDecoding = JwtDecoding;
|
||||||
|
|
||||||
|
/** Provides classes for modeling Email APIs. */
|
||||||
|
module EmailSender {
|
||||||
|
/**
|
||||||
|
* A data-flow node that sends an email.
|
||||||
|
*
|
||||||
|
* Extend this class to model new APIs. If you want to refine existing API models,
|
||||||
|
* extend `EmailSender` instead.
|
||||||
|
*/
|
||||||
|
abstract class Range extends DataFlow::Node {
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the plaintext version of the email body.
|
||||||
|
*/
|
||||||
|
abstract DataFlow::Node getPlainTextBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the html version of the email body.
|
||||||
|
*/
|
||||||
|
abstract DataFlow::Node getHtmlBody();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the recipients of the email.
|
||||||
|
*/
|
||||||
|
abstract DataFlow::Node getTo();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the senders of the email.
|
||||||
|
*/
|
||||||
|
abstract DataFlow::Node getFrom();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the subject of the email.
|
||||||
|
*/
|
||||||
|
abstract DataFlow::Node getSubject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data-flow node that sends an email.
|
||||||
|
*
|
||||||
|
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||||
|
* extend `EmailSender::Range` instead.
|
||||||
|
*/
|
||||||
|
class EmailSender extends DataFlow::Node instanceof EmailSender::Range {
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the plaintext version of the email body.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getPlainTextBody() { result = super.getPlainTextBody() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the html version of the email body.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getHtmlBody() { result = super.getHtmlBody() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the recipients of the email.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getTo() { result = super.getTo() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the senders of the email.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getFrom() { result = super.getFrom() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node holding the subject of the email.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getSubject() { result = super.getSubject() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a data flow node that refers to the HTML body or plaintext body of the email.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getABody() { result in [super.getPlainTextBody(), super.getHtmlBody()] }
|
||||||
|
}
|
||||||
|
|
|
@ -15,3 +15,6 @@ private import experimental.semmle.python.libraries.Python_JWT
|
||||||
private import experimental.semmle.python.libraries.Authlib
|
private import experimental.semmle.python.libraries.Authlib
|
||||||
private import experimental.semmle.python.libraries.PythonJose
|
private import experimental.semmle.python.libraries.PythonJose
|
||||||
private import experimental.semmle.python.frameworks.CopyFile
|
private import experimental.semmle.python.frameworks.CopyFile
|
||||||
|
private import experimental.semmle.python.frameworks.Sendgrid
|
||||||
|
private import experimental.semmle.python.libraries.FlaskMail
|
||||||
|
private import experimental.semmle.python.libraries.SmtpLib
|
||||||
|
|
|
@ -8,8 +8,8 @@ private import semmle.python.frameworks.Django
|
||||||
private import semmle.python.dataflow.new.DataFlow
|
private import semmle.python.dataflow.new.DataFlow
|
||||||
private import experimental.semmle.python.Concepts
|
private import experimental.semmle.python.Concepts
|
||||||
private import semmle.python.ApiGraphs
|
private import semmle.python.ApiGraphs
|
||||||
import semmle.python.dataflow.new.RemoteFlowSources
|
|
||||||
private import semmle.python.Concepts
|
private import semmle.python.Concepts
|
||||||
|
import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
|
|
||||||
private module ExperimentalPrivateDjango {
|
private module ExperimentalPrivateDjango {
|
||||||
private module DjangoMod {
|
private module DjangoMod {
|
||||||
|
@ -189,5 +189,90 @@ private module ExperimentalPrivateDjango {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module Email {
|
||||||
|
/** https://docs.djangoproject.com/en/3.2/topics/email/ */
|
||||||
|
private API::Node djangoMail() {
|
||||||
|
result = API::moduleImport("django").getMember("core").getMember("mail")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a call to `django.core.mail.send_mail()`.
|
||||||
|
*
|
||||||
|
* Given the following example:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* send_mail("Subject", "plain-text body", "from@example.com", ["to@example.com"], html_message=django.http.request.GET.get("html"))
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `this` would be `send_mail("Subject", "plain-text body", "from@example.com", ["to@example.com"], html_message=django.http.request.GET.get("html"))`.
|
||||||
|
* * `getPlainTextBody()`'s result would be `"plain-text body"`.
|
||||||
|
* * `getHtmlBody()`'s result would be `django.http.request.GET.get("html")`.
|
||||||
|
* * `getTo()`'s result would be `["to@example.com"]`.
|
||||||
|
* * `getFrom()`'s result would be `"from@example.com"`.
|
||||||
|
* * `getSubject()`'s result would be `"Subject"`.
|
||||||
|
*/
|
||||||
|
private class DjangoSendMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||||
|
DjangoSendMail() { this = djangoMail().getMember("send_mail").getACall() }
|
||||||
|
|
||||||
|
override DataFlow::Node getPlainTextBody() {
|
||||||
|
result in [this.getArg(1), this.getArgByName("message")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getHtmlBody() {
|
||||||
|
result in [this.getArg(8), this.getArgByName("html_message")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getTo() {
|
||||||
|
result in [this.getArg(3), this.getArgByName("recipient_list")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getFrom() {
|
||||||
|
result in [this.getArg(2), this.getArgByName("from_email")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getSubject() {
|
||||||
|
result in [this.getArg(0), this.getArgByName("subject")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a call to `django.core.mail.mail_admins()` or `django.core.mail.mail_managers()`.
|
||||||
|
*
|
||||||
|
* Given the following example:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* mail_admins("Subject", "plain-text body", html_message=django.http.request.GET.get("html"))
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `this` would be `mail_admins("Subject", "plain-text body", html_message=django.http.request.GET.get("html"))`.
|
||||||
|
* * `getPlainTextBody()`'s result would be `"plain-text body"`.
|
||||||
|
* * `getHtmlBody()`'s result would be `django.http.request.GET.get("html")`.
|
||||||
|
* * `getTo()`'s result would be `none`.
|
||||||
|
* * `getFrom()`'s result would be `none`.
|
||||||
|
* * `getSubject()`'s result would be `"Subject"`.
|
||||||
|
*/
|
||||||
|
private class DjangoMailInternal extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||||
|
DjangoMailInternal() {
|
||||||
|
this = djangoMail().getMember(["mail_admins", "mail_managers"]).getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getPlainTextBody() {
|
||||||
|
result in [this.getArg(1), this.getArgByName("message")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getHtmlBody() {
|
||||||
|
result in [this.getArg(4), this.getArgByName("html_message")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getTo() { none() }
|
||||||
|
|
||||||
|
override DataFlow::Node getFrom() { none() }
|
||||||
|
|
||||||
|
override DataFlow::Node getSubject() {
|
||||||
|
result in [this.getArg(0), this.getArgByName("subject")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,187 @@
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
/** Gets a reference to the `sendgrid` module. */
|
||||||
|
private API::Node sendgrid() { result = API::moduleImport("sendgrid") }
|
||||||
|
|
||||||
|
/** Gets a reference to `sendgrid.helpers.mail` */
|
||||||
|
private API::Node sendgridMailHelper() {
|
||||||
|
result = sendgrid().getMember("helpers").getMember("mail")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a reference to `sendgrid.helpers.mail.Mail` */
|
||||||
|
private API::Node sendgridMailInstance() { result = sendgridMailHelper().getMember("Mail") }
|
||||||
|
|
||||||
|
/** Gets a reference to a `SendGridAPIClient` instance. */
|
||||||
|
private API::Node sendgridApiClient() {
|
||||||
|
result = sendgrid().getMember("SendGridAPIClient").getReturn()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a reference to a `SendGridAPIClient` instance call with `send` or `post`. */
|
||||||
|
private DataFlow::CallCfgNode sendgridApiSendCall() {
|
||||||
|
result = sendgridApiClient().getMember("send").getACall()
|
||||||
|
or
|
||||||
|
result =
|
||||||
|
sendgridApiClient()
|
||||||
|
.getMember("client")
|
||||||
|
.getMember("mail")
|
||||||
|
.getMember("send")
|
||||||
|
.getMember("post")
|
||||||
|
.getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a reference to `sg.send()` and `sg.client.mail.send.post()`.
|
||||||
|
*
|
||||||
|
* Given the following example:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* from_email = Email("from@example.com")
|
||||||
|
* to_email = To("to@example.com")
|
||||||
|
* subject = "Sending with SendGrid is Fun"
|
||||||
|
* content = Content("text/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())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `this` would be `sg.client.mail.send.post(request_body=mail.get())`.
|
||||||
|
* * `getPlainTextBody()`'s result would be `none()`.
|
||||||
|
* * `getHtmlBody()`'s result would be `request.args["html_content"]`.
|
||||||
|
* * `getTo()`'s result would be `"to@example.com"`.
|
||||||
|
* * `getFrom()`'s result would be `"from@example.com"`.
|
||||||
|
* * `getSubject()`'s result would be `"Sending with SendGrid is Fun"`.
|
||||||
|
*/
|
||||||
|
private class SendGridMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||||
|
SendGridMail() { this = sendgridApiSendCall() }
|
||||||
|
|
||||||
|
private DataFlow::CallCfgNode getMailCall() {
|
||||||
|
exists(DataFlow::Node n |
|
||||||
|
n in [this.getArg(0), this.getArgByName("request_body")] and
|
||||||
|
result = [n, n.(DataFlow::MethodCallNode).getObject()].getALocalSource()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataFlow::Node sendgridContent(DataFlow::CallCfgNode contentCall, string mime) {
|
||||||
|
mime in ["text/plain", "text/html", "text/x-amp-html"] and
|
||||||
|
exists(StrConst mimeNode |
|
||||||
|
mimeNode.getText() = mime and
|
||||||
|
DataFlow::exprNode(mimeNode).(DataFlow::LocalSourceNode).flowsTo(contentCall.getArg(0)) and
|
||||||
|
result = contentCall.getArg(1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataFlow::Node sendgridWrite(string attributeName) {
|
||||||
|
attributeName in ["plain_text_content", "html_content", "from_email", "subject"] and
|
||||||
|
exists(DataFlow::AttrWrite attrWrite |
|
||||||
|
attrWrite.getObject().getALocalSource() = this.getMailCall() and
|
||||||
|
attrWrite.getAttributeName() = attributeName and
|
||||||
|
result = attrWrite.getValue()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getPlainTextBody() {
|
||||||
|
result in [
|
||||||
|
this.getMailCall().getArg(3), this.getMailCall().getArgByName("plain_text_content")
|
||||||
|
]
|
||||||
|
or
|
||||||
|
result in [
|
||||||
|
this.sendgridContent([
|
||||||
|
this.getMailCall().getArg(3), this.getMailCall().getArgByName("plain_text_content")
|
||||||
|
].getALocalSource(), "text/plain"),
|
||||||
|
this.sendgridContent(sendgridMailInstance().getMember("add_content").getACall(),
|
||||||
|
"text/plain")
|
||||||
|
]
|
||||||
|
or
|
||||||
|
result = this.sendgridWrite("plain_text_content")
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getHtmlBody() {
|
||||||
|
result in [this.getMailCall().getArg(4), this.getMailCall().getArgByName("html_content")]
|
||||||
|
or
|
||||||
|
result = this.getMailCall().getAMethodCall("set_html").getArg(0)
|
||||||
|
or
|
||||||
|
result =
|
||||||
|
this.sendgridContent([
|
||||||
|
this.getMailCall().getArg(4), this.getMailCall().getArgByName("html_content")
|
||||||
|
].getALocalSource(), ["text/html", "text/x-amp-html"])
|
||||||
|
or
|
||||||
|
result = this.sendgridWrite("html_content")
|
||||||
|
or
|
||||||
|
exists(KeyValuePair content, Dict generalDict, KeyValuePair typePair, KeyValuePair valuePair |
|
||||||
|
content.getKey().(StrConst).getText() = "content" and
|
||||||
|
content.getValue().(List).getAnElt() = generalDict and
|
||||||
|
// declare KeyValuePairs keys and values
|
||||||
|
typePair.getKey().(StrConst).getText() = "type" and
|
||||||
|
typePair.getValue().(StrConst).getText() = ["text/html", "text/x-amp-html"] and
|
||||||
|
valuePair.getKey().(StrConst).getText() = "value" and
|
||||||
|
result.asExpr() = valuePair.getValue() and
|
||||||
|
// correlate generalDict with previously set KeyValuePairs
|
||||||
|
generalDict.getAnItem() in [typePair, valuePair] and
|
||||||
|
[this.getArg(0), this.getArgByName("request_body")].getALocalSource().asExpr() =
|
||||||
|
any(Dict d | d.getAnItem() = content)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(KeyValuePair footer, Dict generalDict, KeyValuePair enablePair, KeyValuePair htmlPair |
|
||||||
|
footer.getKey().(StrConst).getText() = ["footer", "subscription_tracking"] and
|
||||||
|
footer.getValue() = generalDict and
|
||||||
|
// check footer is enabled
|
||||||
|
enablePair.getKey().(StrConst).getText() = "enable" and
|
||||||
|
exists(enablePair.getValue().(True)) and
|
||||||
|
// get html content
|
||||||
|
htmlPair.getKey().(StrConst).getText() = "html" and
|
||||||
|
result.asExpr() = htmlPair.getValue() and
|
||||||
|
// correlate generalDict with previously set KeyValuePairs
|
||||||
|
generalDict.getAnItem() in [enablePair, htmlPair] and
|
||||||
|
exists(KeyValuePair k |
|
||||||
|
k.getKey() =
|
||||||
|
[this.getArg(0), this.getArgByName("request_body")]
|
||||||
|
.getALocalSource()
|
||||||
|
.asExpr()
|
||||||
|
.(Dict)
|
||||||
|
.getAKey() and
|
||||||
|
k.getValue() = any(Dict d | d.getAKey() = footer.getKey())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getTo() {
|
||||||
|
result in [this.getMailCall().getArg(1), this.getMailCall().getArgByName("to_emails")]
|
||||||
|
or
|
||||||
|
result = this.getMailCall().getAMethodCall("To").getArg(0)
|
||||||
|
or
|
||||||
|
result =
|
||||||
|
this.getMailCall()
|
||||||
|
.getAMethodCall(["to", "add_to", "cc", "add_cc", "bcc", "add_bcc"])
|
||||||
|
.getArg(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getFrom() {
|
||||||
|
result in [this.getMailCall().getArg(0), this.getMailCall().getArgByName("from_email")]
|
||||||
|
or
|
||||||
|
result = this.getMailCall().getAMethodCall("Email").getArg(0)
|
||||||
|
or
|
||||||
|
result = this.getMailCall().getAMethodCall(["from_email", "set_from"]).getArg(0)
|
||||||
|
or
|
||||||
|
result = this.sendgridWrite("from_email")
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getSubject() {
|
||||||
|
result in [this.getMailCall().getArg(2), this.getMailCall().getArgByName("subject")]
|
||||||
|
or
|
||||||
|
result = this.getMailCall().getAMethodCall(["subject", "set_subject"]).getArg(0)
|
||||||
|
or
|
||||||
|
result = this.sendgridWrite("subject")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Provides classes modeling security-relevant aspects of the `flask` PyPI package.
|
||||||
|
* See https://flask.palletsprojects.com/en/1.1.x/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
private import python
|
||||||
|
private import semmle.python.dataflow.new.DataFlow
|
||||||
|
private import experimental.semmle.python.Concepts
|
||||||
|
private import semmle.python.ApiGraphs
|
||||||
|
|
||||||
|
/** https://pythonhosted.org/Flask-Mail/#module-flask_mail */
|
||||||
|
private module FlaskMail {
|
||||||
|
/** Gets a reference to `flask_mail`, `flask_sendmail` and `flask.ext.sendmail`. */
|
||||||
|
private API::Node flaskMail() {
|
||||||
|
result = API::moduleImport(["flask_mail", "flask_sendmail", "flask.ext.sendmail"])
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a reference to `flask_mail.Mail()`, `flask_sendmail.Mail()` and `flask.ext.sendmail.Mail()`. */
|
||||||
|
private API::Node flaskMailInstance() { result = flaskMail().getMember("Mail").getReturn() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a call to `mail.send()`.
|
||||||
|
*
|
||||||
|
* Given the following example:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* msg = Message(subject="Subject",
|
||||||
|
* sender="from@example.com",
|
||||||
|
* recipients=["to@example.com"],
|
||||||
|
* body="plain-text body",
|
||||||
|
* html=request.args["html"])
|
||||||
|
* mail.send(msg)
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `this` would be `mail.send(msg)`.
|
||||||
|
* * `getPlainTextBody()`'s result would be `"plain-text body"`.
|
||||||
|
* * `getHtmlBody()`'s result would be `request.args["html"]`.
|
||||||
|
* * `getTo()`'s result would be `["to@example.com"]`.
|
||||||
|
* * `getFrom()`'s result would be `"from@example.com"`.
|
||||||
|
* * `getSubject()`'s result would be `"Subject"`.
|
||||||
|
*/
|
||||||
|
private class FlaskMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||||
|
FlaskMail() {
|
||||||
|
this =
|
||||||
|
[flaskMailInstance(), flaskMailInstance().getMember("connect").getReturn()]
|
||||||
|
.getMember(["send", "send_message"])
|
||||||
|
.getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataFlow::CallCfgNode getMessage() { result = this.getArg(0).getALocalSource() }
|
||||||
|
|
||||||
|
bindingset[argumentPosition]
|
||||||
|
private DataFlow::Node getFlaskMailArgument(int argumentPosition, string argumentName) {
|
||||||
|
argumentPosition in [[0 .. 3], 5] and
|
||||||
|
argumentName in ["body", "html", "recipients", "sender", "subject"] and
|
||||||
|
result in [
|
||||||
|
this.getMessage().getArg(argumentPosition), this.getMessage().getArgByName(argumentName)
|
||||||
|
]
|
||||||
|
or
|
||||||
|
exists(DataFlow::AttrWrite write |
|
||||||
|
write.getObject().getALocalSource() = this.getMessage() and
|
||||||
|
write.getAttributeName() = argumentName and
|
||||||
|
result = write.getValue()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getPlainTextBody() { result = this.getFlaskMailArgument(2, "body") }
|
||||||
|
|
||||||
|
override DataFlow::Node getHtmlBody() { result = this.getFlaskMailArgument(3, "html") }
|
||||||
|
|
||||||
|
override DataFlow::Node getTo() {
|
||||||
|
result = this.getFlaskMailArgument(1, "recipients")
|
||||||
|
or
|
||||||
|
result = this.getMessage().getAMethodCall("add_recipient").getACall().getArg(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getFrom() { result = this.getFlaskMailArgument(5, "sender") }
|
||||||
|
|
||||||
|
override DataFlow::Node getSubject() { result = this.getFlaskMailArgument(0, "subject") }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
private import python
|
||||||
|
private import semmle.python.dataflow.new.DataFlow
|
||||||
|
private import experimental.semmle.python.Concepts
|
||||||
|
private import semmle.python.ApiGraphs
|
||||||
|
private import semmle.python.dataflow.new.TaintTracking2
|
||||||
|
|
||||||
|
module SmtpLib {
|
||||||
|
/** Gets a reference to `smtplib.SMTP_SSL` */
|
||||||
|
private API::Node smtpConnectionInstance() {
|
||||||
|
result = API::moduleImport("smtplib").getMember("SMTP_SSL")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a reference to `email.mime.multipart.MIMEMultipart` */
|
||||||
|
private API::Node smtpMimeMultipartInstance() {
|
||||||
|
result =
|
||||||
|
API::moduleImport("email").getMember("mime").getMember("multipart").getMember("MIMEMultipart")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a reference to `email.mime.text.MIMEText` */
|
||||||
|
private API::Node smtpMimeTextInstance() {
|
||||||
|
result = API::moduleImport("email").getMember("mime").getMember("text").getMember("MIMEText")
|
||||||
|
}
|
||||||
|
|
||||||
|
private DataFlow::CallCfgNode mimeText(string mimetype) {
|
||||||
|
result = smtpMimeTextInstance().getACall() and
|
||||||
|
[result.getArg(1), result.getArgByName("_subtype")].asExpr().(StrConst).getText() = mimetype
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets flow from `MIMEText()` to `MIMEMultipart(_subparts=(part1, part2))`'s `_subparts`
|
||||||
|
* argument. Used because of the impossibility to get local source nodes from `_subparts`'
|
||||||
|
* `(List|Tuple)` elements.
|
||||||
|
*/
|
||||||
|
private class SMTPMessageConfig extends TaintTracking2::Configuration {
|
||||||
|
SMTPMessageConfig() { this = "SMTPMessageConfig" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) { source = mimeText(_) }
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
sink = smtpMimeMultipartInstance().getACall().getArgByName("_subparts")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using the `MimeText` call retrieves the content argument whose type argument equals `mimetype`.
|
||||||
|
* This call flows into `MIMEMultipart`'s `_subparts` argument or the `.attach()` method call
|
||||||
|
* and both local source nodes correlate to `smtp`'s `sendmail` call 3rd argument's local source.
|
||||||
|
*
|
||||||
|
* Given the following example with `getSmtpMessage(any(SmtpLibSendMail s), "html")`:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* part1 = MIMEText(text, "plain")
|
||||||
|
* part2 = MIMEText(html, "html")
|
||||||
|
* message = MIMEMultipart(_subparts=(part1, part2))
|
||||||
|
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `source` would be `MIMEText(text, "html")`.
|
||||||
|
* * `sink` would be `MIMEMultipart(_subparts=(part1, part2))`.
|
||||||
|
* * Then `message` local source node is correlated to `sink`.
|
||||||
|
* * Then the flow from `source` to `_subparts` is checked.
|
||||||
|
*
|
||||||
|
* Given the following example with `getSmtpMessage(any(SmtpLibSendMail s), "html")`:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* part1 = MIMEText(text, "plain")
|
||||||
|
* part2 = MIMEText(html, "html")
|
||||||
|
* message = MIMEMultipart("alternative")
|
||||||
|
* message.attach(part1)
|
||||||
|
* message.attach(part2)
|
||||||
|
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `source` would be `MIMEText(text, "html")`.
|
||||||
|
* * `sink` would be `message.attach(part2)`.
|
||||||
|
* * Then `sink`'s object (`message`) local source is correlated to `server.sendmail`
|
||||||
|
* 3rd argument local source (`MIMEMultipart("alternative")`).
|
||||||
|
* * Then the flow from `source` to `sink` 1st argument is checked.
|
||||||
|
*/
|
||||||
|
bindingset[mimetype]
|
||||||
|
private DataFlow::Node getSmtpMessage(DataFlow::CallCfgNode sendCall, string mimetype) {
|
||||||
|
exists(DataFlow::Node source, DataFlow::Node sink |
|
||||||
|
source = mimeText(mimetype) and
|
||||||
|
(
|
||||||
|
// via _subparts
|
||||||
|
sink = smtpMimeMultipartInstance().getACall() and
|
||||||
|
sink =
|
||||||
|
[sendCall.getArg(2), sendCall.getArg(2).(DataFlow::MethodCallNode).getObject()]
|
||||||
|
.getALocalSource() and
|
||||||
|
any(SMTPMessageConfig a)
|
||||||
|
.hasFlow(source, sink.(DataFlow::CallCfgNode).getArgByName("_subparts"))
|
||||||
|
or
|
||||||
|
// via .attach()
|
||||||
|
sink = smtpMimeMultipartInstance().getReturn().getMember("attach").getACall() and
|
||||||
|
sink.(DataFlow::MethodCallNode).getObject().getALocalSource() =
|
||||||
|
[sendCall.getArg(2), sendCall.getArg(2).(DataFlow::MethodCallNode).getObject()]
|
||||||
|
.getALocalSource() and
|
||||||
|
source.(DataFlow::CallCfgNode).flowsTo(sink.(DataFlow::CallCfgNode).getArg(0))
|
||||||
|
) and
|
||||||
|
result = source.(DataFlow::CallCfgNode).getArg(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a message subscript write by correlating subscript's object local source with
|
||||||
|
* `smtp`'s `sendmail` call 3rd argument's local source.
|
||||||
|
*
|
||||||
|
* Given the following example with `getSMTPSubscriptByIndex(any(SmtpLibSendMail s), "Subject")`:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* message = MIMEMultipart("alternative")
|
||||||
|
* message["Subject"] = "multipart test"
|
||||||
|
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `def` would be `message["Subject"]` (`DefinitionNode`)
|
||||||
|
* * `sub` would be `message["Subject"]` (`Subscript`)
|
||||||
|
* * `result` would be `"multipart test"`
|
||||||
|
*/
|
||||||
|
private DataFlow::Node getSMTPSubscriptByIndex(DataFlow::CallCfgNode sendCall, string index) {
|
||||||
|
exists(DefinitionNode def, Subscript sub |
|
||||||
|
sub = def.getNode() and
|
||||||
|
DataFlow::exprNode(sub.getObject()).getALocalSource() =
|
||||||
|
[sendCall.getArg(2), sendCall.getArg(2).(DataFlow::MethodCallNode).getObject()]
|
||||||
|
.getALocalSource() and
|
||||||
|
sub.getIndex().(StrConst).getText() = index and
|
||||||
|
result.asCfgNode() = def.getValue()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a reference to `smtplib.SMTP_SSL().sendmail()`.
|
||||||
|
*
|
||||||
|
* Given the following example:
|
||||||
|
*
|
||||||
|
* ```py
|
||||||
|
* part1 = MIMEText(text, "plain")
|
||||||
|
* part2 = MIMEText(html, "html")
|
||||||
|
*
|
||||||
|
* message = MIMEMultipart(_subparts=(part1, part2))
|
||||||
|
* message["Subject"] = "multipart test"
|
||||||
|
* message["From"] = sender_email
|
||||||
|
* message["To"] = receiver_email
|
||||||
|
*
|
||||||
|
* server.login(sender_email, "SERVER_PASSWORD")
|
||||||
|
* server.sendmail(sender_email, receiver_email, message.as_string())
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* * `this` would be `server.sendmail(sender_email, receiver_email, message.as_string())`.
|
||||||
|
* * `getPlainTextBody()`'s result would be `text`.
|
||||||
|
* * `getHtmlBody()`'s result would be `html`.
|
||||||
|
* * `getTo()`'s result would be `receiver_email`.
|
||||||
|
* * `getFrom()`'s result would be `sender_email`.
|
||||||
|
* * `getSubject()`'s result would be `"multipart test"`.
|
||||||
|
*/
|
||||||
|
private class SmtpLibSendMail extends DataFlow::CallCfgNode, EmailSender::Range {
|
||||||
|
SmtpLibSendMail() {
|
||||||
|
this = smtpConnectionInstance().getReturn().getMember("sendmail").getACall()
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getPlainTextBody() { result = getSmtpMessage(this, "plain") }
|
||||||
|
|
||||||
|
override DataFlow::Node getHtmlBody() { result = getSmtpMessage(this, "html") }
|
||||||
|
|
||||||
|
override DataFlow::Node getTo() {
|
||||||
|
result in [this.getArg(1), getSMTPSubscriptByIndex(this, "To")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getFrom() {
|
||||||
|
result in [this.getArg(0), getSMTPSubscriptByIndex(this, "From")]
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Node getSubject() {
|
||||||
|
result in [this.getArg(2), getSMTPSubscriptByIndex(this, "Subject")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Provides a taint-tracking configuration for detecting reflected server-side
|
||||||
|
* cross-site scripting vulnerabilities.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import python
|
||||||
|
import semmle.python.dataflow.new.DataFlow
|
||||||
|
import semmle.python.dataflow.new.TaintTracking
|
||||||
|
import semmle.python.dataflow.new.RemoteFlowSources
|
||||||
|
import semmle.python.dataflow.new.BarrierGuards
|
||||||
|
import experimental.semmle.python.Concepts
|
||||||
|
import semmle.python.Concepts
|
||||||
|
import semmle.python.ApiGraphs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A taint-tracking configuration for detecting reflected server-side cross-site
|
||||||
|
* scripting vulnerabilities.
|
||||||
|
*/
|
||||||
|
class ReflectedXssConfiguration extends TaintTracking::Configuration {
|
||||||
|
ReflectedXssConfiguration() { this = "ReflectedXssConfiguration" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) { sink = any(EmailSender email).getHtmlBody() }
|
||||||
|
|
||||||
|
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||||
|
guard instanceof StringConstCompare
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||||
|
sanitizer = any(HtmlEscaping esc).getOutput()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||||
|
exists(DataFlow::CallCfgNode htmlContentCall |
|
||||||
|
htmlContentCall =
|
||||||
|
API::moduleImport("sendgrid")
|
||||||
|
.getMember("helpers")
|
||||||
|
.getMember("mail")
|
||||||
|
.getMember("HtmlContent")
|
||||||
|
.getACall() and
|
||||||
|
nodeTo = htmlContentCall and
|
||||||
|
nodeFrom = htmlContentCall.getArg(0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
edges
|
||||||
|
| flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:13:22:13:33 | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:13:22:13:33 | ControlFlowNode for Attribute | flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript |
|
||||||
|
| flask_mail.py:18:14:18:20 | ControlFlowNode for request | flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute | flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript |
|
||||||
|
| flask_mail.py:31:24:31:30 | ControlFlowNode for request | flask_mail.py:31:24:31:35 | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:31:24:31:35 | ControlFlowNode for Attribute | flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | sendgrid_mail.py:14:22:14:33 | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_mail.py:14:22:14:33 | ControlFlowNode for Attribute | sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute | sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript | sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() |
|
||||||
|
| sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute | sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| 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:41:50:41:61 | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute | sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:78 | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:78 | ControlFlowNode for Subscript | 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:27:50:27:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | 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:41:50:41:61 | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute | sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:76 | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:76 | ControlFlowNode for Subscript | 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:41:50:41:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:78 | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:78 | ControlFlowNode for Subscript | sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() |
|
||||||
|
| smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | smtplib_bad_subparts.py:17:12:17:23 | ControlFlowNode for Attribute |
|
||||||
|
| smtplib_bad_subparts.py:17:12:17:23 | ControlFlowNode for Attribute | smtplib_bad_subparts.py:17:12:17:33 | ControlFlowNode for Subscript |
|
||||||
|
| smtplib_bad_subparts.py:17:12:17:33 | ControlFlowNode for Subscript | smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html |
|
||||||
|
| smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | smtplib_bad_via_attach.py:20:12:20:23 | ControlFlowNode for Attribute |
|
||||||
|
| smtplib_bad_via_attach.py:20:12:20:23 | ControlFlowNode for Attribute | smtplib_bad_via_attach.py:20:12:20:31 | ControlFlowNode for Subscript |
|
||||||
|
| smtplib_bad_via_attach.py:20:12:20:31 | ControlFlowNode for Subscript | smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html |
|
||||||
|
nodes
|
||||||
|
| django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||||
|
| django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||||
|
| django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||||
|
| flask_mail.py:13:22:13:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| flask_mail.py:13:22:13:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| flask_mail.py:18:14:18:20 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| flask_mail.py:18:14:18:25 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| flask_mail.py:31:24:31:30 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| flask_mail.py:31:24:31:35 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| flask_mail.py:31:24:31:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_mail.py:14:22:14:28 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| sendgrid_mail.py:14:22:14:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_mail.py:14:22:14:49 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_mail.py:26:22:26:62 | ControlFlowNode for HtmlContent() | semmle.label | ControlFlowNode for HtmlContent() |
|
||||||
|
| sendgrid_mail.py:26:34:26:40 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| sendgrid_mail.py:26:34:26:45 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_mail.py:26:34:26:61 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_mail.py:37:41:37:47 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| sendgrid_mail.py:37:41:37:52 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_mail.py:37:41:37:68 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:16:26:16:79 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:57 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:62 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:16:51:16:78 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:27:25:27:77 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:56 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:61 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:27:50:27:76 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:61 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:78 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| smtplib_bad_subparts.py:17:12:17:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| smtplib_bad_subparts.py:17:12:17:33 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html | semmle.label | ControlFlowNode for html |
|
||||||
|
| smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
|
||||||
|
| smtplib_bad_via_attach.py:20:12:20:23 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
|
||||||
|
| smtplib_bad_via_attach.py:20:12:20:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
|
||||||
|
| smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html | semmle.label | ControlFlowNode for html |
|
||||||
|
subpaths
|
||||||
|
#select
|
||||||
|
| django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | django_mail.py:14:48:14:82 | ControlFlowNode for Attribute() | a user-provided value |
|
||||||
|
| django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | django_mail.py:23:30:23:64 | ControlFlowNode for Attribute() | a user-provided value |
|
||||||
|
| django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | django_mail.py:25:32:25:66 | ControlFlowNode for Attribute() | a user-provided value |
|
||||||
|
| flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript | flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:13:22:13:41 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:13:22:13:28 | ControlFlowNode for request | a user-provided value |
|
||||||
|
| flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | flask_mail.py:13:22:13:28 | ControlFlowNode for request | flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:13:22:13:28 | ControlFlowNode for request | a user-provided value |
|
||||||
|
| flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | flask_mail.py:18:14:18:20 | ControlFlowNode for request | flask_mail.py:18:14:18:33 | ControlFlowNode for Subscript | Cross-site scripting vulnerability due to $@. | flask_mail.py:18:14:18:20 | ControlFlowNode for request | a user-provided value |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| 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 |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41: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:41:25:41: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 |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | 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:41:25:41:79 | 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 |
|
||||||
|
| sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | sendgrid_via_mail_send_post_request_body_bad.py:41:25:41:79 | ControlFlowNode for Attribute() | Cross-site scripting vulnerability due to $@. | sendgrid_via_mail_send_post_request_body_bad.py:41:50:41:56 | ControlFlowNode for request | a user-provided value |
|
||||||
|
| smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html | smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | smtplib_bad_subparts.py:24:22:24:25 | ControlFlowNode for html | Cross-site scripting vulnerability due to $@. | smtplib_bad_subparts.py:17:12:17:18 | ControlFlowNode for request | a user-provided value |
|
||||||
|
| smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html | smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | smtplib_bad_via_attach.py:27:22:27:25 | ControlFlowNode for html | Cross-site scripting vulnerability due to $@. | smtplib_bad_via_attach.py:20:12:20:18 | ControlFlowNode for request | a user-provided value |
|
|
@ -0,0 +1 @@
|
||||||
|
experimental/Security/CWE-079/ReflectedXSS.ql
|
|
@ -0,0 +1,25 @@
|
||||||
|
import django.http
|
||||||
|
from django.core.mail import send_mail, mail_admins, mail_managers
|
||||||
|
|
||||||
|
|
||||||
|
def django_response(request):
|
||||||
|
"""
|
||||||
|
The Django.core.mail#send_mail function source code can be found in the link below:
|
||||||
|
https://github.com/django/django/blob/ca9872905559026af82000e46cde6f7dedc897b6/django/core/mail/__init__.py#L38
|
||||||
|
|
||||||
|
send_mass_mail does not provide html_message as an argument to it's function. See the link below for more info:
|
||||||
|
https://github.com/django/django/blob/ca9872905559026af82000e46cde6f7dedc897b6/django/core/mail/__init__.py#L64
|
||||||
|
"""
|
||||||
|
send_mail("Subject", "plain-text body", "from@example.com",
|
||||||
|
["to@example.com"], html_message=django.http.request.GET.get("html"))
|
||||||
|
|
||||||
|
|
||||||
|
def django_response(request):
|
||||||
|
"""
|
||||||
|
The Django.core.mail#mail_admins and Django.core.mail#mail_managers functions source code can be found in the link below:
|
||||||
|
https://github.com/django/django/blob/ca9872905559026af82000e46cde6f7dedc897b6/django/core/mail/__init__.py#L90-L121
|
||||||
|
"""
|
||||||
|
mail_admins("Subject", "plain-text body",
|
||||||
|
html_message=django.http.request.GET.get("html"))
|
||||||
|
mail_managers("Subject", "plain-text body",
|
||||||
|
html_message=django.http.request.GET.get("html"))
|
|
@ -0,0 +1,32 @@
|
||||||
|
from flask import request, Flask
|
||||||
|
from flask_mail import Mail, Message
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
mail = Mail(app)
|
||||||
|
|
||||||
|
@app.route("/send")
|
||||||
|
def send():
|
||||||
|
msg = Message(subject="Subject",
|
||||||
|
sender="from@example.com",
|
||||||
|
recipients=["to@example.com"],
|
||||||
|
body="plain-text body",
|
||||||
|
html=request.args["html"])
|
||||||
|
|
||||||
|
# The message can contain a body and/or HTML:
|
||||||
|
msg.body = "plain-text body"
|
||||||
|
# The email's HTML can be set via msg.html or as an initialize argument when creating a Message object.
|
||||||
|
msg.html = request.args["html"]
|
||||||
|
|
||||||
|
mail.send(msg)
|
||||||
|
|
||||||
|
@app.route("/connect")
|
||||||
|
def connect():
|
||||||
|
"""
|
||||||
|
Minimal example to test mail.connect() usage
|
||||||
|
"""
|
||||||
|
with mail.connect() as conn:
|
||||||
|
msg = Message(subject="Subject",
|
||||||
|
sender="from@example.com",
|
||||||
|
recipients=["to@example.com"],
|
||||||
|
html=request.args["html"])
|
||||||
|
conn.send(msg)
|
|
@ -0,0 +1,57 @@
|
||||||
|
from flask import request, Flask
|
||||||
|
from sendgrid import SendGridAPIClient
|
||||||
|
from sendgrid.helpers.mail import Mail, Email, To, Content, MimeType, HtmlContent
|
||||||
|
|
||||||
|
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-HtmlContent")
|
||||||
|
def send():
|
||||||
|
message = Mail(
|
||||||
|
from_email='from_email@example.com',
|
||||||
|
to_emails='to@example.com',
|
||||||
|
subject='Sending with Twilio SendGrid is Fun',
|
||||||
|
html_content=HtmlContent(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"
|
||||||
|
html_content = Content("text/html", request.args["html_content"])
|
||||||
|
plain_content = Content("text/plain", request.args["plain_content"])
|
||||||
|
|
||||||
|
mail = Mail(from_email, to_email, subject, plain_content, html_content)
|
||||||
|
|
||||||
|
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
|
||||||
|
response = sg.client.mail.send.post(request_body=mail.get())
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/send_post2")
|
||||||
|
def send_post2():
|
||||||
|
from_email = Email("test@example.com")
|
||||||
|
to_email = To("test@example.com")
|
||||||
|
subject = "Sending with SendGrid is Fun"
|
||||||
|
html_content = Content(MimeType.html, request.args["html_content"])
|
||||||
|
plain_content = Content(MimeType.text, request.args["plain_content"])
|
||||||
|
|
||||||
|
mail = Mail(from_email, to_email, subject, plain_content, html_content)
|
||||||
|
|
||||||
|
sg = SendGridAPIClient(api_key='SENDGRID_API_KEY')
|
||||||
|
response = sg.client.mail.send.post(request_body=mail.get())
|
|
@ -0,0 +1,48 @@
|
||||||
|
import sendgrid
|
||||||
|
import os
|
||||||
|
from flask import request, Flask
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/sendgrid")
|
||||||
|
def send():
|
||||||
|
sg = sendgrid.SendGridAPIClient(os.environ.get('SENDGRID_API_KEY'))
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"type": "text/html",
|
||||||
|
"value": "<html>{}</html>".format(request.args["html_content"])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"from": {
|
||||||
|
"email": "sam.smith@example.com",
|
||||||
|
"name": "Sam Smith"
|
||||||
|
},
|
||||||
|
"headers": {},
|
||||||
|
"mail_settings": {
|
||||||
|
"footer": {
|
||||||
|
"enable": True,
|
||||||
|
"html": "<html>{}</html>".format(request.args["html_footer"]),
|
||||||
|
"text": "Thanks,/n The SendGrid Team"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"reply_to": {
|
||||||
|
"email": "sam.smith@example.com",
|
||||||
|
"name": "Sam Smith"
|
||||||
|
},
|
||||||
|
"send_at": 1409348513,
|
||||||
|
"subject": "Hello, World!",
|
||||||
|
"template_id": "[YOUR TEMPLATE ID GOES HERE]",
|
||||||
|
"tracking_settings": {
|
||||||
|
"subscription_tracking": {
|
||||||
|
"enable": True,
|
||||||
|
"html": "<html>{}</html>".format(request.args["html_tracking"]),
|
||||||
|
"substitution_tag": "<%click here%>",
|
||||||
|
"text": "If you would like to unsubscribe and stop receiving these emails <% click here %>."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
response = sg.client.mail.send.post(request_body=data)
|
|
@ -0,0 +1,42 @@
|
||||||
|
# This test checks that the developer doesn't pass a MIMEText instance to a MIMEMultipart initializer via the subparts parameter.
|
||||||
|
from flask import Flask, request
|
||||||
|
import json
|
||||||
|
import smtplib
|
||||||
|
import ssl
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def email_person():
|
||||||
|
sender_email = "sender@gmail.com"
|
||||||
|
receiver_email = "receiver@example.com"
|
||||||
|
|
||||||
|
name = request.args['search']
|
||||||
|
# Create the plain-text and HTML version of your message
|
||||||
|
text = "hello there"
|
||||||
|
html = f"hello {name}"
|
||||||
|
|
||||||
|
# Turn these into plain/html MIMEText objects
|
||||||
|
part1 = MIMEText(text, "plain")
|
||||||
|
part2 = MIMEText(html, "html")
|
||||||
|
|
||||||
|
message = MIMEMultipart(_subparts=(part1, part2))
|
||||||
|
message["Subject"] = "multipart test"
|
||||||
|
message["From"] = sender_email
|
||||||
|
message["To"] = receiver_email
|
||||||
|
|
||||||
|
# Create secure connection with server and send email
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context)
|
||||||
|
|
||||||
|
server.login(sender_email, "SERVER_PASSWORD")
|
||||||
|
server.sendmail(
|
||||||
|
sender_email, receiver_email, message.as_string()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
# app.run(debug=True)
|
|
@ -0,0 +1,45 @@
|
||||||
|
# This test checks that the developer doesn't pass a MIMEText instance to a MIMEMultipart message.
|
||||||
|
from flask import Flask, request
|
||||||
|
import json
|
||||||
|
import smtplib, ssl
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def email_person():
|
||||||
|
sender_email = "sender@gmail.com"
|
||||||
|
receiver_email = "receiver@example.com"
|
||||||
|
|
||||||
|
message = MIMEMultipart("alternative")
|
||||||
|
message["Subject"] = "multipart test"
|
||||||
|
message["From"] = sender_email
|
||||||
|
message["To"] = receiver_email
|
||||||
|
|
||||||
|
name = request.args['name']
|
||||||
|
# Create the plain-text and HTML version of your message
|
||||||
|
text = "hello there"
|
||||||
|
html = f"hello {name}"
|
||||||
|
|
||||||
|
# Turn these into plain/html MIMEText objects
|
||||||
|
part1 = MIMEText(text, "plain")
|
||||||
|
part2 = MIMEText(html, "html")
|
||||||
|
|
||||||
|
# Add HTML/plain-text parts to MIMEMultipart message
|
||||||
|
# The email client will try to render the last part first
|
||||||
|
message.attach(part1)
|
||||||
|
message.attach(part2)
|
||||||
|
|
||||||
|
# Create secure connection with server and send email
|
||||||
|
context = ssl.create_default_context()
|
||||||
|
server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=context)
|
||||||
|
|
||||||
|
server.login(sender_email, "SERVER_PASSWORD")
|
||||||
|
server.sendmail(
|
||||||
|
sender_email, receiver_email, message.as_string()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# if __name__ == "__main__":
|
||||||
|
# app.run(debug=True)
|
Загрузка…
Ссылка в новой задаче