Skip to content

Commit

Permalink
Merge pull request #45 from fzipi/override-output-result-tests
Browse files Browse the repository at this point in the history
feat(tests): add overrides for running in cloud mode
  • Loading branch information
fzipi authored Feb 20, 2022
2 parents 1518cfe + 1ab43cc commit 6307d19
Show file tree
Hide file tree
Showing 24 changed files with 539 additions and 288 deletions.
23 changes: 19 additions & 4 deletions check/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/fzipi/go-ftw/config"
"github.com/fzipi/go-ftw/test"
"github.com/fzipi/go-ftw/waflog"
"github.com/rs/zerolog/log"
)

// FTWCheck is the base struct for checking test results
Expand All @@ -32,9 +31,6 @@ func NewCheck(c *config.FTWConfiguration) *FTWCheck {
overrides: &c.TestOverride,
}

log.Trace().Msgf("check/base: time will be truncated to %s", check.log.TimeTruncate)
log.Trace().Msgf("check/base: logfile will be truncated? %t", check.log.LogTruncate)

return check
}

Expand Down Expand Up @@ -91,3 +87,22 @@ func (c *FTWCheck) ForcedFail(id string) bool {
_, ok := c.overrides.ForceFail[id]
return ok
}

// CloudMode returns true if we are running in cloud mode
func (c *FTWCheck) CloudMode() bool {
return c.overrides.Mode == config.CloudMode
}

// SetCloudMode alters the values for expected logs and status code
func (c *FTWCheck) SetCloudMode() {
var status = c.expected.Status

if c.expected.LogContains != "" {
status = append(status, 403)
c.expected.LogContains = ""
} else if c.expected.NoLogContains != "" {
status = append(status, 200, 404, 405)
c.expected.NoLogContains = ""
}
c.expected.Status = status
}
89 changes: 81 additions & 8 deletions check/base_test.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package check

import (
"sort"
"testing"
"time"

"github.com/fzipi/go-ftw/config"
"github.com/fzipi/go-ftw/test"
)

var yamlApacheConfig = `
---
var yamlApacheConfig = `---
logfile: 'tests/logs/modsec2-apache/apache2/error.log'
logtype:
name: 'apache'
timeregex: '\[([A-Z][a-z]{2} [A-z][a-z]{2} \d{1,2} \d{1,2}\:\d{1,2}\:\d{1,2}\.\d+? \d{4})\]'
timeformat: 'ddd MMM DD HH:mm:ss.S YYYY'
`

var yamlNginxConfig = `
---
var yamlNginxConfig = `---
logfile: 'tests/logs/modsec3-nginx/nginx/error.log'
logtype:
name: 'nginx'
Expand All @@ -29,8 +29,16 @@ testoverride:
'942200-1': 'Ignore Me'
`

var yamlCloudConfig = `---
testoverride:
mode: "cloud"
`

func TestNewCheck(t *testing.T) {
config.ImportFromString(yamlNginxConfig)
err := config.NewConfigFromString(yamlNginxConfig)
if err != nil {
t.Error(err)
}

c := NewCheck(config.FTWConfig)

Expand All @@ -43,10 +51,32 @@ func TestNewCheck(t *testing.T) {
t.Errorf("Well, didn't match Ignore Me")
}
}

to := test.Output{
Status: []int{200},
ResponseContains: "",
LogContains: "nothing",
NoLogContains: "",
ExpectError: true,
}
c.SetExpectTestOutput(&to)

if c.expected.ExpectError != true {
t.Error("Problem setting expected output")
}

c.SetNoLogContains("nologcontains")

if c.expected.NoLogContains != "nologcontains" {
t.Error("PRoblem setting nologcontains")
}
}

func TestForced(t *testing.T) {
config.ImportFromString(yamlNginxConfig)
err := config.NewConfigFromString(yamlNginxConfig)
if err != nil {
t.Error(err)
}

c := NewCheck(config.FTWConfig)

Expand All @@ -55,10 +85,53 @@ func TestForced(t *testing.T) {
}

if c.ForcedFail("1245") {
t.Errorf("Valued should not be found")
t.Errorf("Value should not be found")
}

if c.ForcedPass("1245") {
t.Errorf("Valued should not be found")
t.Errorf("Value should not be found")
}
}

