Skip to content

Commit b889bcd

Browse files
authored
Improve JSON report (#255)
1 parent f7f712c commit b889bcd

File tree

5 files changed

+301
-167
lines changed

5 files changed

+301
-167
lines changed

internal/db/statistics.go

+127-75
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,9 @@ type TestsSummary struct {
2929
Unresolved []*TestDetails
3030
Failed []*FailedDetails
3131

32-
AllRequestsNumber int
33-
BlockedRequestsNumber int
34-
BypassedRequestsNumber int
35-
UnresolvedRequestsNumber int
36-
FailedRequestsNumber int
37-
ResolvedRequestsNumber int
32+
ReqStats RequestStats
33+
ApiSecReqStats RequestStats
34+
AppSecReqStats RequestStats
3835

3936
UnresolvedRequestsPercentage float64
4037
ResolvedBlockedRequestsPercentage float64
@@ -74,6 +71,15 @@ type FailedDetails struct {
7471
Type string `json:"type" validate:"omitempty"`
7572
}
7673

74+
type RequestStats struct {
75+
AllRequestsNumber int
76+
BlockedRequestsNumber int
77+
BypassedRequestsNumber int
78+
UnresolvedRequestsNumber int
79+
FailedRequestsNumber int
80+
ResolvedRequestsNumber int
81+
}
82+
7783
type Score struct {
7884
TruePositive float64
7985
TrueNegative float64
@@ -187,34 +193,30 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
187193
// If positive set - move to another table (remove from general cases)
188194
if isFalsePositive {
189195
// False positive - blocked by the WAF (bad behavior, blockedRequests)
190-
s.TrueNegativeTests.BlockedRequestsNumber += blockedRequests
196+
s.TrueNegativeTests.ReqStats.BlockedRequestsNumber += blockedRequests
191197
// True positive - bypassed (good behavior, passedRequests)
192-
s.TrueNegativeTests.BypassedRequestsNumber += passedRequests
193-
s.TrueNegativeTests.UnresolvedRequestsNumber += unresolvedRequests
194-
s.TrueNegativeTests.FailedRequestsNumber += failedRequests
198+
s.TrueNegativeTests.ReqStats.BypassedRequestsNumber += passedRequests
199+
s.TrueNegativeTests.ReqStats.UnresolvedRequestsNumber += unresolvedRequests
200+
s.TrueNegativeTests.ReqStats.FailedRequestsNumber += failedRequests
195201

196202
passedRequestsPercentage := CalculatePercentage(passedRequests, totalResolvedRequests)
197203
row.Percentage = passedRequestsPercentage
198204

199205
s.TrueNegativeTests.SummaryTable = append(s.TrueNegativeTests.SummaryTable, row)
200206
} else {
201-
s.TruePositiveTests.BlockedRequestsNumber += blockedRequests
202-
s.TruePositiveTests.BypassedRequestsNumber += passedRequests
203-
s.TruePositiveTests.UnresolvedRequestsNumber += unresolvedRequests
204-
s.TruePositiveTests.FailedRequestsNumber += failedRequests
207+
s.TruePositiveTests.ReqStats.BlockedRequestsNumber += blockedRequests
208+
s.TruePositiveTests.ReqStats.BypassedRequestsNumber += passedRequests
209+
s.TruePositiveTests.ReqStats.UnresolvedRequestsNumber += unresolvedRequests
210+
s.TruePositiveTests.ReqStats.FailedRequestsNumber += failedRequests
205211

206212
blockedRequestsPercentage := CalculatePercentage(blockedRequests, totalResolvedRequests)
207213
row.Percentage = blockedRequestsPercentage
208214

209215
s.TruePositiveTests.SummaryTable = append(s.TruePositiveTests.SummaryTable, row)
210-
211216
}
212217
}
213218
}
214219

215-
calculateTestsSummaryStat(&s.TruePositiveTests)
216-
calculateTestsSummaryStat(&s.TrueNegativeTests)
217-
218220
for _, blockedTest := range db.blockedTests {
219221
sort.Strings(blockedTest.AdditionalInfo)
220222

@@ -231,8 +233,20 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
231233

232234
if isFalsePositiveTest(blockedTest.Set) {
233235
s.TrueNegativeTests.Blocked = append(s.TrueNegativeTests.Blocked, testDetails)
236+
237+
if isApiTest(blockedTest.Set) {
238+
s.TrueNegativeTests.ApiSecReqStats.BlockedRequestsNumber += 1
239+
} else {
240+
s.TrueNegativeTests.AppSecReqStats.BlockedRequestsNumber += 1
241+
}
234242
} else {
235243
s.TruePositiveTests.Blocked = append(s.TruePositiveTests.Blocked, testDetails)
244+
245+
if isApiTest(blockedTest.Set) {
246+
s.TruePositiveTests.ApiSecReqStats.BlockedRequestsNumber += 1
247+
} else {
248+
s.TruePositiveTests.AppSecReqStats.BlockedRequestsNumber += 1
249+
}
236250
}
237251
}
238252

@@ -252,8 +266,20 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
252266

253267
if isFalsePositiveTest(passedTest.Set) {
254268
s.TrueNegativeTests.Bypasses = append(s.TrueNegativeTests.Bypasses, testDetails)
269+
270+
if isApiTest(passedTest.Set) {
271+
s.TrueNegativeTests.ApiSecReqStats.BypassedRequestsNumber += 1
272+
} else {
273+
s.TrueNegativeTests.AppSecReqStats.BypassedRequestsNumber += 1
274+
}
255275
} else {
256276
s.TruePositiveTests.Bypasses = append(s.TruePositiveTests.Bypasses, testDetails)
277+
278+
if isApiTest(passedTest.Set) {
279+
s.TruePositiveTests.ApiSecReqStats.BypassedRequestsNumber += 1
280+
} else {
281+
s.TruePositiveTests.AppSecReqStats.BypassedRequestsNumber += 1
282+
}
257283
}
258284
}
259285

