Skip to content

Commit

Permalink
internal/appsec: add server.request.method address
Browse files Browse the repository at this point in the history
This address is now received by the WAF. The recommended ruleset
does not use it but custom rules coming we need to support it.

Signed-off-by: Eliott Bouhana <eliott.bouhana@datadoghq.com>
  • Loading branch information
eliottness committed Apr 12, 2023
1 parent 69b9d79 commit b7e2dc2
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 0 deletions.
3 changes: 3 additions & 0 deletions internal/appsec/dyngo/instrumentation/httpsec/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
type (
// HandlerOperationArgs is the HTTP handler operation arguments.
HandlerOperationArgs struct {
// Method is the http method verb of the request, address is `server.request.method`
Method string
// RequestURI corresponds to the address `server.request.uri.raw`
RequestURI string
// Headers corresponds to the address `server.request.headers.no_cookies`
Expand Down Expand Up @@ -143,6 +145,7 @@ func MakeHandlerOperationArgs(r *http.Request, clientIP netip.Addr, pathParams m
cookies := makeCookies(r) // TODO(Julio-Guerra): avoid actively parsing the cookies thanks to dynamic instrumentation
headers["host"] = []string{r.Host}
return HandlerOperationArgs{
Method: r.Method,
RequestURI: r.RequestURI,
Headers: headers,
Cookies: cookies,
Expand Down
31 changes: 31 additions & 0 deletions internal/appsec/testdata/custom_rules.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"version": "2.2",
"metadata": {
"rules_version": "1.4.2"
},
"rules": [
{
"id": "custom-001",
"name": "Custom Rule",
"tags": {
"type": "security_scanner",
"category": "attack_attempt",
"confidence": "1"
},
"conditions": [
{
"parameters": {
"inputs": [
{
"address": "server.request.method"
}
],
"regex": "^POST$"
},
"operator": "match_regex"
}
],
"transformers": []
}
]
}
4 changes: 4 additions & 0 deletions internal/appsec/waf.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ func newHTTPWAFEventListener(handle *waf.Handle, addresses []string, timeout tim
values := make(map[string]interface{}, len(addresses))
for _, addr := range addresses {
switch addr {
case serverRequestMethodAddr:
values[serverRequestMethodAddr] = args.Method
case serverRequestRawURIAddr:
values[serverRequestRawURIAddr] = args.RequestURI
case serverRequestHeadersNoCookiesAddr:
Expand Down Expand Up @@ -378,6 +380,7 @@ func runWAF(wafCtx *waf.Context, values map[string]interface{}, timeout time.Dur

// HTTP rule addresses currently supported by the WAF
const (
serverRequestMethodAddr = "server.request.method"
serverRequestRawURIAddr = "server.request.uri.raw"
serverRequestHeadersNoCookiesAddr = "server.request.headers.no_cookies"
serverRequestCookiesAddr = "server.request.cookies"
Expand All @@ -391,6 +394,7 @@ const (

// List of HTTP rule addresses currently supported by the WAF
var httpAddresses = []string{
serverRequestMethodAddr,
serverRequestRawURIAddr,
serverRequestHeadersNoCookiesAddr,
serverRequestCookiesAddr,
Expand Down
39 changes: 39 additions & 0 deletions internal/appsec/waf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,45 @@ import (
"github.com/stretchr/testify/require"
)

func TestCustomRules(t *testing.T) {
t.Setenv("DD_APPSEC_RULES", "testdata/custom_rules.json")
appsec.Start()
defer appsec.Stop()

if !appsec.Enabled() {
t.Skip("appsec disabled")
}

// Start and trace an HTTP server
mux := httptrace.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!\n"))
})

srv := httptest.NewServer(mux)
defer srv.Close()

t.Run("method", func(t *testing.T) {
mt := mocktracer.Start()
defer mt.Stop()

req, err := http.NewRequest("POST", srv.URL, nil)
if err != nil {
panic(err)
}

_, err = srv.Client().Do(req)
require.NoError(t, err)

spans := mt.FinishedSpans()
require.Len(t, spans, 1)

event := spans[0].Tag("_dd.appsec.json")
require.NotNil(t, event)
require.Contains(t, event, "custom-001")
})
}

// TestWAF is a simple validation test of the WAF protecting a net/http server. It only mockups the agent and tests that
// the WAF is properly detecting an LFI attempt and that the corresponding security event is being sent to the agent.
// Additionally, verifies that rule matching through SDK body instrumentation works as expected
Expand Down

0 comments on commit b7e2dc2

Please sign in to comment.