Skip to content

Commit

Permalink
Merge pull request cloudflare#663 from jacobbednarz/extend-rulesets-f…
Browse files Browse the repository at this point in the history
…or-waf-specifics

rulesets: add more WAF coverage
  • Loading branch information
jacobbednarz authored Jul 8, 2021
2 parents 5c255b7 + 95eaa3d commit 37b72dd
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 54 deletions.
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

0 comments on commit 37b72dd

Please sign in to comment.