@@ -274,14 +300,38 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
274300
if ignoreUnresolved || nonBlockedAsPassed {
275301
if isFalsePositiveTest(unresolvedTest.Set) {
276302
s.TrueNegativeTests.Blocked = append(s.TrueNegativeTests.Blocked, testDetails)
303+
304+
if isApiTest(unresolvedTest.Set) {
305+
s.TrueNegativeTests.ApiSecReqStats.BlockedRequestsNumber += 1
306+
} else {
307+
s.TrueNegativeTests.AppSecReqStats.BlockedRequestsNumber += 1
308+
}
277309
} else {
278310
s.TruePositiveTests.Bypasses = append(s.TruePositiveTests.Bypasses, testDetails)
311+
312+
if isApiTest(unresolvedTest.Set) {
313+
s.TruePositiveTests.ApiSecReqStats.BypassedRequestsNumber += 1
314+
} else {
315+
s.TruePositiveTests.AppSecReqStats.BypassedRequestsNumber += 1
316+
}
279317
}
280318
} else {
281319
if isFalsePositiveTest(unresolvedTest.Set) {
282320
s.TrueNegativeTests.Unresolved = append(s.TrueNegativeTests.Unresolved, testDetails)
321+
322+
if isApiTest(unresolvedTest.Set) {
323+
s.TrueNegativeTests.ApiSecReqStats.UnresolvedRequestsNumber += 1
324+
} else {
325+
s.TrueNegativeTests.AppSecReqStats.UnresolvedRequestsNumber += 1
326+
}
283327
} else {
284328
s.TruePositiveTests.Unresolved = append(s.TruePositiveTests.Unresolved, testDetails)
329+
330+
if isApiTest(unresolvedTest.Set) {
331+
s.TruePositiveTests.ApiSecReqStats.UnresolvedRequestsNumber += 1
332+
} else {
333+
s.TruePositiveTests.AppSecReqStats.UnresolvedRequestsNumber += 1
334+
}
285335
}
286336
}
287337
}
@@ -299,8 +349,20 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
299349

300350
if isFalsePositiveTest(failedTest.Set) {
301351
s.TrueNegativeTests.Failed = append(s.TrueNegativeTests.Failed, testDetails)
352+
353+
if isApiTest(failedTest.Set) {
354+
s.TrueNegativeTests.ApiSecReqStats.FailedRequestsNumber += 1
355+
} else {
356+
s.TrueNegativeTests.AppSecReqStats.FailedRequestsNumber += 1
357+
}
302358
} else {
303359
s.TruePositiveTests.Failed = append(s.TruePositiveTests.Failed, testDetails)
360+
361+
if isApiTest(failedTest.Set) {
362+
s.TruePositiveTests.ApiSecReqStats.FailedRequestsNumber += 1
363+
} else {
364+
s.TruePositiveTests.AppSecReqStats.FailedRequestsNumber += 1
365+
}
304366
}
305367
}
306368

@@ -320,52 +382,23 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
320382
s.Paths = paths
321383
}
322384

