diff --git a/ql/src/experimental/CWE-640/EmailBad.go b/ql/src/experimental/CWE-640/EmailBad.go new file mode 100644 index 000000000..aab8467b3 --- /dev/null +++ b/ql/src/experimental/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/src/experimental/CWE-640/EmailGood.go b/ql/src/experimental/CWE-640/EmailGood.go new file mode 100644 index 000000000..d0cfc569b --- /dev/null +++ b/ql/src/experimental/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.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/src/experimental/CWE-640/EmailInjection.qhelp b/ql/src/experimental/CWE-640/EmailInjection.qhelp new file mode 100644 index 000000000..b1749fbb4 --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjection.qhelp @@ -0,0 +1,48 @@ + + + +

+ Using untrusted input to construct an email induces multiple security + vulnerabilities. For instance, inclusion of an untrusted input in a email body + may allow an attacker to conduct Cross Site Scripting (XSS) Attacks. While + inclusion of an HTTP Header in the email body may allow a full account + compromise as shown in the example below. +

+
+ +

+ Any data which is passed to an email subject or body must be sanitized before use. +

+
+ +

+ 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 his own web site. 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. +

+

+ If the email contains a password reset link, and should the victim click + 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. +

+ +

+ One way to prevent this is to load the host name from a trusted configuration file instead. +

