Skip to content

Commit

Permalink
[mq] working branch - merge 0ca4e94 on top of main at 054d19d
Browse files Browse the repository at this point in the history
{"baseBranch":"main","baseCommit":"054d19dbcec79cf5b2a584d12b461e65b8ea24a7","createdAt":"2024-09-18T08:32:49.130753Z","headSha":"0ca4e949c1a204793d99a8dd8ecacc3404c0946c","id":"fcf0db7a-45cb-437e-b486-fa9b0c2b7b06","priority":"200","pullRequestNumber":"2872","queuedAt":"2024-09-20T08:08:49.262133Z","status":"STATUS_QUEUED"}
  • Loading branch information
dd-mergequeue[bot] committed Sep 20, 2024
2 parents 3d5c85f + 0ca4e94 commit 1910dd2
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 51 deletions.
76 changes: 41 additions & 35 deletions contrib/google.golang.org/grpc/appsec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"net"
"testing"
"time"

pappsec "gopkg.in/DataDog/dd-trace-go.v1/appsec"
"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
"gopkg.in/DataDog/dd-trace-go.v1/internal/appsec"
appsecConfig "gopkg.in/DataDog/dd-trace-go.v1/internal/appsec/config"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
Expand All @@ -24,26 +28,26 @@ import (
)

