diff --git a/go/ql/src/experimental/CWE-321-V2/ExampleBad.go b/go/ql/src/experimental/CWE-321-V2/ExampleBad.go new file mode 100644 index 000000000000..900853b2f2e7 --- /dev/null +++ b/go/ql/src/experimental/CWE-321-V2/ExampleBad.go @@ -0,0 +1,28 @@ +package main + +import ( + "fmt" + "log" + + "github.com/go-jose/go-jose/v3/jwt" +) + +var JwtKey = []byte("AllYourBase") + +func main() { + // BAD: usage of a harcoded Key + verifyJWT(JWTFromUser) +} + +func LoadJwtKey(token *jwt.Token) (interface{}, error) { + return JwtKey, nil +} +func verifyJWT(signedToken string) { + fmt.Println("verifying JWT") + DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo{}, LoadJwtKey) + if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok && DecodedToken.Valid { + fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID) + } else { + log.Fatal(err) + } +} diff --git a/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.qhelp b/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.qhelp new file mode 100644 index 000000000000..de4affc139e9 --- /dev/null +++ b/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.qhelp @@ -0,0 +1,41 @@ + + + +

+ A JSON Web Token (JWT) is used for authenticating and managing users in an application. +

+

+ Using a hard-coded secret key for parsing JWT tokens in open source projects + can leave the application using the token vulnerable to authentication bypasses. +

+ +

+ A JWT token is safe for enforcing authentication and access control as long as it can't be forged by a malicious actor. However, when a project exposes this secret publicly, these seemingly unforgeable tokens can now be easily forged. + Since the authentication as well as access control is typically enforced through these JWT tokens, an attacker armed with the secret can create a valid authentication token for any user and may even gain access to other privileged parts of the application. +

+ +
+ + +

+ Generating a cryptographically secure secret key during application initialization and using this generated key for future JWT parsing requests can prevent this vulnerability. +

+ +
+ + +

+ The following code uses a hard-coded string as a secret for parsing user provided JWTs. In this case, an attacker can very easily forge a token by using the hard-coded secret. +

+ + + +
+ +
  • + CVE-2022-0664: + Use of Hard-coded Cryptographic Key in Go github.com/gravitl/netmaker prior to 0.8.5,0.9.4,0.10.0,0.10.1. +
  • +
    + +
    \ No newline at end of file diff --git a/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.ql b/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.ql new file mode 100644 index 000000000000..39cc2ca99176 --- /dev/null +++ b/go/ql/src/experimental/CWE-321-V2/HardCodedKeys.ql @@ -0,0 +1,59 @@ +/** + * @name Decoding JWT with hardcoded key + * @description Decoding JWT Secret with a Constant value lead to authentication or authorization bypass + * @kind path-problem + * @problem.severity error + * @id go/parse-jwt-with-hardcoded-key + * @tags security + * experimental + * external/cwe/cwe-321 + */ + +import go +import experimental.frameworks.JWT + +module JwtParseWithConstantKeyConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source.asExpr() instanceof StringLit } + + predicate isSink(DataFlow::Node sink) { + // first part is the JWT Parsing Functions that get a func type as an argument + // Find a node that has flow to a key Function argument + // then find the first result node of this Function which is the secret key + exists(FuncDef fd, DataFlow::Node n, DataFlow::ResultNode rn | + fd = n.asExpr() + or + n = fd.(FuncDecl).getFunction().getARead() + | + GolangJwtKeyFunc::flow(n, _) and + sink = rn and + rn.getRoot() = fd and + rn.getIndex() = 0 + ) + or + // second part is the JWT Parsing Functions that get a string or byte as an argument + sink = any(JwtParse jp).getKeyArg() + } +} + +module GolangJwtKeyFuncConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source = any(Function f).getARead() + or + source.asExpr() = any(FuncDef fd) + } + + predicate isSink(DataFlow::Node sink) { + sink = any(JwtParseWithKeyFunction parseJwt).getKeyFuncArg() + } +} + +module JwtParseWithConstantKey = TaintTracking::Global; + +module GolangJwtKeyFunc = TaintTracking::Global; + +import JwtParseWithConstantKey::PathGraph + +from JwtParseWithConstantKey::PathNode source, JwtParseWithConstantKey::PathNode sink +where JwtParseWithConstantKey::flowPath(source, sink) +select sink.getNode(), source, sink, "This $@.", source.getNode(), + "Constant Key is used as JWT Secret key" diff --git a/go/ql/src/experimental/CWE-347/Example.go b/go/ql/src/experimental/CWE-347/Example.go new file mode 100644 index 000000000000..ee59d836439c --- /dev/null +++ b/go/ql/src/experimental/CWE-347/Example.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "log" + + "github.com/golang-jwt/jwt/v5" +) + +func main() { + // BAD: only decode jwt without verification + notVerifyJWT(token) + + // GOOD: decode with verification or verify plus decode + notVerifyJWT(token) + VerifyJWT(token) +} + +func notVerifyJWT(signedToken string) { + fmt.Println("only decoding JWT") + DecodedToken, _, err := jwt.NewParser().ParseUnverified(signedToken, &CustomerInfo{}) + if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok { + fmt.Printf("DecodedToken:%v\n", claims) + } else { + log.Fatal("error", err) + } +} +func LoadJwtKey(token *jwt.Token) (interface{}, error) { + return ARandomJwtKey, nil +} +func verifyJWT(signedToken string) { + fmt.Println("verifying JWT") + DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo{}, LoadJwtKey) + if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok && DecodedToken.Valid { + fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID) + } else { + log.Fatal(err) + } +} diff --git a/go/ql/src/experimental/CWE-347/ParseJWTWithoutVerification.qhelp b/go/ql/src/experimental/CWE-347/ParseJWTWithoutVerification.qhelp new file mode 100644 index 000000000000..cb1edb2f6599 --- /dev/null +++ b/go/ql/src/experimental/CWE-347/ParseJWTWithoutVerification.qhelp @@ -0,0 +1,34 @@ + + + +

    + A JSON Web Token (JWT) is used for authenticating and managing users in an application. +

    +

    + Only Decoding JWTs without checking if they have a valid signature or not can lead to security vulnerabilities. +

    + +
    + + +

    + Don't use methods that only decode JWT, Instead use methods that verify the signature of JWT. +

    + +
    + + +

    + In the following code you can see an Example from a popular Library. +

    + + + +
    + +
  • + JWT audience claim is not verified +
  • +
    + +
    \ No newline at end of file diff --git a/go/ql/src/experimental/CWE-347/ParseJWTWithoutVerification.ql b/go/ql/src/experimental/CWE-347/ParseJWTWithoutVerification.ql new file mode 100644 index 000000000000..182685efa97b --- /dev/null +++ b/go/ql/src/experimental/CWE-347/ParseJWTWithoutVerification.ql @@ -0,0 +1,57 @@ +/** + * @name Use of JWT Methods that only decode user provided Token + * @description Using JWT methods without verification can cause to authorization or authentication bypass + * @kind path-problem + * @problem.severity error + * @id go/parse-jwt-without-verification + * @tags security + * experimental + * external/cwe/cwe-321 + */ + +import go +import experimental.frameworks.JWT + +module WithValidationConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource } + + predicate isSink(DataFlow::Node sink) { + sink = any(JwtParse jwtParse).getTokenArg() or + sink = any(JwtParseWithKeyFunction jwtParseWithKeyFunction).getTokenArg() + } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + golangJwtIsAdditionalFlowStep(nodeFrom, nodeTo) + or + goJoseIsAdditionalFlowStep(nodeFrom, nodeTo) + } +} + +module NoValidationConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source instanceof UntrustedFlowSource and + not WithValidation::flow(source, _) + } + + predicate isSink(DataFlow::Node sink) { + sink = any(JwtUnverifiedParse parseUnverified).getTokenArg() + } + + predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + golangJwtIsAdditionalFlowStep(nodeFrom, nodeTo) + or + goJoseIsAdditionalFlowStep(nodeFrom, nodeTo) + } +} + +module WithValidation = TaintTracking::Global; + +module NoValidation = TaintTracking::Global; + +import NoValidation::PathGraph + +from NoValidation::PathNode source, NoValidation::PathNode sink +where NoValidation::flowPath(source, sink) +select sink.getNode(), source, sink, + "This JWT is parsed without verification and received from $@.", source.getNode(), + "this user-controlled source" diff --git a/go/ql/src/experimental/frameworks/JWT.qll b/go/ql/src/experimental/frameworks/JWT.qll new file mode 100644 index 000000000000..ceb7ffc94099 --- /dev/null +++ b/go/ql/src/experimental/frameworks/JWT.qll @@ -0,0 +1,265 @@ +import go + +/** + * A abstract class which responsible for parsing a JWT token. + */ +abstract class JwtParseBase extends Function { + /** + * Gets argument number that responsible for JWT + * + * `-1` means the receiver is a argument node that responsible for JWT. + * In this case, we must declare some additional taint steps. + */ + abstract int getTokenArgNum(); + + /** + * Gets Argument as DataFlow node that responsible for JWT + */ + DataFlow::Node getTokenArg() { + this.getTokenArgNum() != -1 and result = this.getACall().getArgument(this.getTokenArgNum()) + or + this.getTokenArgNum() = -1 and result = this.getACall().getReceiver() + } +} + +/** + * A abstract class which responsible for parsing a JWT token which the key parameter is a function type. + * + * Extends this class for Jwt parsing methods that accepts a function type as key. + */ +abstract class JwtParseWithKeyFunction extends JwtParseBase { + /** + * Gets argument number that responsible for a function returning the secret key + */ + abstract int getKeyFuncArgNum(); + + /** + * Gets Argument as DataFlow node that responsible for a function returning the secret key + */ + DataFlow::Node getKeyFuncArg() { result = this.getACall().getArgument(this.getKeyFuncArgNum()) } +} + +/** + * A abstract class which responsible for parsing a JWT token which the key parameter can be a string or byte type. + * + * Extends this class for Jwt parsing methods that accepts a non-function type as key. + */ +abstract class JwtParse extends JwtParseBase { + /** + * Gets argument number that responsible for secret key + */ + abstract int getKeyArgNum(); + + /** + * Gets Argument as DataFlow node that responsible for secret key + */ + DataFlow::Node getKeyArg() { result = this.getACall().getArgument(this.getKeyArgNum()) } +} + +/** + * A abstract class which responsible for parsing a JWT without verifying it + * + * Extends this class for Jwt parsing methods that don't verify JWT signature + */ +abstract class JwtUnverifiedParse extends JwtParseBase { } + +/** + * Gets `github.com/golang-jwt/jwt` and `github.com/dgrijalva/jwt-go`(previous name of `golang-jwt`) JWT packages + */ +string golangJwtPackage() { + result = package(["github.com/golang-jwt/jwt", "github.com/dgrijalva/jwt-go"], "") +} + +/** + * A class that contains the following function and method: + * + * func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) + * + * func Parse(tokenString string, keyFunc Keyfunc) + */ +class GolangJwtParse extends JwtParseWithKeyFunction { + GolangJwtParse() { + exists(Function f | f.hasQualifiedName(golangJwtPackage(), "Parse") | this = f) + or + exists(Method f | f.hasQualifiedName(golangJwtPackage(), "Parser", "Parse") | this = f) + } + + override int getKeyFuncArgNum() { result = 1 } + + override int getTokenArgNum() { result = 0 } +} + +/** + * A class that contains the following function and method: + * + * func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) + * + * func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) + */ +class GolangJwtParseWithClaims extends JwtParseWithKeyFunction { + GolangJwtParseWithClaims() { + exists(Function f | f.hasQualifiedName(golangJwtPackage(), "ParseWithClaims") | this = f) + or + exists(Method f | f.hasQualifiedName(golangJwtPackage(), "Parser", "ParseWithClaims") | + this = f + ) + } + + override int getKeyFuncArgNum() { result = 2 } + + override int getTokenArgNum() { result = 0 } +} + +/** + * A class that contains the following method: + * + * func (p *Parser) ParseUnverified(tokenString string, claims Claims) + */ +class GolangJwtParseUnverified extends JwtUnverifiedParse { + GolangJwtParseUnverified() { + exists(Method f | f.hasQualifiedName(golangJwtPackage(), "Parser", "ParseUnverified") | + this = f + ) + } + + override int getTokenArgNum() { result = 0 } +} + +/** + * Gets `github.com/golang-jwt/jwt` and `github.com/dgrijalva/jwt-go`(previous name of `golang-jwt`) JWT packages + */ +string golangJwtRequestPackage() { + result = package(["github.com/golang-jwt/jwt", "github.com/dgrijalva/jwt-go"], "request") +} + +/** + * A class that contains the following function: + * + * func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc, options ...ParseFromRequestOption) + */ +class GolangJwtParseFromRequest extends JwtParseWithKeyFunction { + GolangJwtParseFromRequest() { + exists(Function f | f.hasQualifiedName(golangJwtRequestPackage(), "ParseFromRequest") | + this = f + ) + } + + override int getKeyFuncArgNum() { result = 2 } + + override int getTokenArgNum() { result = 0 } +} + +/** + * A class that contains the following function: + * + * func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) + */ +class GolangJwtParseFromRequestWithClaims extends JwtParseWithKeyFunction { + GolangJwtParseFromRequestWithClaims() { + exists(Function f | + f.hasQualifiedName(golangJwtRequestPackage(), "ParseFromRequestWithClaims") + | + this = f + ) + } + + override int getKeyFuncArgNum() { result = 3 } + + override int getTokenArgNum() { result = 0 } +} + +/** + * Gets `gopkg.in/square/go-jose` and `github.com/go-jose/go-jose` jwt package + */ +string goJoseJwtPackage() { + result = package(["gopkg.in/square/go-jose", "github.com/go-jose/go-jose"], "jwt") +} + +/** + * A class that contains the following method: + * + * func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) + */ +class GoJoseParseWithClaims extends JwtParse { + GoJoseParseWithClaims() { + exists(Method f | f.hasQualifiedName(goJoseJwtPackage(), "JSONWebToken", "Claims") | this = f) + } + + override int getKeyArgNum() { result = 0 } + + override int getTokenArgNum() { result = -1 } +} + +/** + * A class that contains the following method: + * + * func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) + */ +class GoJoseUnsafeClaims extends JwtUnverifiedParse { + GoJoseUnsafeClaims() { + exists(Method f | + f.hasQualifiedName(goJoseJwtPackage(), "JSONWebToken", "UnsafeClaimsWithoutVerification") + | + this = f + ) + } + + override int getTokenArgNum() { result = -1 } +} + +/** + * Holds for general additional steps related to parsing the secret keys in `golang-jwt/jwt`,`dgrijalva/jwt-go` packages + */ +predicate golangJwtIsAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(Function f, DataFlow::CallNode call | + ( + f.hasQualifiedName(package("github.com/golang-jwt/jwt", ""), + [ + "ParseECPrivateKeyFromPEM", "ParseECPublicKeyFromPEM", "ParseEdPrivateKeyFromPEM", + "ParseEdPublicKeyFromPEM", "ParseRSAPrivateKeyFromPEM", "ParseRSAPublicKeyFromPEM", + "RegisterSigningMethod" + ]) or + f.hasQualifiedName(package("github.com/dgrijalva/jwt-go", ""), + [ + "ParseECPrivateKeyFromPEM", "ParseECPublicKeyFromPEM", "ParseRSAPrivateKeyFromPEM", + "ParseRSAPrivateKeyFromPEMWithPassword", "ParseRSAPublicKeyFromPEM" + ]) + ) and + call = f.getACall() and + nodeFrom = call.getArgument(0) and + nodeTo = call.getResult(0) + or + ( + f instanceof GolangJwtParse + or + f instanceof GolangJwtParseWithClaims + ) and + call = f.getACall() and + nodeFrom = call.getArgument(0) and + nodeTo = call.getResult(0) + ) +} + +/** + * Holds for general additioanl steps related to parsing the secret keys in `go-jose` package + */ +predicate goJoseIsAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + exists(Function f, DataFlow::CallNode call | + f.hasQualifiedName(goJoseJwtPackage(), ["ParseEncrypted", "ParseSigned"]) and + call = f.getACall() and + nodeFrom = call.getArgument(0) and + nodeTo = call.getResult(0) + ) + or + exists(Method m, DataFlow::CallNode call | + m.hasQualifiedName(goJoseJwtPackage(), "NestedJSONWebToken", "ParseSignedAndEncrypted") and + call = m.getACall() and + nodeFrom = call.getArgument(0) and + nodeTo = call.getResult(0) + or + m.hasQualifiedName(goJoseJwtPackage(), "NestedJSONWebToken", "Decrypt") and + call = m.getACall() and + nodeFrom = call.getReceiver() and + nodeTo = call.getResult(0) + ) +} diff --git a/go/ql/test/experimental/CWE-321-V2/HardCodedKeys.expected b/go/ql/test/experimental/CWE-321-V2/HardCodedKeys.expected new file mode 100644 index 000000000000..6d1d1693ab71 --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/HardCodedKeys.expected @@ -0,0 +1,16 @@ +edges +| go-jose.v3.go:13:14:13:34 | type conversion | go-jose.v3.go:24:32:24:37 | JwtKey | +| go-jose.v3.go:13:21:13:33 | "AllYourBase" | go-jose.v3.go:13:14:13:34 | type conversion | +| golang-jwt-v5.go:19:15:19:35 | type conversion | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | +| golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | golang-jwt-v5.go:19:15:19:35 | type conversion | +nodes +| go-jose.v3.go:13:14:13:34 | type conversion | semmle.label | type conversion | +| go-jose.v3.go:13:21:13:33 | "AllYourBase" | semmle.label | "AllYourBase" | +| go-jose.v3.go:24:32:24:37 | JwtKey | semmle.label | JwtKey | +| golang-jwt-v5.go:19:15:19:35 | type conversion | semmle.label | type conversion | +| golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | semmle.label | "AllYourBase" | +| golang-jwt-v5.go:27:9:27:15 | JwtKey1 | semmle.label | JwtKey1 | +subpaths +#select +| go-jose.v3.go:24:32:24:37 | JwtKey | go-jose.v3.go:13:21:13:33 | "AllYourBase" | go-jose.v3.go:24:32:24:37 | JwtKey | This $@. | go-jose.v3.go:13:21:13:33 | "AllYourBase" | Constant Key is used as JWT Secret key | +| golang-jwt-v5.go:27:9:27:15 | JwtKey1 | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | golang-jwt-v5.go:27:9:27:15 | JwtKey1 | This $@. | golang-jwt-v5.go:19:22:19:34 | "AllYourBase" | Constant Key is used as JWT Secret key | diff --git a/go/ql/test/experimental/CWE-321-V2/HardCodedKeys.qlref b/go/ql/test/experimental/CWE-321-V2/HardCodedKeys.qlref new file mode 100644 index 000000000000..e6cee5464208 --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/HardCodedKeys.qlref @@ -0,0 +1 @@ +experimental/CWE-321-V2/HardCodedKeys.ql \ No newline at end of file diff --git a/go/ql/test/experimental/CWE-321-V2/go-jose.v3.go b/go/ql/test/experimental/CWE-321-V2/go-jose.v3.go new file mode 100644 index 000000000000..e25624bb680f --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/go-jose.v3.go @@ -0,0 +1,28 @@ +package jwt + +//go:generate depstubber -vendor github.com/go-jose/go-jose/v3/jwt JSONWebToken ParseSigned + +import ( + "fmt" + "net/http" + + "github.com/go-jose/go-jose/v3/jwt" +) + +// NOT OK +var JwtKey = []byte("AllYourBase") + +func main2(r *http.Request) { + signedToken := r.URL.Query().Get("signedToken") + verifyJWT(signedToken) +} + +func verifyJWT(signedToken string) { + fmt.Println("verifying JWT") + DecodedToken, _ := jwt.ParseSigned(signedToken) + out := CustomerInfo{} + if err := DecodedToken.Claims(JwtKey, &out); err != nil { + panic(err) + } + fmt.Printf("%v\n", out) +} diff --git a/go/ql/test/experimental/CWE-321-V2/go.mod b/go/ql/test/experimental/CWE-321-V2/go.mod new file mode 100644 index 000000000000..1f1dec543c2d --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/go.mod @@ -0,0 +1,37 @@ +module main + +go 1.21 + +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/go-jose/go-jose/v3 v3.0.0 + github.com/golang-jwt/jwt/v5 v5.0.0 +) + +require ( + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/google/go-cmp v0.5.9 // indirect + golang.org/x/crypto v0.12.0 // indirect +) diff --git a/go/ql/test/experimental/CWE-321-V2/golang-jwt-v5.go b/go/ql/test/experimental/CWE-321-V2/golang-jwt-v5.go new file mode 100644 index 000000000000..71917160bdaa --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/golang-jwt-v5.go @@ -0,0 +1,38 @@ +package jwt + +//go:generate depstubber -vendor github.com/golang-jwt/jwt/v5 RegisteredClaims,Parser,Token Parse,ParseWithClaims + +import ( + "fmt" + "github.com/golang-jwt/jwt/v5" + "log" + "net/http" +) + +type CustomerInfo struct { + Name string + ID int + jwt.RegisteredClaims +} + +// BAD constant key +var JwtKey1 = []byte("AllYourBase") + +func main1(r *http.Request) { + signedToken := r.URL.Query().Get("signedToken") + verifyJWT_golangjwt(signedToken) +} + +func LoadJwtKey(token *jwt.Token) (interface{}, error) { + return JwtKey1, nil +} + +func verifyJWT_golangjwt(signedToken string) { + fmt.Println("verifying JWT") + DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo{}, LoadJwtKey) + if claims, ok := DecodedToken.Claims.(*CustomerInfo); ok && DecodedToken.Valid { + fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID) + } else { + log.Fatal(err) + } +} diff --git a/go/ql/test/experimental/CWE-321-V2/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go b/go/ql/test/experimental/CWE-321-V2/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go new file mode 100644 index 000000000000..a160226c423f --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go @@ -0,0 +1,24 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/go-jose/go-jose/v3/jwt, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/go-jose/go-jose/v3/jwt (exports: JSONWebToken; functions: ParseSigned) + +// Package jwt is a stub of github.com/go-jose/go-jose/v3/jwt, generated by depstubber. +package jwt + +type JSONWebToken struct { + Headers []interface{} +} + +func (_ *JSONWebToken) Claims(_ interface{}, _ ...interface{}) error { + return nil +} + +func (_ *JSONWebToken) UnsafeClaimsWithoutVerification(_ ...interface{}) error { + return nil +} + +func ParseSigned(_ string) (*JSONWebToken, error) { + return nil, nil +} diff --git a/go/ql/test/experimental/CWE-321-V2/vendor/github.com/golang-jwt/jwt/v5/stub.go b/go/ql/test/experimental/CWE-321-V2/vendor/github.com/golang-jwt/jwt/v5/stub.go new file mode 100644 index 000000000000..7588530ba3ef --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/vendor/github.com/golang-jwt/jwt/v5/stub.go @@ -0,0 +1,306 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/golang-jwt/jwt/v5, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/golang-jwt/jwt/v5 (exports: RegisteredClaims,Parser,Token; functions: Parse,ParseWithClaims) + +// Package jwt is a stub of github.com/golang-jwt/jwt/v5, generated by depstubber. +package jwt + +import ( + time "time" +) + +type ClaimStrings []string + +func (_ ClaimStrings) MarshalJSON() ([]byte, error) { + return nil, nil +} + +func (_ *ClaimStrings) UnmarshalJSON(_ []byte) error { + return nil +} + +type Claims interface { + GetAudience() (ClaimStrings, error) + GetExpirationTime() (*NumericDate, error) + GetIssuedAt() (*NumericDate, error) + GetIssuer() (string, error) + GetNotBefore() (*NumericDate, error) + GetSubject() (string, error) +} + +type Keyfunc func(*Token) (interface{}, error) + +type NumericDate struct { + Time time.Time +} + +func (_ NumericDate) Add(_ time.Duration) time.Time { + return time.Time{} +} + +func (_ NumericDate) AddDate(_ int, _ int, _ int) time.Time { + return time.Time{} +} + +func (_ NumericDate) After(_ time.Time) bool { + return false +} + +func (_ NumericDate) AppendFormat(_ []byte, _ string) []byte { + return nil +} + +func (_ NumericDate) Before(_ time.Time) bool { + return false +} + +func (_ NumericDate) Clock() (int, int, int) { + return 0, 0, 0 +} + +func (_ NumericDate) Compare(_ time.Time) int { + return 0 +} + +func (_ NumericDate) Date() (int, time.Month, int) { + return 0, 0, 0 +} + +func (_ NumericDate) Day() int { + return 0 +} + +func (_ NumericDate) Equal(_ time.Time) bool { + return false +} + +func (_ NumericDate) Format(_ string) string { + return "" +} + +func (_ NumericDate) GoString() string { + return "" +} + +func (_ NumericDate) GobEncode() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) Hour() int { + return 0 +} + +func (_ NumericDate) ISOWeek() (int, int) { + return 0, 0 +} + +func (_ NumericDate) In(_ *time.Location) time.Time { + return time.Time{} +} + +func (_ NumericDate) IsDST() bool { + return false +} + +func (_ NumericDate) IsZero() bool { + return false +} + +func (_ NumericDate) Local() time.Time { + return time.Time{} +} + +func (_ NumericDate) Location() *time.Location { + return nil +} + +func (_ NumericDate) MarshalBinary() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) MarshalJSON() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) MarshalText() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) Minute() int { + return 0 +} + +func (_ NumericDate) Month() time.Month { + return 0 +} + +func (_ NumericDate) Nanosecond() int { + return 0 +} + +func (_ NumericDate) Round(_ time.Duration) time.Time { + return time.Time{} +} + +func (_ NumericDate) Second() int { + return 0 +} + +func (_ NumericDate) String() string { + return "" +} + +func (_ NumericDate) Sub(_ time.Time) time.Duration { + return 0 +} + +func (_ NumericDate) Truncate(_ time.Duration) time.Time { + return time.Time{} +} + +func (_ NumericDate) UTC() time.Time { + return time.Time{} +} + +func (_ NumericDate) Unix() int64 { + return 0 +} + +func (_ NumericDate) UnixMicro() int64 { + return 0 +} + +func (_ NumericDate) UnixMilli() int64 { + return 0 +} + +func (_ NumericDate) UnixNano() int64 { + return 0 +} + +func (_ NumericDate) Weekday() time.Weekday { + return 0 +} + +func (_ NumericDate) Year() int { + return 0 +} + +func (_ NumericDate) YearDay() int { + return 0 +} + +func (_ NumericDate) Zone() (string, int) { + return "", 0 +} + +func (_ NumericDate) ZoneBounds() (time.Time, time.Time) { + return time.Time{}, time.Time{} +} + +func (_ *NumericDate) GobDecode(_ []byte) error { + return nil +} + +func (_ *NumericDate) UnmarshalBinary(_ []byte) error { + return nil +} + +func (_ *NumericDate) UnmarshalJSON(_ []byte) error { + return nil +} + +func (_ *NumericDate) UnmarshalText(_ []byte) error { + return nil +} + +func Parse(_ string, _ Keyfunc, _ ...ParserOption) (*Token, error) { + return nil, nil +} + +func ParseWithClaims(_ string, _ Claims, _ Keyfunc, _ ...ParserOption) (*Token, error) { + return nil, nil +} + +type Parser struct{} + +func (_ *Parser) DecodeSegment(_ string) ([]byte, error) { + return nil, nil +} + +func (_ *Parser) Parse(_ string, _ Keyfunc) (*Token, error) { + return nil, nil +} + +func (_ *Parser) ParseUnverified(_ string, _ Claims) (*Token, []string, error) { + return nil, nil, nil +} + +func (_ *Parser) ParseWithClaims(_ string, _ Claims, _ Keyfunc) (*Token, error) { + return nil, nil +} + +type ParserOption func(*Parser) + +type RegisteredClaims struct { + Issuer string + Subject string + Audience ClaimStrings + ExpiresAt *NumericDate + NotBefore *NumericDate + IssuedAt *NumericDate + ID string +} + +func (_ RegisteredClaims) GetAudience() (ClaimStrings, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetExpirationTime() (*NumericDate, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetIssuedAt() (*NumericDate, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetIssuer() (string, error) { + return "", nil +} + +func (_ RegisteredClaims) GetNotBefore() (*NumericDate, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetSubject() (string, error) { + return "", nil +} + +type SigningMethod interface { + Alg() string + Sign(_ string, _ interface{}) ([]byte, error) + Verify(_ string, _ []byte, _ interface{}) error +} + +type Token struct { + Raw string + Method SigningMethod + Header map[string]interface{} + Claims Claims + Signature []byte + Valid bool +} + +func (_ *Token) EncodeSegment(_ []byte) string { + return "" +} + +func (_ *Token) SignedString(_ interface{}) (string, error) { + return "", nil +} + +func (_ *Token) SigningString() (string, error) { + return "", nil +} diff --git a/go/ql/test/experimental/CWE-321-V2/vendor/modules.txt b/go/ql/test/experimental/CWE-321-V2/vendor/modules.txt new file mode 100644 index 000000000000..01144bc92497 --- /dev/null +++ b/go/ql/test/experimental/CWE-321-V2/vendor/modules.txt @@ -0,0 +1,84 @@ +# github.com/gin-gonic/gin v1.9.1 +## explicit +github.com/gin-gonic/gin +# github.com/go-jose/go-jose/v3 v3.0.0 +## explicit +github.com/go-jose/go-jose/v3 +# github.com/golang-jwt/jwt/v5 v5.0.0 +## explicit +github.com/golang-jwt/jwt/v5 +# github.com/bytedance/sonic v1.9.1 +## explicit +github.com/bytedance/sonic +# github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 +## explicit +github.com/chenzhuoyu/base64x +# github.com/gabriel-vasile/mimetype v1.4.2 +## explicit +github.com/gabriel-vasile/mimetype +# github.com/gin-contrib/sse v0.1.0 +## explicit +github.com/gin-contrib/sse +# github.com/go-playground/locales v0.14.1 +## explicit +github.com/go-playground/locales +# github.com/go-playground/universal-translator v0.18.1 +## explicit +github.com/go-playground/universal-translator +# github.com/go-playground/validator/v10 v10.14.0 +## explicit +github.com/go-playground/validator/v10 +# github.com/goccy/go-json v0.10.2 +## explicit +github.com/goccy/go-json +# github.com/json-iterator/go v1.1.12 +## explicit +github.com/json-iterator/go +# github.com/klauspost/cpuid/v2 v2.2.4 +## explicit +github.com/klauspost/cpuid/v2 +# github.com/leodido/go-urn v1.2.4 +## explicit +github.com/leodido/go-urn +# github.com/mattn/go-isatty v0.0.19 +## explicit +github.com/mattn/go-isatty +# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd +## explicit +github.com/modern-go/concurrent +# github.com/modern-go/reflect2 v1.0.2 +## explicit +github.com/modern-go/reflect2 +# github.com/pelletier/go-toml/v2 v2.0.8 +## explicit +github.com/pelletier/go-toml/v2 +# github.com/twitchyliquid64/golang-asm v0.15.1 +## explicit +github.com/twitchyliquid64/golang-asm +# github.com/ugorji/go/codec v1.2.11 +## explicit +github.com/ugorji/go/codec +# golang.org/x/arch v0.3.0 +## explicit +golang.org/x/arch +# golang.org/x/net v0.10.0 +## explicit +golang.org/x/net +# golang.org/x/sys v0.11.0 +## explicit +golang.org/x/sys +# golang.org/x/text v0.12.0 +## explicit +golang.org/x/text +# google.golang.org/protobuf v1.30.0 +## explicit +google.golang.org/protobuf +# gopkg.in/yaml.v3 v3.0.1 +## explicit +gopkg.in/yaml.v3 +# github.com/google/go-cmp v0.5.9 +## explicit +github.com/google/go-cmp +# golang.org/x/crypto v0.12.0 +## explicit +golang.org/x/crypto diff --git a/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.expected b/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.expected new file mode 100644 index 000000000000..7162ed5802f2 --- /dev/null +++ b/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.expected @@ -0,0 +1,32 @@ +edges +| go-jose.v3.go:25:16:25:20 | selection of URL | go-jose.v3.go:25:16:25:28 | call to Query | +| go-jose.v3.go:25:16:25:28 | call to Query | go-jose.v3.go:25:16:25:47 | call to Get | +| go-jose.v3.go:25:16:25:47 | call to Get | go-jose.v3.go:26:15:26:25 | signedToken | +| go-jose.v3.go:26:15:26:25 | signedToken | go-jose.v3.go:29:19:29:29 | definition of signedToken | +| go-jose.v3.go:29:19:29:29 | definition of signedToken | go-jose.v3.go:31:37:31:47 | signedToken | +| go-jose.v3.go:31:2:31:48 | ... := ...[0] | go-jose.v3.go:33:12:33:23 | DecodedToken | +| go-jose.v3.go:31:37:31:47 | signedToken | go-jose.v3.go:31:2:31:48 | ... := ...[0] | +| golang-jwt-v5.go:28:16:28:20 | selection of URL | golang-jwt-v5.go:28:16:28:28 | call to Query | +| golang-jwt-v5.go:28:16:28:28 | call to Query | golang-jwt-v5.go:28:16:28:47 | call to Get | +| golang-jwt-v5.go:28:16:28:47 | call to Get | golang-jwt-v5.go:29:25:29:35 | signedToken | +| golang-jwt-v5.go:29:25:29:35 | signedToken | golang-jwt-v5.go:32:29:32:39 | definition of signedToken | +| golang-jwt-v5.go:32:29:32:39 | definition of signedToken | golang-jwt-v5.go:34:58:34:68 | signedToken | +nodes +| go-jose.v3.go:25:16:25:20 | selection of URL | semmle.label | selection of URL | +| go-jose.v3.go:25:16:25:28 | call to Query | semmle.label | call to Query | +| go-jose.v3.go:25:16:25:47 | call to Get | semmle.label | call to Get | +| go-jose.v3.go:26:15:26:25 | signedToken | semmle.label | signedToken | +| go-jose.v3.go:29:19:29:29 | definition of signedToken | semmle.label | definition of signedToken | +| go-jose.v3.go:31:2:31:48 | ... := ...[0] | semmle.label | ... := ...[0] | +| go-jose.v3.go:31:37:31:47 | signedToken | semmle.label | signedToken | +| go-jose.v3.go:33:12:33:23 | DecodedToken | semmle.label | DecodedToken | +| golang-jwt-v5.go:28:16:28:20 | selection of URL | semmle.label | selection of URL | +| golang-jwt-v5.go:28:16:28:28 | call to Query | semmle.label | call to Query | +| golang-jwt-v5.go:28:16:28:47 | call to Get | semmle.label | call to Get | +| golang-jwt-v5.go:29:25:29:35 | signedToken | semmle.label | signedToken | +| golang-jwt-v5.go:32:29:32:39 | definition of signedToken | semmle.label | definition of signedToken | +| golang-jwt-v5.go:34:58:34:68 | signedToken | semmle.label | signedToken | +subpaths +#select +| go-jose.v3.go:33:12:33:23 | DecodedToken | go-jose.v3.go:25:16:25:20 | selection of URL | go-jose.v3.go:33:12:33:23 | DecodedToken | This JWT is parsed without verification and received from $@. | go-jose.v3.go:25:16:25:20 | selection of URL | this user-controlled source | +| golang-jwt-v5.go:34:58:34:68 | signedToken | golang-jwt-v5.go:28:16:28:20 | selection of URL | golang-jwt-v5.go:34:58:34:68 | signedToken | This JWT is parsed without verification and received from $@. | golang-jwt-v5.go:28:16:28:20 | selection of URL | this user-controlled source | diff --git a/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.qlref b/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.qlref new file mode 100644 index 000000000000..a4326ff97e69 --- /dev/null +++ b/go/ql/test/experimental/CWE-347/ParseJWTWithoutVerification.qlref @@ -0,0 +1 @@ +experimental/CWE-347/ParseJWTWithoutVerification.ql \ No newline at end of file diff --git a/go/ql/test/experimental/CWE-347/go-jose.v3.go b/go/ql/test/experimental/CWE-347/go-jose.v3.go new file mode 100644 index 000000000000..3e55ced31f6a --- /dev/null +++ b/go/ql/test/experimental/CWE-347/go-jose.v3.go @@ -0,0 +1,46 @@ +package jwt + +//go:generate depstubber -vendor github.com/go-jose/go-jose/v3/jwt JSONWebToken ParseSigned + +import ( + "fmt" + "github.com/go-jose/go-jose/v3/jwt" + "net/http" +) + +type CustomerInfo struct { + Name string + ID int +} + +var JwtKey = []byte("AllYourBase") + +func jose(r *http.Request) { + signedToken := r.URL.Query().Get("signedToken") + // OK: first decode and then verify + notVerifyJWT(signedToken) + verifyJWT(signedToken) + + // NOT OK: no verification + signedToken = r.URL.Query().Get("signedToken") + notVerifyJWT(signedToken) +} + +func notVerifyJWT(signedToken string) { + fmt.Println("only decoding JWT") + DecodedToken, _ := jwt.ParseSigned(signedToken) + out := CustomerInfo{} + if err := DecodedToken.UnsafeClaimsWithoutVerification(&out); err != nil { + panic(err) + } + fmt.Printf("%v\n", out) +} +func verifyJWT(signedToken string) { + fmt.Println("verifying JWT") + DecodedToken, _ := jwt.ParseSigned(signedToken) + out := CustomerInfo{} + if err := DecodedToken.Claims(JwtKey, &out); err != nil { + panic(err) + } + fmt.Printf("%v\n", out) +} diff --git a/go/ql/test/experimental/CWE-347/go.mod b/go/ql/test/experimental/CWE-347/go.mod new file mode 100644 index 000000000000..84e592efcf35 --- /dev/null +++ b/go/ql/test/experimental/CWE-347/go.mod @@ -0,0 +1,37 @@ +module main + +go 1.18 + +require ( + github.com/gin-gonic/gin v1.9.1 + github.com/go-jose/go-jose/v3 v3.0.0 + github.com/golang-jwt/jwt/v5 v5.0.0 +) + +require ( + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/google/go-cmp v0.5.9 // indirect + golang.org/x/crypto v0.12.0 // indirect +) diff --git a/go/ql/test/experimental/CWE-347/golang-jwt-v5.go b/go/ql/test/experimental/CWE-347/golang-jwt-v5.go new file mode 100644 index 000000000000..e37265f03c04 --- /dev/null +++ b/go/ql/test/experimental/CWE-347/golang-jwt-v5.go @@ -0,0 +1,54 @@ +package jwt + +//go:generate depstubber -vendor github.com/golang-jwt/jwt/v5 RegisteredClaims,Parser,Token ParseWithClaims,NewParser + +import ( + "fmt" + "github.com/golang-jwt/jwt/v5" + "log" + "net/http" +) + +type CustomerInfo1 struct { + Name string + ID int + jwt.RegisteredClaims +} + +// BAD constant key +var JwtKey1 = []byte("AllYourBase") + +func golangjwt(r *http.Request) { + signedToken := r.URL.Query().Get("signedToken") + // OK: first decode and then verify + notVerifyJWT_golangjwt(signedToken) + verifyJWT_golangjwt(signedToken) + + // NOT OK: only unverified parse + signedToken = r.URL.Query().Get("signedToken") + notVerifyJWT_golangjwt(signedToken) +} + +func notVerifyJWT_golangjwt(signedToken string) { + fmt.Println("only decoding JWT") + DecodedToken, _, err := jwt.NewParser().ParseUnverified(signedToken, &CustomerInfo1{}) + if claims, ok := DecodedToken.Claims.(*CustomerInfo1); ok { + fmt.Printf("DecodedToken:%v\n", claims) + } else { + log.Fatal("error", err) + } +} + +func LoadJwtKey(token *jwt.Token) (interface{}, error) { + return JwtKey, nil +} + +func verifyJWT_golangjwt(signedToken string) { + fmt.Println("verifying JWT") + DecodedToken, err := jwt.ParseWithClaims(signedToken, &CustomerInfo1{}, LoadJwtKey) + if claims, ok := DecodedToken.Claims.(*CustomerInfo1); ok && DecodedToken.Valid { + fmt.Printf("NAME:%v ,ID:%v\n", claims.Name, claims.ID) + } else { + log.Fatal(err) + } +} diff --git a/go/ql/test/experimental/CWE-347/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go b/go/ql/test/experimental/CWE-347/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go new file mode 100644 index 000000000000..a160226c423f --- /dev/null +++ b/go/ql/test/experimental/CWE-347/vendor/github.com/go-jose/go-jose/v3/jwt/stub.go @@ -0,0 +1,24 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/go-jose/go-jose/v3/jwt, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/go-jose/go-jose/v3/jwt (exports: JSONWebToken; functions: ParseSigned) + +// Package jwt is a stub of github.com/go-jose/go-jose/v3/jwt, generated by depstubber. +package jwt + +type JSONWebToken struct { + Headers []interface{} +} + +func (_ *JSONWebToken) Claims(_ interface{}, _ ...interface{}) error { + return nil +} + +func (_ *JSONWebToken) UnsafeClaimsWithoutVerification(_ ...interface{}) error { + return nil +} + +func ParseSigned(_ string) (*JSONWebToken, error) { + return nil, nil +} diff --git a/go/ql/test/experimental/CWE-347/vendor/github.com/golang-jwt/jwt/v5/stub.go b/go/ql/test/experimental/CWE-347/vendor/github.com/golang-jwt/jwt/v5/stub.go new file mode 100644 index 000000000000..6e4c4f327afa --- /dev/null +++ b/go/ql/test/experimental/CWE-347/vendor/github.com/golang-jwt/jwt/v5/stub.go @@ -0,0 +1,306 @@ +// Code generated by depstubber. DO NOT EDIT. +// This is a simple stub for github.com/golang-jwt/jwt/v5, strictly for use in testing. + +// See the LICENSE file for information about the licensing of the original library. +// Source: github.com/golang-jwt/jwt/v5 (exports: RegisteredClaims,Parser,Token; functions: ParseWithClaims,NewParser) + +// Package jwt is a stub of github.com/golang-jwt/jwt/v5, generated by depstubber. +package jwt + +import ( + time "time" +) + +type ClaimStrings []string + +func (_ ClaimStrings) MarshalJSON() ([]byte, error) { + return nil, nil +} + +func (_ *ClaimStrings) UnmarshalJSON(_ []byte) error { + return nil +} + +type Claims interface { + GetAudience() (ClaimStrings, error) + GetExpirationTime() (*NumericDate, error) + GetIssuedAt() (*NumericDate, error) + GetIssuer() (string, error) + GetNotBefore() (*NumericDate, error) + GetSubject() (string, error) +} + +type Keyfunc func(*Token) (interface{}, error) + +func NewParser(_ ...ParserOption) *Parser { + return nil +} + +type NumericDate struct { + Time time.Time +} + +func (_ NumericDate) Add(_ time.Duration) time.Time { + return time.Time{} +} + +func (_ NumericDate) AddDate(_ int, _ int, _ int) time.Time { + return time.Time{} +} + +func (_ NumericDate) After(_ time.Time) bool { + return false +} + +func (_ NumericDate) AppendFormat(_ []byte, _ string) []byte { + return nil +} + +func (_ NumericDate) Before(_ time.Time) bool { + return false +} + +func (_ NumericDate) Clock() (int, int, int) { + return 0, 0, 0 +} + +func (_ NumericDate) Compare(_ time.Time) int { + return 0 +} + +func (_ NumericDate) Date() (int, time.Month, int) { + return 0, 0, 0 +} + +func (_ NumericDate) Day() int { + return 0 +} + +func (_ NumericDate) Equal(_ time.Time) bool { + return false +} + +func (_ NumericDate) Format(_ string) string { + return "" +} + +func (_ NumericDate) GoString() string { + return "" +} + +func (_ NumericDate) GobEncode() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) Hour() int { + return 0 +} + +func (_ NumericDate) ISOWeek() (int, int) { + return 0, 0 +} + +func (_ NumericDate) In(_ *time.Location) time.Time { + return time.Time{} +} + +func (_ NumericDate) IsDST() bool { + return false +} + +func (_ NumericDate) IsZero() bool { + return false +} + +func (_ NumericDate) Local() time.Time { + return time.Time{} +} + +func (_ NumericDate) Location() *time.Location { + return nil +} + +func (_ NumericDate) MarshalBinary() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) MarshalJSON() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) MarshalText() ([]byte, error) { + return nil, nil +} + +func (_ NumericDate) Minute() int { + return 0 +} + +func (_ NumericDate) Month() time.Month { + return 0 +} + +func (_ NumericDate) Nanosecond() int { + return 0 +} + +func (_ NumericDate) Round(_ time.Duration) time.Time { + return time.Time{} +} + +func (_ NumericDate) Second() int { + return 0 +} + +func (_ NumericDate) String() string { + return "" +} + +func (_ NumericDate) Sub(_ time.Time) time.Duration { + return 0 +} + +func (_ NumericDate) Truncate(_ time.Duration) time.Time { + return time.Time{} +} + +func (_ NumericDate) UTC() time.Time { + return time.Time{} +} + +func (_ NumericDate) Unix() int64 { + return 0 +} + +func (_ NumericDate) UnixMicro() int64 { + return 0 +} + +func (_ NumericDate) UnixMilli() int64 { + return 0 +} + +func (_ NumericDate) UnixNano() int64 { + return 0 +} + +func (_ NumericDate) Weekday() time.Weekday { + return 0 +} + +func (_ NumericDate) Year() int { + return 0 +} + +func (_ NumericDate) YearDay() int { + return 0 +} + +func (_ NumericDate) Zone() (string, int) { + return "", 0 +} + +func (_ NumericDate) ZoneBounds() (time.Time, time.Time) { + return time.Time{}, time.Time{} +} + +func (_ *NumericDate) GobDecode(_ []byte) error { + return nil +} + +func (_ *NumericDate) UnmarshalBinary(_ []byte) error { + return nil +} + +func (_ *NumericDate) UnmarshalJSON(_ []byte) error { + return nil +} + +func (_ *NumericDate) UnmarshalText(_ []byte) error { + return nil +} + +func ParseWithClaims(_ string, _ Claims, _ Keyfunc, _ ...ParserOption) (*Token, error) { + return nil, nil +} + +type Parser struct{} + +func (_ *Parser) DecodeSegment(_ string) ([]byte, error) { + return nil, nil +} + +func (_ *Parser) Parse(_ string, _ Keyfunc) (*Token, error) { + return nil, nil +} + +func (_ *Parser) ParseUnverified(_ string, _ Claims) (*Token, []string, error) { + return nil, nil, nil +} + +func (_ *Parser) ParseWithClaims(_ string, _ Claims, _ Keyfunc) (*Token, error) { + return nil, nil +} + +type ParserOption func(*Parser) + +type RegisteredClaims struct { + Issuer string + Subject string + Audience ClaimStrings + ExpiresAt *NumericDate + NotBefore *NumericDate + IssuedAt *NumericDate + ID string +} + +func (_ RegisteredClaims) GetAudience() (ClaimStrings, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetExpirationTime() (*NumericDate, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetIssuedAt() (*NumericDate, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetIssuer() (string, error) { + return "", nil +} + +func (_ RegisteredClaims) GetNotBefore() (*NumericDate, error) { + return nil, nil +} + +func (_ RegisteredClaims) GetSubject() (string, error) { + return "", nil +} + +type SigningMethod interface { + Alg() string + Sign(_ string, _ interface{}) ([]byte, error) + Verify(_ string, _ []byte, _ interface{}) error +} + +type Token struct { + Raw string + Method SigningMethod + Header map[string]interface{} + Claims Claims + Signature []byte + Valid bool +} + +func (_ *Token) EncodeSegment(_ []byte) string { + return "" +} + +func (_ *Token) SignedString(_ interface{}) (string, error) { + return "", nil +} + +func (_ *Token) SigningString() (string, error) { + return "", nil +} diff --git a/go/ql/test/experimental/CWE-347/vendor/modules.txt b/go/ql/test/experimental/CWE-347/vendor/modules.txt new file mode 100644 index 000000000000..01144bc92497 --- /dev/null +++ b/go/ql/test/experimental/CWE-347/vendor/modules.txt @@ -0,0 +1,84 @@ +# github.com/gin-gonic/gin v1.9.1 +## explicit +github.com/gin-gonic/gin +# github.com/go-jose/go-jose/v3 v3.0.0 +## explicit +github.com/go-jose/go-jose/v3 +# github.com/golang-jwt/jwt/v5 v5.0.0 +## explicit +github.com/golang-jwt/jwt/v5 +# github.com/bytedance/sonic v1.9.1 +## explicit +github.com/bytedance/sonic +# github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 +## explicit +github.com/chenzhuoyu/base64x +# github.com/gabriel-vasile/mimetype v1.4.2 +## explicit +github.com/gabriel-vasile/mimetype +# github.com/gin-contrib/sse v0.1.0 +## explicit +github.com/gin-contrib/sse +# github.com/go-playground/locales v0.14.1 +## explicit +github.com/go-playground/locales +# github.com/go-playground/universal-translator v0.18.1 +## explicit +github.com/go-playground/universal-translator +# github.com/go-playground/validator/v10 v10.14.0 +## explicit +github.com/go-playground/validator/v10 +# github.com/goccy/go-json v0.10.2 +## explicit +github.com/goccy/go-json +# github.com/json-iterator/go v1.1.12 +## explicit +github.com/json-iterator/go +# github.com/klauspost/cpuid/v2 v2.2.4 +## explicit +github.com/klauspost/cpuid/v2 +# github.com/leodido/go-urn v1.2.4 +## explicit +github.com/leodido/go-urn +# github.com/mattn/go-isatty v0.0.19 +## explicit +github.com/mattn/go-isatty +# github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd +## explicit +github.com/modern-go/concurrent +# github.com/modern-go/reflect2 v1.0.2 +## explicit +github.com/modern-go/reflect2 +# github.com/pelletier/go-toml/v2 v2.0.8 +## explicit +github.com/pelletier/go-toml/v2 +# github.com/twitchyliquid64/golang-asm v0.15.1 +## explicit +github.com/twitchyliquid64/golang-asm +# github.com/ugorji/go/codec v1.2.11 +## explicit +github.com/ugorji/go/codec +# golang.org/x/arch v0.3.0 +## explicit +golang.org/x/arch +# golang.org/x/net v0.10.0 +## explicit +golang.org/x/net +# golang.org/x/sys v0.11.0 +## explicit +golang.org/x/sys +# golang.org/x/text v0.12.0 +## explicit +golang.org/x/text +# google.golang.org/protobuf v1.30.0 +## explicit +google.golang.org/protobuf +# gopkg.in/yaml.v3 v3.0.1 +## explicit +gopkg.in/yaml.v3 +# github.com/google/go-cmp v0.5.9 +## explicit +github.com/google/go-cmp +# golang.org/x/crypto v0.12.0 +## explicit +golang.org/x/crypto