diff --git a/manifests/golang.yml b/manifests/golang.yml index 96ec0244958..2f3c8ef8ffb 100644 --- a/manifests/golang.yml +++ b/manifests/golang.yml @@ -238,18 +238,51 @@ manifest: tests/appsec/rasp/test_api10.py::Test_API10_response_status: v2.4.0 tests/appsec/rasp/test_api10.py::Test_API10_without_downstream_body_analysis_using_max: v2.4.0 tests/appsec/rasp/test_api10.py::Test_API10_without_downstream_body_analysis_using_sample_rate: v2.7.0-dev - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_BodyJson: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_BodyUrlEncoded: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_BodyXml: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Capability: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Mandatory_SpanTags: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Optional_SpanTags: missing_feature + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_BodyJson: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_BodyUrlEncoded: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_BodyXml: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Capability: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Mandatory_SpanTags: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Optional_SpanTags: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Rules_Version: v2.3.0-dev # Possibly earlier - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_StackTrace: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Telemetry: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Telemetry_V2: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Telemetry_Variant_Tag: missing_feature - tests/appsec/rasp/test_cmdi.py::Test_Cmdi_UrlQuery: missing_feature + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_StackTrace: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Telemetry: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Telemetry_V2: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Telemetry_Variant_Tag: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev + tests/appsec/rasp/test_cmdi.py::Test_Cmdi_UrlQuery: + - weblog_declaration: + "*": irrelevant (CMDi detection requires orchestrion) + net-http-orchestrion: v2.11.0-dev tests/appsec/rasp/test_cmdi.py::Test_Cmdi_Waf_Version: v2.3.0-dev # Possibly earlier tests/appsec/rasp/test_lfi.py::Test_Lfi_BodyJson: - weblog_declaration: diff --git a/utils/build/docker/golang/app/_shared/rasp/rasp.go b/utils/build/docker/golang/app/_shared/rasp/rasp.go index 443a4a7b081..71ab6961847 100644 --- a/utils/build/docker/golang/app/_shared/rasp/rasp.go +++ b/utils/build/docker/golang/app/_shared/rasp/rasp.go @@ -7,6 +7,7 @@ import ( "log" "net/http" "os" + "os/exec" sqltrace "github.com/DataDog/dd-trace-go/contrib/database/sql/v2" httptrace "github.com/DataDog/dd-trace-go/contrib/net/http/v2" @@ -151,3 +152,66 @@ func SQLi(w http.ResponseWriter, r *http.Request) { log.Println("unknown error during sql call: ", err.Error()) } } + +func parseCommandRASPRequest(r *http.Request) []string { + switch r.Method { + case http.MethodGet: + return []string{r.URL.Query().Get("command")} + case http.MethodPost: + switch r.Header.Get("Content-Type") { + case "application/json": + var body struct { + Command []string `json:"command"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + log.Fatalf("failed to parse body: %v\n", err) + } + if err := appsec.MonitorParsedHTTPBody(r.Context(), map[string]any{"command": body.Command}); err != nil { + log.Fatalf("Body Monitoring should not block the request: %v\n", err) + } + return body.Command + case "application/xml": + var body struct { + Command []string `xml:"cmd"` + } + if err := xml.NewDecoder(r.Body).Decode(&body); err != nil { + log.Fatalf("failed to parse body: %v\n", err) + } + if err := appsec.MonitorParsedHTTPBody(r.Context(), map[string]any{"command": body.Command}); err != nil { + log.Fatalf("Body Monitoring should not block the request: %v\n", err) + } + return body.Command + case "application/x-www-form-urlencoded": + if err := r.ParseForm(); err != nil { + log.Fatalf("failed to parse body: %v\n", err) + } + command := r.Form.Get("command") + if err := appsec.MonitorParsedHTTPBody(r.Context(), map[string]string{"command": command}); err != nil { + log.Fatalf("Body Monitoring should not block the request: %v\n", err) + } + return []string{command} + default: + log.Fatalln("unsupported content type") + } + default: + log.Fatalln("method not allowed") + } + + return nil +} + +func CMDI(w http.ResponseWriter, r *http.Request) { + command := parseCommandRASPRequest(r) + if len(command) == 0 { + return + } + + err := (&exec.Cmd{Path: command[0], Args: command}).Run() + if events.IsSecurityError(err) { + return + } + + if err != nil { + log.Println("unknown error during command execution: ", err.Error()) + } +} diff --git a/utils/build/docker/golang/app/chi/main.go b/utils/build/docker/golang/app/chi/main.go index 164be76ff70..5e53068f360 100644 --- a/utils/build/docker/golang/app/chi/main.go +++ b/utils/build/docker/golang/app/chi/main.go @@ -368,6 +368,7 @@ func main() { mux.HandleFunc("/rasp/multiple", rasp.LFIMultiple) mux.HandleFunc("/rasp/ssrf", rasp.SSRF) mux.HandleFunc("/rasp/sqli", rasp.SQLi) + mux.HandleFunc("/rasp/cmdi", rasp.CMDI) mux.HandleFunc("/external_request", rasp.ExternalRequest) mux.HandleFunc("GET /external_request/redirect", rasp.ExternalRedirectRequest) diff --git a/utils/build/docker/golang/app/echo/main.go b/utils/build/docker/golang/app/echo/main.go index 3ce1628585c..f7fd2800660 100644 --- a/utils/build/docker/golang/app/echo/main.go +++ b/utils/build/docker/golang/app/echo/main.go @@ -377,6 +377,7 @@ func main() { r.Any("/rasp/multiple", echoHandleFunc(rasp.LFIMultiple)) r.Any("/rasp/ssrf", echoHandleFunc(rasp.SSRF)) r.Any("/rasp/sqli", echoHandleFunc(rasp.SQLi)) + r.Any("/rasp/cmdi", echoHandleFunc(rasp.CMDI)) r.Any("/external_request", echoHandleFunc(rasp.ExternalRequest)) r.GET("/external_request/redirect", echoHandleFunc(rasp.ExternalRedirectRequest)) diff --git a/utils/build/docker/golang/app/gin/main.go b/utils/build/docker/golang/app/gin/main.go index 3ddb2b6834a..e1196e21bbc 100644 --- a/utils/build/docker/golang/app/gin/main.go +++ b/utils/build/docker/golang/app/gin/main.go @@ -363,6 +363,7 @@ func main() { r.Any("/rasp/multiple", ginHandleFunc(rasp.LFIMultiple)) r.Any("/rasp/ssrf", ginHandleFunc(rasp.SSRF)) r.Any("/rasp/sqli", ginHandleFunc(rasp.SQLi)) + r.Any("/rasp/cmdi", ginHandleFunc(rasp.CMDI)) r.Any("/external_request", ginHandleFunc(rasp.ExternalRequest)) r.GET("/external_request/redirect", ginHandleFunc(rasp.ExternalRedirectRequest)) diff --git a/utils/build/docker/golang/app/net-http-orchestrion/main.go b/utils/build/docker/golang/app/net-http-orchestrion/main.go index e96521a1f83..965e1ac121f 100644 --- a/utils/build/docker/golang/app/net-http-orchestrion/main.go +++ b/utils/build/docker/golang/app/net-http-orchestrion/main.go @@ -613,6 +613,7 @@ func main() { mux.HandleFunc("/rasp/multiple", rasp.LFIMultiple) mux.HandleFunc("/rasp/ssrf", rasp.SSRF) mux.HandleFunc("/rasp/sqli", rasp.SQLi) + mux.HandleFunc("/rasp/cmdi", rasp.CMDI) mux.HandleFunc("/external_request", rasp.ExternalRequest) mux.HandleFunc("GET /external_request/redirect", rasp.ExternalRedirectRequest) diff --git a/utils/build/docker/golang/app/net-http/main.go b/utils/build/docker/golang/app/net-http/main.go index 136c5ecd2ed..70b1bd5caf5 100644 --- a/utils/build/docker/golang/app/net-http/main.go +++ b/utils/build/docker/golang/app/net-http/main.go @@ -753,6 +753,7 @@ func main() { mux.HandleFunc("/rasp/multiple", rasp.LFIMultiple) mux.HandleFunc("/rasp/ssrf", rasp.SSRF) mux.HandleFunc("/rasp/sqli", rasp.SQLi) + mux.HandleFunc("/rasp/cmdi", rasp.CMDI) mux.HandleFunc("/add_event", func(w http.ResponseWriter, r *http.Request) { span, ok := tracer.SpanFromContext(r.Context())