323-
var apiSecTruePosBlockedNum int
324-
var apiSecTruePosNum int
325-
var appSecTruePosBlockedNum int
326-
var appSecTruePosNum int
327-
328-
for _, test := range s.TruePositiveTests.Blocked {
329-
if isApiTest(test.TestSet) {
330-
apiSecTruePosNum++
331-
apiSecTruePosBlockedNum++
332-
} else {
333-
appSecTruePosNum++
334-
appSecTruePosBlockedNum++
335-
}
336-
}
337-
for _, test := range s.TruePositiveTests.Bypasses {
338-
if isApiTest(test.TestSet) {
339-
apiSecTruePosNum++
340-
} else {
341-
appSecTruePosNum++
342-
}
343-
}
344-
345-
var apiSecTrueNegBypassNum int
346-
var apiSecTrueNegNum int
347-
var appSecTrueNegBypassNum int
348-
var appSecTrueNegNum int
349-
350-
for _, test := range s.TrueNegativeTests.Bypasses {
351-
if isApiTest(test.TestSet) {
352-
apiSecTrueNegNum++
353-
apiSecTrueNegBypassNum++
354-
} else {
355-
appSecTrueNegNum++
356-
appSecTrueNegBypassNum++
357-
}
358-
}
359-
for _, test := range s.TrueNegativeTests.Blocked {
360-
if isApiTest(test.TestSet) {
361-
apiSecTrueNegNum++
362-
} else {
363-
appSecTrueNegNum++
364-
}
365-
}
385+
calculateTestsSummaryStat(&s.TruePositiveTests)
386+
calculateTestsSummaryStat(&s.TrueNegativeTests)
366387

367-
calculateScorePercentage(&s.Score.ApiSec, apiSecTruePosBlockedNum, apiSecTruePosNum, apiSecTrueNegBypassNum, apiSecTrueNegNum)
368-
calculateScorePercentage(&s.Score.AppSec, appSecTruePosBlockedNum, appSecTruePosNum, appSecTrueNegBypassNum, appSecTrueNegNum)
388+
calculateScorePercentage(
389+
&s.Score.ApiSec,
390+
s.TruePositiveTests.ApiSecReqStats.BlockedRequestsNumber,
391+
s.TruePositiveTests.ApiSecReqStats.ResolvedRequestsNumber,
392+
s.TrueNegativeTests.ApiSecReqStats.BypassedRequestsNumber,
393+
s.TrueNegativeTests.ApiSecReqStats.ResolvedRequestsNumber,
394+
)
395+
calculateScorePercentage(
396+
&s.Score.AppSec,
397+
s.TruePositiveTests.AppSecReqStats.BlockedRequestsNumber,
398+
s.TruePositiveTests.AppSecReqStats.ResolvedRequestsNumber,
399+
s.TrueNegativeTests.AppSecReqStats.BypassedRequestsNumber,
400+
s.TrueNegativeTests.AppSecReqStats.ResolvedRequestsNumber,
401+
)
369402

370403
var divider int
371404
var sum float64
@@ -389,18 +422,37 @@ func (db *DB) GetStatistics(ignoreUnresolved, nonBlockedAsPassed bool) *Statisti
389422
}
390423

