This commit is contained in:
Kevin Stubbings
2023-11-21 14:27:17 -08:00
parent 9958ad904c
commit d7e2fbc11d
7 changed files with 221 additions and 150 deletions

View File

@@ -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() =

View File

@@ -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 }
}
}

View File

@@ -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()

View File

@@ -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` |

View 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))
}

View File

@@ -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 (

View File

@@ -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