Sorry, you cannot access this page. Please contact the customer service team.
diff --git a/internal/appsec/dyngo/instrumentation/common.go b/internal/appsec/dyngo/instrumentation/common.go index 9d0d66736e..f462577b4f 100644 --- a/internal/appsec/dyngo/instrumentation/common.go +++ b/internal/appsec/dyngo/instrumentation/common.go @@ -102,9 +102,11 @@ func SetEventSpanTags(span TagSetter, events []json.RawMessage) error { // Create the value of the security event tag. // TODO(Julio-Guerra): a future libddwaf version should return something -// avoiding us the following events concatenation logic which currently -// involves unserializing the top-level JSON arrays to concatenate them -// together. +// +// avoiding us the following events concatenation logic which currently +// involves unserializing the top-level JSON arrays to concatenate them +// together. +// // TODO(Julio-Guerra): avoid serializing the json in the request hot path func makeEventTagValue(events []json.RawMessage) (json.RawMessage, error) { var v interface{} diff --git a/internal/appsec/dyngo/instrumentation/httpsec/actions.go b/internal/appsec/dyngo/instrumentation/httpsec/actions.go new file mode 100644 index 0000000000..51bf157d21 --- /dev/null +++ b/internal/appsec/dyngo/instrumentation/httpsec/actions.go @@ -0,0 +1,91 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2022 Datadog, Inc. + +//go:build appsec +// +build appsec + +package httpsec + +import ( + "net/http" +) + +type action struct { + id string + params ActionParam +} + +// ActionHandler handles WAF actions registration and execution +type ActionHandler interface { + RegisterAction(id string, params ActionParam) + Exec(id string, op *Operation) +} + +type actionsHandler struct { + actions map[string]action +} + +// NewActionsHandler returns an action handler holding the default ASM actions. +// Currently, only the default "block" action is supported +func NewActionsHandler() ActionHandler { + defaultBlockAction := action{ + id: "block", + params: BlockRequestParams{ + Status: 403, + Template: "html", + }, + } + // Register the default "block" action as specified in the RFC for HTTP blocking + actions := map[string]action{defaultBlockAction.id: defaultBlockAction} + + return &actionsHandler{ + actions: actions, + } +} + +// RegisterAction registers a specific action to the actions handler. If the action kind is unknown +// the action will have no effect +func (h *actionsHandler) RegisterAction(id string, params ActionParam) { + h.actions[id] = action{ + id: id, + params: params, + } +} + +// Exec executes the action identified by `id` +func (h *actionsHandler) Exec(id string, op *Operation) { + a, ok := h.actions[id] + if !ok { + return + } + // Currently, only the "block_request" type is supported, so we only need to check for blockRequestParams + if p, ok := a.params.(BlockRequestParams); ok { + payload := blockedPayload(&p) + op.handler = http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Write(payload) + writer.WriteHeader(p.Status) + }) + } +} + +// ActionParam is used to identify an action parameters data type +type ActionParam interface{} + +// BlockRequestParams is the parameter struct used to perform actions of kind ActionBlockRequest +type BlockRequestParams struct { + ActionParam + // Status is the return code to use when blocking the request + Status int + // Template is the payload template to use to write the response (html or json) + Template string +} + +func blockedPayload(params *BlockRequestParams) []byte { + payload := BlockedTemplateJSON + if params.Template == "html" { + payload = BlockedTemplateHTML + } + return payload +} diff --git a/internal/appsec/dyngo/instrumentation/httpsec/http.go b/internal/appsec/dyngo/instrumentation/httpsec/http.go index 3809522ebd..dcd885cf8c 100644 --- a/internal/appsec/dyngo/instrumentation/httpsec/http.go +++ b/internal/appsec/dyngo/instrumentation/httpsec/http.go @@ -15,6 +15,7 @@ import ( "encoding/json" "net" "net/http" + "os" "reflect" "strings" @@ -81,6 +82,10 @@ func WrapHandler(handler http.Handler, span ddtrace.Span, pathParams map[string] args := MakeHandlerOperationArgs(r, pathParams) ctx, op := StartOperation(r.Context(), args) r = r.WithContext(ctx) + if op.handler != nil { + op.handler.ServeHTTP(w, r) + return + } defer func() { var status int if mw, ok := w.(interface{ Status() int }); ok { @@ -99,7 +104,6 @@ func WrapHandler(handler http.Handler, span ddtrace.Span, pathParams map[string] } SetSecurityEventTags(span, events, remoteIP, args.Headers, w.Header()) }() - handler.ServeHTTP(w, r) }) } @@ -152,6 +156,8 @@ type ( dyngo.Operation instrumentation.TagsHolder instrumentation.SecurityEventsHolder + // handler is the alternate HTTP handler that must be called for a given operation, if not nil + handler http.Handler } // SDKBodyOperation type representing an SDK body. It must be created with @@ -294,3 +300,26 @@ func IPFromRequest(r *http.Request) netaddrIP { return netaddrIP{} } + +var ( + BlockedTemplateJSON = []byte(`{"errors": [{"title": "You've been blocked", "detail": "Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]}`) + BlockedTemplateHTML = []byte(`
Sorry, you cannot access this page. Please contact the customer service team.