diff --git a/data/botPolicies.yaml b/data/botPolicies.yaml index d27c342..da5245a 100644 --- a/data/botPolicies.yaml +++ b/data/botPolicies.yaml @@ -44,6 +44,10 @@ bots: # algorithm: slow # intentionally waste CPU cycles and time # Challenge clients with "Mozilla" or "Opera" in their user-agent string -- import: (data)/common/legacy-challenge-everything.yaml +#- import: (data)/common/legacy-challenge-everything.yaml + +- name: reject-browsers + action: DENY + expression: userAgent.isBrowser() dnsbl: false diff --git a/data/common/challenge-browser-like.yaml b/data/common/challenge-browser-like.yaml index afd4cc8..c5be6c2 100644 --- a/data/common/challenge-browser-like.yaml +++ b/data/common/challenge-browser-like.yaml @@ -5,12 +5,4 @@ all: - '"X-Http-Version" in headers' - headers["X-Http-Version"] == "HTTP/1.1" - - >- - ( userAgent.contains("Mozilla") - || userAgent.contains("Opera") - || userAgent.contains("Safari") - || userAgent.contains("Edge") - || userAgent.contains("Gecko") - || userAgent.contains("Windows") - || userAgent.contains("Linux") - ) \ No newline at end of file + - userAgent.isBrowserLike() \ No newline at end of file diff --git a/data/common/legacy-challenge-everything.yaml b/data/common/legacy-challenge-everything.yaml index 722d9cc..084f86b 100644 --- a/data/common/legacy-challenge-everything.yaml +++ b/data/common/legacy-challenge-everything.yaml @@ -1,5 +1,4 @@ # Generic catchall rule - name: generic-browser - user_agent_regex: >- - Mozilla|Opera + expression: userAgent.isBrowserLike() action: CHALLENGE \ No newline at end of file diff --git a/lib/policy/expressions/environment.go b/lib/policy/expressions/environment.go index 5b90399..c0863eb 100644 --- a/lib/policy/expressions/environment.go +++ b/lib/policy/expressions/environment.go @@ -1,7 +1,11 @@ package expressions import ( + "strings" + "github.com/google/cel-go/cel" + "github.com/google/cel-go/common/types" + "github.com/google/cel-go/common/types/ref" "github.com/google/cel-go/ext" ) @@ -20,6 +24,8 @@ func NewEnvironment() (*cel.Env, error) { cel.DefaultUTCTimeZone(true), // Variables exposed to CEL programs: + + // Request metadata cel.Variable("remoteAddress", cel.StringType), cel.Variable("host", cel.StringType), cel.Variable("method", cel.StringType), @@ -28,11 +34,37 @@ func NewEnvironment() (*cel.Env, error) { cel.Variable("query", cel.MapType(cel.StringType, cel.StringType)), cel.Variable("headers", cel.MapType(cel.StringType, cel.StringType)), + // System load metadata cel.Variable("load_1m", cel.DoubleType), cel.Variable("load_5m", cel.DoubleType), cel.Variable("load_15m", cel.DoubleType), // Functions exposed to CEL programs: + + // userAgent.isBrowserLike() method, used to detect if a user agent is likely a browser + // based on shibboleth words in the User-Agent string. + cel.Function("isBrowserLike", + cel.MemberOverload("userAgent_isBrowserLike_string", + []*cel.Type{cel.StringType}, + cel.BoolType, + cel.UnaryBinding(func(userAgentVal ref.Val) ref.Val { + var userAgent string + switch v := userAgentVal.Value().(type) { + case string: + userAgent = v + default: + return types.NewErr("invalid type %T", userAgentVal) + } + + switch { + case strings.Contains(userAgent, "Mozilla"), strings.Contains(userAgent, "Opera"), strings.Contains(userAgent, "Gecko"), strings.Contains(userAgent, "WebKit"), strings.Contains(userAgent, "Apple"), strings.Contains(userAgent, "Chrome"), strings.Contains(userAgent, "Windows"), strings.Contains(userAgent, "Linux"): + return types.Bool(true) + default: + return types.Bool(false) + } + }), + ), + ), ) }