mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Finish
This commit is contained in:
@@ -21,7 +21,7 @@ module GinCors {
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Credentials header
|
||||
*/
|
||||
class AllowCredentialsWrite extends DataFlow::ExprNode {
|
||||
class AllowCredentialsWrite extends UniversalAllowCredentialsWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowCredentialsWrite() {
|
||||
@@ -35,12 +35,12 @@ module GinCors {
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
DataFlow::Node getBase() { result = base }
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
GinConfig getConfig() {
|
||||
override GinConfig getConfig() {
|
||||
exists(GinConfig gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
@@ -55,7 +55,7 @@ module GinCors {
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins header
|
||||
*/
|
||||
class AllowOriginsWrite extends DataFlow::ExprNode {
|
||||
class AllowOriginsWrite extends UniversalOriginWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowOriginsWrite() {
|
||||
@@ -69,12 +69,12 @@ module GinCors {
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
DataFlow::Node getBase() { result = base }
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
GinConfig getConfig() {
|
||||
override GinConfig getConfig() {
|
||||
exists(GinConfig gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
@@ -89,7 +89,7 @@ module GinCors {
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins of value "*", overriding AllowOrigins
|
||||
*/
|
||||
class AllowAllOriginsWrite extends DataFlow::ExprNode {
|
||||
class AllowAllOriginsWrite extends UniversalAllowAllOriginsWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowAllOriginsWrite() {
|
||||
@@ -103,12 +103,12 @@ module GinCors {
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
DataFlow::Node getBase() { result = base }
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
GinConfig getConfig() {
|
||||
override GinConfig getConfig() {
|
||||
exists(GinConfig gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
|
||||
@@ -2,138 +2,165 @@
|
||||
* Provides classes for modeling the `github.com/rs/cors` package.
|
||||
*/
|
||||
|
||||
import go
|
||||
import go
|
||||
|
||||
/**
|
||||
* 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 gin Handler that passed to gin as middleware
|
||||
*/
|
||||
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
|
||||
*/
|
||||
class AllowCredentialsWrite extends DataFlow::ExprNode {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowCredentialsWrite() {
|
||||
exists(Field f, Write w |
|
||||
f.hasQualifiedName(packagePath(), "Options", "AllowCredentials") and
|
||||
w.writesField(base, f, this) and
|
||||
this.getType() instanceof BoolType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
base.asInstruction() or
|
||||
gc.getV().getAUse() = base
|
||||
) and
|
||||
result = gc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins header
|
||||
*/
|
||||
class AllowOriginsWrite extends DataFlow::ExprNode {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowOriginsWrite() {
|
||||
exists(Field f, Write w |
|
||||
f.hasQualifiedName(packagePath(), "Options", "AllowedOrigins") and
|
||||
w.writesField(base, f, this) and
|
||||
this.asExpr() instanceof SliceLit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
base.asInstruction() or
|
||||
gc.getV().getAUse() = base
|
||||
) and
|
||||
result = gc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins of value "*", overriding AllowOrigins
|
||||
*/
|
||||
class AllowAllOriginsWrite extends DataFlow::ExprNode {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowAllOriginsWrite() {
|
||||
exists(Field f, Write w |
|
||||
f.hasQualifiedName(packagePath(), "Options", "AllowAllOrigins") and
|
||||
w.writesField(base, f, this) and
|
||||
this.getType() instanceof BoolType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config struct holding header values
|
||||
*/
|
||||
DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get config variable holding header values
|
||||
*/
|
||||
RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
base.asInstruction() or
|
||||
gc.getV().getAUse() = base
|
||||
) and
|
||||
result = gc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable of type Config 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variable declaration of GinConfig
|
||||
*/
|
||||
SsaWithFields getV() { result = v }
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Provides abstract class for modeling the Go CORS handler model origin write.
|
||||
*/
|
||||
abstract class UniversalOriginWrite extends DataFlow::ExprNode {
|
||||
abstract DataFlow::Node getBase();
|
||||
|
||||
abstract Variable getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides abstract class for modeling the Go CORS handler model allow all origins write.
|
||||
*/
|
||||
abstract class UniversalAllowAllOriginsWrite extends DataFlow::ExprNode {
|
||||
abstract DataFlow::Node getBase();
|
||||
|
||||
abstract Variable getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides abstract class for modeling the Go CORS handler model allow credentials write.
|
||||
*/
|
||||
abstract class UniversalAllowCredentialsWrite extends DataFlow::ExprNode {
|
||||
abstract DataFlow::Node getBase();
|
||||
|
||||
abstract Variable getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
class AllowCredentialsWrite extends UniversalAllowCredentialsWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowCredentialsWrite() {
|
||||
exists(Field f, Write w |
|
||||
f.hasQualifiedName(packagePath(), "Options", "AllowCredentials") and
|
||||
w.writesField(base, f, this) and
|
||||
this.getType() instanceof BoolType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options struct holding header values
|
||||
*/
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get options variable holding header values
|
||||
*/
|
||||
override RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
base.asInstruction() or
|
||||
gc.getV().getAUse() = base
|
||||
) and
|
||||
result = gc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins header
|
||||
*/
|
||||
class AllowOriginsWrite extends UniversalOriginWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowOriginsWrite() {
|
||||
exists(Field f, Write w |
|
||||
f.hasQualifiedName(packagePath(), "Options", "AllowedOrigins") and
|
||||
w.writesField(base, f, this) and
|
||||
this.asExpr() instanceof SliceLit
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options struct holding header values
|
||||
*/
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get options variable holding header values
|
||||
*/
|
||||
override RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
base.asInstruction() or
|
||||
gc.getV().getAUse() = base
|
||||
) and
|
||||
result = gc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A write to the value of Access-Control-Allow-Origins of value "*", overriding AllowOrigins
|
||||
*/
|
||||
class AllowAllOriginsWrite extends UniversalAllowAllOriginsWrite {
|
||||
DataFlow::Node base;
|
||||
|
||||
AllowAllOriginsWrite() {
|
||||
exists(Field f, Write w |
|
||||
f.hasQualifiedName(packagePath(), "Options", "AllowAllOrigins") and
|
||||
w.writesField(base, f, this) and
|
||||
this.getType() instanceof BoolType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get options struct holding header values
|
||||
*/
|
||||
override DataFlow::Node getBase() { result = base }
|
||||
|
||||
/**
|
||||
* Get options variable holding header values
|
||||
*/
|
||||
override RsOptions getConfig() {
|
||||
exists(RsOptions gc |
|
||||
(
|
||||
gc.getV().getBaseVariable().getDefinition().(SsaExplicitDefinition).getRhs() =
|
||||
base.asInstruction() or
|
||||
gc.getV().getAUse() = base
|
||||
) and
|
||||
result = gc
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get variable declaration of Options
|
||||
*/
|
||||
SsaWithFields getV() { result = v }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ module UntrustedToAllowOriginHeaderConfig implements DataFlow::ConfigSig {
|
||||
module UntrustedToAllowOriginConfigConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
|
||||
|
||||
additional predicate isSinkWrite(DataFlow::Node sink, GinCors::AllowOriginsWrite w) { sink = w }
|
||||
additional predicate isSinkWrite(DataFlow::Node sink, UniversalOriginWrite w) { sink = w }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSinkWrite(sink, _) }
|
||||
}
|
||||
@@ -102,17 +102,17 @@ predicate allowCredentialsIsSetToTrue(DataFlow::ExprNode allowOriginHW) {
|
||||
allowCredentialsHW.getResponseWriter()
|
||||
)
|
||||
or
|
||||
exists(GinCors::AllowCredentialsWrite allowCredentialsGin |
|
||||
exists(UniversalAllowCredentialsWrite allowCredentialsGin |
|
||||
allowCredentialsGin.getExpr().getBoolValue() = true
|
||||
|
|
||||
allowCredentialsGin.getConfig() = allowOriginHW.(GinCors::AllowOriginsWrite).getConfig() and
|
||||
not exists(GinCors::AllowAllOriginsWrite allowAllOrigins |
|
||||
allowCredentialsGin.getConfig() = allowOriginHW.(UniversalOriginWrite).getConfig() and
|
||||
not exists(UniversalAllowAllOriginsWrite allowAllOrigins |
|
||||
allowAllOrigins.getExpr().getBoolValue() = true and
|
||||
allowCredentialsGin.getConfig() = allowAllOrigins.getConfig()
|
||||
)
|
||||
or
|
||||
allowCredentialsGin.getBase() = allowOriginHW.(GinCors::AllowOriginsWrite).getBase() and
|
||||
not exists(GinCors::AllowAllOriginsWrite allowAllOrigins |
|
||||
allowCredentialsGin.getBase() = allowOriginHW.(UniversalOriginWrite).getBase() and
|
||||
not exists(UniversalAllowAllOriginsWrite allowAllOrigins |
|
||||
allowAllOrigins.getExpr().getBoolValue() = true and
|
||||
allowCredentialsGin.getBase() = allowAllOrigins.getBase()
|
||||
)
|
||||
@@ -150,7 +150,7 @@ predicate allowOriginIsNull(DataFlow::ExprNode allowOriginHW, string message) {
|
||||
+ " is set to `true`"
|
||||
or
|
||||
allowOriginHW
|
||||
.(GinCors::AllowOriginsWrite)
|
||||
.(UniversalOriginWrite)
|
||||
.asExpr()
|
||||
.(SliceLit)
|
||||
.getAnElement()
|
||||
|
||||
@@ -5,3 +5,4 @@
|
||||
| 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` |
|
||||
|
||||
39
go/ql/test/experimental/CWE-942/RsCors.go
Normal file
39
go/ql/test/experimental/CWE-942/RsCors.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/cors"
|
||||
)
|
||||
|
||||
func rs_vulnerable() {
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"null", "http://foo.com:8080"},
|
||||
AllowCredentials: true,
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
Debug: true,
|
||||
})
|
||||
|
||||
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"},
|
||||
AllowCredentials: true,
|
||||
// Enable Debugging for testing, consider disabling in production
|
||||
Debug: true,
|
||||
})
|
||||
|
||||
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))
|
||||
}
|
||||
@@ -5,6 +5,7 @@ go 1.21
|
||||
require (
|
||||
github.com/gin-contrib/cors v1.4.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/rs/cors v1.10.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -4,6 +4,9 @@ github.com/gin-contrib/cors
|
||||
# github.com/gin-gonic/gin v1.9.1
|
||||
## explicit
|
||||
github.com/gin-gonic/gin
|
||||
# github.com/rs/cors v1.10.1
|
||||
## explicit
|
||||
github.com/rs/cors
|
||||
# github.com/bytedance/sonic v1.9.1
|
||||
## explicit
|
||||
github.com/bytedance/sonic
|
||||
|
||||
Reference in New Issue
Block a user