func TestAppSec(t *testing.T) {
appsec.Start()
appsec.Start(appsecConfig.WithWAFTimeout(time.Hour /* functionally unlimited */))
defer appsec.Stop()
if !appsec.Enabled() {
t.Skip("appsec disabled")
}

setup := func() (FixtureClient, mocktracer.Tracer, func()) {
rig, err := newAppsecRig(false)
setup := func(t *testing.T) (FixtureClient, mocktracer.Tracer, func()) {
rig, err := newAppsecRig(t, false)
require.NoError(t, err)

mt := mocktracer.Start()

return rig.client, mt, func() {
rig.Close()
assert.NoError(t, rig.Close())
mt.Stop()
}
}

t.Run("unary", func(t *testing.T) {
client, mt, cleanup := setup()
client, mt, cleanup := setup(t)
defer cleanup()

// Send a XSS attack in the payload along with the canary value in the RPC metadata
Expand All @@ -64,7 +68,7 @@ func TestAppSec(t *testing.T) {
})

t.Run("stream", func(t *testing.T) {
client, mt, cleanup := setup()
client, mt, cleanup := setup(t)
defer cleanup()

// Send a XSS attack in the payload along with the canary value in the RPC metadata
Expand All @@ -73,8 +77,9 @@ func TestAppSec(t *testing.T) {
require.NoError(t, err)

// Send a XSS attack
err = stream.Send(&FixtureRequest{Name: "<script>window.location;</script>"})
require.NoError(t, err)
if err := stream.Send(&FixtureRequest{Name: "<script>window.location;</script>"}); err != io.EOF {
require.NoError(t, err)
}

// Check that the handler was properly called
res, err := stream.Recv()
Expand All @@ -83,8 +88,9 @@ func TestAppSec(t *testing.T) {

for i := 0; i < 5; i++ { // Fire multiple times, each time should result in a detected event
// Send a SQLi attack
err = stream.Send(&FixtureRequest{Name: fmt.Sprintf("-%[1]d' and %[1]d=%[1]d union select * from users--", i)})
require.NoError(t, err)
if err := stream.Send(&FixtureRequest{Name: fmt.Sprintf("-%[1]d' and %[1]d=%[1]d union select * from users--", i)}); err != io.EOF {
require.NoError(t, err)
}

// Check that the handler was properly called
res, err = stream.Recv()
Expand Down Expand Up @@ -121,9 +127,9 @@ func TestAppSec(t *testing.T) {
histogram[tr.Rule.ID]++
}

require.EqualValues(t, 1, histogram["crs-941-180"]) // XSS attack attempt
require.EqualValues(t, 5, histogram["crs-942-270"]) // SQL-injection attack attempt
require.EqualValues(t, 1, histogram["ua0-600-55x"]) // canary rule attack attempt
assert.EqualValues(t, 1, histogram["crs-941-180"]) // XSS attack attempt
assert.EqualValues(t, 5, histogram["crs-942-270"]) // SQL-injection attack attempt
assert.EqualValues(t, 1, histogram["ua0-600-55x"]) // canary rule attack attempt

require.Len(t, histogram, 3)
})
Expand All @@ -139,13 +145,13 @@ func TestBlocking(t *testing.T) {
}

setup := func() (FixtureClient, mocktracer.Tracer, func()) {
rig, err := newAppsecRig(false)
rig, err := newAppsecRig(t, false)
require.NoError(t, err)

mt := mocktracer.Start()

return rig.client, mt, func() {
rig.Close()
assert.NoError(t, rig.Close())
mt.Stop()
}
}
Expand Down Expand Up @@ -183,7 +189,7 @@ func TestBlocking(t *testing.T) {
} {
t.Run(tc.name, func(t *testing.T) {
// Helper assertion function to run for the unary and stream tests
assert := func(t *testing.T, do func(client FixtureClient)) {
withClient := func(t *testing.T, do func(client FixtureClient)) {
client, mt, cleanup := setup()
defer cleanup()

Expand All @@ -204,7 +210,7 @@ func TestBlocking(t *testing.T) {
}

t.Run("unary", func(t *testing.T) {
assert(t, func(client FixtureClient) {
withClient(t, func(client FixtureClient) {
ctx := metadata.NewOutgoingContext(context.Background(), tc.md)
reply, err := client.Ping(ctx, &FixtureRequest{Name: tc.message})
require.Nil(t, reply)
Expand All @@ -213,19 +219,18 @@ func TestBlocking(t *testing.T) {
})

t.Run("stream", func(t *testing.T) {
assert(t, func(client FixtureClient) {
withClient(t, func(client FixtureClient) {
ctx := metadata.NewOutgoingContext(context.Background(), tc.md)

// Open the stream
stream, err := client.StreamPing(ctx)
require.NoError(t, err)
defer func() {
require.NoError(t, stream.CloseSend())
}()
defer func() { assert.NoError(t, stream.CloseSend()) }()

// Send a message
err = stream.Send(&FixtureRequest{Name: tc.message})
require.NoError(t, err)
if err := stream.Send(&FixtureRequest{Name: tc.message}); err != io.EOF {
require.NoError(t, err)
}

// Receive a message
reply, err := stream.Recv()
Expand All @@ -249,20 +254,20 @@ func TestPasslist(t *testing.T) {
t.Skip("appsec disabled")
}

setup := func() (FixtureClient, mocktracer.Tracer, func()) {
rig, err := newAppsecRig(false)
setup := func(t *testing.T) (FixtureClient, mocktracer.Tracer, func()) {
rig, err := newAppsecRig(t, false)
require.NoError(t, err)

mt := mocktracer.Start()

return rig.client, mt, func() {
rig.Close()
assert.NoError(t, rig.Close())
mt.Stop()
}
}

t.Run("unary", func(t *testing.T) {
client, mt, cleanup := setup()
client, mt, cleanup := setup(t)
defer cleanup()

// Send the payload triggering the sec event thanks to the "zouzou" value in the RPC metadata
Expand All @@ -284,7 +289,7 @@ func TestPasslist(t *testing.T) {
})

t.Run("stream", func(t *testing.T) {
client, mt, cleanup := setup()
client, mt, cleanup := setup(t)
defer cleanup()

// Open the steam triggering the sec event thanks to the "zouzou" value in the RPC metadata
Expand All @@ -294,8 +299,9 @@ func TestPasslist(t *testing.T) {

// Send some messages
for i := 0; i < 5; i++ {
err = stream.Send(&FixtureRequest{Name: "hello"})
require.NoError(t, err)
if err := stream.Send(&FixtureRequest{Name: "hello"}); err != io.EOF {
require.NoError(t, err)
}

// Check that the handler was properly called
res, err := stream.Recv()
Expand All @@ -319,7 +325,7 @@ func TestPasslist(t *testing.T) {
})
}

func newAppsecRig(traceClient bool, interceptorOpts ...Option) (*appsecRig, error) {
func newAppsecRig(t *testing.T, traceClient bool, interceptorOpts ...Option) (*appsecRig, error) {
interceptorOpts = append([]InterceptorOption{WithServiceName("grpc")}, interceptorOpts...)

server := grpc.NewServer(
Expand All @@ -336,7 +342,7 @@ func newAppsecRig(traceClient bool, interceptorOpts ...Option) (*appsecRig, erro
}
_, port, _ := net.SplitHostPort(li.Addr().String())
// start our test fixtureServer.
go server.Serve(li)
go func() { assert.NoError(t, server.Serve(li)) }()

opts := []grpc.DialOption{grpc.WithInsecure()}
if traceClient {
Expand Down Expand Up @@ -370,9 +376,9 @@ type appsecRig struct {
client FixtureClient
}

func (r *appsecRig) Close() {
r.server.Stop()
r.conn.Close()
func (r *appsecRig) Close() error {
defer r.server.GracefulStop()
return r.conn.Close()
}

type appsecFixtureServer struct {
Expand Down
32 changes: 16 additions & 16 deletions contrib/google.golang.org/grpc/grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestUnary(t *testing.T) {
t.Run(name, func(t *testing.T) {
rig, err := newRig(true, WithServiceName("grpc"), WithRequestTags())
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(rig.Close()) }()
client := rig.client

mt := mocktracer.Start()
Expand Down Expand Up @@ -226,7 +226,7 @@ func TestStreaming(t *testing.T) {

rig, err := newRig(true, WithServiceName("grpc"))
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

span, ctx := tracer.StartSpanFromContext(context.Background(), "a",
tracer.ServiceName("b"),
Expand All @@ -251,7 +251,7 @@ func TestStreaming(t *testing.T) {

rig, err := newRig(true, WithServiceName("grpc"), WithStreamMessages(false))
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

span, ctx := tracer.StartSpanFromContext(context.Background(), "a",
tracer.ServiceName("b"),
Expand All @@ -276,7 +276,7 @@ func TestStreaming(t *testing.T) {

rig, err := newRig(true, WithServiceName("grpc"), WithStreamCalls(false))
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

span, ctx := tracer.StartSpanFromContext(context.Background(), "a",
tracer.ServiceName("b"),
Expand Down Expand Up @@ -318,7 +318,7 @@ func TestSpanTree(t *testing.T) {

rig, err := newRig(true, WithServiceName("grpc"))
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(rig.Close()) }()

{
// Unary Ping rpc leading to trace:
Expand Down Expand Up @@ -353,7 +353,7 @@ func TestSpanTree(t *testing.T) {

rig, err := newRig(true, WithServiceName("grpc"), WithRequestTags(), WithMetadataTags())
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(rig.Close()) }()
client := rig.client

{
Expand Down Expand Up @@ -438,7 +438,7 @@ func TestPass(t *testing.T) {

rig, err := newRig(false, WithServiceName("grpc"))
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(rig.Close()) }()
client := rig.client

ctx := context.Background()
Expand Down Expand Up @@ -472,7 +472,7 @@ func TestPreservesMetadata(t *testing.T) {
if err != nil {
t.Fatalf("error setting up rig: %s", err)
}
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

ctx := context.Background()
ctx = metadata.AppendToOutgoingContext(ctx, "test-key", "test-value")
Expand Down Expand Up @@ -500,7 +500,7 @@ func TestStreamSendsErrorCode(t *testing.T) {

rig, err := newRig(true)
require.NoError(t, err, "error setting up rig")
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

ctx := context.Background()

Expand Down Expand Up @@ -529,7 +529,7 @@ func TestStreamSendsErrorCode(t *testing.T) {
containsErrorCode = true
}
}
assert.True(t, containsErrorCode, "at least one span should contain error code")
assert.True(t, containsErrorCode, "at least one span should contain error code, the spans were:\n%v", spans)

// ensure that last span contains error code also
gotLastSpanCode := spans[len(spans)-1].Tag(tagCode)
Expand Down Expand Up @@ -603,9 +603,9 @@ type rig struct {
client FixtureClient
}

func (r *rig) Close() {
r.server.Stop()
r.conn.Close()
func (r *rig) Close() error {
defer r.server.GracefulStop()
return r.conn.Close()
}

func newRigWithInterceptors(
Expand Down Expand Up @@ -669,7 +669,7 @@ func TestAnalyticsSettings(t *testing.T) {
if err != nil {
t.Fatalf("error setting up rig: %s", err)
}
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

client := rig.client
resp, err := client.Ping(context.Background(), &FixtureRequest{Name: "pass"})
Expand Down Expand Up @@ -1145,7 +1145,7 @@ func getGenSpansFn(traceClient, traceServer bool) namingschematest.GenSpansFn {
}
rig, err := newRigWithInterceptors(serverInterceptors, clientInterceptors)
require.NoError(t, err)
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()
_, err = rig.client.Ping(context.Background(), &FixtureRequest{Name: "pass"})
require.NoError(t, err)

Expand Down Expand Up @@ -1312,7 +1312,7 @@ func TestIssue2050(t *testing.T) {
}
rig, err := newRigWithInterceptors(serverInterceptors, clientInterceptors)
require.NoError(t, err)
defer rig.Close()
defer func() { assert.NoError(t, rig.Close()) }()

// call tracer.Start after integration is initialized, to reproduce the issue
tracer.Start(tracer.WithHTTPClient(httpClient))
Expand Down
9 changes: 9 additions & 0 deletions internal/appsec/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ func WithRCConfig(cfg remoteconfig.ClientConfig) StartOption {
}
}

// WithWAFTimeout sets the AppSec WAF timeout to the specified cfg. This is primarily used for testing purposes. The
// recommended way to specify the WAF timeout for production workloads is by specifying the DD_APPSEC_WAF_TIMEOUT
// environment variable.
func WithWAFTimeout(timeout time.Duration) StartOption {
return func(c *Config) {
c.WAFTimeout = timeout
}
}

// IsEnabled returns true when appsec is enabled by the environment variable DD_APPSEC_ENABLED (as of strconv's boolean
// parsing rules). When false, it also returns whether the env var was actually set or not.
// In case of a parsing error, it returns a detailed error.
Expand Down

0 comments on commit 1910dd2

Please sign in to comment.