Skip to content

Commit

Permalink
.github: add system tests in CI (#1059)
Browse files Browse the repository at this point in the history
System tests is a functional test suite that perform black box testing to ensure
the good behavior of feature across all components. As now, the coverage is
mainly on AppSec, but it can check all features, as long as they have a visible
effect (stdout, communication with agent ...).

Co-authored-by: Gabriel Aszalos <[email protected]>
Co-authored-by: Julio Guerra <[email protected]>
  • Loading branch information
3 people authored Dec 2, 2021
1 parent c48db64 commit 11c835c
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 7 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/system-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: System Tests

on:
pull_request:
branches:
- "**"
workflow_dispatch: {}
schedule:
- cron: '00 04 * * 2-6'

jobs:
system-tests:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- library: golang
weblog-variant: net-http
- library: golang
weblog-variant: gorilla
fail-fast: false
env:
TEST_LIBRARY: golang
WEBLOG_VARIANT: ${{ matrix.weblog-variant }}
DD_API_KEY: ${{ secrets.DD_API_KEY }}
steps:
- name: Checkout system tests
uses: actions/checkout@v2
with:
repository: 'DataDog/system-tests'

- name: Checkout dd-trace-go
uses: actions/checkout@v2
with:
path: 'binaries/dd-trace-go'

- name: Build
run: ./build.sh

- name: Run
run: ./run.sh

- name: Compress artifact
if: ${{ always() }}
run: tar -czvf artifact.tar.gz $(ls | grep logs)

- name: Upload artifact
uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: logs_${{ matrix.weblog-variant }}
path: artifact.tar.gz
12 changes: 11 additions & 1 deletion internal/appsec/appsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ func Start(cfg *Config) {
log.Info("appsec: starting with default recommended security rules")
}

cfg.wafTimeout = 4 * time.Millisecond
if wafTimeout := os.Getenv("DD_APPSEC_WAF_TIMEOUT"); wafTimeout != "" {
timeout, err := time.ParseDuration(wafTimeout)
if err != nil {
cfg.wafTimeout = timeout
} else {
log.Error("appsec: could not parse the value of DD_APPSEC_WAF_TIMEOUT %s as a duration: %v. Using default value %s.", wafTimeout, err, cfg.wafTimeout)
}
}

appsec, err := newAppSec(cfg)
if err != nil {
logUnexpectedStartError(err)
Expand Down Expand Up @@ -138,7 +148,7 @@ func newAppSec(cfg *Config) (*appsec, error) {
// Start starts the AppSec background goroutine.
func (a *appsec) start() error {
// Register the WAF operation event listener
unregisterWAF, err := registerWAF(a.cfg.rules, a)
unregisterWAF, err := registerWAF(a.cfg.rules, a.cfg.wafTimeout, a)
if err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions internal/appsec/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ type (

// rules loaded via the env var DD_APPSEC_RULES. When not set, the builtin rules will be used.
rules []byte
// Maximum WAF execution time
wafTimeout time.Duration
}

// ServiceConfig is the optional context about the running service.
Expand Down
12 changes: 6 additions & 6 deletions internal/appsec/waf.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type wafEvent struct {
}

// Register the WAF event listener.
func registerWAF(rules []byte, appsec *appsec) (unreg dyngo.UnregisterFunc, err error) {
func registerWAF(rules []byte, timeout time.Duration, appsec *appsec) (unreg dyngo.UnregisterFunc, err error) {
// Check the WAF is healthy
if _, err := waf.Health(); err != nil {
return nil, err
Expand Down Expand Up @@ -65,7 +65,7 @@ func registerWAF(rules []byte, appsec *appsec) (unreg dyngo.UnregisterFunc, err
}

// Register the WAF event listener
unregister := dyngo.Register(newWAFEventListener(waf, addresses, appsec))
unregister := dyngo.Register(newWAFEventListener(waf, addresses, appsec, timeout))
// Return an unregistration function that will also release the WAF instance.
return func() {
defer waf.Close()
Expand All @@ -74,7 +74,7 @@ func registerWAF(rules []byte, appsec *appsec) (unreg dyngo.UnregisterFunc, err
}

// newWAFEventListener returns the WAF event listener to register in order to enable it.
func newWAFEventListener(handle *waf.Handle, addresses []string, appsec *appsec) dyngo.EventListener {
func newWAFEventListener(handle *waf.Handle, addresses []string, appsec *appsec, timeout time.Duration) dyngo.EventListener {
return httpinstr.OnHandlerOperationStart(func(op dyngo.Operation, args httpinstr.HandlerOperationArgs) {
// For this handler operation lifetime, create a WAF context and the
// list of detected attacks
Expand Down Expand Up @@ -126,12 +126,12 @@ func newWAFEventListener(handle *waf.Handle, addresses []string, appsec *appsec)
values[serverRequestQueryAddr] = args.Query
}
}
baseEvent = runWAF(wafCtx, values)
baseEvent = runWAF(wafCtx, values, timeout)
})
}

func runWAF(wafCtx *waf.Context, values map[string]interface{}) *wafEvent {
matches, err := wafCtx.Run(values, 4*time.Millisecond)
func runWAF(wafCtx *waf.Context, values map[string]interface{}, timeout time.Duration) *wafEvent {
matches, err := wafCtx.Run(values, timeout)
if err != nil {
log.Error("appsec: waf error: %v", err)
return nil
Expand Down

0 comments on commit 11c835c

Please sign in to comment.