Skip to content

Commit

Permalink
Fix statistics calculation (#254)
Browse files Browse the repository at this point in the history
* Fix statistics calculation

* Add test

* Update chromedp dependency

* Small refactoring
  • Loading branch information
svkirillov authored Aug 21, 2024
1 parent 21eaa2d commit f7f712c
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 221 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ module github.com/wallarm/gotestwaf
go 1.22

require (
github.com/chromedp/cdproto v0.0.0-20240501202034-ef67d660e9fd
github.com/chromedp/chromedp v0.9.5
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476
github.com/chromedp/chromedp v0.10.0
github.com/clbanning/mxj v1.8.4
github.com/getkin/kin-openapi v0.114.0
github.com/go-echarts/go-echarts/v2 v2.2.5
Expand Down Expand Up @@ -55,7 +55,7 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,13 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/chromedp/cdproto v0.0.0-20240202021202-6d0b6a386732/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20240501202034-ef67d660e9fd h1:5/HXKq8EaAWVmnl6Hnyl4SVq7FF5990DBW6AuTrWtVw=
github.com/chromedp/cdproto v0.0.0-20240501202034-ef67d660e9fd/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20240801214329-3f85d328b335/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476 h1:VnjHsRXCRti7Av7E+j4DCha3kf68echfDzQ+wD11SBU=
github.com/chromedp/cdproto v0.0.0-20240810084448-b931b754e476/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93rg=
github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y=
github.com/chromedp/chromedp v0.10.0 h1:bRclRYVpMm/UVD76+1HcRW9eV3l58rFfy7AdBvKab1E=
github.com/chromedp/chromedp v0.10.0/go.mod h1:ei/1ncZIqXX1YnAYDkxhD4gzBgavMEUu7JCKvztdomE=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down Expand Up @@ -410,6 +415,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
Expand Down
213 changes: 83 additions & 130 deletions internal/db/statistics.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,36 @@ type Statistics struct {

TestCasesFingerprint string

TruePositiveTests struct {
SummaryTable []*SummaryTableRow
Blocked []*TestDetails
Bypasses []*TestDetails
Unresolved []*TestDetails
Failed []*FailedDetails

AllRequestsNumber int
BlockedRequestsNumber int
BypassedRequestsNumber int
UnresolvedRequestsNumber int
FailedRequestsNumber int
ResolvedRequestsNumber int

UnresolvedRequestsPercentage float64
ResolvedBlockedRequestsPercentage float64
ResolvedBypassedRequestsPercentage float64
FailedRequestsPercentage float64
}

TrueNegativeTests struct {
SummaryTable []*SummaryTableRow
FalsePositive []*TestDetails
TruePositive []*TestDetails
Unresolved []*TestDetails
Failed []*FailedDetails

AllRequestsNumber int
BlockedRequestsNumber int
BypassedRequestsNumber int
UnresolvedRequestsNumber int
FailedRequestsNumber int
ResolvedRequestsNumber int

UnresolvedRequestsPercentage float64
ResolvedFalseRequestsPercentage float64
ResolvedTrueRequestsPercentage float64
FailedRequestsPercentage float64
}
TruePositiveTests TestsSummary
TrueNegativeTests TestsSummary

Score struct {
ApiSec struct {
TruePositive float64
TrueNegative float64
Average float64
}

AppSec struct {
TruePositive float64
TrueNegative float64
Average float64
}

ApiSec Score
AppSec Score
Average float64
}
}

type TestsSummary struct {
SummaryTable []*SummaryTableRow
Blocked []*TestDetails
Bypasses []*TestDetails
Unresolved []*TestDetails
Failed []*FailedDetails

AllRequestsNumber int
BlockedRequestsNumber int
BypassedRequestsNumber int
UnresolvedRequestsNumber int
FailedRequestsNumber int
ResolvedRequestsNumber int

UnresolvedRequestsPercentage float64
ResolvedBlockedRequestsPercentage float64
ResolvedBypassedRequestsPercentage float64
FailedRequestsPercentage float64
}

type SummaryTableRow struct {
TestSet string `json:"test_set" validate:"required,printascii,max=256"`
TestCase string `json:"test_case" validate:"required,printascii,max=256"`
Expand Down Expand Up @@ -101,6 +74,12 @@ type FailedDetails struct {
Type string `json:"type" validate:"omitempty"`
}

type Score struct {
TruePositive float64
TrueNegative float64
Average float64
}

type Path struct {
Method string `json:"method" validate:"required,printascii,max=32"`
Path string `json:"path" validate:"required,printascii,max=1024"`
Expand Down Expand Up @@ -233,35 +212,8 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
}
}

// Number of all negative requests
s.TruePositiveTests.AllRequestsNumber = s.TruePositiveTests.BlockedRequestsNumber +
s.TruePositiveTests.BypassedRequestsNumber +
s.TruePositiveTests.UnresolvedRequestsNumber +
s.TruePositiveTests.FailedRequestsNumber

// Number of negative resolved requests
s.TruePositiveTests.ResolvedRequestsNumber = s.TruePositiveTests.BlockedRequestsNumber +
s.TruePositiveTests.BypassedRequestsNumber

// Number of all negative requests
s.TrueNegativeTests.AllRequestsNumber = s.TrueNegativeTests.BlockedRequestsNumber +
s.TrueNegativeTests.BypassedRequestsNumber +
s.TrueNegativeTests.UnresolvedRequestsNumber +
s.TrueNegativeTests.FailedRequestsNumber

// Number of positive resolved requests
s.TrueNegativeTests.ResolvedRequestsNumber = s.TrueNegativeTests.BlockedRequestsNumber +
s.TrueNegativeTests.BypassedRequestsNumber

s.TruePositiveTests.UnresolvedRequestsPercentage = CalculatePercentage(s.TruePositiveTests.UnresolvedRequestsNumber, s.TruePositiveTests.AllRequestsNumber)
s.TruePositiveTests.ResolvedBlockedRequestsPercentage = CalculatePercentage(s.TruePositiveTests.BlockedRequestsNumber, s.TruePositiveTests.ResolvedRequestsNumber)
s.TruePositiveTests.ResolvedBypassedRequestsPercentage = CalculatePercentage(s.TruePositiveTests.BypassedRequestsNumber, s.TruePositiveTests.ResolvedRequestsNumber)
s.TruePositiveTests.FailedRequestsPercentage = CalculatePercentage(s.TruePositiveTests.FailedRequestsNumber, s.TruePositiveTests.AllRequestsNumber)

s.TrueNegativeTests.UnresolvedRequestsPercentage = CalculatePercentage(s.TrueNegativeTests.UnresolvedRequestsNumber, s.TrueNegativeTests.AllRequestsNumber)
s.TrueNegativeTests.ResolvedFalseRequestsPercentage = CalculatePercentage(s.TrueNegativeTests.BlockedRequestsNumber, s.TrueNegativeTests.ResolvedRequestsNumber)
s.TrueNegativeTests.ResolvedTrueRequestsPercentage = CalculatePercentage(s.TrueNegativeTests.BypassedRequestsNumber, s.TrueNegativeTests.ResolvedRequestsNumber)
s.TrueNegativeTests.FailedRequestsPercentage = CalculatePercentage(s.TrueNegativeTests.FailedRequestsNumber, s.TrueNegativeTests.AllRequestsNumber)
calculateTestsSummaryStat(&s.TruePositiveTests)
calculateTestsSummaryStat(&s.TrueNegativeTests)

for _, blockedTest := range db.blockedTests {
sort.Strings(blockedTest.AdditionalInfo)
Expand All @@ -278,7 +230,7 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
}

if isFalsePositiveTest(blockedTest.Set) {
s.TrueNegativeTests.FalsePositive = append(s.TrueNegativeTests.FalsePositive, testDetails)
s.TrueNegativeTests.Blocked = append(s.TrueNegativeTests.Blocked, testDetails)
} else {
s.TruePositiveTests.Blocked = append(s.TruePositiveTests.Blocked, testDetails)
}
Expand All @@ -299,7 +251,7 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
}

if isFalsePositiveTest(passedTest.Set) {
s.TrueNegativeTests.TruePositive = append(s.TrueNegativeTests.TruePositive, testDetails)
s.TrueNegativeTests.Bypasses = append(s.TrueNegativeTests.Bypasses, testDetails)
} else {
s.TruePositiveTests.Bypasses = append(s.TruePositiveTests.Bypasses, testDetails)
}
Expand All @@ -321,7 +273,7 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti

if ignoreUnresolved || nonBlockedAsPassed {
if isFalsePositiveTest(unresolvedTest.Set) {
s.TrueNegativeTests.FalsePositive = append(s.TrueNegativeTests.FalsePositive, testDetails)
s.TrueNegativeTests.Blocked = append(s.TrueNegativeTests.Blocked, testDetails)
} else {
s.TruePositiveTests.Bypasses = append(s.TruePositiveTests.Bypasses, testDetails)
}
Expand Down Expand Up @@ -395,7 +347,7 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
var appSecTrueNegBypassNum int
var appSecTrueNegNum int

for _, test := range s.TrueNegativeTests.TruePositive {
for _, test := range s.TrueNegativeTests.Bypasses {
if isApiTest(test.TestSet) {
apiSecTrueNegNum++
apiSecTrueNegBypassNum++
Expand All @@ -404,83 +356,84 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
appSecTrueNegBypassNum++
}
}
for _, test := range s.TrueNegativeTests.FalsePositive {
for _, test := range s.TrueNegativeTests.Blocked {
if isApiTest(test.TestSet) {
apiSecTrueNegNum++
} else {
appSecTrueNegNum++
}
}

calculateScorePercentage(&s.Score.ApiSec, apiSecTruePosBlockedNum, apiSecTruePosNum, apiSecTrueNegBypassNum, apiSecTrueNegNum)
calculateScorePercentage(&s.Score.AppSec, appSecTruePosBlockedNum, appSecTruePosNum, appSecTrueNegBypassNum, appSecTrueNegNum)

var divider int
var sum float64

s.Score.ApiSec.TruePositive = CalculatePercentage(apiSecTruePosBlockedNum, apiSecTruePosNum)
s.Score.ApiSec.TrueNegative = CalculatePercentage(apiSecTrueNegBypassNum, apiSecTrueNegNum)

if apiSecTruePosNum != 0 {
if s.Score.ApiSec.Average != -1.0 {
divider++
sum += s.Score.ApiSec.TruePositive
} else {
s.Score.ApiSec.TruePositive = -1.0
sum += s.Score.ApiSec.Average
}

if apiSecTrueNegNum != 0 {
if s.Score.AppSec.Average != -1.0 {
divider++
sum += s.Score.ApiSec.TrueNegative
} else {
s.Score.ApiSec.TrueNegative = -1.0
sum += s.Score.AppSec.Average
}

if divider != 0 {
s.Score.ApiSec.Average = Round(sum / float64(divider))
s.Score.Average = Round(sum / float64(divider))
} else {
s.Score.ApiSec.Average = -1.0
s.Score.Average = -1.0
}

divider = 0
sum = 0.0
return s
}

s.Score.AppSec.TruePositive = CalculatePercentage(appSecTruePosBlockedNum, appSecTruePosNum)
s.Score.AppSec.TrueNegative = CalculatePercentage(appSecTrueNegBypassNum, appSecTrueNegNum)
func calculateTestsSummaryStat(s *TestsSummary) {
s.AllRequestsNumber = s.BlockedRequestsNumber +
s.BypassedRequestsNumber +
s.UnresolvedRequestsNumber +
s.FailedRequestsNumber

if appSecTruePosNum != 0 {
divider++
sum += s.Score.AppSec.TruePositive
} else {
s.Score.AppSec.TruePositive = -1.0
}
s.ResolvedRequestsNumber = s.BlockedRequestsNumber +
s.BypassedRequestsNumber

if appSecTrueNegNum != 0 {
divider++
sum += s.Score.AppSec.TrueNegative
} else {
s.Score.AppSec.TrueNegative = -1.0
}
s.UnresolvedRequestsPercentage = CalculatePercentage(s.UnresolvedRequestsNumber, s.AllRequestsNumber)
s.ResolvedBlockedRequestsPercentage = CalculatePercentage(s.BlockedRequestsNumber, s.ResolvedRequestsNumber)
s.ResolvedBypassedRequestsPercentage = CalculatePercentage(s.BypassedRequestsNumber, s.ResolvedRequestsNumber)
s.FailedRequestsPercentage = CalculatePercentage(s.FailedRequestsNumber, s.AllRequestsNumber)
}

if divider != 0 {
s.Score.AppSec.Average = Round(sum / float64(divider))
} else {
s.Score.AppSec.Average = -1.0
}
func calculateScorePercentage(s *Score, truePosBlockedNum, truePosNum, trueNegBypassNum, trueNegNum int) {
var (
divider int
sum float64
)

divider = 0
sum = 0.0
s.TruePositive = CalculatePercentage(truePosBlockedNum, truePosNum)
s.TrueNegative = CalculatePercentage(trueNegBypassNum, trueNegNum)

if s.Score.ApiSec.Average != -1.0 {
if truePosNum != 0 {
divider++
sum += s.Score.ApiSec.Average
sum += s.TruePositive
} else {
s.TruePositive = -1.0
}
if s.Score.AppSec.Average != -1.0 {

if trueNegNum != 0 {
divider++
sum += s.Score.AppSec.Average
sum += s.TrueNegative
} else {
s.TrueNegative = -1.0
}

if divider != 0 {
s.Score.Average = Round(sum / float64(divider))
// If all malicious request were passed then grade is 0.
if truePosBlockedNum == 0 {
s.Average = 0.0
} else {
s.Average = Round(sum / float64(divider))
}
} else {
s.Score.Average = -1.0
s.Average = -1.0
}

return s
}
Loading

0 comments on commit f7f712c

Please sign in to comment.