func TestCloudMode(t *testing.T) {
err := config.NewConfigFromString(yamlCloudConfig)
if err != nil {
t.Error(err)
}

c := NewCheck(config.FTWConfig)

if c.CloudMode() != true {
t.Errorf("couldn't detect cloud mode")
}

status := []int{200, 301}
c.SetExpectStatus(status)
c.SetLogContains("this text")
// this should override logcontains
c.SetCloudMode()

cloudStatus := c.expected.Status
sort.Ints(cloudStatus)
if res := sort.SearchInts(cloudStatus, 403); res == 0 {
t.Errorf("couldn't find expected 403 status in %#v -> %d", cloudStatus, res)
}

c.SetLogContains("")
c.SetNoLogContains("no log contains")
// this should override logcontains
c.SetCloudMode()

cloudStatus = c.expected.Status
sort.Ints(cloudStatus)
found := false
for _, n := range cloudStatus {
if n == 200 {
found = true
}
}
if !found {
t.Errorf("couldn't find expected 200 status\n")
}

}
11 changes: 9 additions & 2 deletions check/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ var expectedFailTests = []struct {
}

func TestAssertResponseErrorOK(t *testing.T) {
config.ImportFromString(yamlApacheConfig)
err := config.NewConfigFromString(yamlApacheConfig)

if err != nil {
t.Errorf("Failed!")
}
c := NewCheck(config.FTWConfig)
for _, e := range expectedOKTests {
c.SetExpectError(e.expected)
Expand All @@ -36,7 +39,11 @@ func TestAssertResponseErrorOK(t *testing.T) {
}

func TestAssertResponseFail(t *testing.T) {
config.ImportFromString(yamlApacheConfig)
err := config.NewConfigFromString(yamlApacheConfig)

if err != nil {
t.Errorf("Failed!")
}

c := NewCheck(config.FTWConfig)

Expand Down
4 changes: 0 additions & 4 deletions check/logs.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package check

import "github.com/rs/zerolog/log"

// AssertNoLogContains returns true is the string is not found in the logs
func (c *FTWCheck) AssertNoLogContains() bool {
if c.expected.NoLogContains != "" {
Expand All @@ -12,9 +10,7 @@ func (c *FTWCheck) AssertNoLogContains() bool {

// AssertLogContains returns true when the logs contain the string
func (c *FTWCheck) AssertLogContains() bool {
log.Trace().Msgf("ftw/check: check will truncate at %s", c.log.TimeTruncate)
if c.expected.LogContains != "" {
log.Debug().Msgf("ftw/check: log contains? -> %s", c.expected.LogContains)
return c.log.Contains(c.expected.LogContains)
}
return false
Expand Down
20 changes: 14 additions & 6 deletions check/logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,25 @@ import (
"github.com/fzipi/go-ftw/utils"
)

var nginxLogText = `
2021/03/16 12:40:19 [info] 17#17: *2495 ModSecurity: Warning. Matched "Operator ` + "`" + `Within' with parameter ` + "`" + `GET HEAD POST OPTIONS' against variable ` + "`" + `REQUEST_METHOD' (Value: ` + "`" + `OTHER' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf"] [line "27"] [id "911100"] [rev ""] [msg "Method is not allowed by policy"] [data "OTHER"] [severity "2"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/210/272/220/274"] [tag "PCI/12.1"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841954.023243"] [ref "v0,5"], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
var nginxLogText = `2021/03/16 12:40:19 [info] 17#17: *2495 ModSecurity: Warning. Matched "Operator ` + "`" + `Within' with parameter ` + "`" + `GET HEAD POST OPTIONS' against variable ` + "`" + `REQUEST_METHOD' (Value: ` + "`" + `OTHER' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf"] [line "27"] [id "911100"] [rev ""] [msg "Method is not allowed by policy"] [data "OTHER"] [severity "2"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/210/272/220/274"] [tag "PCI/12.1"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841954.023243"] [ref "v0,5"], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
2021/03/16 12:40:19 [info] 17#17: *2495 ModSecurity: Warning. Matched "Operator ` + "`" + `Pm' with parameter ` + "`" + `AppleWebKit Android' against variable ` + "`" + `REQUEST_HEADERS:User-Agent' (Value: ` + "`" + `ModSecurity CRS 3 Tests' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1360"] [id "920300"] [rev ""] [msg "Request Missing an Accept Header"] [data ""] [severity "5"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "OWASP_CRS"] [tag "capec/1000/210/272"] [tag "PCI/6.5.10"] [tag "paranoia-level/3"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841954.023243"] [ref "v0,5v63,23"], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
2021/03/16 12:40:19 [info] 17#17: *2495 ModSecurity: Warning. Matched "Operator ` + "`" + `Ge' with parameter ` + "`" + `5' against variable ` + "`" + `TX:ANOMALY_SCORE' (Value: ` + "`" + `7' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "138"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 7)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841954.023243"] [ref ""], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
2021/03/16 12:40:19 [info] 17#17: *2497 ModSecurity: Warning. Matched "Operator ` + "`" + `Within' with parameter ` + "`" + `GET HEAD POST OPTIONS' against variable ` + "`" + `REQUEST_METHOD' (Value: ` + "`" + `OTHER' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-911-METHOD-ENFORCEMENT.conf"] [line "27"] [id "911100"] [rev ""] [msg "Method is not allowed by policy"] [data "OTHER"] [severity "2"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/210/272/220/274"] [tag "PCI/12.1"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841970.216949"] [ref "v0,5"], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
2021/03/16 12:40:19 [info] 17#17: *2497 ModSecurity: Warning. Matched "Operator ` + "`" + `Pm' with parameter ` + "`" + `AppleWebKit Android' against variable ` + "`" + `REQUEST_HEADERS:User-Agent' (Value: ` + "`" + `ModSecurity CRS 3 Tests' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1360"] [id "920300"] [rev ""] [msg "Request Missing an Accept Header"] [data ""] [severity "5"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "OWASP_CRS"] [tag "capec/1000/210/272"] [tag "PCI/6.5.10"] [tag "paranoia-level/3"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841970.216949"] [ref "v0,5v63,23"], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
2021/03/16 12:40:19 [info] 17#17: *2497 ModSecurity: Warning. Matched "Operator ` + "`" + `Ge' with parameter ` + "`" + `5' against variable ` + "`" + `TX:ANOMALY_SCORE' (Value: ` + "`" + `7' ) [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "138"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 7)"] [data ""] [severity "2"] [ver "OWASP_CRS/3.3.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "172.19.0.3"] [uri "/"] [unique_id "161589841970.216949"] [ref ""], client: 172.19.0.1, server: modsec3-nginx, request: "OTHER / HTTP/1.1", host: "localhost"
`

var apacheLogText = `
[Tue Jan 05 02:21:09.637165 2021] [:error] [pid 76:tid 139683434571520] [client 172.23.0.1:58998] [client 172.23.0.1] ModSecurity: Warning. Pattern match "\\\\b(?:keep-alive|close),\\\\s?(?:keep-alive|close)\\\\b" at REQUEST_HEADERS:Connection. [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "339"] [id "920210"] [msg "Multiple/Conflicting Connection Header Data Found"] [data "close,close"] [severity "WARNING"] [ver "OWASP_CRS/3.3.0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/210/272"] [hostname "localhost"] [uri "/"] [unique_id "X-PNFSe1VwjCgYRI9FsbHgAAAIY"]
var apacheLogText = `[Tue Jan 05 02:21:09.637165 2021] [:error] [pid 76:tid 139683434571520] [client 172.23.0.1:58998] [client 172.23.0.1] ModSecurity: Warning. Pattern match "\\\\b(?:keep-alive|close),\\\\s?(?:keep-alive|close)\\\\b" at REQUEST_HEADERS:Connection. [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "339"] [id "920210"] [msg "Multiple/Conflicting Connection Header Data Found"] [data "close,close"] [severity "WARNING"] [ver "OWASP_CRS/3.3.0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/210/272"] [hostname "localhost"] [uri "/"] [unique_id "X-PNFSe1VwjCgYRI9FsbHgAAAIY"]
[Tue Jan 05 02:21:09.637731 2021] [:error] [pid 76:tid 139683434571520] [client 172.23.0.1:58998] [client 172.23.0.1] ModSecurity: Warning. Match of "pm AppleWebKit Android" against "REQUEST_HEADERS:User-Agent" required. [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1230"] [id "920300"] [msg "Request Missing an Accept Header"] [severity "NOTICE"] [ver "OWASP_CRS/3.3.0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "OWASP_CRS"] [tag "capec/1000/210/272"] [tag "PCI/6.5.10"] [tag "paranoia-level/2"] [hostname "localhost"] [uri "/"] [unique_id "X-PNFSe1VwjCgYRI9FsbHgAAAIY"]
[Tue Jan 05 02:21:09.638572 2021] [:error] [pid 76:tid 139683434571520] [client 172.23.0.1:58998] [client 172.23.0.1] ModSecurity: Warning. Operator GE matched 5 at TX:anomaly_score. [file "/etc/modsecurity.d/owasp-crs/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "91"] [id "949110"] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [severity "CRITICAL"] [ver "OWASP_CRS/3.3.0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "localhost"] [uri "/"] [unique_id "X-PNFSe1VwjCgYRI9FsbHgAAAIY"]
[Tue Jan 05 02:21:09.647668 2021] [:error] [pid 76:tid 139683434571520] [client 172.23.0.1:58998] [client 172.23.0.1] ModSecurity: Warning. Operator GE matched 5 at TX:inbound_anomaly_score. [file "/etc/modsecurity.d/owasp-crs/rules/RESPONSE-980-CORRELATION.conf"] [line "87"] [id "980130"] [msg "Inbound Anomaly Score Exceeded (Total Inbound Score: 5 - SQLI=0,XSS=0,RFI=0,LFI=0,RCE=0,PHPI=0,HTTP=0,SESS=0): individual paranoia level scores: 3, 2, 0, 0"] [ver "OWASP_CRS/3.3.0"] [tag "event-correlation"] [hostname "localhost"] [uri "/"] [unique_id "X-PNFSe1VwjCgYRI9FsbHgAAAIY"]
`

func TestAssertApacheLogContainsOK(t *testing.T) {
config.ImportFromString(yamlApacheConfig)
err := config.NewConfigFromString(yamlApacheConfig)
if err != nil {
t.Errorf("Failed!")
}
logName, _ := utils.CreateTempFileWithContent(apacheLogText, "test-apache-*.log")
defer os.Remove(logName)
config.FTWConfig.LogFile = logName
Expand All @@ -50,7 +51,10 @@ func TestAssertApacheLogContainsOK(t *testing.T) {
}

func TestAssertNginxLogContainsOK(t *testing.T) {
config.ImportFromString(yamlNginxConfig)
err := config.NewConfigFromString(yamlNginxConfig)
if err != nil {
t.Errorf("Failed!")
}
logName, _ := utils.CreateTempFileWithContent(nginxLogText, "test-nginx-*.log")
defer os.Remove(logName)
config.FTWConfig.LogFile = logName
Expand All @@ -66,4 +70,8 @@ func TestAssertNginxLogContainsOK(t *testing.T) {
if !c.AssertLogContains() {
t.Errorf("Failed !")
}

if c.AssertNoLogContains() {
t.Error("No log contains failed")
}
}
3 changes: 0 additions & 3 deletions check/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ package check

import (
"strings"

"github.com/rs/zerolog/log"
)

// AssertResponseContains checks that the http response contains the needle
func (c *FTWCheck) AssertResponseContains(response string) bool {
if c.expected.ResponseContains != "" {
log.Trace().Msgf("ftw/check: is %s contained in response %s", c.expected.ResponseContains, response)
return strings.Contains(response, c.expected.ResponseContains)
}
return false
Expand Down
11 changes: 8 additions & 3 deletions check/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ var expectedResponseFailTests = []struct {
}

func TestAssertResponseTextErrorOK(t *testing.T) {
config.ImportFromString(yamlApacheConfig)
err := config.NewConfigFromString(yamlApacheConfig)

if err != nil {
t.Errorf("Failed!")
}
c := NewCheck(config.FTWConfig)
for _, e := range expectedResponseOKTests {
c.SetExpectResponse(e.expected)
Expand All @@ -33,8 +36,10 @@ func TestAssertResponseTextErrorOK(t *testing.T) {
}

func TestAssertResponseTextFailOK(t *testing.T) {
config.ImportFromString(yamlApacheConfig)

err := config.NewConfigFromString(yamlApacheConfig)
if err != nil {
t.Errorf("Failed!")
}
c := NewCheck(config.FTWConfig)
for _, e := range expectedResponseFailTests {
c.SetExpectResponse(e.expected)
Expand Down
3 changes: 0 additions & 3 deletions check/status.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package check

import "github.com/rs/zerolog/log"

// AssertStatus will match the expected status list with the one received in the response
func (c *FTWCheck) AssertStatus(status int) bool {
log.Trace().Msgf("ftw/check: status %d, expected %v", status, c.expected.Status)
for _, i := range c.expected.Status {
if i == status {
return true
Expand Down
10 changes: 8 additions & 2 deletions check/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ var statusFailTests = []struct {
}

func TestStatusOK(t *testing.T) {
config.ImportFromString(yamlApacheConfig)
err := config.NewConfigFromString(yamlApacheConfig)
if err != nil {
t.Errorf("Failed!")
}

c := NewCheck(config.FTWConfig)

Expand All @@ -37,7 +40,10 @@ func TestStatusOK(t *testing.T) {
}

func TestStatusFail(t *testing.T) {
config.ImportFromString(yamlApacheConfig)
err := config.NewConfigFromString(yamlApacheConfig)
if err != nil {
t.Errorf("Failed!")
}

c := NewCheck(config.FTWConfig)

Expand Down
11 changes: 9 additions & 2 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package cmd

import (
"log"
"os"

config "github.com/fzipi/go-ftw/config"
"github.com/fzipi/go-ftw/config"

"github.com/rs/zerolog"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -42,12 +43,18 @@ func init() {
}

func initConfig() {
config.Init(cfgFile)
zerolog.SetGlobalLevel(zerolog.InfoLevel)
if debug {
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
if trace {
zerolog.SetGlobalLevel(zerolog.TraceLevel)
}
errFile := config.NewConfigFromFile(cfgFile)
if errFile != nil {
errEnv := config.NewConfigFromEnv()
if errEnv != nil {
log.Fatalf("cannot read config from file (%s) nor environment (%s).", errFile.Error(), errEnv.Error())
}
}
}
Loading

0 comments on commit 6307d19

Please sign in to comment.