From 9a42fa1803efd1dfc6f3e3203558528b00de7c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Mazeau?= Date: Mon, 7 Feb 2022 17:06:33 +0100 Subject: [PATCH] contrib/gin-gonic: add testing for AppSec to gintrace_test.go --- contrib/gin-gonic/gin/gintrace_test.go | 114 +++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/contrib/gin-gonic/gin/gintrace_test.go b/contrib/gin-gonic/gin/gintrace_test.go index 1009caa061..69e79400d3 100644 --- a/contrib/gin-gonic/gin/gintrace_test.go +++ b/contrib/gin-gonic/gin/gintrace_test.go @@ -9,17 +9,22 @@ import ( "errors" "fmt" "html/template" + "io/ioutil" + "net/http" "net/http/httptest" + "strconv" "strings" "testing" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func init() { @@ -460,3 +465,112 @@ func TestServiceName(t *testing.T) { assert.Equal("my-service", span.Tag(ext.ServiceName)) }) } + +func TestAppSec(t *testing.T) { + + appsec.Start() + defer appsec.Stop() + if !appsec.Enabled() { + t.Skip("appsec disabled") + } + + r := gin.New() + r.Use(Middleware("appsec")) + + statusCodes := []int{200, 403, 404, 500} + for _, code := range statusCodes { + strC := strconv.Itoa(code) + code := code + r.Any("/"+strC, func(c *gin.Context) { + c.String(code, "Hello "+strC+"\n") + }) + } + + r.Any("/lfi/*allPaths", func(c *gin.Context) { + c.String(200, "Hello World!\n") + }) + r.Any("/path0.0/:myPathParam0/path0.1/:myPathParam1/path0.2/:myPathParam2/path0.3/*param3", func(c *gin.Context) { + c.String(200, "Hello Params!\n") + }) + + srv := httptest.NewServer(r) + defer srv.Close() + + t.Run("request-uri", func(t *testing.T) { + mt := mocktracer.Start() + defer mt.Stop() + // Send an LFI attack (according to appsec rule id crs-930-100) + req, err := http.NewRequest("POST", srv.URL+"/lfi/../../../secret.txt", nil) + if err != nil { + panic(err) + } + res, err := srv.Client().Do(req) + require.NoError(t, err) + // Check that the server behaved as intended + require.Equal(t, http.StatusOK, res.StatusCode) + b, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) + require.Equal(t, "Hello World!\n", string(b)) + // The span should contain the security event + finished := mt.FinishedSpans() + require.Len(t, finished, 1) + + // The first 301 redirection should contain the attack via the request uri + event := finished[0].Tag("_dd.appsec.json").(string) + require.NotNil(t, event) + require.True(t, strings.Contains(event, "server.request.uri.raw")) + require.True(t, strings.Contains(event, "crs-930-100")) + }) + + // Test a security scanner attack via path parameters + t.Run("path-params", func(t *testing.T) { + mt := mocktracer.Start() + defer mt.Stop() + // Send a security scanner attack (according to appsec rule id crs-913-120) + req, err := http.NewRequest("POST", srv.URL+"/path0.0/param0/path0.1/param1/path0.2/appscan_fingerprint/path0.3/param3", nil) + if err != nil { + panic(err) + } + res, err := srv.Client().Do(req) + require.NoError(t, err) + // Check that the handler was properly called + b, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) + require.Equal(t, "Hello Params!\n", string(b)) + require.Equal(t, http.StatusOK, res.StatusCode) + // The span should contain the security event + finished := mt.FinishedSpans() + require.Len(t, finished, 1) + event := finished[0].Tag("_dd.appsec.json").(string) + require.NotNil(t, event) + require.True(t, strings.Contains(event, "crs-913-120")) + require.True(t, strings.Contains(event, "myPathParam2")) + require.True(t, strings.Contains(event, "server.request.path_params")) + }) + + t.Run("status-code", func(t *testing.T) { + for _, code := range statusCodes { + codeStr := strconv.Itoa(code) + t.Run(codeStr, func(t *testing.T) { + mt := mocktracer.Start() + defer mt.Stop() + + req, err := http.NewRequest("POST", srv.URL+"/"+codeStr, nil) + req.Header.Set("User-agent", "Arachni/v1.0") + if err != nil { + panic(err) + } + res, err := srv.Client().Do(req) + require.NoError(t, err) + require.Equal(t, code, res.StatusCode) + + finished := mt.FinishedSpans() + require.Len(t, finished, 1) + tagStatusCode := finished[0].Tag("http.status_code").(string) + require.NotNil(t, tagStatusCode) + require.Equal(t, codeStr, tagStatusCode) + + }) + } + }) +}