+ +
+ +
  • + OWASP + Content Spoofing + . +
  • +
    +
    \ No newline at end of file diff --git a/ql/src/experimental/CWE-640/EmailInjection.ql b/ql/src/experimental/CWE-640/EmailInjection.ql new file mode 100644 index 000000000..7cf11fa1a --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjection.ql @@ -0,0 +1,19 @@ +/** + * @name Email content injection + * @description Incorporating untrusted input directly into an email message can enable + * content spoofing, which in turn may lead to information leaks and other + * security issues. + * @id go/email-injection + * @kind path-problem + * @problem.severity error + * @tags security + * external/cwe/cwe-640 + */ + +import go +import DataFlow::PathGraph +import EmailInjection::EmailInjection + +from DataFlow::PathNode source, DataFlow::PathNode sink, Configuration config +where config.hasFlowPath(source, sink) +select sink, source, sink, "Email content may contain $@.", source.getNode(), "untrusted input" diff --git a/ql/src/experimental/CWE-640/EmailInjection.qll b/ql/src/experimental/CWE-640/EmailInjection.qll new file mode 100644 index 000000000..4cf8b382c --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjection.qll @@ -0,0 +1,29 @@ +/** + * Provides a taint-tracking configuration for reasoning about + * server-side email-injection vulnerabilities. + * + * Note, for performance reasons: only import this file if + * `EmailInjection::Configuration` is needed, otherwise + * `EmailInjectionCustomizations` should be imported instead. + */ + +import go + +/** + * Provides a taint-tracking configuration for reasoning about + * email-injection vulnerabilities. + */ +module EmailInjection { + import EmailInjectionCustomizations::EmailInjection + + /** + * A taint-tracking configuration for reasoning about email-injection vulnerabilities. + */ + class Configuration extends TaintTracking::Configuration { + Configuration() { this = "Email Injection" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + } +} diff --git a/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll b/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll new file mode 100644 index 000000000..104a5111c --- /dev/null +++ b/ql/src/experimental/CWE-640/EmailInjectionCustomizations.qll @@ -0,0 +1,30 @@ +/** Provides classes for reasoning about email-injection vulnerabilities. */ + +import go + +/** + * Provides a library for reasoning about email-injection vulnerabilities. + */ +module EmailInjection { + /** + * A data-flow node that should be considered a source of untrusted data for email-injection vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data-flow node that should be considered a sink for email-injection vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** A source of untrusted data, considered as a taint source for email injection. */ + class UntrustedFlowSourceAsSource extends Source { + UntrustedFlowSourceAsSource() { this instanceof UntrustedFlowSource } + } + + /** + * A data-flow node that becomes part of an email considered as a taint sink for email injection. + */ + class MailDataAsSink extends Sink { + MailDataAsSink() { this instanceof MailData } + } +} diff --git a/ql/src/go.qll b/ql/src/go.qll index f4f459f2e..aacfa7935 100644 --- a/ql/src/go.qll +++ b/ql/src/go.qll @@ -24,6 +24,7 @@ import semmle.go.dataflow.DataFlow import semmle.go.dataflow.GlobalValueNumbering import semmle.go.dataflow.TaintTracking import semmle.go.dataflow.SSA +import semmle.go.frameworks.Email import semmle.go.frameworks.HTTP import semmle.go.frameworks.SystemCommandExecutors import semmle.go.frameworks.SQL diff --git a/ql/src/semmle/go/frameworks/Email.qll b/ql/src/semmle/go/frameworks/Email.qll new file mode 100644 index 000000000..7041cf86b --- /dev/null +++ b/ql/src/semmle/go/frameworks/Email.qll @@ -0,0 +1,110 @@ +/** Provides classes for working with email-related APIs. */ + +import go + +/** + * A data-flow node that represents data written to an email. + * Data in this case includes the email headers and the mail body + * + * Extend this class to refine existing API models. If you want to model new APIs, + * extend `MailDataCall::Range` instead. + */ +class MailData extends DataFlow::Node { + MailDataCall::Range self; + + MailData() { this = self.getData() } +} + +/** Provides classes for working with calls which write data to an email. */ +module MailDataCall { + /** + * A data-flow node that represents a call which writes data to an email. + * Data in this case refers to email headers and the mail body + * + */ + abstract class Range extends DataFlow::CallNode { + /** Gets data written to an email connection. */ + abstract DataFlow::Node getData(); + } + + /** Get the package name `github.com/sendgrid/sendgrid-go/helpers/mail`. */ + bindingset[result] + private string sendgridMail() { result = "github.com/sendgrid/sendgrid-go/helpers/mail" } + + /** A Client.Data expression string used in an API function of the net/smtp package. */ + private class SmtpData extends Range { + SmtpData() { + // func (c *Client) Data() (io.WriteCloser, error) + this.getTarget().(Method).hasQualifiedName("net/smtp", "Client", "Data") + } + + override DataFlow::Node getData() { + exists(DataFlow::CallNode write, DataFlow::Node writer, int i | + this.getResult(0) = writer and + ( + write.getTarget().hasQualifiedName("fmt", "Fprintf") + or + write.getTarget().hasQualifiedName("io", "WriteString") + ) and + writer.getASuccessor*() = write.getArgument(0) and + i > 0 and + write.getArgument(i) = result + ) + } + } + + /** A send mail expression string used in an API function of the net/smtp package. */ + private class SmtpSendMail extends Range { + SmtpSendMail() { + // func SendMail(addr string, a Auth, from string, to []string, msg []byte) error + this.getTarget().hasQualifiedName("net/smtp", "SendMail") + } + + override DataFlow::Node getData() { result = this.getArgument(4) } + } + + /** A call to `NewSingleEmail` API function of the Sendgrid mail package. */ + private class SendGridSingleEmail extends Range { + SendGridSingleEmail() { + // func NewSingleEmail(from *Email, subject string, to *Email, plainTextContent string, htmlContent string) *SGMailV3 + this.getTarget().hasQualifiedName(sendgridMail(), "NewSingleEmail") + } + + override DataFlow::Node getData() { result = this.getArgument([1, 3, 4]) } + } + + /* Gets the value of the `i`-th content parameter of the given `call` */ + private DataFlow::Node getContent(DataFlow::CallNode call, int i) { + exists(DataFlow::CallNode cn, DataFlow::Node content | + // func NewContent(contentType string, value string) *Content + cn.getTarget().hasQualifiedName(sendgridMail(), "NewContent") and + cn.getResult() = content and + content.getASuccessor*() = call.getArgument(i) and + result = cn.getArgument(1) + ) + } + + /** A call to `NewV3MailInit` API function of the Sendgrid mail package. */ + private class SendGridV3Init extends Range { + SendGridV3Init() { + // func NewV3MailInit(from *Email, subject string, to *Email, content ...*Content) *SGMailV3 + this.getTarget().hasQualifiedName(sendgridMail(), "NewV3MailInit") + } + + override DataFlow::Node getData() { + exists(int i | result = getContent(this, i) and i >= 3) + or + result = this.getArgument(1) + } + } + + /** A call to `AddContent` API function of the Sendgrid mail package. */ + private class SendGridAddContent extends Range { + SendGridAddContent() { + // func (s *SGMailV3) AddContent(c ...*Content) *SGMailV3 + this.getTarget().(Method).hasQualifiedName(sendgridMail(), "SGMailV3", "AddContent") + } + + override DataFlow::Node getData() { result = getContent(this, _) } + } +} diff --git a/ql/test/experimental/CWE-640/EmailInjection.expected b/ql/test/experimental/CWE-640/EmailInjection.expected new file mode 100644 index 000000000..7d7e2988d --- /dev/null +++ b/ql/test/experimental/CWE-640/EmailInjection.expected @@ -0,0 +1,37 @@ +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:46:25:46:38 | untrustedInput | +| 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:65:47:65:60 | untrustedInput | +| email.go:73:21:73:31 | call to Referer : string | email.go:79:47:79:60 | untrustedInput | +| 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:96:48:96:61 | untrustedInput | +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:46:25:46:38 | untrustedInput | semmle.label | untrustedInput | +| 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:65:47:65:60 | untrustedInput | semmle.label | untrustedInput | +| email.go:73:21:73:31 | call to Referer : string | semmle.label | call to Referer : string | +| email.go:79:47:79:60 | untrustedInput | semmle.label | untrustedInput | +| 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:96:48:96:61 | untrustedInput | semmle.label | untrustedInput | +#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:46:25:46:38 | untrustedInput | email.go:42:21:42:31 | call to Referer : string | email.go:46:25:46:38 | untrustedInput | 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:65:47:65:60 | untrustedInput | email.go:63:21:63:31 | call to Referer : string | email.go:65:47:65:60 | untrustedInput | Email content may contain $@. | email.go:63:21:63:31 | call to Referer | untrusted input | +| email.go:79:47:79:60 | untrustedInput | email.go:73:21:73:31 | call to Referer : string | email.go:79:47:79:60 | untrustedInput | 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:96:48:96:61 | untrustedInput | email.go:87:21:87:31 | call to Referer : string | email.go:96:48:96:61 | untrustedInput | Email content may contain $@. | email.go:87:21:87:31 | call to Referer | untrusted input | diff --git a/ql/test/experimental/CWE-640/EmailInjection.qlref b/ql/test/experimental/CWE-640/EmailInjection.qlref new file mode 100644 index 000000000..a6d8abad1 --- /dev/null +++ b/ql/test/experimental/CWE-640/EmailInjection.qlref @@ -0,0 +1 @@ +experimental/CWE-640/EmailInjection.ql \ No newline at end of file diff --git a/ql/test/experimental/CWE-640/email.go b/ql/test/experimental/CWE-640/email.go new file mode 100644 index 000000000..44a54b59f --- /dev/null +++ b/ql/test/experimental/CWE-640/email.go @@ -0,0 +1,115 @@ +package main + +//go:generate depstubber -vendor github.com/sendgrid/sendgrid-go/helpers/mail "" NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit + +import ( + "io" + "log" + "net/http" + "net/smtp" + + 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() { + + // Not OK + http.HandleFunc("/ex0", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(untrustedInput)) + + }) + + // Not OK + http.HandleFunc("/ex1", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + s, _ := smtp.Dial("test.test") + write, _ := s.Data() + io.WriteString(write, untrustedInput) + }) + + // Not OK + http.HandleFunc("/ex2", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + subject := "test" + body := "body" + sendgrid.NewSingleEmail(from, subject, to, untrustedInput, body) + sendgrid.NewSingleEmail(from, subject, to, body, untrustedInput) + }) + + // Not OK + http.HandleFunc("/ex3", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + content := sendgrid.NewContent("text/html", untrustedInput) + + v := sendgrid.NewV3Mail() + v.AddContent(content) + }) + + // Not OK + http.HandleFunc("/ex4", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + subject := "test" + + content := sendgrid.NewContent("text/html", untrustedInput) + + v := sendgrid.NewV3MailInit(from, subject, to, content, content) + v.AddContent(content) + }) + + // Not OK + http.HandleFunc("/ex5", func(w http.ResponseWriter, r *http.Request) { + untrustedInput := r.Referer() + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + + content := sendgrid.NewContent("text/html", "test") + + v := sendgrid.NewV3MailInit(from, untrustedInput, to, content, content) + + content2 := sendgrid.NewContent("text/html", untrustedInput) + + v.AddContent(content2) + }) + + log.Println(http.ListenAndServe(":80", nil)) + +} + +// Backend is an empty struct +type Backend struct{} + +func (*Backend) getUserSecretResetToken(email string) string { + return "" +} + +var email = "test@test.com" + +var config map[string]string +var backend = &Backend{} diff --git a/ql/test/experimental/CWE-640/go.mod b/ql/test/experimental/CWE-640/go.mod new file mode 100644 index 000000000..979044b6b --- /dev/null +++ b/ql/test/experimental/CWE-640/go.mod @@ -0,0 +1,7 @@ +module main + +go 1.14 + +require ( + github.com/sendgrid/sendgrid-go v3.5.0+incompatible +) diff --git a/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE new file mode 100644 index 000000000..e5441aa6a --- /dev/null +++ b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2019 Twilio SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go new file mode 100644 index 000000000..cd1956cec --- /dev/null +++ b/ql/test/experimental/CWE-640/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go @@ -0,0 +1,391 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/sendgrid/sendgrid-go/helpers/mail, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/sendgrid/sendgrid-go/helpers/mail (exports: ; functions: NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit) + +// Package mail is a stub of github.com/sendgrid/sendgrid-go/helpers/mail, generated by depstubber. +package mail + +import () + +type Asm struct { + GroupID int + GroupsToDisplay []int +} + +func (_ *Asm) AddGroupsToDisplay(_ ...int) *Asm { + return nil +} + +func (_ *Asm) SetGroupID(_ int) *Asm { + return nil +} + +type Attachment struct { + Content string + Type string + Name string + Filename string + Disposition string + ContentID string +} + +func (_ *Attachment) SetContent(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetContentID(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetDisposition(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetFilename(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetType(_ string) *Attachment { + return nil +} + +type BccSetting struct { + Enable *bool + Email string +} + +func (_ *BccSetting) SetEmail(_ string) *BccSetting { + return nil +} + +func (_ *BccSetting) SetEnable(_ bool) *BccSetting { + return nil +} + +type ClickTrackingSetting struct { + Enable *bool + EnableText *bool +} + +func (_ *ClickTrackingSetting) SetEnable(_ bool) *ClickTrackingSetting { + return nil +} + +func (_ *ClickTrackingSetting) SetEnableText(_ bool) *ClickTrackingSetting { + return nil +} + +type Content struct { + Type string + Value string +} + +type Email struct { + Name string + Address string +} + +type FooterSetting struct { + Enable *bool + Text string + Html string +} + +func (_ *FooterSetting) SetEnable(_ bool) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetHTML(_ string) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetText(_ string) *FooterSetting { + return nil +} + +type GaSetting struct { + Enable *bool + CampaignSource string + CampaignTerm string + CampaignContent string + CampaignName string + CampaignMedium string +} + +func (_ *GaSetting) SetCampaignContent(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignMedium(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignName(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignSource(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignTerm(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetEnable(_ bool) *GaSetting { + return nil +} + +type MailSettings struct { + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *Setting + SpamCheckSetting *SpamCheckSetting +} + +func (_ *MailSettings) SetBCC(_ *BccSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetBypassListManagement(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetFooter(_ *FooterSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSandboxMode(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSpamCheckSettings(_ *SpamCheckSetting) *MailSettings { + return nil +} + +func NewContent(_ string, _ string) *Content { + return nil +} + +func NewEmail(_ string, _ string) *Email { + return nil +} + +func NewSingleEmail(_ *Email, _ string, _ *Email, _ string, _ string) *SGMailV3 { + return nil +} + +func NewV3Mail() *SGMailV3 { + return nil +} + +func NewV3MailInit(_ *Email, _ string, _ *Email, _ ...*Content) *SGMailV3 { + return nil +} + +type OpenTrackingSetting struct { + Enable *bool + SubstitutionTag string +} + +func (_ *OpenTrackingSetting) SetEnable(_ bool) *OpenTrackingSetting { + return nil +} + +func (_ *OpenTrackingSetting) SetSubstitutionTag(_ string) *OpenTrackingSetting { + return nil +} + +type Personalization struct { + To []*Email + CC []*Email + BCC []*Email + Subject string + Headers map[string]string + Substitutions map[string]string + CustomArgs map[string]string + DynamicTemplateData map[string]interface{} + Categories []string + SendAt int +} + +func (_ *Personalization) AddBCCs(_ ...*Email) {} + +func (_ *Personalization) AddCCs(_ ...*Email) {} + +func (_ *Personalization) AddTos(_ ...*Email) {} + +func (_ *Personalization) SetCustomArg(_ string, _ string) {} + +func (_ *Personalization) SetDynamicTemplateData(_ string, _ interface{}) {} + +func (_ *Personalization) SetHeader(_ string, _ string) {} + +func (_ *Personalization) SetSendAt(_ int) {} + +func (_ *Personalization) SetSubstitution(_ string, _ string) {} + +type SGMailV3 struct { + From *Email + Subject string + Personalizations []*Personalization + Content []*Content + Attachments []*Attachment + TemplateID string + Sections map[string]string + Headers map[string]string + Categories []string + CustomArgs map[string]string + SendAt int + BatchID string + Asm *Asm + IPPoolID string + MailSettings *MailSettings + TrackingSettings *TrackingSettings + ReplyTo *Email +} + +func (_ *SGMailV3) AddAttachment(_ ...*Attachment) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddCategories(_ ...string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddContent(_ ...*Content) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddPersonalizations(_ ...*Personalization) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddSection(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetASM(_ *Asm) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetBatchID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetCustomArg(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetFrom(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetHeader(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetIPPoolID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetMailSettings(_ *MailSettings) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetReplyTo(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetSendAt(_ int) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTemplateID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTrackingSettings(_ *TrackingSettings) *SGMailV3 { + return nil +} + +type SandboxModeSetting struct { + Enable *bool + ForwardSpam *bool + SpamCheck *SpamCheckSetting +} + +type Setting struct { + Enable *bool +} + +type SpamCheckSetting struct { + Enable *bool + SpamThreshold int + PostToURL string +} + +func (_ *SpamCheckSetting) SetEnable(_ bool) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetPostToURL(_ string) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetSpamThreshold(_ int) *SpamCheckSetting { + return nil +} + +type SubscriptionTrackingSetting struct { + Enable *bool + Text string + Html string + SubstitutionTag string +} + +func (_ *SubscriptionTrackingSetting) SetEnable(_ bool) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetHTML(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetSubstitutionTag(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetText(_ string) *SubscriptionTrackingSetting { + return nil +} + +type TrackingSettings struct { + ClickTracking *ClickTrackingSetting + OpenTracking *OpenTrackingSetting + SubscriptionTracking *SubscriptionTrackingSetting + GoogleAnalytics *GaSetting + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *SandboxModeSetting +} + +func (_ *TrackingSettings) SetClickTracking(_ *ClickTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetGoogleAnalytics(_ *GaSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetOpenTracking(_ *OpenTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetSubscriptionTracking(_ *SubscriptionTrackingSetting) *TrackingSettings { + return nil +} diff --git a/ql/test/experimental/CWE-640/vendor/modules.txt b/ql/test/experimental/CWE-640/vendor/modules.txt new file mode 100644 index 000000000..4b7525957 --- /dev/null +++ b/ql/test/experimental/CWE-640/vendor/modules.txt @@ -0,0 +1,3 @@ +# github.com/sendgrid/sendgrid-go v3.5.0+incompatible +## explicit +github.com/sendgrid/sendgrid-go diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/MailData.expected b/ql/test/library-tests/semmle/go/frameworks/Email/MailData.expected new file mode 100644 index 000000000..9e9801a28 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/MailData.expected @@ -0,0 +1,9 @@ +| mail.go:16:56:16:77 | type conversion | +| mail.go:22:24:22:37 | untrustedInput | +| mail.go:29:32:29:36 | alert | +| mail.go:29:43:29:47 | alert | +| mail.go:29:50:29:54 | alert | +| mail.go:32:46:32:50 | alert | +| mail.go:36:47:36:51 | alert | +| mail.go:37:47:37:51 | alert | +| mail.go:40:35:40:39 | alert | diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/MailData.ql b/ql/test/library-tests/semmle/go/frameworks/Email/MailData.ql new file mode 100644 index 000000000..4a630d2a9 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/MailData.ql @@ -0,0 +1,4 @@ +import go + +from MailData f +select f diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/go.mod b/ql/test/library-tests/semmle/go/frameworks/Email/go.mod new file mode 100644 index 000000000..d3b7fdb67 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/go.mod @@ -0,0 +1,8 @@ +module main + +go 1.14 + +require ( + github.com/sendgrid/sendgrid-go v3.5.0+incompatible + github.com/stretchr/testify v1.5.1 // indirect +) diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/mail.go b/ql/test/library-tests/semmle/go/frameworks/Email/mail.go new file mode 100644 index 000000000..823ec92ac --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/mail.go @@ -0,0 +1,42 @@ +package main + +//go:generate depstubber -vendor github.com/sendgrid/sendgrid-go/helpers/mail "" NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit + +import ( + "io" + "net/smtp" + + sendgrid "github.com/sendgrid/sendgrid-go/helpers/mail" +) + +func main() { + untrustedInput := "test" + + // Not OK - 1 alert + smtp.SendMail("test.test", nil, "from@from.com", nil, []byte(untrustedInput)) + + s, _ := smtp.Dial("test.test") + write, _ := s.Data() + + // Not OK - 1 alert + io.WriteString(write, untrustedInput) + + from := sendgrid.NewEmail("from", "from@from.com") + to := sendgrid.NewEmail("to", "to@to.com") + alert := "sub" + + // Not OK - 3 alerts + sendgrid.NewSingleEmail(from, alert, to, alert, alert) + + // Not OK - 1 alert + content := sendgrid.NewContent("text/html", alert) + v := sendgrid.NewV3Mail() + v.AddContent(content) + + content2 := sendgrid.NewContent("text/html", alert) + content3 := sendgrid.NewContent("text/html", alert) + + // Not OK - 3 alerts + v = sendgrid.NewV3MailInit(from, alert, to, content2, content3) + +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/LICENSE b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/LICENSE new file mode 100644 index 000000000..e5441aa6a --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2019 Twilio SendGrid, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go new file mode 100644 index 000000000..cd1956cec --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/github.com/sendgrid/sendgrid-go/helpers/mail/stub.go @@ -0,0 +1,391 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/sendgrid/sendgrid-go/helpers/mail, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/sendgrid/sendgrid-go/helpers/mail (exports: ; functions: NewEmail,NewSingleEmail,NewContent,NewV3Mail,NewV3MailInit) + +// Package mail is a stub of github.com/sendgrid/sendgrid-go/helpers/mail, generated by depstubber. +package mail + +import () + +type Asm struct { + GroupID int + GroupsToDisplay []int +} + +func (_ *Asm) AddGroupsToDisplay(_ ...int) *Asm { + return nil +} + +func (_ *Asm) SetGroupID(_ int) *Asm { + return nil +} + +type Attachment struct { + Content string + Type string + Name string + Filename string + Disposition string + ContentID string +} + +func (_ *Attachment) SetContent(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetContentID(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetDisposition(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetFilename(_ string) *Attachment { + return nil +} + +func (_ *Attachment) SetType(_ string) *Attachment { + return nil +} + +type BccSetting struct { + Enable *bool + Email string +} + +func (_ *BccSetting) SetEmail(_ string) *BccSetting { + return nil +} + +func (_ *BccSetting) SetEnable(_ bool) *BccSetting { + return nil +} + +type ClickTrackingSetting struct { + Enable *bool + EnableText *bool +} + +func (_ *ClickTrackingSetting) SetEnable(_ bool) *ClickTrackingSetting { + return nil +} + +func (_ *ClickTrackingSetting) SetEnableText(_ bool) *ClickTrackingSetting { + return nil +} + +type Content struct { + Type string + Value string +} + +type Email struct { + Name string + Address string +} + +type FooterSetting struct { + Enable *bool + Text string + Html string +} + +func (_ *FooterSetting) SetEnable(_ bool) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetHTML(_ string) *FooterSetting { + return nil +} + +func (_ *FooterSetting) SetText(_ string) *FooterSetting { + return nil +} + +type GaSetting struct { + Enable *bool + CampaignSource string + CampaignTerm string + CampaignContent string + CampaignName string + CampaignMedium string +} + +func (_ *GaSetting) SetCampaignContent(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignMedium(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignName(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignSource(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetCampaignTerm(_ string) *GaSetting { + return nil +} + +func (_ *GaSetting) SetEnable(_ bool) *GaSetting { + return nil +} + +type MailSettings struct { + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *Setting + SpamCheckSetting *SpamCheckSetting +} + +func (_ *MailSettings) SetBCC(_ *BccSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetBypassListManagement(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetFooter(_ *FooterSetting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSandboxMode(_ *Setting) *MailSettings { + return nil +} + +func (_ *MailSettings) SetSpamCheckSettings(_ *SpamCheckSetting) *MailSettings { + return nil +} + +func NewContent(_ string, _ string) *Content { + return nil +} + +func NewEmail(_ string, _ string) *Email { + return nil +} + +func NewSingleEmail(_ *Email, _ string, _ *Email, _ string, _ string) *SGMailV3 { + return nil +} + +func NewV3Mail() *SGMailV3 { + return nil +} + +func NewV3MailInit(_ *Email, _ string, _ *Email, _ ...*Content) *SGMailV3 { + return nil +} + +type OpenTrackingSetting struct { + Enable *bool + SubstitutionTag string +} + +func (_ *OpenTrackingSetting) SetEnable(_ bool) *OpenTrackingSetting { + return nil +} + +func (_ *OpenTrackingSetting) SetSubstitutionTag(_ string) *OpenTrackingSetting { + return nil +} + +type Personalization struct { + To []*Email + CC []*Email + BCC []*Email + Subject string + Headers map[string]string + Substitutions map[string]string + CustomArgs map[string]string + DynamicTemplateData map[string]interface{} + Categories []string + SendAt int +} + +func (_ *Personalization) AddBCCs(_ ...*Email) {} + +func (_ *Personalization) AddCCs(_ ...*Email) {} + +func (_ *Personalization) AddTos(_ ...*Email) {} + +func (_ *Personalization) SetCustomArg(_ string, _ string) {} + +func (_ *Personalization) SetDynamicTemplateData(_ string, _ interface{}) {} + +func (_ *Personalization) SetHeader(_ string, _ string) {} + +func (_ *Personalization) SetSendAt(_ int) {} + +func (_ *Personalization) SetSubstitution(_ string, _ string) {} + +type SGMailV3 struct { + From *Email + Subject string + Personalizations []*Personalization + Content []*Content + Attachments []*Attachment + TemplateID string + Sections map[string]string + Headers map[string]string + Categories []string + CustomArgs map[string]string + SendAt int + BatchID string + Asm *Asm + IPPoolID string + MailSettings *MailSettings + TrackingSettings *TrackingSettings + ReplyTo *Email +} + +func (_ *SGMailV3) AddAttachment(_ ...*Attachment) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddCategories(_ ...string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddContent(_ ...*Content) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddPersonalizations(_ ...*Personalization) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) AddSection(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetASM(_ *Asm) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetBatchID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetCustomArg(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetFrom(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetHeader(_ string, _ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetIPPoolID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetMailSettings(_ *MailSettings) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetReplyTo(_ *Email) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetSendAt(_ int) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTemplateID(_ string) *SGMailV3 { + return nil +} + +func (_ *SGMailV3) SetTrackingSettings(_ *TrackingSettings) *SGMailV3 { + return nil +} + +type SandboxModeSetting struct { + Enable *bool + ForwardSpam *bool + SpamCheck *SpamCheckSetting +} + +type Setting struct { + Enable *bool +} + +type SpamCheckSetting struct { + Enable *bool + SpamThreshold int + PostToURL string +} + +func (_ *SpamCheckSetting) SetEnable(_ bool) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetPostToURL(_ string) *SpamCheckSetting { + return nil +} + +func (_ *SpamCheckSetting) SetSpamThreshold(_ int) *SpamCheckSetting { + return nil +} + +type SubscriptionTrackingSetting struct { + Enable *bool + Text string + Html string + SubstitutionTag string +} + +func (_ *SubscriptionTrackingSetting) SetEnable(_ bool) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetHTML(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetSubstitutionTag(_ string) *SubscriptionTrackingSetting { + return nil +} + +func (_ *SubscriptionTrackingSetting) SetText(_ string) *SubscriptionTrackingSetting { + return nil +} + +type TrackingSettings struct { + ClickTracking *ClickTrackingSetting + OpenTracking *OpenTrackingSetting + SubscriptionTracking *SubscriptionTrackingSetting + GoogleAnalytics *GaSetting + BCC *BccSetting + BypassListManagement *Setting + Footer *FooterSetting + SandboxMode *SandboxModeSetting +} + +func (_ *TrackingSettings) SetClickTracking(_ *ClickTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetGoogleAnalytics(_ *GaSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetOpenTracking(_ *OpenTrackingSetting) *TrackingSettings { + return nil +} + +func (_ *TrackingSettings) SetSubscriptionTracking(_ *SubscriptionTrackingSetting) *TrackingSettings { + return nil +} diff --git a/ql/test/library-tests/semmle/go/frameworks/Email/vendor/modules.txt b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/modules.txt new file mode 100644 index 000000000..d782a4cc2 --- /dev/null +++ b/ql/test/library-tests/semmle/go/frameworks/Email/vendor/modules.txt @@ -0,0 +1,6 @@ +# github.com/sendgrid/sendgrid-go v3.5.0+incompatible +## explicit +github.com/sendgrid/sendgrid-go +# github.com/stretchr/testify v1.5.1 +## explicit +github.com/stretchr/testify