mirror of
https://github.com/github/codeql.git
synced 2026-04-21 23:14:03 +02:00
Merge pull request #18543 from owen-mc/go/misc-improvements-rs-cors
Go: miscellaneous improvements rs cors models
This commit is contained in:
@@ -128,7 +128,7 @@ module GinCors {
|
||||
|
||||
GinConfig() {
|
||||
this = v.getBaseVariable().getSourceVariable() and
|
||||
exists(Type t | t.hasQualifiedName(packagePath(), "Config") | v.getType() = t)
|
||||
v.getType().hasQualifiedName(packagePath(), "Config")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,70 +1,52 @@
|
||||
/**
|
||||
* Provides classes for modeling the `github.com/rs/cors` package.
|
||||
*/
|
||||
/** Provides classes for modeling the `github.com/rs/cors` package. */
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* An abstract class for modeling the Go CORS handler model origin write.
|
||||
*/
|
||||
/** An abstract class for modeling the Go CORS handler model origin write. */
|
||||
abstract class UniversalOriginWrite extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
/** Gets the config variable holding header values. */
|
||||
abstract DataFlow::Node getBase();
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
/** Gets the config variable holding header values. */
|
||||
abstract Variable getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class for modeling the Go CORS handler model allow all origins write.
|
||||
* An abstract class for modeling the Go CORS handler model allow all origins
|
||||
* write.
|
||||
*/
|
||||
abstract class UniversalAllowAllOriginsWrite extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
/** Gets the config variable holding header values. */
|
||||
abstract DataFlow::Node getBase();
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
/** Gets the config variable holding header values. */
|
||||
abstract Variable getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract class for modeling the Go CORS handler model allow credentials write.
|
||||
* An abstract class for modeling the Go CORS handler model allow credentials
|
||||
* write.
|
||||
*/
|
||||
abstract class UniversalAllowCredentialsWrite extends DataFlow::ExprNode {
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
/** Gets the config struct holding header values. */
|
||||
abstract DataFlow::Node getBase();
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
/** Gets the config variable holding header values. */
|
||||
abstract Variable getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides classes for modeling the `github.com/rs/cors` package.
|
||||
*/
|
||||
/** Provides classes for modeling the `github.com/rs/cors` package. */
|
||||
module RsCors {
|
||||
/** Gets the package name `github.com/gin-gonic/gin`. */
|
||||
string packagePath() { result = package("github.com/rs/cors", "") }
|
||||
|
||||
/**
|
||||
* A new function create a new rs Handler
|
||||
*/
|
||||
/** The `New` function that creates a new rs Handler. */
|
||||
class New extends Function {
|
||||
New() { exists(Function f | f.hasQualifiedName(packagePath(), "New") | this = f) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Credentials header
|
||||
* A write to the value of Access-Control-Allow-Credentials header.
|
||||
*/
|
||||
class AllowCredentialsWrite extends UniversalAllowCredentialsWrite {
|
||||
DataFlow::Node base;
|
||||
@@ -77,14 +59,10 @@ module RsCors {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options struct holding header values
|
||||
*/
|
||||
/** Gets the options struct holding header values. */
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get options variable holding header values
|
||||
*/
|
||||
/** Gets the options variable holding header values. */
|
||||
override RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
@@ -97,9 +75,7 @@ module RsCors {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins header
|
||||
*/
|
||||
/** A write to the value of Access-Control-Allow-Origins header. */
|
||||
class AllowOriginsWrite extends UniversalOriginWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
@@ -111,14 +87,10 @@ module RsCors {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options struct holding header values
|
||||
*/
|
||||
/** Gets the options struct holding header values. */
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get options variable holding header values
|
||||
*/
|
||||
/** Gets the options variable holding header values. */
|
||||
override RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
@@ -132,7 +104,8 @@ module RsCors {
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins of value "*", overriding AllowOrigins
|
||||
* A write to the value of Access-Control-Allow-Origins of value "*",
|
||||
* overriding `AllowOrigins`.
|
||||
*/
|
||||
class AllowAllOriginsWrite extends UniversalAllowAllOriginsWrite {
|
||||
DataFlow::Node base;
|
||||
@@ -145,14 +118,10 @@ module RsCors {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options struct holding header values
|
||||
*/
|
||||
/** Gets the options struct holding header values. */
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get options variable holding header values
|
||||
*/
|
||||
/** Gets options variable holding header values. */
|
||||
override RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
@@ -165,20 +134,16 @@ module RsCors {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable of type Options that holds the headers to be set.
|
||||
*/
|
||||
/** A variable of type Options that holds the headers to be set. */
|
||||
class RsOptions extends Variable {
|
||||
SsaWithFields v;
|
||||
|
||||
RsOptions() {
|
||||
this = v.getBaseVariable().getSourceVariable() and
|
||||
exists(Type t | t.hasQualifiedName(packagePath(), "Options") | v.getType() = t)
|
||||
v.getType().hasQualifiedName(packagePath(), "Options")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variable declaration of Options
|
||||
*/
|
||||
/** Gets the SSA variable declaration of Options. */
|
||||
SsaWithFields getV() { result = v }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,8 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
/*
|
||||
** Function is vulnerable due to AllowAllOrigins = true aka Access-Control-Allow-Origin: null
|
||||
*/
|
||||
func vunlnerable() {
|
||||
// Function is vulnerable due to AllowAllOrigins = true aka Access-Control-Allow-Origin: null
|
||||
func vulnerable1() {
|
||||
router := gin.Default()
|
||||
// CORS for https://foo.com and null
|
||||
// - PUT and PATCH methods
|
||||
@@ -25,7 +23,7 @@ func vunlnerable() {
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
config_vulnerable.AllowOrigins = []string{"null", "https://foo.com"}
|
||||
config_vulnerable.AllowOrigins = []string{"null", "https://foo.com"} // $ Alert
|
||||
router.Use(cors.New(config_vulnerable))
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "hello world")
|
||||
@@ -33,9 +31,30 @@ func vunlnerable() {
|
||||
router.Run()
|
||||
}
|
||||
|
||||
/*
|
||||
** Function is safe due to hardcoded origin and AllowCredentials: true
|
||||
*/
|
||||
// Function is vulnerable due to AllowAllOrigins = true aka Access-Control-Allow-Origin: null
|
||||
func vulnerable2() {
|
||||
router := gin.Default()
|
||||
// CORS for https://foo.com and null
|
||||
// - PUT and PATCH methods
|
||||
// - Origin header
|
||||
// - Credentials share
|
||||
// - Preflight requests cached for 12 hours
|
||||
config_vulnerable := cors.Config{
|
||||
AllowMethods: []string{"PUT", "PATCH"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
AllowOrigins: []string{"null", "https://foo.com"}, // $ Alert
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
router.Use(cors.New(config_vulnerable))
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "hello world")
|
||||
})
|
||||
router.Run()
|
||||
}
|
||||
|
||||
// Function is safe due to hardcoded origin and AllowCredentials: true
|
||||
func safe() {
|
||||
router := gin.Default()
|
||||
// CORS for https://foo.com origin, allowing:
|
||||
@@ -58,10 +77,8 @@ func safe() {
|
||||
router.Run()
|
||||
}
|
||||
|
||||
/*
|
||||
** Function is safe due to AllowAllOrigins = true aka Access-Control-Allow-Origin: *
|
||||
*/
|
||||
func AllowAllTrue() {
|
||||
// Function is safe due to AllowAllOrigins = true aka Access-Control-Allow-Origin: *
|
||||
func AllowAllTrue1() {
|
||||
router := gin.Default()
|
||||
// CORS for "*" origin, allowing:
|
||||
// - PUT and PATCH methods
|
||||
@@ -84,6 +101,30 @@ func AllowAllTrue() {
|
||||
router.Run()
|
||||
}
|
||||
|
||||
// Function is safe due to AllowAllOrigins = true aka Access-Control-Allow-Origin: *
|
||||
func AllowAllTrue2() {
|
||||
router := gin.Default()
|
||||
// CORS for "*" origin, allowing:
|
||||
// - PUT and PATCH methods
|
||||
// - Origin header
|
||||
// - Credentials share
|
||||
// - Preflight requests cached for 12 hours
|
||||
config_allowall := cors.Config{
|
||||
AllowMethods: []string{"PUT", "PATCH"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowAllOrigins: true,
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
config_allowall.AllowOrigins = []string{"null"}
|
||||
router.Use(cors.New(config_allowall))
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "hello world")
|
||||
})
|
||||
router.Run()
|
||||
}
|
||||
|
||||
func NoVariableVulnerable() {
|
||||
router := gin.Default()
|
||||
// CORS for https://foo.com origin, allowing:
|
||||
@@ -95,7 +136,7 @@ func NoVariableVulnerable() {
|
||||
AllowMethods: []string{"GET", "POST"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowOrigins: []string{"null", "https://foo.com"},
|
||||
AllowOrigins: []string{"null", "https://foo.com"}, // $ Alert
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}))
|
||||
@@ -104,3 +145,39 @@ func NoVariableVulnerable() {
|
||||
})
|
||||
router.Run()
|
||||
}
|
||||
|
||||
var global_config1 = cors.Config{
|
||||
AllowMethods: []string{"PUT", "PATCH"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
AllowOrigins: []string{"null", "https://foo.com"}, // $ Alert
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
|
||||
func vulnerableGlobal1() {
|
||||
router := gin.Default()
|
||||
router.Use(cors.New(global_config1))
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "hello world")
|
||||
})
|
||||
router.Run()
|
||||
}
|
||||
|
||||
var global_config2 = cors.Config{
|
||||
AllowMethods: []string{"PUT", "PATCH"},
|
||||
AllowHeaders: []string{"Origin"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}
|
||||
|
||||
func vulnerableGlobal2() {
|
||||
router := gin.Default()
|
||||
global_config2.AllowOrigins = []string{"null", "https://foo.com"} // $ MISSING: Alert
|
||||
router.Use(cors.New(global_config2))
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusOK, "hello world")
|
||||
})
|
||||
router.Run()
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
| CorsGin.go:28:35:28:69 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsGin.go:98:21:98:55 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsGin.go:26:35:26:69 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsGin.go:47:21:47:55 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsGin.go:139:21:139:55 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsGin.go:154:20:154:54 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsMisconfiguration.go:26:4:26:56 | call to Set | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsMisconfiguration.go:32:4:32:42 | call to Set | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| CorsMisconfiguration.go:53:4:53:44 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
|
||||
| CorsMisconfiguration.go:60:4:60:56 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
|
||||
| CorsMisconfiguration.go:67:5:67:57 | call to Set | access-control-allow-origin header is set to a user-defined value, and access-control-allow-credentials is set to `true` |
|
||||
| RsCors.go:11:21:11:59 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| RsCors.go:31:23:31:61 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
| RsCors.go:59:20:59:58 | slice literal | access-control-allow-origin header is set to `null`, and access-control-allow-credentials is set to `true` |
|
||||
|
||||
@@ -23,13 +23,13 @@ func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
// BAD: 'null' origin is allowed,
|
||||
// and Access-Control-Allow-Credentials is set to 'true'.
|
||||
w.Header().Set("Access-Control-Allow-Origin", "null")
|
||||
w.Header().Set("Access-Control-Allow-Origin", "null") // $ Alert
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
})
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
// BAD: 'null' origin is allowed,
|
||||
// and `Access-Control-Allow-Credentials` is set to 'true':
|
||||
w.Header().Set(HeaderAllowOrigin, Null)
|
||||
w.Header().Set(HeaderAllowOrigin, Null) // $ Alert
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
})
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -50,21 +50,21 @@ func main() {
|
||||
// BAD: the `Access-Control-Allow-Origin` header is set using a user-defined value,
|
||||
// and `Access-Control-Allow-Credentials` is set to 'true':
|
||||
origin := req.Header.Get("origin")
|
||||
w.Header().Set(HeaderAllowOrigin, origin)
|
||||
w.Header().Set(HeaderAllowOrigin, origin) // $ Alert
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
})
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
// BAD: the `Access-Control-Allow-Origin` header is set using a user-defined value,
|
||||
// and `Access-Control-Allow-Credentials` is set to 'true':
|
||||
origin := req.Header.Get("origin")
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin) // $ Alert
|
||||
w.Header().Set(HeaderAllowCredentials, "true")
|
||||
})
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
|
||||
// BAD: the `Access-Control-Allow-Origin` header is set using a user-defined value,
|
||||
// and `Access-Control-Allow-Credentials` is set to 'true':
|
||||
if origin := req.Header.Get("Origin"); origin != "" {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin) // $ Alert
|
||||
}
|
||||
w.Header().Set(HeaderAllowCredentials, "true")
|
||||
})
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
experimental/CWE-942/CorsMisconfiguration.ql
|
||||
query: experimental/CWE-942/CorsMisconfiguration.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
func rs_vulnerable() {
|
||||
func rs_vulnerable1() {
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"null", "http://foo.com:8080"},
|
||||
AllowedOrigins: []string{"null", "http://foo.com:8080"}, // $ Alert
|
||||
AllowCredentials: true,
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
Debug: true,
|
||||
@@ -22,9 +22,26 @@ func rs_vulnerable() {
|
||||
http.ListenAndServe(":8080", c.Handler(handler))
|
||||
}
|
||||
|
||||
func rs_vulnerable2() {
|
||||
opt := cors.Options{
|
||||
AllowCredentials: true,
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
Debug: true,
|
||||
}
|
||||
opt.AllowedOrigins = []string{"null", "http://foo.com:8080"} // $ Alert
|
||||
c := cors.New(opt)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":8080", c.Handler(handler))
|
||||
}
|
||||
|
||||
func rs_safe() {
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"http://foo.com:8080"},
|
||||
AllowedOrigins: []string{"http://foo.com:8080"}, // GOOD
|
||||
AllowCredentials: true,
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
Debug: true,
|
||||
@@ -37,3 +54,40 @@ func rs_safe() {
|
||||
|
||||
http.ListenAndServe(":8080", c.Handler(handler))
|
||||
}
|
||||
|
||||
var globalCorsOptions1 = cors.Options{
|
||||
AllowedOrigins: []string{"null", "http://foo.com:8080"}, // $ Alert
|
||||
AllowCredentials: true,
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
Debug: true,
|
||||
}
|
||||
|
||||
func rs_vulnerable_global1() {
|
||||
c := cors.New(globalCorsOptions1)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":8080", c.Handler(handler))
|
||||
|
||||
}
|
||||
|
||||
var globalCorsOptions2 cors.Options
|
||||
|
||||
func rs_vulnerable_global2() {
|
||||
globalCorsOptions2.AllowedOrigins = []string{"null", "http://foo.com:8080"} // $ MISSING: Alert
|
||||
globalCorsOptions2.AllowCredentials = true
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
globalCorsOptions2.Debug = true
|
||||
c := cors.New(globalCorsOptions1)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte("{\"hello\": \"world\"}"))
|
||||
})
|
||||
|
||||
http.ListenAndServe(":8080", c.Handler(handler))
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user