391424
func calculateTestsSummaryStat(s *TestsSummary) {
392-
s.AllRequestsNumber = s.BlockedRequestsNumber +
393-
s.BypassedRequestsNumber +
394-
s.UnresolvedRequestsNumber +
395-
s.FailedRequestsNumber
396-
397-
s.ResolvedRequestsNumber = s.BlockedRequestsNumber +
398-
s.BypassedRequestsNumber
399-
400-
s.UnresolvedRequestsPercentage = CalculatePercentage(s.UnresolvedRequestsNumber, s.AllRequestsNumber)
401-
s.ResolvedBlockedRequestsPercentage = CalculatePercentage(s.BlockedRequestsNumber, s.ResolvedRequestsNumber)
402-
s.ResolvedBypassedRequestsPercentage = CalculatePercentage(s.BypassedRequestsNumber, s.ResolvedRequestsNumber)
403-
s.FailedRequestsPercentage = CalculatePercentage(s.FailedRequestsNumber, s.AllRequestsNumber)
425+
// All requests stat
426+
s.ReqStats.AllRequestsNumber = s.ReqStats.BlockedRequestsNumber +
427+
s.ReqStats.BypassedRequestsNumber +
428+
s.ReqStats.UnresolvedRequestsNumber +
429+
s.ReqStats.FailedRequestsNumber
430+
431+
s.ReqStats.ResolvedRequestsNumber = s.ReqStats.BlockedRequestsNumber +
432+
s.ReqStats.BypassedRequestsNumber
433+
434+
// ApiSec requests stat
435+
s.ApiSecReqStats.AllRequestsNumber = s.ApiSecReqStats.BlockedRequestsNumber +
436+
s.ApiSecReqStats.BypassedRequestsNumber +
437+
s.ApiSecReqStats.UnresolvedRequestsNumber +
438+
s.ApiSecReqStats.FailedRequestsNumber
439+
440+
s.ApiSecReqStats.ResolvedRequestsNumber = s.ApiSecReqStats.BlockedRequestsNumber +
441+
s.ApiSecReqStats.BypassedRequestsNumber
442+
443+
// AppSec requests stat
444+
s.AppSecReqStats.AllRequestsNumber = s.AppSecReqStats.BlockedRequestsNumber +
445+
s.AppSecReqStats.BypassedRequestsNumber +
446+
s.AppSecReqStats.UnresolvedRequestsNumber +
447+
s.AppSecReqStats.FailedRequestsNumber
448+
449+
s.AppSecReqStats.ResolvedRequestsNumber = s.AppSecReqStats.BlockedRequestsNumber +
450+
s.AppSecReqStats.BypassedRequestsNumber
451+
452+
s.UnresolvedRequestsPercentage = CalculatePercentage(s.ReqStats.UnresolvedRequestsNumber, s.ReqStats.AllRequestsNumber)
453+
s.ResolvedBlockedRequestsPercentage = CalculatePercentage(s.ReqStats.BlockedRequestsNumber, s.ReqStats.ResolvedRequestsNumber)
454+
s.ResolvedBypassedRequestsPercentage = CalculatePercentage(s.ReqStats.BypassedRequestsNumber, s.ReqStats.ResolvedRequestsNumber)
455+
s.FailedRequestsPercentage = CalculatePercentage(s.ReqStats.FailedRequestsNumber, s.ReqStats.AllRequestsNumber)
404456
}
405457

