github.com/fastly/go-fastly/v5@v5.3.0/fastly/waf_active_rule.go (about) 1 package fastly 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "reflect" 8 "strconv" 9 "time" 10 11 "github.com/google/jsonapi" 12 ) 13 14 // WAFActiveRuleType is used for reflection because JSONAPI wants to know what it's 15 // decoding into. 16 var WAFActiveRuleType = reflect.TypeOf(new(WAFActiveRule)) 17 18 // WAFActiveRule is the information about a WAF active rule object. 19 type WAFActiveRule struct { 20 ID string `jsonapi:"primary,waf_active_rule,omitempty"` 21 Status string `jsonapi:"attr,status,omitempty"` 22 ModSecID int `jsonapi:"attr,modsec_rule_id,omitempty"` 23 Revision int `jsonapi:"attr,revision,omitempty"` 24 Outdated bool `jsonapi:"attr,outdated,omitempty"` 25 LatestRevision int `jsonapi:"attr,latest_revision,omitempty"` 26 CreatedAt *time.Time `jsonapi:"attr,created_at,iso8601,omitempty"` 27 UpdatedAt *time.Time `jsonapi:"attr,updated_at,iso8601,omitempty"` 28 } 29 30 // WAFActiveRuleResponse represents a list of active rules - full response. 31 type WAFActiveRuleResponse struct { 32 Items []*WAFActiveRule 33 Info infoResponse 34 } 35 36 // ListWAFActiveRulesInput used as input for listing a WAF's active rules. 37 type ListWAFActiveRulesInput struct { 38 // The Web Application Firewall's ID. 39 WAFID string 40 // The Web Application Firewall's version number. 41 WAFVersionNumber int 42 // Limit results to active rules with the specified status. 43 FilterStatus string 44 // Limit results to active rules with the specified message. 45 FilterMessage string 46 // Limit results to active rules that represent the specified ModSecurity modsec_rule_id. 47 FilterModSedID string 48 // Limit the number of returned pages. 49 PageSize int 50 // Request a specific page of active rules. 51 PageNumber int 52 // Include relationships. Optional, comma-separated values. Permitted values: waf_rule_revision and waf_firewall_version. 53 Include string 54 } 55 56 func (i *ListWAFActiveRulesInput) formatFilters() map[string]string { 57 58 result := map[string]string{} 59 pairings := map[string]interface{}{ 60 "filter[status]": i.FilterStatus, 61 "filter[waf_rule_revision][message]": i.FilterMessage, 62 "filter[waf_rule_revision][modsec_rule_id]": i.FilterModSedID, 63 "page[size]": i.PageSize, 64 "page[number]": i.PageNumber, 65 "include": i.Include, 66 } 67 68 for key, value := range pairings { 69 switch value := value.(type) { 70 case string: 71 if value != "" { 72 result[key] = value 73 } 74 case int: 75 if value != 0 { 76 result[key] = strconv.Itoa(value) 77 } 78 } 79 } 80 return result 81 } 82 83 // ListWAFActiveRules returns the list of active rules for a given WAF ID. 84 func (c *Client) ListWAFActiveRules(i *ListWAFActiveRulesInput) (*WAFActiveRuleResponse, error) { 85 86 if i.WAFID == "" { 87 return nil, ErrMissingWAFID 88 } 89 90 if i.WAFVersionNumber == 0 { 91 return nil, ErrMissingWAFVersionNumber 92 } 93 94 path := fmt.Sprintf("/waf/firewalls/%s/versions/%d/active-rules", i.WAFID, i.WAFVersionNumber) 95 resp, err := c.Get(path, &RequestOptions{ 96 Params: i.formatFilters(), 97 }) 98 if err != nil { 99 return nil, err 100 } 101 102 var buf bytes.Buffer 103 tee := io.TeeReader(resp.Body, &buf) 104 105 info, err := getResponseInfo(tee) 106 if err != nil { 107 return nil, err 108 } 109 110 data, err := jsonapi.UnmarshalManyPayload(bytes.NewReader(buf.Bytes()), WAFActiveRuleType) 111 if err != nil { 112 return nil, err 113 } 114 115 wafRules := make([]*WAFActiveRule, len(data)) 116 for i := range data { 117 typed, ok := data[i].(*WAFActiveRule) 118 if !ok { 119 return nil, fmt.Errorf("got back a non-WAFActiveRule response") 120 } 121 wafRules[i] = typed 122 } 123 return &WAFActiveRuleResponse{ 124 Items: wafRules, 125 Info: info, 126 }, nil 127 } 128 129 // ListAllWAFActiveRulesInput used as input for listing all WAF active rules. 130 type ListAllWAFActiveRulesInput struct { 131 // The Web Application Firewall's ID. 132 WAFID string 133 // The Web Application Firewall's version number. 134 WAFVersionNumber int 135 // Limit results to active rules with the specified status. 136 FilterStatus string 137 // Limit results to active rules with the specified message. 138 FilterMessage string 139 // Limit results to active rules that represent the specified ModSecurity modsec_rule_id. 140 FilterModSedID string 141 // Include relationships. Optional, comma-separated values. Permitted values: waf_rule_revision and waf_firewall_version. 142 Include string 143 } 144 145 // ListAllWAFActiveRules returns the complete list of WAF active rules for a given WAF ID. It iterates through 146 // all existing pages to ensure all WAF active rules are returned at once. 147 func (c *Client) ListAllWAFActiveRules(i *ListAllWAFActiveRulesInput) (*WAFActiveRuleResponse, error) { 148 149 if i.WAFID == "" { 150 return nil, ErrMissingWAFID 151 } 152 153 if i.WAFVersionNumber == 0 { 154 return nil, ErrMissingWAFVersionNumber 155 } 156 157 currentPage := 1 158 result := &WAFActiveRuleResponse{Items: []*WAFActiveRule{}} 159 for { 160 r, err := c.ListWAFActiveRules(&ListWAFActiveRulesInput{ 161 WAFID: i.WAFID, 162 WAFVersionNumber: i.WAFVersionNumber, 163 PageNumber: currentPage, 164 PageSize: WAFPaginationPageSize, 165 Include: i.Include, 166 FilterStatus: i.FilterStatus, 167 FilterModSedID: i.FilterModSedID, 168 FilterMessage: i.FilterMessage, 169 }) 170 if err != nil { 171 return r, err 172 } 173 174 currentPage++ 175 result.Items = append(result.Items, r.Items...) 176 177 if r.Info.Links.Next == "" || len(r.Items) == 0 { 178 return result, nil 179 } 180 } 181 } 182 183 // CreateWAFActiveRulesInput used as input for adding rules to a WAF. 184 type CreateWAFActiveRulesInput struct { 185 // The Web Application Firewall's ID. 186 WAFID string 187 // The Web Application Firewall's version number. 188 WAFVersionNumber int 189 // The list of WAF active rules (ModSecID, Status and Revision are required). 190 Rules []*WAFActiveRule 191 } 192 193 // CreateWAFActiveRules adds rules to a particular WAF. 194 func (c *Client) CreateWAFActiveRules(i *CreateWAFActiveRulesInput) ([]*WAFActiveRule, error) { 195 196 if i.WAFID == "" { 197 return nil, ErrMissingWAFID 198 } 199 200 if i.WAFVersionNumber == 0 { 201 return nil, ErrMissingWAFVersionNumber 202 } 203 204 if len(i.Rules) == 0 { 205 return nil, ErrMissingWAFActiveRule 206 } 207 208 path := fmt.Sprintf("/waf/firewalls/%s/versions/%d/active-rules", i.WAFID, i.WAFVersionNumber) 209 resp, err := c.PostJSONAPIBulk(path, i.Rules, nil) 210 if err != nil { 211 return nil, err 212 } 213 214 data, err := jsonapi.UnmarshalManyPayload(resp.Body, WAFActiveRuleType) 215 if err != nil { 216 return nil, err 217 } 218 219 wafRules := make([]*WAFActiveRule, len(data)) 220 for i := range data { 221 typed, ok := data[i].(*WAFActiveRule) 222 if !ok { 223 return nil, fmt.Errorf("got back a non-WAFActiveRule response") 224 } 225 wafRules[i] = typed 226 } 227 228 return wafRules, nil 229 } 230 231 // BatchModificationWAFActiveRulesInput is used for active rules batch modifications. 232 type BatchModificationWAFActiveRulesInput struct { 233 // The Web Application Firewall's ID. 234 WAFID string 235 // The Web Application Firewall's version number. 236 WAFVersionNumber int 237 // The list of WAF active rules (ModSecID, Status and Revision are required for upsert, ModSecID is required for delete). 238 Rules []*WAFActiveRule 239 // The batch operation to be performed (allowed operations are upsert and delete). 240 OP BatchOperation 241 } 242 243 // BatchModificationWAFActiveRules is a generic function for creating or deleting WAF active rules in batches. 244 // Upsert and delete are the only operations allowed. 245 func (c *Client) BatchModificationWAFActiveRules(i *BatchModificationWAFActiveRulesInput) ([]*WAFActiveRule, error) { 246 247 if len(i.Rules) > BatchModifyMaximumOperations { 248 return nil, ErrMaxExceededRules 249 } 250 251 switch i.OP { 252 case UpsertBatchOperation: 253 return c.CreateWAFActiveRules(&CreateWAFActiveRulesInput{ 254 WAFID: i.WAFID, 255 WAFVersionNumber: i.WAFVersionNumber, 256 Rules: i.Rules, 257 }) 258 case DeleteBatchOperation: 259 return nil, c.DeleteWAFActiveRules(&DeleteWAFActiveRulesInput{ 260 WAFID: i.WAFID, 261 WAFVersionNumber: i.WAFVersionNumber, 262 Rules: i.Rules, 263 }) 264 default: 265 return nil, fmt.Errorf("operation %s not supported", i.OP) 266 } 267 } 268 269 // DeleteWAFActiveRulesInput used as input for removing rules from a WAF. 270 type DeleteWAFActiveRulesInput struct { 271 // The Web Application Firewall's ID. 272 WAFID string 273 // The Web Application Firewall's version number. 274 WAFVersionNumber int 275 // The list of WAF active rules (ModSecID is required). 276 Rules []*WAFActiveRule 277 } 278 279 // DeleteWAFActiveRules removes rules from a particular WAF. 280 func (c *Client) DeleteWAFActiveRules(i *DeleteWAFActiveRulesInput) error { 281 282 if i.WAFID == "" { 283 return ErrMissingWAFID 284 } 285 286 if i.WAFVersionNumber == 0 { 287 return ErrMissingWAFVersionNumber 288 } 289 290 if len(i.Rules) == 0 { 291 return ErrMissingWAFActiveRule 292 } 293 294 path := fmt.Sprintf("/waf/firewalls/%s/versions/%d/active-rules", i.WAFID, i.WAFVersionNumber) 295 _, err := c.DeleteJSONAPIBulk(path, i.Rules, nil) 296 return err 297 }