Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rulesets: add more WAF coverage #663

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 76 additions & 53 deletions rulesets.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,59 +11,41 @@ import (
)

const (
// RulesetKindRoot is definition for an account level ruleset.
RulesetKindRoot RulesetKind = "root"

// RulesetKindCustom is the user defined rulesets.
RulesetKindCustom RulesetKind = "custom"

// RulesetKindManaged denotes Cloudflare managed rulesets.
RulesetKindCustom RulesetKind = "custom"
RulesetKindManaged RulesetKind = "managed"
RulesetKindRoot RulesetKind = "root"
RulesetKindSchema RulesetKind = "schema"
RulesetKindZone RulesetKind = "zone"

// RulesetKindSchema denotes a schema ruleset.
RulesetKindSchema RulesetKind = "schema"

// RulesetKindZone expresses a zone level ruleset.
RulesetKindZone RulesetKind = "zone"

// RulesetPhaseDDoSL7 phase runs during DDoS mitigation stage.
RulesetPhaseDDoSL7 RulesetPhase = "ddos_l7"

// RulesetPhaseMagicTransit phase is invoked when traffic is routed via Magic
// Transit.
RulesetPhaseMagicTransit RulesetPhase = "magic_transit"

// RulesetPhaseHTTPRequestMain runs in the primary part of the HTTP request.
RulesetPhaseHTTPRequestMain RulesetPhase = "http_request_main"

// RulesetPhaseHTTPRequestFirewallCustom runs on custom firewall rulesets.
RulesetPhaseHTTPRequestFirewallCustom RulesetPhase = "http_request_firewall_custom"

// RulesetPhaseHTTPRequestFirewallManaged runs for Cloudflare managed rulesets.
RulesetPhaseDDoSL7 RulesetPhase = "ddos_l7"
RulesetPhaseHTTPRequestFirewallCustom RulesetPhase = "http_request_firewall_custom"
RulesetPhaseHTTPRequestFirewallManaged RulesetPhase = "http_request_firewall_managed"

// RulesetPhaseHTTPRequestTransform is performed at the HTTP request
// transformation phase.
RulesetPhaseHTTPRequestTransform RulesetPhase = "http_request_transform"

// RulesetPhaseHTTPRequestSanitize is run during the HTTP request sanitisation
// phase.
RulesetPhaseHTTPRequestSanitize RulesetPhase = "http_request_sanitize"

// RulesetRuleActionSkip represents the "skip" action.
RulesetRuleActionSkip RulesetRuleAction = "skip"

// RulesetRuleActionBlock represents the "block" action.
RulesetRuleActionBlock RulesetRuleAction = "block"

// RulesetRuleActionJSChallenge represents the "js_challenge" action.
RulesetRuleActionJSChallenge RulesetRuleAction = "js_challenge"

// RulesetRuleActionChallenge represents the "challenge" action.
RulesetRuleActionChallenge RulesetRuleAction = "challenge"

// RulesetRuleActionLog represents the "log" action.
RulesetRuleActionLog RulesetRuleAction = "log"
RulesetPhaseHTTPRequestMain RulesetPhase = "http_request_main"
RulesetPhaseHTTPRequestSanitize RulesetPhase = "http_request_sanitize"
RulesetPhaseHTTPRequestTransform RulesetPhase = "http_request_transform"
RulesetPhaseMagicTransit RulesetPhase = "magic_transit"

RulesetRuleActionBlock RulesetRuleAction = "block"
RulesetRuleActionChallenge RulesetRuleAction = "challenge"
RulesetRuleActionDDoSDynamic RulesetRuleAction = "ddos_dynamic"
RulesetRuleActionExecute RulesetRuleAction = "execute"
RulesetRuleActionForceConnectionClose RulesetRuleAction = "force_connection_close"
RulesetRuleActionJSChallenge RulesetRuleAction = "js_challenge"
RulesetRuleActionLog RulesetRuleAction = "log"
RulesetRuleActionRewrite RulesetRuleAction = "rewrite"
RulesetRuleActionScore RulesetRuleAction = "score"
RulesetRuleActionSkip RulesetRuleAction = "skip"

RulesetActionParameterProductBIC RulesetActionParameterProduct = "bic"
RulesetActionParameterProductHOT RulesetActionParameterProduct = "hot"
RulesetActionParameterProductRateLimit RulesetActionParameterProduct = "ratelimit"
RulesetActionParameterProductSecurityLevel RulesetActionParameterProduct = "securityLevel"
RulesetActionParameterProductUABlock RulesetActionParameterProduct = "uablock"
RulesetActionParameterProductWAF RulesetActionParameterProduct = "waf"
RulesetActionParameterProductZoneLockdown RulesetActionParameterProduct = "zonelockdown"

RulesetRuleActionParametersHTTPHeaderOperationRemove RulesetRuleActionParametersHTTPHeaderOperation = "remove"
RulesetRuleActionParametersHTTPHeaderOperationSet RulesetRuleActionParametersHTTPHeaderOperation = "set"
)

// RulesetRuleAction defines a custom type that is used to express allowed
Expand All @@ -74,15 +56,21 @@ type RulesetRuleAction string
type RulesetKind string

// RulesetPhase is the custom type for defining at what point the ruleset will
// be applied and limited to expected values.
// be applied in the request pipeline.
type RulesetPhase string

type RulesetActionParameterProduct string

// RulesetRuleActionParametersHTTPHeaderOperation defines available options for
// HTTP header operations in actions.
type RulesetRuleActionParametersHTTPHeaderOperation string

