mirror of
https://github.com/github/codeql.git
synced 2026-01-29 22:32:58 +01:00
Merge pull request #259 from gagliardetto/oauth2-fixed-state
CWE-352: Use of constant `state` in Oauth2 flow
This commit is contained in:
26
ql/src/experimental/CWE-352/ConstantOauth2State.qhelp
Normal file
26
ql/src/experimental/CWE-352/ConstantOauth2State.qhelp
Normal file
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
OAuth 2.0 clients must implement CSRF protection for the redirection URI, which is typically accomplished by including a "state" value that binds the request to
|
||||
the user's authenticated state. The Go OAuth 2.0 library allows to specify a "state" value which is then included in the auth code URL, and then provided back by the remote authentication server in the redirect callback, from where it must be validated; failure to do so makes the client susceptible to an CSRF attack.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Always include a unique, non-guessable <code>state</code> value (provided to the call to <code>AuthCodeURL</code> function) that is also bound to the user's authenticated state with each authentication request, and then validated in the redirect callback.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
The first example shows you the use of a constant state (bad).
|
||||
</p>
|
||||
<sample src="ConstantOauth2StateBad.go" />
|
||||
<p>
|
||||
The second example shows a better implementation idea.
|
||||
</p>
|
||||
<sample src="ConstantOauth2StateBetter.go" />
|
||||
</example>
|
||||
</qhelp>
|
||||
101
ql/src/experimental/CWE-352/ConstantOauth2State.ql
Normal file
101
ql/src/experimental/CWE-352/ConstantOauth2State.ql
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @name Use of constant `state` value in OAuth 2.0 URL.
|
||||
* @description Using a constant value for the `state` in the OAuth 2.0 URL makes the application
|
||||
* susceptible to CSRF attacks.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @id go/constant-oauth2-state
|
||||
* @tags security
|
||||
* external/cwe/cwe-352
|
||||
*/
|
||||
|
||||
import go
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A method that creates a new URL that will send the user
|
||||
* to the OAuth 2.0 authorization dialog of the provider.
|
||||
*/
|
||||
class AuthCodeURL extends Method {
|
||||
AuthCodeURL() { this.hasQualifiedName("golang.org/x/oauth2", "Config", "AuthCodeURL") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A flow of a constant string value to a call to AuthCodeURL as the
|
||||
* `state` parameter.
|
||||
*/
|
||||
class ConstantStateFlowConf extends DataFlow::Configuration {
|
||||
ConstantStateFlowConf() { this = "ConstantStateFlowConf" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, Literal state) {
|
||||
state.isConst() and source.asExpr() = state
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
exists(AuthCodeURL m | call = m.getACall() | sink = call.getArgument(0))
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
}
|
||||
|
||||
/** A flow to a printer function of the fmt package. */
|
||||
class FlowToPrint extends DataFlow::Configuration {
|
||||
FlowToPrint() { this = "FlowToPrint" }
|
||||
|
||||
predicate isSource(DataFlow::Node source, DataFlow::CallNode call) {
|
||||
exists(AuthCodeURL m | call = m.getACall() | source = call.getResult())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, DataFlow::CallNode call) {
|
||||
exists(Fmt::Printer printer | call = printer.getACall() | sink = call.getArgument(_))
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
}
|
||||
|
||||
/** Holds if the provided CallNode's result flows to a Printer call as argument. */
|
||||
predicate resultFlowsToPrinter(DataFlow::CallNode authCodeURLCall) {
|
||||
exists(FlowToPrint cfg, DataFlow::PathNode source, DataFlow::PathNode sink |
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSource(source.getNode(), authCodeURLCall)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the provided CallNode is within the same root as a call
|
||||
* to a scanner that reads from os.Stdin.
|
||||
*/
|
||||
predicate rootContainsCallToStdinScanner(DataFlow::CallNode authCodeURLCall) {
|
||||
exists(Fmt::ScannerCall scannerCall | scannerCall.getRoot() = authCodeURLCall.getRoot())
|
||||
or
|
||||
exists(Fmt::FScannerCall fScannerCall |
|
||||
fScannerCall.getReader() = any(ValueEntity v | v.hasQualifiedName("os", "Stdin")).getARead() and
|
||||
fScannerCall.getRoot() = authCodeURLCall.getRoot()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the authCodeURLCall seems to be done within a terminal
|
||||
* because there are calls to a Printer (fmt.Println and similar),
|
||||
* and a call to a Scanner (fmt.Scan and similar),
|
||||
* all of which are typically done within a terminal session.
|
||||
*/
|
||||
predicate seemsLikeDoneWithinATerminal(DataFlow::CallNode authCodeURLCall) {
|
||||
resultFlowsToPrinter(authCodeURLCall) and
|
||||
rootContainsCallToStdinScanner(authCodeURLCall)
|
||||
}
|
||||
|
||||
from
|
||||
ConstantStateFlowConf cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
DataFlow::CallNode sinkCall
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
cfg.isSink(sink.getNode(), sinkCall) and
|
||||
// Exclude cases that seem to be oauth flows done from within a terminal:
|
||||
not seemsLikeDoneWithinATerminal(sinkCall)
|
||||
select sink.getNode(), source, sink, "Using a constant $@ to create oauth2 URLs.", source.getNode(),
|
||||
"state string"
|
||||
24
ql/src/experimental/CWE-352/ConstantOauth2StateBad.go
Normal file
24
ql/src/experimental/CWE-352/ConstantOauth2StateBad.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
|
||||
var stateStringVar = "state"
|
||||
|
||||
func badWithStringLiteralState() {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL(stateStringVar)
|
||||
// ...
|
||||
}
|
||||
35
ql/src/experimental/CWE-352/ConstantOauth2StateBetter.go
Normal file
35
ql/src/experimental/CWE-352/ConstantOauth2StateBetter.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func betterWithVariableStateReturned(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
state := generateStateOauthCookie(w)
|
||||
url := conf.AuthCodeURL(state)
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
func generateStateOauthCookie(w http.ResponseWriter) string {
|
||||
b := make([]byte, 128)
|
||||
rand.Read(b)
|
||||
// TODO: save the state string to cookies or HTML storage,
|
||||
// and bind it to the authenticated status of the user.
|
||||
state := base64.URLEncoding.EncodeToString(b)
|
||||
|
||||
return state
|
||||
}
|
||||
@@ -94,7 +94,7 @@ module Fmt {
|
||||
}
|
||||
|
||||
/** The `Print` function or one of its variants. */
|
||||
private class Printer extends Function {
|
||||
class Printer extends Function {
|
||||
Printer() { this.hasQualifiedName("fmt", ["Print", "Printf", "Println"]) }
|
||||
}
|
||||
|
||||
@@ -131,6 +131,35 @@ module Fmt {
|
||||
exists(int i | if getName() = "Sscanf" then i > 1 else i > 0 | output.isParameter(i))
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Scan` function or one of its variants, all of which read from os.Stdin */
|
||||
class Scanner extends Function {
|
||||
Scanner() { this.hasQualifiedName("fmt", ["Scan", "Scanf", "Scanln"]) }
|
||||
}
|
||||
|
||||
/** A call to a `Scanner`. */
|
||||
class ScannerCall extends DataFlow::CallNode {
|
||||
ScannerCall() { this.getTarget() instanceof Scanner }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Fscan` function or one of its variants,
|
||||
* all of which read from a specified io.Reader
|
||||
*/
|
||||
class FScanner extends Function {
|
||||
FScanner() { this.hasQualifiedName("fmt", ["Fscan", "Fscanf", "Fscanln"]) }
|
||||
}
|
||||
|
||||
/** A call to a `FScanner`. */
|
||||
class FScannerCall extends DataFlow::CallNode {
|
||||
FScannerCall() { this.getTarget() instanceof FScanner }
|
||||
|
||||
/**
|
||||
* Returns the node corresponding to the io.Reader
|
||||
* argument provided in the call.
|
||||
*/
|
||||
DataFlow::Node getReader() { result = this.getArgument(0) }
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides models of commonly used functions in the `io` package. */
|
||||
|
||||
35
ql/test/experimental/CWE-352/ConstantOauth2State.expected
Normal file
35
ql/test/experimental/CWE-352/ConstantOauth2State.expected
Normal file
@@ -0,0 +1,35 @@
|
||||
edges
|
||||
| ConstantOauth2State.go:18:26:18:32 | "state" : string literal | ConstantOauth2State.go:48:26:48:41 | stateStringConst |
|
||||
| ConstantOauth2State.go:18:26:18:32 | "state" : string literal | ConstantOauth2State.go:145:26:145:41 | stateStringConst |
|
||||
| ConstantOauth2State.go:18:26:18:32 | "state" : string literal | ConstantOauth2State.go:167:26:167:41 | stateStringConst |
|
||||
| ConstantOauth2State.go:18:26:18:32 | "state" : string literal | ConstantOauth2State.go:189:26:189:41 | stateStringConst |
|
||||
| ConstantOauth2State.go:20:22:20:28 | "state" : string | ConstantOauth2State.go:63:26:63:39 | stateStringVar |
|
||||
| ConstantOauth2State.go:78:11:78:25 | call to newFixedState : string | ConstantOauth2State.go:79:26:79:30 | state |
|
||||
| ConstantOauth2State.go:84:9:84:15 | "state" : string | ConstantOauth2State.go:78:11:78:25 | call to newFixedState : string |
|
||||
| ConstantOauth2State.go:145:9:145:42 | call to AuthCodeURL : string | ConstantOauth2State.go:146:54:146:56 | url |
|
||||
| ConstantOauth2State.go:167:9:167:42 | call to AuthCodeURL : string | ConstantOauth2State.go:168:54:168:56 | url |
|
||||
| ConstantOauth2State.go:189:9:189:42 | call to AuthCodeURL : string | ConstantOauth2State.go:190:28:190:30 | url |
|
||||
nodes
|
||||
| ConstantOauth2State.go:18:26:18:32 | "state" : string literal | semmle.label | "state" : string literal |
|
||||
| ConstantOauth2State.go:20:22:20:28 | "state" : string | semmle.label | "state" : string |
|
||||
| ConstantOauth2State.go:33:26:33:32 | "state" | semmle.label | "state" |
|
||||
| ConstantOauth2State.go:48:26:48:41 | stateStringConst | semmle.label | stateStringConst |
|
||||
| ConstantOauth2State.go:63:26:63:39 | stateStringVar | semmle.label | stateStringVar |
|
||||
| ConstantOauth2State.go:78:11:78:25 | call to newFixedState : string | semmle.label | call to newFixedState : string |
|
||||
| ConstantOauth2State.go:79:26:79:30 | state | semmle.label | state |
|
||||
| ConstantOauth2State.go:84:9:84:15 | "state" : string | semmle.label | "state" : string |
|
||||
| ConstantOauth2State.go:145:9:145:42 | call to AuthCodeURL : string | semmle.label | call to AuthCodeURL : string |
|
||||
| ConstantOauth2State.go:145:26:145:41 | stateStringConst | semmle.label | stateStringConst |
|
||||
| ConstantOauth2State.go:146:54:146:56 | url | semmle.label | url |
|
||||
| ConstantOauth2State.go:167:9:167:42 | call to AuthCodeURL : string | semmle.label | call to AuthCodeURL : string |
|
||||
| ConstantOauth2State.go:167:26:167:41 | stateStringConst | semmle.label | stateStringConst |
|
||||
| ConstantOauth2State.go:168:54:168:56 | url | semmle.label | url |
|
||||
| ConstantOauth2State.go:189:9:189:42 | call to AuthCodeURL : string | semmle.label | call to AuthCodeURL : string |
|
||||
| ConstantOauth2State.go:189:26:189:41 | stateStringConst | semmle.label | stateStringConst |
|
||||
| ConstantOauth2State.go:190:28:190:30 | url | semmle.label | url |
|
||||
#select
|
||||
| ConstantOauth2State.go:33:26:33:32 | "state" | ConstantOauth2State.go:33:26:33:32 | "state" | ConstantOauth2State.go:33:26:33:32 | "state" | Using a constant $@ to create oauth2 URLs. | ConstantOauth2State.go:33:26:33:32 | "state" | state string |
|
||||
| ConstantOauth2State.go:48:26:48:41 | stateStringConst | ConstantOauth2State.go:18:26:18:32 | "state" : string literal | ConstantOauth2State.go:48:26:48:41 | stateStringConst | Using a constant $@ to create oauth2 URLs. | ConstantOauth2State.go:18:26:18:32 | "state" | state string |
|
||||
| ConstantOauth2State.go:63:26:63:39 | stateStringVar | ConstantOauth2State.go:20:22:20:28 | "state" : string | ConstantOauth2State.go:63:26:63:39 | stateStringVar | Using a constant $@ to create oauth2 URLs. | ConstantOauth2State.go:20:22:20:28 | "state" | state string |
|
||||
| ConstantOauth2State.go:79:26:79:30 | state | ConstantOauth2State.go:84:9:84:15 | "state" : string | ConstantOauth2State.go:79:26:79:30 | state | Using a constant $@ to create oauth2 URLs. | ConstantOauth2State.go:84:9:84:15 | "state" | state string |
|
||||
| ConstantOauth2State.go:189:26:189:41 | stateStringConst | ConstantOauth2State.go:18:26:18:32 | "state" : string literal | ConstantOauth2State.go:189:26:189:41 | stateStringConst | Using a constant $@ to create oauth2 URLs. | ConstantOauth2State.go:18:26:18:32 | "state" | state string |
|
||||
192
ql/test/experimental/CWE-352/ConstantOauth2State.go
Normal file
192
ql/test/experimental/CWE-352/ConstantOauth2State.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
//go:generate depstubber -vendor golang.org/x/oauth2 Config,Endpoint
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
|
||||
const stateStringConst = "state"
|
||||
|
||||
var stateStringVar = "state"
|
||||
|
||||
func badWithStringLiteralState(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL("state") // BAD
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
func badWithConstState(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL(stateStringConst) // BAD
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
func badWithFixedVarState(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL(stateStringVar) // BAD
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
func badWithFixedStateReturned(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
state := newFixedState()
|
||||
url := conf.AuthCodeURL(state) // BAD
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
func newFixedState() string {
|
||||
return "state"
|
||||
}
|
||||
|
||||
func betterWithVariableStateReturned(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
state := generateStateOauthCookie(w)
|
||||
url := conf.AuthCodeURL(state) // OK, because the state is not a constant.
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
func generateStateOauthCookie(w http.ResponseWriter) string {
|
||||
b := make([]byte, 128)
|
||||
rand.Read(b)
|
||||
state := base64.URLEncoding.EncodeToString(b)
|
||||
// TODO: save the state string to cookies or HTML storage.
|
||||
return state
|
||||
}
|
||||
func okWithMixedVarState(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
state := fmt.Sprintf("%s-%s", stateStringVar, NewCSRFToken())
|
||||
|
||||
url := conf.AuthCodeURL(state) // OK, because the state is not a constant.
|
||||
_ = url
|
||||
// ...
|
||||
}
|
||||
|
||||
func NewCSRFToken() string {
|
||||
b := make([]byte, 128)
|
||||
rand.Read(b)
|
||||
randomToken := base64.URLEncoding.EncodeToString(b)
|
||||
return randomToken
|
||||
}
|
||||
func okWithConstStatePrinter(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL(stateStringConst) // OK, because we're supposedly not exposed to the web, but within a terminal.
|
||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||
// ...
|
||||
|
||||
var code string
|
||||
if _, err := fmt.Scan(&code); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_ = code
|
||||
// ...
|
||||
}
|
||||
func okWithConstStateFPrinter(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL(stateStringConst) // OK, because we're supposedly not exposed to the web, but within a terminal.
|
||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||
// ...
|
||||
|
||||
var code string
|
||||
if _, err := fmt.Fscan(os.Stdin, &code); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
_ = code
|
||||
// ...
|
||||
}
|
||||
func badWithConstStatePrinter(w http.ResponseWriter) {
|
||||
conf := &oauth2.Config{
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
ClientSecret: "YOUR_CLIENT_SECRET",
|
||||
Scopes: []string{"SCOPE1", "SCOPE2"},
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: "https://provider.com/o/oauth2/auth",
|
||||
TokenURL: "https://provider.com/o/oauth2/token",
|
||||
},
|
||||
}
|
||||
|
||||
url := conf.AuthCodeURL(stateStringConst) // BAD
|
||||
fmt.Printf("LOG: URL %v", url)
|
||||
// ...
|
||||
}
|
||||
1
ql/test/experimental/CWE-352/ConstantOauth2State.qlref
Normal file
1
ql/test/experimental/CWE-352/ConstantOauth2State.qlref
Normal file
@@ -0,0 +1 @@
|
||||
experimental/CWE-352/ConstantOauth2State.ql
|
||||
5
ql/test/experimental/CWE-352/go.mod
Normal file
5
ql/test/experimental/CWE-352/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module custom-queries-go-tests/constant-oauth2-state
|
||||
|
||||
go 1.14
|
||||
|
||||
require golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
27
ql/test/experimental/CWE-352/vendor/golang.org/x/oauth2/LICENSE
generated
vendored
Normal file
27
ql/test/experimental/CWE-352/vendor/golang.org/x/oauth2/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
81
ql/test/experimental/CWE-352/vendor/golang.org/x/oauth2/stub.go
generated
vendored
Normal file
81
ql/test/experimental/CWE-352/vendor/golang.org/x/oauth2/stub.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
// Code generated by depstubber. DO NOT EDIT.
|
||||
// This is a simple stub for golang.org/x/oauth2, strictly for use in testing.
|
||||
|
||||
// See the LICENSE file for information about the licensing of the original library.
|
||||
// Source: golang.org/x/oauth2 (exports: Config,Endpoint; functions: )
|
||||
|
||||
// Package oauth2 is a stub of golang.org/x/oauth2, generated by depstubber.
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
context "context"
|
||||
http "net/http"
|
||||
time "time"
|
||||
)
|
||||
|
||||
type AuthCodeOption interface{}
|
||||
|
||||
type AuthStyle int
|
||||
|
||||
type Config struct {
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Endpoint Endpoint
|
||||
RedirectURL string
|
||||
Scopes []string
|
||||
}
|
||||
|
||||
func (_ *Config) AuthCodeURL(_ string, _ ...AuthCodeOption) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Config) Client(_ context.Context, _ *Token) *http.Client {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Config) Exchange(_ context.Context, _ string, _ ...AuthCodeOption) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *Config) PasswordCredentialsToken(_ context.Context, _ string, _ string) (*Token, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (_ *Config) TokenSource(_ context.Context, _ *Token) TokenSource {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Endpoint struct {
|
||||
AuthURL string
|
||||
TokenURL string
|
||||
AuthStyle AuthStyle
|
||||
}
|
||||
|
||||
type Token struct {
|
||||
AccessToken string
|
||||
TokenType string
|
||||
RefreshToken string
|
||||
Expiry time.Time
|
||||
}
|
||||
|
||||
func (_ *Token) Extra(_ string) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (_ *Token) SetAuthHeader(_ *http.Request) {}
|
||||
|
||||
func (_ *Token) Type() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (_ *Token) Valid() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (_ *Token) WithExtra(_ interface{}) *Token {
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenSource interface {
|
||||
Token() (*Token, error)
|
||||
}
|
||||
3
ql/test/experimental/CWE-352/vendor/modules.txt
vendored
Normal file
3
ql/test/experimental/CWE-352/vendor/modules.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
## explicit
|
||||
golang.org/x/oauth2
|
||||
Reference in New Issue
Block a user