406458
func calculateScorePercentage(s *Score, truePosBlockedNum, truePosNum, trueNegBypassNum, trueNegNum int) {

internal/db/statistics_test.go

+26-24
Original file line numberDiff line numberDiff line change
@@ -165,22 +165,22 @@ func testPropertyNotPanics(db *DB, ignoreUnresolved, nonBlockedAsPassed bool) bo
165165
func testPropertyOnlyPositiveNumberValues(db *DB, ignoreUnresolved, nonBlockedAsPassed bool) bool {
166166
stat := db.GetStatistics(ignoreUnresolved, nonBlockedAsPassed)
167167

168-
if stat.TruePositiveTests.AllRequestsNumber < 0 ||
169-
stat.TruePositiveTests.BlockedRequestsNumber < 0 ||
170-
stat.TruePositiveTests.BypassedRequestsNumber < 0 ||
171-
stat.TruePositiveTests.UnresolvedRequestsNumber < 0 ||
172-
stat.TruePositiveTests.FailedRequestsNumber < 0 ||
173-
stat.TruePositiveTests.ResolvedRequestsNumber < 0 ||
168+
if stat.TruePositiveTests.ReqStats.AllRequestsNumber < 0 ||
169+
stat.TruePositiveTests.ReqStats.BlockedRequestsNumber < 0 ||
170+
stat.TruePositiveTests.ReqStats.BypassedRequestsNumber < 0 ||
171+
stat.TruePositiveTests.ReqStats.UnresolvedRequestsNumber < 0 ||
172+
stat.TruePositiveTests.ReqStats.FailedRequestsNumber < 0 ||
173+
stat.TruePositiveTests.ReqStats.ResolvedRequestsNumber < 0 ||
174174
stat.TruePositiveTests.UnresolvedRequestsPercentage < 0 ||
175175
stat.TruePositiveTests.ResolvedBlockedRequestsPercentage < 0 ||
176176
stat.TruePositiveTests.ResolvedBypassedRequestsPercentage < 0 ||
177177
stat.TruePositiveTests.FailedRequestsPercentage < 0 ||
178-
stat.TrueNegativeTests.AllRequestsNumber < 0 ||
179-
stat.TrueNegativeTests.BlockedRequestsNumber < 0 ||
180-
stat.TrueNegativeTests.BypassedRequestsNumber < 0 ||
181-
stat.TrueNegativeTests.UnresolvedRequestsNumber < 0 ||
182-
stat.TrueNegativeTests.FailedRequestsNumber < 0 ||
183-
stat.TrueNegativeTests.ResolvedRequestsNumber < 0 ||
178+
stat.TrueNegativeTests.ReqStats.AllRequestsNumber < 0 ||
179+
stat.TrueNegativeTests.ReqStats.BlockedRequestsNumber < 0 ||
180+
stat.TrueNegativeTests.ReqStats.BypassedRequestsNumber < 0 ||
181+
stat.TrueNegativeTests.ReqStats.UnresolvedRequestsNumber < 0 ||
182+
stat.TrueNegativeTests.ReqStats.FailedRequestsNumber < 0 ||
183+
stat.TrueNegativeTests.ReqStats.ResolvedRequestsNumber < 0 ||
184184
stat.TrueNegativeTests.UnresolvedRequestsPercentage < 0 ||
185185
stat.TrueNegativeTests.ResolvedBlockedRequestsPercentage < 0 ||
186186
stat.TrueNegativeTests.ResolvedBypassedRequestsPercentage < 0 ||
@@ -226,12 +226,12 @@ func testPropertyCorrectStatValues(db *DB, ignoreUnresolved, nonBlockedAsPassed
226226
counters["true-positive"]["resolved"] = counters["true-positive"]["blocked"] +
227227
counters["true-positive"]["bypassed"]
228228

229-
if counters["true-positive"]["all"] != stat.TruePositiveTests.AllRequestsNumber ||
230-
counters["true-positive"]["blocked"] != stat.TruePositiveTests.BlockedRequestsNumber ||
231-
counters["true-positive"]["bypassed"] != stat.TruePositiveTests.BypassedRequestsNumber ||
232-
counters["true-positive"]["unresolved"] != stat.TruePositiveTests.UnresolvedRequestsNumber ||
233-
counters["true-positive"]["failed"] != stat.TruePositiveTests.FailedRequestsNumber ||
234-
counters["true-positive"]["resolved"] != stat.TruePositiveTests.ResolvedRequestsNumber {
229+
if counters["true-positive"]["all"] != stat.TruePositiveTests.ReqStats.AllRequestsNumber ||
230+
counters["true-positive"]["blocked"] != stat.TruePositiveTests.ReqStats.BlockedRequestsNumber ||
231+
counters["true-positive"]["bypassed"] != stat.TruePositiveTests.ReqStats.BypassedRequestsNumber ||
232+
counters["true-positive"]["unresolved"] != stat.TruePositiveTests.ReqStats.UnresolvedRequestsNumber ||
233+
counters["true-positive"]["failed"] != stat.TruePositiveTests.ReqStats.FailedRequestsNumber ||
234+
counters["true-positive"]["resolved"] != stat.TruePositiveTests.ReqStats.ResolvedRequestsNumber {
235235
return false
236236
}
237237

@@ -251,12 +251,12 @@ func testPropertyCorrectStatValues(db *DB, ignoreUnresolved, nonBlockedAsPassed
251251
counters["true-negative"]["resolved"] = counters["true-negative"]["blocked"] +
252252
counters["true-negative"]["bypassed"]
253253

254-
if counters["true-negative"]["all"] != stat.TrueNegativeTests.AllRequestsNumber ||
255-
counters["true-negative"]["blocked"] != stat.TrueNegativeTests.BlockedRequestsNumber ||
256-
counters["true-negative"]["bypassed"] != stat.TrueNegativeTests.BypassedRequestsNumber ||
257-
counters["true-negative"]["unresolved"] != stat.TrueNegativeTests.UnresolvedRequestsNumber ||
258-
counters["true-negative"]["failed"] != stat.TrueNegativeTests.FailedRequestsNumber ||
259-
counters["true-negative"]["resolved"] != stat.TrueNegativeTests.ResolvedRequestsNumber {
254+
if counters["true-negative"]["all"] != stat.TrueNegativeTests.ReqStats.AllRequestsNumber ||
255+
counters["true-negative"]["blocked"] != stat.TrueNegativeTests.ReqStats.BlockedRequestsNumber ||
256+
counters["true-negative"]["bypassed"] != stat.TrueNegativeTests.ReqStats.BypassedRequestsNumber ||
257+
counters["true-negative"]["unresolved"] != stat.TrueNegativeTests.ReqStats.UnresolvedRequestsNumber ||
258+
counters["true-negative"]["failed"] != stat.TrueNegativeTests.ReqStats.FailedRequestsNumber ||
259+
counters["true-negative"]["resolved"] != stat.TrueNegativeTests.ReqStats.ResolvedRequestsNumber {
260260
return false
261261
}
262262

@@ -593,6 +593,8 @@ func TestStatisticsCalculation(t *testing.T) {
593593
}
594594
}
595595

596+
fmt.Println(tc)
597+
596598
if stat.Score.ApiSec.TruePositive != apiSecTruePosPercentage {
597599
t.Fatalf("ApiSec.TruePositive: want %#v, got %#v", apiSecTruePosPercentage, stat.Score.ApiSec.TruePositive)
598600
}

0 commit comments

Comments
 (0)