// Ruleset contains the structure of a Ruleset.
type Ruleset struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Description string `json:"description"`
Kind string `json:"kind"`
Kind RulesetKind `json:"kind"`
Version string `json:"version,omitempty"`
LastUpdated *time.Time `json:"last_updated,omitempty"`
Phase RulesetPhase `json:"phase"`
Expand All @@ -92,7 +80,40 @@ type Ruleset struct {
// RulesetRuleActionParameters specifies the action parameters for a Ruleset
// rule.
type RulesetRuleActionParameters struct {
Ruleset string `json:"ruleset,omitempty"`
ID string `json:"id,omitempty"`
Ruleset string `json:"ruleset,omitempty"`
Increment int `json:"increment,omitempty"`
URI RulesetRuleActionParametersURI `json:"uri,omitempty"`
Headers map[string]RulesetRuleActionParametersHTTPHeader `json:"headers,omitempty"`
Products []RulesetActionParameterProduct `json:"products,omitempty"`
}

// RulesetRuleActionParametersURI holds the URI struct for an action parameter.
type RulesetRuleActionParametersURI struct {
Path RulesetRuleActionParametersURIPath `json:"path,omitempty"`
Query RulesetRuleActionParametersURIQuery `json:"query,omitempty"`
Origin bool `json:"origin,omitempty"`
}

// RulesetRuleActionParametersURIPath holds the path specific portion of a URI
// action parameter.
type RulesetRuleActionParametersURIPath struct {
Expression string `json:"expression,omitempty"`
}

// RulesetRuleActionParametersURIQuery holds the query specific portion of a URI
// action parameter.
type RulesetRuleActionParametersURIQuery struct {
Value string `json:"value,omitempty"`
Expression string `json:"expression,omitempty"`
}

// RulesetRuleActionParametersHTTPHeader is the definition for define action
// parameters that involve HTTP headers.
type RulesetRuleActionParametersHTTPHeader struct {
Operation string `json:"operation,omitempty"`
Value string `json:"value,omitempty"`
Expression string `json:"expression,omitempty"`
}

// RulesetRule contains information about a single Ruleset Rule.
Expand All @@ -106,6 +127,8 @@ type RulesetRule struct {
LastUpdated *time.Time `json:"last_updated,omitempty"`
Ref string `json:"ref,omitempty"`
Enabled bool `json:"enabled"`
Categories []string `json:"categories,omitempty"`
ScoreThreshold int `json:"score_threshold,omitempty"`
}

// UpdateRulesetRequest is the representation of a Ruleset update.
Expand Down
89 changes: 88 additions & 1 deletion rulesets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func TestListRulesets(t *testing.T) {
}
}

func TestGetRuleset(t *testing.T) {
func TestGetRuleset_MagicTransit(t *testing.T) {
setup()
defer teardown()

Expand Down Expand Up @@ -112,6 +112,93 @@ func TestGetRuleset(t *testing.T) {
}
}

func TestGetRuleset_WAF(t *testing.T) {
setup()
defer teardown()

handler := func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method)
w.Header().Set("content-type", "application/json")
fmt.Fprint(w, `{
"result": {
"id": "70339d97bdb34195bbf054b1ebe81f76",
"name": "Cloudflare Normalization Ruleset",
"description": "Created by the Cloudflare security team, this ruleset provides normalization on the URL path",
"kind": "managed",
"version": "1",
"rules": [
{
"id": "78723a9e0c7c4c6dbec5684cb766231d",
"version": "1",
"action": "rewrite",
"action_parameters": {
"uri": {
"path": {
"expression": "normalize_url_path(raw.http.request.uri.path)"
},
"origin": false
}
},
"description": "Normalization on the URL path, without propagating it to the origin",
"last_updated": "2020-12-18T09:28:09.655749Z",
"ref": "272936dc447b41fe976255ff6b768ec0",
"enabled": true
}
],
"last_updated": "2020-12-18T09:28:09.655749Z",
"phase": "http_request_sanitize"
},
"success": true,
"errors": [],
"messages": []
}`)
}

mux.HandleFunc("/accounts/"+testAccountID+"/rulesets/b232b534beea4e00a21dcbb7a8a545e9", handler)
mux.HandleFunc("/zones/"+testZoneID+"/rulesets/b232b534beea4e00a21dcbb7a8a545e9", handler)

lastUpdated, _ := time.Parse(time.RFC3339, "2020-12-18T09:28:09.655749Z")

rules := []RulesetRule{{
ID: "78723a9e0c7c4c6dbec5684cb766231d",
Version: "1",
Action: RulesetRuleActionRewrite,
ActionParameters: &RulesetRuleActionParameters{
URI: RulesetRuleActionParametersURI{
Path: RulesetRuleActionParametersURIPath{
Expression: "normalize_url_path(raw.http.request.uri.path)",
},
Origin: false,
},
},
Description: "Normalization on the URL path, without propagating it to the origin",
LastUpdated: &lastUpdated,
Ref: "272936dc447b41fe976255ff6b768ec0",
Enabled: true,
}}

want := Ruleset{
ID: "70339d97bdb34195bbf054b1ebe81f76",
Name: "Cloudflare Normalization Ruleset",
Description: "Created by the Cloudflare security team, this ruleset provides normalization on the URL path",
Kind: RulesetKindManaged,
Version: "1",
LastUpdated: &lastUpdated,
Phase: RulesetPhaseHTTPRequestSanitize,
Rules: rules,
}

zoneActual, err := client.GetZoneRuleset(context.Background(), testZoneID, "b232b534beea4e00a21dcbb7a8a545e9")
if assert.NoError(t, err) {
assert.Equal(t, want, zoneActual)
}

accountActual, err := client.GetAccountRuleset(context.Background(), testAccountID, "b232b534beea4e00a21dcbb7a8a545e9")
if assert.NoError(t, err) {
assert.Equal(t, want, accountActual)
}
}

func TestCreateRuleset(t *testing.T) {
setup()
defer teardown()
Expand Down