Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
### Breaking:

### Enhancements:
- feat(ngwaf): add support for requests ([#688](https://github.com/fastly/go-fastly/pull/688))
- feat(ngwaf): add support for timeseries ([#689](https://github.com/fastly/go-fastly/pull/689))

### Bug fixes:
Expand Down
8 changes: 8 additions & 0 deletions fastly/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ var ErrMissingIsExpired = NewFieldError("IsExpired")
// requires a "RedactionID" key, but one was not set.
var ErrMissingRedactionID = NewFieldError("RedactionID")

// ErrMissingRequestID is an error that is returned when an input struct
// requires a "RequestID" key, but one was not set.
var ErrMissingRequestID = NewFieldError("RequestID")

// ErrMissingLimit is an error that is returned when an input struct
// requires a "Limit" key, but one was not set.
var ErrMissingLimit = NewFieldError("Limit")

// ErrMissingField is an error that is returned when an input struct
// requires a "Field" key, but one was not set.
var ErrMissingField = NewFieldError("Field")
Expand Down
10 changes: 7 additions & 3 deletions fastly/ngwaf/v1/events/api_response.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package events

import "time"
import (
"time"

"github.com/fastly/go-fastly/v10/fastly/ngwaf/v1/requests"
)

// Event is the API response structure for the get, list, and expire operations.
type Event struct {
Expand Down Expand Up @@ -31,7 +35,7 @@ type Event struct {
// RequestCount is the total numer of requests.
RequestCount int `json:"request_count"`
// SampleRequest is an example of a request that triggered the event.
SampleRequest map[string]any `json:"sample_request"`
SampleRequest requests.Request `json:"sample_request"`
// Source is the IP address of the source of the event.
Source string `json:"source"`
// Type is the type of event
Expand All @@ -42,7 +46,7 @@ type Event struct {
Window int `json:"window"`
}

// Reason is the reason an event was triggered.
// Reason is the signal that corresponds to the reason an event was triggered.
type Reason struct {
// Signal ID is the ID of the signal that triggered the event
SignalID string `json:"signal_id"`
Expand Down
29 changes: 14 additions & 15 deletions fastly/ngwaf/v1/events/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@ import (
)

const (
TestWorkspaceID = "WI90k86caQU0u7frYeRgsP"
TestEventID = "6835c746dedf49b89d19db33"
TestEventID = "6841c2c07d3691b0f5b95130"
)

func TestClient_GetEvent(t *testing.T) {
t.Parallel()

getEventInput := new(GetInput)
getEventInput.EventID = fastly.ToPointer(TestEventID)
getEventInput.WorkspaceID = fastly.ToPointer(TestWorkspaceID)
getEventInput.WorkspaceID = fastly.ToPointer(fastly.TestNGWAFWorkspaceID)

var event *Event
var err error
createdAt, _ := time.Parse(time.RFC3339, "2025-05-27T14:08:03Z")
detectedAt, _ := time.Parse(time.RFC3339, "2025-05-27T14:08:06Z")
expiresAt, _ := time.Parse(time.RFC3339, "2025-05-27T18:42:47Z")
createdAt, _ := time.Parse(time.RFC3339, "2025-06-05T16:15:58Z")
detectedAt, _ := time.Parse(time.RFC3339, "2025-06-05T16:16:00Z")
expiresAt, _ := time.Parse(time.RFC3339, "2025-06-05T16:20:02Z")
testEvent := Event{
Action: "flagged",
BlockSignals: nil,
Expand All @@ -34,16 +33,16 @@ func TestClient_GetEvent(t *testing.T) {
DetectedAt: detectedAt,
ExpiresAt: expiresAt,
EventID: TestEventID,
FlaggedRequestCount: 200,
FlaggedRequestCount: 0,
IsExpired: true,
Reasons: []Reason{
{
SignalID: "CMDEXE",
Count: 139,
Count: 97,
},
},
RemoteHostname: "pool-96-224-50-187.nycmny.fios.verizon.net",
RequestCount: 139,
RequestCount: 97,
Source: "96.224.50.187",
Type: "attack",
UserAgents: []string{
Expand Down Expand Up @@ -119,7 +118,7 @@ func TestClient_GetEvent(t *testing.T) {

var events *Events
listEventInput := new(ListInput)
listEventInput.WorkspaceID = fastly.ToPointer(TestWorkspaceID)
listEventInput.WorkspaceID = fastly.ToPointer(fastly.TestNGWAFWorkspaceID)
listEventInput.From = fastly.ToPointer("2024-05-27T14:08:03Z")

// get a list of events
Expand Down Expand Up @@ -189,7 +188,7 @@ func TestClient_GetEvent(t *testing.T) {
}

expireEventInput := new(ExpireInput)
expireEventInput.WorkspaceID = fastly.ToPointer(TestWorkspaceID)
expireEventInput.WorkspaceID = fastly.ToPointer(fastly.TestNGWAFWorkspaceID)
expireEventInput.EventID = fastly.ToPointer(TestEventID)
expireEventInput.IsExpired = fastly.ToPointer(true)

Expand Down Expand Up @@ -269,7 +268,7 @@ func TestClient_GetEvent_validation(t *testing.T) {
t.Errorf("expected ErrMissingWorkspaceID: got %s", err)
}
_, err = Get(fastly.TestClient, &GetInput{
WorkspaceID: fastly.ToPointer(TestWorkspaceID),
WorkspaceID: fastly.ToPointer(fastly.TestNGWAFWorkspaceID),
EventID: nil,
})
if !errors.Is(err, fastly.ErrMissingEventID) {
Expand All @@ -286,7 +285,7 @@ func TestClient_ListEvent_validation(t *testing.T) {
t.Errorf("expected ErrMissingWorkspaceID: got %s", err)
}
_, err = List(fastly.TestClient, &ListInput{
WorkspaceID: fastly.ToPointer(TestWorkspaceID),
WorkspaceID: fastly.ToPointer(fastly.TestNGWAFWorkspaceID),
From: nil,
})
if !errors.Is(err, fastly.ErrMissingFrom) {
Expand All @@ -303,15 +302,15 @@ func TestClient_ExpireEvent_validation(t *testing.T) {
t.Errorf("expected ErrMissingWorkspaceID: got %s", err)
}
_, err = Expire(fastly.TestClient, &ExpireInput{
WorkspaceID: fastly.ToPointer(TestWorkspaceID),
WorkspaceID: fastly.ToPointer(fastly.TestNGWAFWorkspaceID),
EventID: nil,
})
if !errors.Is(err, fastly.ErrMissingEventID) {
t.Errorf("expected ErrMissingEventID: got %s", err)
}

_, err = Expire(fastly.TestClient, &ExpireInput{
WorkspaceID: fastly.ToPointer(TestWorkspaceID),
WorkspaceID: fastly.ToPointer(fastly.TestNGWAFWorkspaceID),
EventID: fastly.ToPointer(string(TestEventID)),
IsExpired: nil,
})
Expand Down
18 changes: 11 additions & 7 deletions fastly/ngwaf/v1/events/fixtures/expire_event.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ interactions:
body: '{"is_expired":true}'
form: {}
headers:
Accept:
- application/json
Content-Type:
- application/json
User-Agent:
- FastlyGo/10.2.0 (+github.com/fastly/go-fastly; go1.24.2)
url: https://api.fastly.com/ngwaf/v1/workspaces/WI90k86caQU0u7frYeRgsP/events/6835c746dedf49b89d19db33
- FastlyGo/10.3.0 (+github.com/fastly/go-fastly; go1.24.2)
url: https://api.fastly.com/ngwaf/v1/workspaces/alk6DTsYKHKucJCOIavaJM/events/6841c2c07d3691b0f5b95130
method: PATCH
response:
body: |
{"id":"6835c746dedf49b89d19db33","created_at":"2025-05-27T14:08:03Z","detected_at":"2025-05-27T14:08:06Z","source":"96.224.50.187","country":"US","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","user_agents":["curl/8.7.1"],"action":"flagged","type":"attack","reasons":[{"signal_id":"CMDEXE","count":139}],"block_signals":null,"request_count":139,"blocked_request_count":0,"flagged_request_count":200,"window":60,"expires_at":"2025-05-27T18:42:47Z","is_expired":true,"sample_request":{"id":"c39aa440a65e4f37be93000000000001","timestamp":"2025-05-27T14:07:19Z","server_hostname":"ff3eb80ddda467d3d9fc2c6cbfb9fb95.com","server_name":"ff3eb80ddda467d3d9fc2c6cbfb9fb95.com","uri":"/","path":"/","user_agent":"curl/8.7.1","remote_ip":"96.224.50.187","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","country":"US","method":"POST","protocol":"HTTP/1.1","tls_protocol":"","tls_cipher":"","scheme":"http","response_code":503,"response_size":19,"response_time":7,"agent_response_code":200,"request_headers":[{"name":"X-Sigsci-Client-Geo-City","value":"ossining"},{"name":"Accept","value":"*/*"},{"name":"X-Sigsci-Serviceid-Prod","value":"5bHVNvR3QwYBtF59iFkL72"},{"name":"X-Uat-Ip","value":"10.2.3.4"},{"name":"X-Sigsci-Edgemodule","value":"vcl 3.1.0"},{"name":"Fastly-Ff","value":"jjtNGp3COE7Cf2N9cZIJxo8uJocXdwD8AsyHr2Ir3HA=!EWR!cache-ewr-kewr1740056-EWR"},{"name":"Cdn-Loop","value":"Fastly"},{"name":"Content-Length","value":"22"},{"name":"Fastly-Client-Ip","value":"96.224.50.187"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"X-Timer","value":"S1748354840.954550,VS0"},{"name":"X-Sigsci-Client-Geo-Country-Code","value":"US"},{"name":"X-Sigsci-Requestid","value":"c39aa440a65e4f37be93000000000001"},{"name":"Host","value":"ff3eb80ddda467d3d9fc2c6cbfb9fb95.com"},{"name":"X-Varnish","value":"4019989678"},{"name":"User-Agent","value":"curl/8.7.1"}],"response_headers":[{"name":"Connection","value":"keep-alive"},{"name":"Content-Type","value":"text/plain"},{"name":"Date","value":"Tue, 27 May 2025 14:07:19 GMT"},{"name":"Transfer-Encoding","value":"chunked"},{"name":"X-Cache","value":"MISS"},{"name":"X-Cache-Hits","value":"0"},{"name":"X-Served-By","value":"cache-ewr-kewr1740053-EWR"}],"signals":[{"id":"CMDEXE","location":"POST","value":"bar31=;cat /etc/passwd","detector":"CmdExeRule"},{"id":"HTTP503","location":"","value":"503","detector":"HTTPErrorRule"}],"ja3":"","ja4":"","summation":{"attrs":{},"attacks":[]}}}
{"id":"6841c2c07d3691b0f5b95130","created_at":"2025-06-05T16:15:58Z","detected_at":"2025-06-05T16:16:00Z","source":"96.224.50.187","country":"US","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","user_agents":["curl/8.7.1"],"action":"flagged","type":"attack","reasons":[{"signal_id":"CMDEXE","count":97}],"block_signals":null,"request_count":97,"blocked_request_count":0,"flagged_request_count":0,"window":60,"expires_at":"2025-06-05T16:20:02Z","is_expired":true,"sample_request":{"id":"2b353ef6ae3347e5b8e1000000000001","timestamp":"2025-06-05T16:15:16Z","server_hostname":"fastlydevtoolstesting.com","server_name":"fastlydevtoolstesting.com","uri":"/","path":"/","user_agent":"curl/8.7.1","remote_ip":"96.224.50.187","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","country":"US","method":"POST","protocol":"HTTP/1.1","tls_protocol":"","tls_cipher":"","scheme":"http","response_code":401,"response_size":4696,"response_time":70,"agent_response_code":200,"request_headers":[{"name":"Fastly-Ff","value":"7rhL8T7Um/0khntXIHgopW0fTXKYvcj2vPpd1I+60Rc=!EWR!cache-ewr-kewr1740075-EWR"},{"name":"Accept","value":"*/*"},{"name":"Cdn-Loop","value":"Fastly"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"X-Sigsci-Client-Geo-Country-Code","value":"US"},{"name":"Host","value":"fastlydevtoolstesting.com"},{"name":"X-Varnish","value":"2644044629"},{"name":"X-Sigsci-Serviceid-Prod","value":"kKJb5bOFI47uHeBVluGfX1"},{"name":"X-Sigsci-Client-Geo-City","value":"ossining"},{"name":"X-Sigsci-Requestid","value":"2b353ef6ae3347e5b8e1000000000001"},{"name":"Content-Length","value":"22"},{"name":"X-Uat-Ip","value":"10.2.3.4"},{"name":"User-Agent","value":"curl/8.7.1"},{"name":"X-Sigsci-Edgemodule","value":"vcl 3.1.0"},{"name":"Fastly-Client-Ip","value":"96.224.50.187"},{"name":"X-Timer","value":"S1749140117.613294,VS0"}],"response_headers":[{"name":"X-Glitch-Proxy","value":"true"},{"name":"Date","value":"Thu, 05 Jun 2025 16:15:16 GMT"},{"name":"Content-Type","value":"text/html; charset=utf-8"},{"name":"X-Cache","value":"MISS"},{"name":"Content-Length","value":"4696"},{"name":"Connection","value":"keep-alive"},{"name":"X-Cache-Hits","value":"0"},{"name":"X-Served-By","value":"cache-ewr-kewr1740071-EWR"},{"name":"Cache-Control","value":"no-cache"},{"name":"Etag","value":"W/\"1258-L4oi8+nPFPCcbfyW0wTUanWWJ3U\""}],"signals":[{"id":"CMDEXE","location":"POST","value":"bar70=;cat /etc/passwd","detector":"CmdExeRule"},{"id":"HTTP4XX","location":"","value":"401","detector":"HTTPErrorRule"}],"ja3":"","ja4":"","summation":{"attrs":{},"attacks":[]}}}
headers:
Accept-Ranges:
- bytes
Expand All @@ -20,11 +24,11 @@ interactions:
Content-Type:
- application/json
Date:
- Tue, 27 May 2025 18:42:48 GMT
- Thu, 05 Jun 2025 16:20:03 GMT
Pragma:
- no-cache
Server:
- fastly control-gateway
- fastly
Strict-Transport-Security:
- max-age=31536000
Vary:
Expand All @@ -36,9 +40,9 @@ interactions:
X-Cache-Hits:
- 0, 0
X-Served-By:
- cache-chi-kigq8000058-CHI, cache-ewr-kewr1740091-EWR
- cache-chi-klot8100150-CHI, cache-ewr-kewr1740029-EWR
X-Timer:
- S1748371368.843018,VS0,VE259
- S1749140402.206514,VS0,VE1263
status: 200 OK
code: 200
duration: ""
18 changes: 9 additions & 9 deletions fastly/ngwaf/v1/events/fixtures/get_event.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ interactions:
form: {}
headers:
User-Agent:
- FastlyGo/10.2.0 (+github.com/fastly/go-fastly; go1.24.2)
url: https://api.fastly.com/ngwaf/v1/workspaces/WI90k86caQU0u7frYeRgsP/events/6835c746dedf49b89d19db33
- FastlyGo/10.3.0 (+github.com/fastly/go-fastly; go1.24.2)
url: https://api.fastly.com/ngwaf/v1/workspaces/alk6DTsYKHKucJCOIavaJM/events/6841c2c07d3691b0f5b95130
method: GET
response:
body: |
{"id":"6835c746dedf49b89d19db33","created_at":"2025-05-27T14:08:03Z","detected_at":"2025-05-27T14:08:06Z","source":"96.224.50.187","country":"US","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","user_agents":["curl/8.7.1"],"action":"flagged","type":"attack","reasons":[{"signal_id":"CMDEXE","count":139}],"block_signals":null,"request_count":139,"blocked_request_count":0,"flagged_request_count":200,"window":60,"expires_at":"2025-05-27T18:42:47Z","is_expired":true,"sample_request":{"id":"c39aa440a65e4f37be93000000000001","timestamp":"2025-05-27T14:07:19Z","server_hostname":"ff3eb80ddda467d3d9fc2c6cbfb9fb95.com","server_name":"ff3eb80ddda467d3d9fc2c6cbfb9fb95.com","uri":"/","path":"/","user_agent":"curl/8.7.1","remote_ip":"96.224.50.187","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","country":"US","method":"POST","protocol":"HTTP/1.1","tls_protocol":"","tls_cipher":"","scheme":"http","response_code":503,"response_size":19,"response_time":7,"agent_response_code":200,"request_headers":[{"name":"X-Sigsci-Client-Geo-City","value":"ossining"},{"name":"Accept","value":"*/*"},{"name":"X-Sigsci-Serviceid-Prod","value":"5bHVNvR3QwYBtF59iFkL72"},{"name":"X-Uat-Ip","value":"10.2.3.4"},{"name":"X-Sigsci-Edgemodule","value":"vcl 3.1.0"},{"name":"Fastly-Ff","value":"jjtNGp3COE7Cf2N9cZIJxo8uJocXdwD8AsyHr2Ir3HA=!EWR!cache-ewr-kewr1740056-EWR"},{"name":"Cdn-Loop","value":"Fastly"},{"name":"Content-Length","value":"22"},{"name":"Fastly-Client-Ip","value":"96.224.50.187"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"X-Timer","value":"S1748354840.954550,VS0"},{"name":"X-Sigsci-Client-Geo-Country-Code","value":"US"},{"name":"X-Sigsci-Requestid","value":"c39aa440a65e4f37be93000000000001"},{"name":"Host","value":"ff3eb80ddda467d3d9fc2c6cbfb9fb95.com"},{"name":"X-Varnish","value":"4019989678"},{"name":"User-Agent","value":"curl/8.7.1"}],"response_headers":[{"name":"Connection","value":"keep-alive"},{"name":"Content-Type","value":"text/plain"},{"name":"Date","value":"Tue, 27 May 2025 14:07:19 GMT"},{"name":"Transfer-Encoding","value":"chunked"},{"name":"X-Cache","value":"MISS"},{"name":"X-Cache-Hits","value":"0"},{"name":"X-Served-By","value":"cache-ewr-kewr1740053-EWR"}],"signals":[{"id":"CMDEXE","location":"POST","value":"bar31=;cat /etc/passwd","detector":"CmdExeRule"},{"id":"HTTP503","location":"","value":"503","detector":"HTTPErrorRule"}],"ja3":"","ja4":"","summation":{"attrs":{},"attacks":[]}}}
{"id":"6841c2c07d3691b0f5b95130","created_at":"2025-06-05T16:15:58Z","detected_at":"2025-06-05T16:16:00Z","source":"96.224.50.187","country":"US","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","user_agents":["curl/8.7.1"],"action":"flagged","type":"attack","reasons":[{"signal_id":"CMDEXE","count":97}],"block_signals":null,"request_count":97,"blocked_request_count":0,"flagged_request_count":0,"window":60,"expires_at":"2025-06-05T16:20:02Z","is_expired":true,"sample_request":{"id":"2b353ef6ae3347e5b8e1000000000001","timestamp":"2025-06-05T16:15:16Z","server_hostname":"fastlydevtoolstesting.com","server_name":"fastlydevtoolstesting.com","uri":"/","path":"/","user_agent":"curl/8.7.1","remote_ip":"96.224.50.187","remote_hostname":"pool-96-224-50-187.nycmny.fios.verizon.net","country":"US","method":"POST","protocol":"HTTP/1.1","tls_protocol":"","tls_cipher":"","scheme":"http","response_code":401,"response_size":4696,"response_time":70,"agent_response_code":200,"request_headers":[{"name":"Fastly-Ff","value":"7rhL8T7Um/0khntXIHgopW0fTXKYvcj2vPpd1I+60Rc=!EWR!cache-ewr-kewr1740075-EWR"},{"name":"Accept","value":"*/*"},{"name":"Cdn-Loop","value":"Fastly"},{"name":"Content-Type","value":"application/x-www-form-urlencoded"},{"name":"X-Sigsci-Client-Geo-Country-Code","value":"US"},{"name":"Host","value":"fastlydevtoolstesting.com"},{"name":"X-Varnish","value":"2644044629"},{"name":"X-Sigsci-Serviceid-Prod","value":"kKJb5bOFI47uHeBVluGfX1"},{"name":"X-Sigsci-Client-Geo-City","value":"ossining"},{"name":"X-Sigsci-Requestid","value":"2b353ef6ae3347e5b8e1000000000001"},{"name":"Content-Length","value":"22"},{"name":"X-Uat-Ip","value":"10.2.3.4"},{"name":"User-Agent","value":"curl/8.7.1"},{"name":"X-Sigsci-Edgemodule","value":"vcl 3.1.0"},{"name":"Fastly-Client-Ip","value":"96.224.50.187"},{"name":"X-Timer","value":"S1749140117.613294,VS0"}],"response_headers":[{"name":"X-Glitch-Proxy","value":"true"},{"name":"Date","value":"Thu, 05 Jun 2025 16:15:16 GMT"},{"name":"Content-Type","value":"text/html; charset=utf-8"},{"name":"X-Cache","value":"MISS"},{"name":"Content-Length","value":"4696"},{"name":"Connection","value":"keep-alive"},{"name":"X-Cache-Hits","value":"0"},{"name":"X-Served-By","value":"cache-ewr-kewr1740071-EWR"},{"name":"Cache-Control","value":"no-cache"},{"name":"Etag","value":"W/\"1258-L4oi8+nPFPCcbfyW0wTUanWWJ3U\""}],"signals":[{"id":"CMDEXE","location":"POST","value":"bar70=;cat /etc/passwd","detector":"CmdExeRule"},{"id":"HTTP4XX","location":"","value":"401","detector":"HTTPErrorRule"}],"ja3":"","ja4":"","summation":{"attrs":{},"attacks":[]}}}
headers:
Accept-Ranges:
- bytes
Expand All @@ -20,7 +20,7 @@ interactions:
Content-Type:
- application/json
Date:
- Fri, 30 May 2025 18:37:55 GMT
- Thu, 05 Jun 2025 16:33:04 GMT
Pragma:
- no-cache
Server:
Expand All @@ -30,15 +30,15 @@ interactions:
Vary:
- Accept-Encoding
Via:
- 1.1 varnish
- 1.1 varnish, 1.1 varnish
X-Cache:
- MISS
- MISS, MISS
X-Cache-Hits:
- "0"
- 0, 0
X-Served-By:
- cache-ewr-kewr1740043-EWR
- cache-chi-klot8100150-CHI, cache-nyc-kteb1890056-NYC
X-Timer:
- S1748630275.471387,VS0,VE182
- S1749141185.527635,VS0,VE471
status: 200 OK
code: 200
duration: ""
Loading