diff --git a/.github/scripts/report/e2e-report b/.github/scripts/report/e2e-report deleted file mode 100644 index 4616b566283..00000000000 Binary files a/.github/scripts/report/e2e-report and /dev/null differ diff --git a/.github/scripts/report/go.sum b/.github/scripts/report/go.sum index e83fb29040d..d9206ca3ae6 100644 --- a/.github/scripts/report/go.sum +++ b/.github/scripts/report/go.sum @@ -1,28 +1,28 @@ -github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= -github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/tdewolff/minify/v2 v2.9.21 h1:nO4s1PEMy7aRjlIlbr3Jgr+bJby8QYuifa2Vs2f9lh4= -github.com/tdewolff/minify/v2 v2.9.21/go.mod h1:PoDBts2L7sCwUT28vTAlozGeD6qxjrrihtin4bR/RMM= -github.com/tdewolff/parse/v2 v2.5.19 h1:Kjaj3KQOx/4elIxlBSglus4E2oMfdROphvbq2b+OBZ0= -github.com/tdewolff/parse/v2 v2.5.19/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= -github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= -github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/tdewolff/minify/v2 v2.9.21 h1:nO4s1PEMy7aRjlIlbr3Jgr+bJby8QYuifa2Vs2f9lh4= +github.com/tdewolff/minify/v2 v2.9.21/go.mod h1:PoDBts2L7sCwUT28vTAlozGeD6qxjrrihtin4bR/RMM= +github.com/tdewolff/parse/v2 v2.5.19 h1:Kjaj3KQOx/4elIxlBSglus4E2oMfdROphvbq2b+OBZ0= +github.com/tdewolff/parse/v2 v2.5.19/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho= +github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4= +github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/.github/scripts/report/main.go b/.github/scripts/report/main.go index 3d0983453b9..e120914e4d7 100644 --- a/.github/scripts/report/main.go +++ b/.github/scripts/report/main.go @@ -7,6 +7,7 @@ import ( "math" "os" "path/filepath" + "regexp" "sort" "strings" ) @@ -23,8 +24,26 @@ type Counters struct { CountTotal int } +type ExpectedActual struct { + ExtraElements ActualExpectedWithStatus // list of extra elements + TestInfo []string + Messages ActualExpectedWithStatus + FailOutput []string +} + +type ActualExpectedWithStatus struct { + ExpectedContent []CodeLineStatus + ActualContent []CodeLineStatus +} + +type CodeLineStatus struct { + Line string + Status bool // differed or not +} + type TestsData struct { TestLog TestLog + ExpectedActual ExpectedActual FailLog []string } @@ -42,6 +61,22 @@ type TestFail struct { Output string `json:"Output"` } +const ( + prefixActualPayload = "actualPayload" + prefixExpectedPayload = "expectedPayload" + prefixFail = "--- FAIL:" + prefixQueries = `"queries": [` + extraElementsListA = "extra elements in list A:" + extraElementsListB = "extra elements in list B:" + prefixTest = "Test:" + suffixExpectedQueries = "Expected Queries content: 'fixtures/{" + suffixActualQueries = "doesn't match the Actual Queries content: 'output/{" + prefixTypeInterface = "([]interface {})" + prefixTypeVulnerableFile = "(model.VulnerableFile) {" + expectedNumberOflines = "Expected file number of lines:" + actualNumberOfLines = "Actual file number of lines:" +) + func FindTest(tests []TestsData, testName string) (*TestsData, bool) { for i := range tests { if tests[i].TestLog.Test == testName { @@ -51,6 +86,256 @@ func FindTest(tests []TestsData, testName string) (*TestsData, bool) { return nil, false } +func cleanOutput(s string) string { + // remove (len=N) + lenPattern := regexp.MustCompile(`\(len=\d+\)\s*`) + s = lenPattern.ReplaceAllString(s, "") + // Remove type annotations like (string), (int), (*string), (model.IssueType)... + typePattern := regexp.MustCompile(`\([a-zA-Z*\[][^)]*\)\s*`) + s = typePattern.ReplaceAllString(s, "") + return s +} + +func extractPayloadDiffLines(failLog []string) ExpectedActual { + var testInfo []string + var messages ActualExpectedWithStatus + var failOutput []string + + const ( + stateNone = iota + stateMessagesExpected + stateMessagesActual + stateTestInfo + stateFailLog + ) + state := stateTestInfo + + for _, line := range failLog { + trimmed := strings.TrimSpace(line) + if strings.HasPrefix(trimmed, prefixActualPayload) { + state = stateMessagesActual + } else if strings.HasPrefix(trimmed, prefixExpectedPayload) { + state = stateMessagesExpected + } else if strings.HasPrefix(trimmed, prefixFail) { + state = stateFailLog + } else if strings.HasPrefix(trimmed, prefixQueries) { + state = stateNone + } + + switch state { + case stateMessagesActual: + if !strings.HasPrefix(trimmed, prefixActualPayload) { + messages.ActualContent = append(messages.ActualContent, CodeLineStatus{ + Line: line, + Status: false, + }) + } + case stateMessagesExpected: + if !strings.HasPrefix(trimmed, prefixExpectedPayload) { + messages.ExpectedContent = append(messages.ExpectedContent, CodeLineStatus{ + Line: line, + Status: false, + }) + } + case stateFailLog: + failOutput = append(failOutput, line) + state = stateNone + case stateTestInfo: + testInfo = append(testInfo, line) + } + } + return ExpectedActual{ + TestInfo: testInfo, + Messages: messages, + FailOutput: failOutput, + } +} + +func isExtraElementsContentLine(trimmed string) bool { + return !strings.HasPrefix(trimmed, prefixTypeInterface) && !strings.HasPrefix(trimmed, prefixTypeVulnerableFile) +} + +func extractExpectedActualLines(failLog []string) ExpectedActual { + var extraElements ActualExpectedWithStatus + var testInfo []string + var messages ActualExpectedWithStatus + var failOutput []string + + const ( + stateNone = iota + stateExtraA + stateExtraB + stateTestInfo + stateMessagesExpected + stateMessagesActual + stateFailLog + ) + state := stateNone + + for _, line := range failLog { + trimmed := strings.TrimSpace(line) + + switch trimmed { + case extraElementsListA: + state = stateExtraA + continue + case extraElementsListB: + state = stateExtraB + continue + } + + if strings.HasPrefix(trimmed, prefixTest) { + state = stateTestInfo + } else if strings.HasSuffix(trimmed, suffixExpectedQueries) { + state = stateMessagesExpected + } else if strings.HasSuffix(trimmed, suffixActualQueries) { + state = stateMessagesActual + } else if strings.HasPrefix(trimmed, prefixFail) { + state = stateFailLog + } + + if trimmed == "" && (state == stateExtraA || state == stateExtraB) { + state = stateNone + continue + } + + switch state { + case stateExtraA: + if isExtraElementsContentLine(trimmed) { + cleanedLine := cleanOutput(line) + extraElements.ExpectedContent = append(extraElements.ExpectedContent, CodeLineStatus{ + Line: cleanedLine, + Status: false, + }) + } + case stateExtraB: + if isExtraElementsContentLine(trimmed) { + cleanedLine := cleanOutput(line) + extraElements.ActualContent = append(extraElements.ActualContent, CodeLineStatus{ + Line: cleanedLine, + Status: false, + }) + } + case stateTestInfo: + testInfo = append(testInfo, line) + case stateMessagesActual: + if !strings.HasSuffix(trimmed, suffixActualQueries) { + messages.ActualContent = append(messages.ActualContent, CodeLineStatus{ + Line: line, + Status: false, + }) + } + case stateMessagesExpected: + if !strings.HasSuffix(trimmed, suffixExpectedQueries) { + messages.ExpectedContent = append(messages.ExpectedContent, CodeLineStatus{ + Line: line, + Status: false, + }) + } + case stateFailLog: + failOutput = append(failOutput, line) + state = stateNone + } + } + + return ExpectedActual{ + ExtraElements: extraElements, + TestInfo: testInfo, + Messages: messages, + FailOutput: failOutput, + } +} + +func isDifferentNumberOfLines(failLog []string) bool { + var hasExpectedFileNumberLines, hasActualFileNumberLines bool + for _, failLogEntry := range failLog { + trimmedEntry := strings.TrimSpace(failLogEntry) + if trimmedEntry == "" { + continue + } + if strings.Contains(trimmedEntry, expectedNumberOflines) { + hasExpectedFileNumberLines = true + } + if strings.Contains(failLogEntry, actualNumberOfLines) { + hasActualFileNumberLines = true + } + if hasExpectedFileNumberLines && hasActualFileNumberLines { + return true + } + } + return false +} + +func isExpectedVsActual(failLog []string) bool { + var hasExtraA, hasExtraB bool + for _, failLogEntry := range failLog { + trimmedEntry := strings.TrimSpace(failLogEntry) + if trimmedEntry == extraElementsListA { + hasExtraA = true + } + if trimmedEntry == extraElementsListB { + hasExtraB = true + } + if hasExtraA && hasExtraB { + return true + } + } + return false +} + +func compareMessageContent(expectedActual *ExpectedActual) { + expectedLen := len(expectedActual.Messages.ExpectedContent) + actualLen := len(expectedActual.Messages.ActualContent) + actualLenExtraElements := len(expectedActual.ExtraElements.ExpectedContent) + expectedLenExtraElements := len(expectedActual.ExtraElements.ActualContent) + + maxLen := expectedLen + if actualLen > maxLen { + maxLen = actualLen + } + maxLenExtraElements := expectedLenExtraElements + if actualLenExtraElements > maxLenExtraElements { + maxLenExtraElements = actualLenExtraElements + } + + for i := range maxLen { + // if one side has no line at this index, the line is marked as different + if i >= expectedLen || i >= actualLen { + if i < expectedLen { + expectedActual.Messages.ExpectedContent[i].Status = true + } + if i < actualLen { + expectedActual.Messages.ActualContent[i].Status = true + } + continue + } + expectedContentLine := strings.TrimSpace(expectedActual.Messages.ExpectedContent[i].Line) + actualContentLine := strings.TrimSpace(expectedActual.Messages.ActualContent[i].Line) + if expectedContentLine != actualContentLine { + expectedActual.Messages.ExpectedContent[i].Status = true + expectedActual.Messages.ActualContent[i].Status = true + } + } + + for j := range maxLenExtraElements { + if j >= expectedLenExtraElements || j >= actualLenExtraElements { + if j < expectedLenExtraElements { + expectedActual.ExtraElements.ExpectedContent[j].Status = true + } + if j < actualLenExtraElements { + expectedActual.ExtraElements.ActualContent[j].Status = true + } + continue + } + expectedContentLine := strings.TrimSpace(expectedActual.ExtraElements.ExpectedContent[j].Line) + actualContentLine := strings.TrimSpace(expectedActual.ExtraElements.ActualContent[j].Line) + if expectedContentLine != actualContentLine { + expectedActual.ExtraElements.ExpectedContent[j].Status = true + expectedActual.ExtraElements.ActualContent[j].Status = true + } + } +} + func main() { var testPath, testName, reportPath, reportName string @@ -88,7 +373,6 @@ func main() { fmt.Printf("Error when trying to decode: %v\n", err) fmt.Printf("Verify if the JSON File has UTF8 encoding") } - if log.Action == "pass" || log.Action == "fail" { if log.Test == "" { finalStatus = log @@ -98,7 +382,6 @@ func main() { hasFailures = true } test, exists := FindTest(testList, log.Test) - if exists { if log.Action == "fail" { test.TestLog = log @@ -117,7 +400,6 @@ func main() { fmt.Printf("Error when trying to open: %v\n", filepath.Join(filepath.ToSlash(testPath), testName)) os.Exit(1) } - decoder2 := json.NewDecoder(jsonTestsOutputClean) for decoder2.More() { var log TestLog @@ -132,13 +414,28 @@ func main() { } test, exists := FindTest(testList, log.Test) - if !exists || test.TestLog.Action != "fail" { continue } - test.FailLog = append(test.FailLog, log.Output) } + + for i := range testList { + test := &testList[i] + if test.TestLog.Action != "fail" { + continue + } + + if isExpectedVsActual(test.FailLog) { + expectedActual := extractExpectedActualLines(test.FailLog) + compareMessageContent(&expectedActual) + test.ExpectedActual = expectedActual + } else if isDifferentNumberOfLines(test.FailLog) { + expectedActual := extractPayloadDiffLines(test.FailLog) + compareMessageContent(&expectedActual) + test.ExpectedActual = expectedActual + } + } } fmt.Printf("Parsing tests data... Done!\n") diff --git a/.github/scripts/report/template/html/e2e-report.css b/.github/scripts/report/template/html/e2e-report.css index d6cd1dd1fe7..d20778584b8 100644 --- a/.github/scripts/report/template/html/e2e-report.css +++ b/.github/scripts/report/template/html/e2e-report.css @@ -1,142 +1,199 @@ -* { - margin: 0; - padding: 0; - outline: 0; - box-sizing: border-box; -} - -body { - font-family: sans-serif; -} - -.container { - display: flex; - align-items: center; - flex-direction: column; - margin: 5px; - border: 1px solid #bebebe; -} - -.report-header-footer { - display: grid; - align-items: center; - grid-template-columns: 1fr 1fr 1fr; - border-bottom: 1px solid #bebebe; - width: 100%; - padding: 15px 21px; - background-color: #503e9e; - height: 50px; - font-weight: bold; - font-size: 14px; - color: #fff; - cursor: default; - user-select: none; -} - -.report-header-footer > a { - color: inherit; - text-decoration: inherit; - text-align: right; -} - -.report-header-footer > .title { - font-size: 18px; -} - -.report-header-footer > .title > span { - color: #000; -} - -.report-header-footer > .timestamp { - font-weight: normal; - font-style: italic; - opacity: 0.5; - text-align: center; -} - -.separator { - border-top: 1px solid #979797; - opacity: 0.5; - width: 95%; - margin: 22px 0; -} - -.hide { - display: none; -} - -summary { - cursor: pointer; - user-select: none; - font-size: 18px; - font-weight: bold; -} - -.testBlock { - width: 95vw; - display: flex; - flex-direction: column; - gap: 10px; -} - -.test-status { - font-weight: bold; -} - -.test-status.fail { - color: #d60000; -} - -.test-status.pass { - color: #248e01; -} - -.test-status.all { - color: blue; -} - -.counters { - display: flex; - flex-direction: row; - margin: 22px 0; - gap: 15px; -} - -.counter-btn { - display: flex; - flex-direction: column; - align-items: center; - border: 1px solid rgba(0,0,0,0.1); - padding: 10px; - border-radius: 5px; - cursor: pointer; - min-width: 100px; - gap: 5px; - -webkit-box-shadow: 0 0 10px 0px rgb(0 0 0 / 10%); - box-shadow: 0 0 10px 0px rgb(0 0 0 / 10%); -} - -.counter-btn.selected { - border-color: #c000ff; -} - -.counter-count { - font-style: italic; - color: gray; -} - -.results { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 10px; - margin-top: 22px; -} - -.code-box { - display: flex; - flex-direction: column; - background-color: #503e9e10; - gap: 5px; -} +* { + margin: 0; + padding: 0; + outline: 0; + box-sizing: border-box; +} + +body { + font-family: sans-serif; +} + +.container { + display: flex; + align-items: center; + flex-direction: column; + margin: 5px; + border: 1px solid #bebebe; +} + +.report-header-footer { + display: grid; + align-items: center; + grid-template-columns: 1fr 1fr 1fr; + border-bottom: 1px solid #bebebe; + width: 100%; + padding: 15px 21px; + background-color: #503e9e; + height: 50px; + font-weight: bold; + font-size: 14px; + color: #fff; + cursor: default; + user-select: none; +} + +.report-header-footer > a { + color: inherit; + text-decoration: inherit; + text-align: right; +} + +.report-header-footer > .title { + font-size: 18px; +} + +.report-header-footer > .title > span { + color: #000; +} + +.report-header-footer > .timestamp { + font-weight: normal; + font-style: italic; + opacity: 0.5; + text-align: center; +} + +.separator { + border-top: 1px solid #979797; + opacity: 0.5; + width: 95%; + margin: 22px 0; +} + +.hide { + display: none; +} + +summary { + cursor: pointer; + user-select: none; + font-size: 18px; + font-weight: bold; +} + +summary.sub-summary { + font-size: 15px; + font-weight: 700; + margin: 5px 0; +} + +summary.expected-actual { + font-size: 14px; + font-weight: 580; +} + +.testBlock { + width: 95vw; + display: flex; + flex-direction: column; + gap: 10px; +} + +.test-status { + font-weight: bold; +} + +.test-status.fail { + color: #d60000; +} + +.test-status.pass { + color: #248e01; +} + +.test-status.all { + color: blue; +} + +.counters { + display: flex; + flex-direction: row; + margin: 22px 0; + gap: 15px; +} + +.counter-btn { + display: flex; + flex-direction: column; + align-items: center; + border: 1px solid rgba(0,0,0,0.1); + padding: 10px; + border-radius: 5px; + cursor: pointer; + min-width: 100px; + gap: 5px; + -webkit-box-shadow: 0 0 10px 0px rgb(0 0 0 / 10%); + box-shadow: 0 0 10px 0px rgb(0 0 0 / 10%); +} + +.counter-btn.selected { + border-color: #c000ff; +} + +.counter-count { + font-style: italic; + color: gray; +} + +.code.diff { + background-color: rgba(255, 0, 0, 0.2); + border-radius: 4px; +} + +.results { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + margin-top: 22px; +} + +.code-box { + display: flex; + flex-direction: column; + background-color: #503e9e10; + gap: 5px; +} + +.code-box.side-by-side { + display: flex; + flex-direction: row; + align-items: flex-start; + gap: 10px; + background-color: transparent; + margin-bottom: 10px; + margin-top: 10px; +} + +.code-box.first { + width: 50%; + float: none; + height: auto; + align-self: auto; +} + +.code-box.second { + width: 50%; + float: none; + height: auto; + align-self: auto; +} + +.code-box.test-info { + background-color: white; + margin-bottom: 5px; +} + +.code.pre { + white-space: pre-wrap; +} + +.container_expected_actual { + width: auto; + background-color: #fff; + margin: auto; + display: flex; + float: none; +} diff --git a/.github/scripts/report/template/html/e2e-report.js b/.github/scripts/report/template/html/e2e-report.js index 2aab56abb84..5cb61538008 100644 --- a/.github/scripts/report/template/html/e2e-report.js +++ b/.github/scripts/report/template/html/e2e-report.js @@ -1,11 +1,11 @@ -function filter(filt) { - const tests = document.querySelectorAll("[data-type='testInfo']"); - tests.forEach((query) => filt !== "all" && filt !== query.getAttribute("data-name") ? query.classList.add("hide") : query.classList.remove("hide")); - - const severitiesCaptions = document.querySelectorAll(".counters > .counter-btn"); - const selectedElement = document.getElementsByClassName(`counter test-status ${filt}`)[0] - - severitiesCaptions.forEach((counter) => { - filt && selectedElement === counter.getElementsByClassName('counter test-status')[0] ? counter.classList.add("selected") : counter.classList.remove("selected") - }); -} +function filter(filt) { + const tests = document.querySelectorAll("[data-type='testInfo']"); + tests.forEach((query) => filt !== "all" && filt !== query.getAttribute("data-name") ? query.classList.add("hide") : query.classList.remove("hide")); + + const severitiesCaptions = document.querySelectorAll(".counters > .counter-btn"); + const selectedElement = document.getElementsByClassName(`counter test-status ${filt}`)[0] + + severitiesCaptions.forEach((counter) => { + filt && selectedElement === counter.getElementsByClassName('counter test-status')[0] ? counter.classList.add("selected") : counter.classList.remove("selected") + }); +} \ No newline at end of file diff --git a/.github/scripts/report/template/html/e2e-report.tmpl b/.github/scripts/report/template/html/e2e-report.tmpl index 9c4407c905e..ff71b33421b 100644 --- a/.github/scripts/report/template/html/e2e-report.tmpl +++ b/.github/scripts/report/template/html/e2e-report.tmpl @@ -1,62 +1,163 @@ - - - - - - KICS Scan Result - {{ includeCSS "report.css" }} - {{ includeJS "report.js" }} - - -
- - -
-

E2E Tests Result: {{.Result.Action}}

-

Time Elapsed: {{.Result.Elapsed}}s

-

Kics Docker Image: {{ getCurrentDockerImage }}

-
-
-

Filters

-
- -
- PASSED - {{.Counters.CountPass}} -
- -
- FAILED - {{.Counters.CountFail}} -
- -
- ALL - {{.Counters.CountTotal}} -
- -
- {{- range $val := .TestsData}} -
-
-
- Test: {{$val.TestLog.Test}} - Elapsed: {{$val.TestLog.Elapsed}}s - Status: {{$val.TestLog.Action}} - {{ if eq $val.TestLog.Action "fail" }} -
- View Details -
- {{ range $failLine := $val.FailLog }} - {{ $failLine }} - {{ end }} -
-
- {{ end }} -
-
- {{- end -}} - -
- - + + + + + + KICS Scan Result + {{ includeCSS "report.css" }} + {{ includeJS "report.js" }} + + +
+ + +
+

E2E Tests Result: {{.Result.Action}}

+

Time Elapsed: {{.Result.Elapsed}}s

+

Kics Docker Image: {{ getCurrentDockerImage }}

+
+
+

Filters

+
+ +
+ PASSED + {{.Counters.CountPass}} +
+ +
+ FAILED + {{.Counters.CountFail}} +
+ +
+ ALL + {{.Counters.CountTotal}} +
+ +
+ {{- range $val := .TestsData}} +
+
+
+ Test: {{$val.TestLog.Test}} + Elapsed: {{$val.TestLog.Elapsed}}s + Status: {{$val.TestLog.Action}} + {{ if eq $val.TestLog.Action "fail" }} +
+ View Details + {{ if and $val.ExpectedActual.Messages.ExpectedContent $val.ExpectedActual.Messages.ActualContent (not (and $val.ExpectedActual.ExtraElements.ExpectedContent $val.ExpectedActual.ExtraElements.ActualContent)) }} +
+ {{ range $line := $val.ExpectedActual.TestInfo }} + {{ $line }} + {{ end }} +
+
+ {{ range $line := $val.ExpectedActual.FailOutput }} + {{ $line }} + {{ end }} +
+
+ Show additional information +
+
+ Expected in fixtures: + {{ range $ec := $val.ExpectedActual.Messages.ExpectedContent }} + {{ if $ec.Status }} + {{ $ec.Line }} + {{ else }} + {{ $ec.Line }} + {{ end }} + {{ end }} +
+
+ Actual output: + {{ range $ac := $val.ExpectedActual.Messages.ActualContent }} + {{ if $ac.Status }} + {{ $ac.Line }} + {{ else }} + {{ $ac.Line }} + {{ end }} + {{ end }} +
+
+
+ {{ else if and $val.ExpectedActual.Messages.ExpectedContent $val.ExpectedActual.Messages.ActualContent }} +
+ {{ range $line := $val.ExpectedActual.TestInfo }} + {{ $line }} + {{ end }} +
+
+ {{ range $line := $val.ExpectedActual.FailOutput }} + {{ $line }} + {{ end }} +
+ {{ if and $val.ExpectedActual.ExtraElements.ExpectedContent $val.ExpectedActual.ExtraElements.ActualContent }} +
+ View extra elements +
+
+ Expected in fixtures: + {{ range $ec := $val.ExpectedActual.ExtraElements.ExpectedContent }} + {{ if $ec.Status }} + {{ $ec.Line }} + {{ else }} + {{ $ec.Line }} + {{ end }} + {{ end }} +
+
+ Actual output + {{ range $ac := $val.ExpectedActual.ExtraElements.ActualContent }} + {{ if $ac.Status }} + {{ $ac.Line }} + {{ else }} + {{ $ac.Line }} + {{ end }} + {{ end }} +
+
+
+ {{ end }} +
+ Show full Actual and Expected +
+
+ Expected in fixtures: + {{ range $ec := $val.ExpectedActual.Messages.ExpectedContent }} + {{ if $ec.Status }} + {{ $ec.Line }} + {{ else }} + {{ $ec.Line }} + {{ end }} + {{ end }} +
+
+ Actual output: + {{ range $ac := $val.ExpectedActual.Messages.ActualContent }} + {{ if $ac.Status }} + {{ $ac.Line }} + {{ else }} + {{ $ac.Line }} + {{ end }} + {{ end }} +
+
+
+ {{ else }} +
+ {{ range $failLine := $val.FailLog }} + {{ $failLine }} + {{ end }} +
+ {{ end }} +
+ {{ end }} +
+
+ {{- end -}} + +
+ + diff --git a/e2e/utils/json.go b/e2e/utils/json.go index 527e7e5fa89..43f9d61b92e 100644 --- a/e2e/utils/json.go +++ b/e2e/utils/json.go @@ -2,7 +2,9 @@ package utils import ( "encoding/json" + "fmt" "io" + "os" "path/filepath" "reflect" @@ -132,8 +134,10 @@ func FileCheck(t *testing.T, actualPayloadName, expectPayloadName, location stri require.NoError(t, err, "[output/%s] Reading a fixture should not yield an error", actualPayloadName) require.Equal(t, len(expectPayload), len(actualPayload), - "[fixtures/%s] Expected file number of lines: %d\n[output/%s] Actual file number of lines: %d\n", - expectPayloadName, len(expectPayload), actualPayloadName, len(actualPayload)) + "[fixtures/%s] Expected file number of lines: %d\n[output/%s] Actual file number of lines: %d\n"+ + "expectedPayload:\n%v\nactualPayload:\n%v\n", + expectPayloadName, len(expectPayload), actualPayloadName, len(actualPayload), + formatPayload(expectPayload), formatPayload(actualPayload)) setFields(t, expectPayload, actualPayload, expectPayloadName, actualPayloadName, location) } @@ -151,6 +155,45 @@ func CheckLine(t *testing.T, expec, want string, line int) { } } +func formatPayload(payload []string) string { + var sb strings.Builder + for _, line := range payload { + sb.WriteString(line) + sb.WriteString("\n") + } + return sb.String() +} + +func formatVulnFiles(files []map[string]interface{}) string { + var sb strings.Builder + for _, f := range files { + b, err := json.MarshalIndent(f, "", " ") + if err != nil { + sb.WriteString(fmt.Sprintf("error formatting file: %v\n", err)) + continue + } + sb.WriteString(string(b)) + sb.WriteString("\n") + } + return sb.String() +} + +func toComparableFiles(queries []model.QueryResult) []map[string]interface{} { + result := []map[string]interface{}{} + for i := range queries { + for j := range queries[i].Files { + b, _ := json.Marshal(queries[i].Files[j]) + m := map[string]interface{}{} + if err := json.Unmarshal(b, &m); err != nil { + continue + } + m["queryName"] = queries[i].QueryName + result = append(result, m) + } + } + return result +} + //nolint:funlen func setFields(t *testing.T, expect, actual []string, expectFileName, actualFileName, location string) { switch location { @@ -240,21 +283,16 @@ func setFields(t *testing.T, expect, actual []string, expectFileName, actualFile expectFileName, actualFileName) // compare the results - expectToCompare := []model.VulnerableFile{} - for i := range expectI.Queries { - expectToCompare = append(expectToCompare, expectI.Queries[i].Files...) - } - actualToCompare := []model.VulnerableFile{} - for i := range actualI.Queries { - actualToCompare = append(actualToCompare, actualI.Queries[i].Files...) - } + expectToCompare := toComparableFiles(expectI.Queries) + actualToCompare := toComparableFiles(actualI.Queries) + require.ElementsMatch(t, expectToCompare, actualToCompare, - "Expected Queries content: 'fixtures/%s' doesn't match the Actual Queries content: 'output/%s'.", - expectToCompare, actualToCompare) + "Expected Queries content: 'fixtures/%s' doesn't match the Actual Queries content: 'output/%s", + formatVulnFiles(expectToCompare), formatVulnFiles(actualToCompare)) // compare severity counters compare := reflect.DeepEqual(expectI.SeverityCounters, actualI.SeverityCounters) - require.True(t, compare, "Expected Severity Counters content: 'fixtures/%s' doesn't match the Actual Severity Counters content: 'output/%s'.", //nolint:lll + require.True(t, compare, "Expected Severity Counters content: 'fixtures/%s' doesn't match the Actual Severity Counters content: 'output/%s", //nolint:lll expectI.SeverityCounters, actualI.SeverityCounters) } } diff --git a/internal/console/remediate.go b/internal/console/remediate.go index 869e97bec8e..d3dce6fa3dd 100644 --- a/internal/console/remediate.go +++ b/internal/console/remediate.go @@ -107,7 +107,7 @@ func remediate() error { for filePath := range remediationSets { fix := remediationSets[filePath].(remediation.Set) - err = summary.RemediateFile(filePath, fix, openAPIResolveReferences, maxResolverDepth) + err = summary.RemediateFile(filePath, "", fix, openAPIResolveReferences, maxResolverDepth) if err != nil { return err } diff --git a/pkg/remediation/remediation.go b/pkg/remediation/remediation.go index 242eaf92aef..d4c4a3cb57a 100644 --- a/pkg/remediation/remediation.go +++ b/pkg/remediation/remediation.go @@ -53,10 +53,19 @@ type Set struct { } // RemediateFile remediationSets the replacements first and secondly, the additions sorted down -func (s *Summary) RemediateFile(filePath string, remediationSet Set, openAPIResolveReferences bool, maxResolverDepth int) error { +func (s *Summary) RemediateFile( + filePath, originalFileName string, + remediationSet Set, + openAPIResolveReferences bool, + maxResolverDepth int, +) error { filepath.Clean(filePath) content, err := os.ReadFile(filePath) + if originalFileName == "" { + originalFileName = filePath + } + if err != nil { log.Error().Msgf("failed to read file: %s", err) return err @@ -70,7 +79,7 @@ func (s *Summary) RemediateFile(filePath string, remediationSet Set, openAPIReso r := remediationSet.Replacement[i] remediatedLines := replacement(&r, lines) if len(remediatedLines) > 0 && willRemediate(remediatedLines, filePath, &r, openAPIResolveReferences, maxResolverDepth) { - lines = s.writeRemediation(remediatedLines, lines, filePath, r.SimilarityID) + lines = s.writeRemediation(remediatedLines, lines, filePath, originalFileName, r.SimilarityID) } } } @@ -86,7 +95,7 @@ func (s *Summary) RemediateFile(filePath string, remediationSet Set, openAPIReso a := remediationSet.Addition[i] remediatedLines := addition(&a, &lines) if len(remediatedLines) > 0 && willRemediate(remediatedLines, filePath, &a, openAPIResolveReferences, maxResolverDepth) { - lines = s.writeRemediation(remediatedLines, lines, filePath, a.SimilarityID) + lines = s.writeRemediation(remediatedLines, lines, filePath, originalFileName, a.SimilarityID) } } } @@ -159,7 +168,7 @@ const ( FilePermMode = 0777 ) -func (s *Summary) writeRemediation(remediatedLines, lines []string, filePath, similarityID string) []string { +func (s *Summary) writeRemediation(remediatedLines, lines []string, filePath, originalFileName, similarityID string) []string { remediated := []byte(strings.Join(remediatedLines, "\n")) mode := os.FileMode(FilePermMode) @@ -171,6 +180,7 @@ func (s *Summary) writeRemediation(remediatedLines, lines []string, filePath, si log.Info().Msgf("file '%s' was remediated with '%s'", filePath, similarityID) s.ActualRemediationDoneNumber++ + s.RemediatedFiles = append(s.RemediatedFiles, originalFileName) return remediatedLines } diff --git a/pkg/remediation/remediation_test.go b/pkg/remediation/remediation_test.go index 4ad860955b5..a1852cb5230 100644 --- a/pkg/remediation/remediation_test.go +++ b/pkg/remediation/remediation_test.go @@ -111,7 +111,7 @@ func Test_RemediateFile(t *testing.T) { tmpFileName := filepath.Join(os.TempDir(), "temporary-remediation"+utils.NextRandom()+filepath.Ext(filePathCopyFrom)) tmpFile := CreateTempFile(filePathCopyFrom, tmpFileName) - s.RemediateFile(tmpFile, tt.args.remediate, false, 15) + s.RemediateFile(tmpFile, filePathCopyFrom, tt.args.remediate, false, 15) os.Remove(tmpFile) require.Equal(t, s.ActualRemediationDoneNumber, tt.actualRemediationDoneNumber) diff --git a/pkg/remediation/utils.go b/pkg/remediation/utils.go index 39c43e8428d..9f5ce63f099 100644 --- a/pkg/remediation/utils.go +++ b/pkg/remediation/utils.go @@ -15,6 +15,7 @@ import ( type Summary struct { SelectedRemediationNumber int ActualRemediationDoneNumber int + RemediatedFiles []string } // GetRemediationSets collects all the replacements and additions per file diff --git a/pkg/report/template/html/report.css b/pkg/report/template/html/report.css index c81416fbe1b..f8ee13f8e6d 100644 --- a/pkg/report/template/html/report.css +++ b/pkg/report/template/html/report.css @@ -1,325 +1,325 @@ -* { - margin: 0; - padding: 0; - outline: 0; - box-sizing: border-box; -} - -body { - font-family: sans-serif; -} - -.container { - display: flex; - align-items: center; - flex-direction: column; - margin: 5px; - border: 1px solid #bebebe; -} - -.run-info { - display: flex; - flex-wrap: wrap; - border: 1px solid #bebebe; - margin-top: 10px; - width: 50vw; -} - -.run-info > span { - flex-basis: 50%; - text-align: center; -} - -.counters { - display: flex; - flex-direction: row; - margin: 22px 0; -} - -.report-header-footer { - display: flex; - flex-direction: row; - justify-content: space-between; - border-bottom: 1px solid #bebebe; - width: 100%; - padding: 15px 21px; - background-color: #503e9e; - height: 50px; - font-weight: bold; - font-size: 14px; - color: #fff; - cursor: default; - user-select: none; -} - -.report-header-footer > a { - color: inherit; - text-decoration: inherit; -} - -.report-header-footer > .title { - font-size: 18px; -} - -.report-header-footer > .title > span { - color: #000; -} - -.report-header-footer > .timestamp { - font-weight: normal; - font-style: italic; - opacity: 0.5; -} - -.severity { - display: flex; - flex-direction: column; - cursor: pointer; - position: relative; - margin: 0 22px; - align-items: center; -} - -.severity > .caption.selected { - text-decoration: underline overline; -} - -.badge { - color: #fff; - border: 2px solid #e8e8e8; - border-radius: 50%; - cursor: default; - user-select: none; - padding: 3px; - font-size: 10px; - display: flex; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - position: absolute; - left: 60%; - top: 50%; -} - -.kics-black { - color: #000; -} - -.kics-red { - color: #EE3F3F; -} - -.kics-red > svg { - fill: #EE3F3F; -} - -.kics-red ~ .badge{ - background-color: #503e9e; -} - -.kics-orange { - color: #fc6e3a; -} - -.kics-orange > svg { - fill: #fc6e3a; -} - -.kics-orange ~ .badge { - background-color: #503e9e; -} - -.kics-purple { - color: #503e9e; -} - -.kics-purple > svg { - fill: #503e9e; -} - -.kics-purple ~ .badge { - background-color: #fc6e3a; -} - -.severity > .icon > svg { - width: 80px; - height: auto; -} - -.severity > .caption { - font-size: 16px; - font-weight: bolder; - user-select: none; - cursor: default; -} - -.separator { - border-top: 1px solid #979797; - opacity: 0.5; - width: 95%; - margin: 22px 0; -} - -.query { - width: 95vw; -} - -.query-title { - display: flex; - align-items: flex-start; - flex-direction: column; - width: 100%; -} - -.query-title > h2 { - display: flex; -} - -.query-title > h2 > div { - width: 20px; - margin-right: 12px; - margin-left: -30px; -} - -.query > * { - margin-left: 30px; -} - -.query-info { - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.query-details { - margin: 12px 0; - display: flex; - flex-direction: column; - text-align: justify; -} - -.query-details > span.query-description-title { - font-size: 18px; - margin-top: 5px; -} - -.query-details > span.cis-description-text { - margin-top: 5px; -} - -.query-details > span:last-child { - font-size: 14px; -} - -.vulnerable-info { - border: 1px #969696 solid; - border-radius: 2px; - display: flex; - flex-direction: column; - margin: 6px 9px; -} - -.vulnerable-info-header { - display: flex; - flex-direction: row; - justify-content: space-between; - margin: 6px 9px; -} - -.vulnerable-info-details { - display: flex; - flex-direction: column; - margin: 6px 9px; -} - -.vulnerable-info-details > span > strong { - width: 5vw; -} - -.code-box { - display: flex; - flex-direction: column; - background-color: #503e9e10; -} - -.code-line { - display: flex; - flex-direction: row; - align-items: center; - height: 20px; -} - -.code-box > .error { - background-color: #fc6e3a50; -} - -.code-line > .code-line-counter { - font-size: 10px; - margin-left: 9px; - margin-right: 10vw; -} - -.code-line > .code { - font-family: monospace; - font-size: 16px; -} - -.kics-message { - margin: 24px 30vw; - text-align: center; -} - -.love { - color: #503e9d; - font-style: italic; -} - -.social-networks { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - margin-bottom: 24px; -} - -.social-networks > a { - margin: 0 15px; -} - -.social-networks > a > div > svg { - width: 20px; - height: 20px; -} - -.footer-text { - font-style: italic; - opacity: 0.5; - font-weight: normal; - width: 100%; - display: flex; - align-self: center; - justify-content: center; -} - -a.checkmarx, -a.checkmarx:visited, -a.checkmarx:hover, -a.checkmarx:active { - cursor: pointer; - font-weight: bold; - text-decoration: underline; - color: #fff; - opacity: 0.8; -} - -.hide { - display: none; -} - -summary { - cursor: pointer; - user-select: none; - font-size: 18px; - font-weight: bold; -} +* { + margin: 0; + padding: 0; + outline: 0; + box-sizing: border-box; +} + +body { + font-family: sans-serif; +} + +.container { + display: flex; + align-items: center; + flex-direction: column; + margin: 5px; + border: 1px solid #bebebe; +} + +.run-info { + display: flex; + flex-wrap: wrap; + border: 1px solid #bebebe; + margin-top: 10px; + width: 50vw; +} + +.run-info > span { + flex-basis: 50%; + text-align: center; +} + +.counters { + display: flex; + flex-direction: row; + margin: 22px 0; +} + +.report-header-footer { + display: flex; + flex-direction: row; + justify-content: space-between; + border-bottom: 1px solid #bebebe; + width: 100%; + padding: 15px 21px; + background-color: #503e9e; + height: 50px; + font-weight: bold; + font-size: 14px; + color: #fff; + cursor: default; + user-select: none; +} + +.report-header-footer > a { + color: inherit; + text-decoration: inherit; +} + +.report-header-footer > .title { + font-size: 18px; +} + +.report-header-footer > .title > span { + color: #000; +} + +.report-header-footer > .timestamp { + font-weight: normal; + font-style: italic; + opacity: 0.5; +} + +.severity { + display: flex; + flex-direction: column; + cursor: pointer; + position: relative; + margin: 0 22px; + align-items: center; +} + +.severity > .caption.selected { + text-decoration: underline overline; +} + +.badge { + color: #fff; + border: 2px solid #e8e8e8; + border-radius: 50%; + cursor: default; + user-select: none; + padding: 3px; + font-size: 10px; + display: flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + position: absolute; + left: 60%; + top: 50%; +} + +.kics-black { + color: #000; +} + +.kics-red { + color: #EE3F3F; +} + +.kics-red > svg { + fill: #EE3F3F; +} + +.kics-red ~ .badge{ + background-color: #503e9e; +} + +.kics-orange { + color: #fc6e3a; +} + +.kics-orange > svg { + fill: #fc6e3a; +} + +.kics-orange ~ .badge { + background-color: #503e9e; +} + +.kics-purple { + color: #503e9e; +} + +.kics-purple > svg { + fill: #503e9e; +} + +.kics-purple ~ .badge { + background-color: #fc6e3a; +} + +.severity > .icon > svg { + width: 80px; + height: auto; +} + +.severity > .caption { + font-size: 16px; + font-weight: bolder; + user-select: none; + cursor: default; +} + +.separator { + border-top: 1px solid #979797; + opacity: 0.5; + width: 95%; + margin: 22px 0; +} + +.query { + width: 95vw; +} + +.query-title { + display: flex; + align-items: flex-start; + flex-direction: column; + width: 100%; +} + +.query-title > h2 { + display: flex; +} + +.query-title > h2 > div { + width: 20px; + margin-right: 12px; + margin-left: -30px; +} + +.query > * { + margin-left: 30px; +} + +.query-info { + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.query-details { + margin: 12px 0; + display: flex; + flex-direction: column; + text-align: justify; +} + +.query-details > span.query-description-title { + font-size: 18px; + margin-top: 5px; +} + +.query-details > span.cis-description-text { + margin-top: 5px; +} + +.query-details > span:last-child { + font-size: 14px; +} + +.vulnerable-info { + border: 1px #969696 solid; + border-radius: 2px; + display: flex; + flex-direction: column; + margin: 6px 9px; +} + +.vulnerable-info-header { + display: flex; + flex-direction: row; + justify-content: space-between; + margin: 6px 9px; +} + +.vulnerable-info-details { + display: flex; + flex-direction: column; + margin: 6px 9px; +} + +.vulnerable-info-details > span > strong { + width: 5vw; +} + +.code-box { + display: flex; + flex-direction: column; + background-color: #503e9e10; +} + +.code-line { + display: flex; + flex-direction: row; + align-items: center; + height: 20px; +} + +.code-box > .error { + background-color: #fc6e3a50; +} + +.code-line > .code-line-counter { + font-size: 10px; + margin-left: 9px; + margin-right: 10vw; +} + +.code-line > .code { + font-family: monospace; + font-size: 16px; +} + +.kics-message { + margin: 24px 30vw; + text-align: center; +} + +.love { + color: #503e9d; + font-style: italic; +} + +.social-networks { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + margin-bottom: 24px; +} + +.social-networks > a { + margin: 0 15px; +} + +.social-networks > a > div > svg { + width: 20px; + height: 20px; +} + +.footer-text { + font-style: italic; + opacity: 0.5; + font-weight: normal; + width: 100%; + display: flex; + align-self: center; + justify-content: center; +} + +a.checkmarx, +a.checkmarx:visited, +a.checkmarx:hover, +a.checkmarx:active { + cursor: pointer; + font-weight: bold; + text-decoration: underline; + color: #fff; + opacity: 0.8; +} + +.hide { + display: none; +} + +summary { + cursor: pointer; + user-select: none; + font-size: 18px; + font-weight: bold; +} diff --git a/pkg/report/template/html/report.js b/pkg/report/template/html/report.js index 18cf173ef9a..e20085d0af0 100644 --- a/pkg/report/template/html/report.js +++ b/pkg/report/template/html/report.js @@ -1,7 +1,7 @@ -function filter(filt) { - const queries = document.querySelectorAll("[data-type='severity']"); - queries.forEach((query) => filt !== "TOTAL" && filt !== query.getAttribute("data-name") ? query.classList.add("hide") : query.classList.remove("hide")); - - const severitiesCaptions = document.querySelectorAll(".severity > .caption"); - severitiesCaptions.forEach((caption) => filt && filt === caption.innerText ? caption.classList.add("selected") : caption.classList.remove("selected")); -} +function filter(filt) { + const queries = document.querySelectorAll("[data-type='severity']"); + queries.forEach((query) => filt !== "TOTAL" && filt !== query.getAttribute("data-name") ? query.classList.add("hide") : query.classList.remove("hide")); + + const severitiesCaptions = document.querySelectorAll(".severity > .caption"); + severitiesCaptions.forEach((caption) => filt && filt === caption.innerText ? caption.classList.add("selected") : caption.classList.remove("selected")); +} diff --git a/pkg/report/template/html/report.tmpl b/pkg/report/template/html/report.tmpl index feccf93f839..d3f3d45609d 100644 --- a/pkg/report/template/html/report.tmpl +++ b/pkg/report/template/html/report.tmpl @@ -1,142 +1,142 @@ - - - - - - KICS Scan Result - {{ includeCSS "report.css" }} - {{ includeJS "report.js" }} - - - Checkmarx logo -
- -
- KICS {{ getVersion }} - Scanned paths: {{ getPaths .ScannedPaths }} - Platforms: {{ getPlatforms .Queries }} - {{- with .Times -}} - Start time: {{ .Start.Format "15:04:05, Jan 02 2006" }} - End time: {{ .End.Format "15:04:05, Jan 02 2006" }} - {{- end}} -
-

Vulnerabilities:

-
- {{- with .SeveritySummary -}} -
-
{{ includeSVG "vulnerability_fill.svg" }}
- {{ index .SeverityCounters (severity "critical") }} - CRITICAL -
-
-
{{ includeSVG "vulnerability_fill.svg" }}
- {{ index .SeverityCounters (severity "high") }} - HIGH -
-
-
{{ includeSVG "vulnerability_out.svg" }}
- {{ index .SeverityCounters (severity "medium") }} - MEDIUM -
-
-
{{ includeSVG "vulnerability_out.svg" }}
- {{ index .SeverityCounters (severity "low") }} - LOW -
-
-
{{ includeSVG "info.svg" }}
- {{ index .SeverityCounters (severity "info") }} - INFO -
- {{- end}} -
-
{{ includeSVG "info.svg" }}
- {{ .TotalCounter }} - TOTAL -
-
- {{- range .Queries}} -
-
-
-
-
-

- {{- if eq .Severity "CRITICAL" -}} -
{{ includeSVG "vulnerability_fill.svg" }}
- {{- end -}} - {{- if eq .Severity "HIGH" -}} -
{{ includeSVG "vulnerability_fill.svg" }}
- {{- end -}} - {{- if eq .Severity "MEDIUM" -}} -
{{ includeSVG "vulnerability_out.svg" }}
- {{- end -}} - {{- if eq .Severity "LOW" -}} -
{{ includeSVG "vulnerability_out.svg" }}
- {{- end -}} - {{- if eq .Severity "INFO" -}} -
{{ includeSVG "info.svg" }}
- {{- end -}} - {{- .QueryName -}} -

- Platform: {{ .Platform }} - {{ if .CWE }}CWE: {{ .CWE }}{{ end }} - {{ if .RiskScore }}Risk Score: {{ .RiskScore }}{{ end }} - Category: {{ .Category }} -
-
- {{- if not .CISDescriptionID -}} - {{ .Description }} - {{- end -}} - {{- if .CISDescriptionID -}} - {{ .CISDescriptionIDFormatted }} - {{ .CISDescriptionTitle }} - {{ .CISDescriptionTextFormatted }} - {{- end -}} - {{ .QueryURI }} -
-
-
- Results ({{ len .Files }}) - {{- range .Files}} - {{- $vulLine := .Line -}} -
-
- File: {{ .FileName }} - Line {{ $vulLine }} -
-
- Expected: {{ .KeyExpectedValue }} - Found: {{ .KeyActualValue }} -
-
- {{- range .VulnLines -}} -
- {{ .Position }}{{ trimSpaces .Line }} -
- {{- end}} -
-
- {{- end -}} -
-
-
- {{- end -}} -
-
- KICS is open and will always stay such. Both the scanning engine and the security queries are clear and open for the software development community. -
-
- Spread the love: -
-
- -
{{ includeSVG "github.svg" }}
-
-
- -
- - + + + + + + KICS Scan Result + {{ includeCSS "report.css" }} + {{ includeJS "report.js" }} + + + Checkmarx logo +
+ +
+ KICS {{ getVersion }} + Scanned paths: {{ getPaths .ScannedPaths }} + Platforms: {{ getPlatforms .Queries }} + {{- with .Times -}} + Start time: {{ .Start.Format "15:04:05, Jan 02 2006" }} + End time: {{ .End.Format "15:04:05, Jan 02 2006" }} + {{- end}} +
+

Vulnerabilities:

+
+ {{- with .SeveritySummary -}} +
+
{{ includeSVG "vulnerability_fill.svg" }}
+ {{ index .SeverityCounters (severity "critical") }} + CRITICAL +
+
+
{{ includeSVG "vulnerability_fill.svg" }}
+ {{ index .SeverityCounters (severity "high") }} + HIGH +
+
+
{{ includeSVG "vulnerability_out.svg" }}
+ {{ index .SeverityCounters (severity "medium") }} + MEDIUM +
+
+
{{ includeSVG "vulnerability_out.svg" }}
+ {{ index .SeverityCounters (severity "low") }} + LOW +
+
+
{{ includeSVG "info.svg" }}
+ {{ index .SeverityCounters (severity "info") }} + INFO +
+ {{- end}} +
+
{{ includeSVG "info.svg" }}
+ {{ .TotalCounter }} + TOTAL +
+
+ {{- range .Queries}} +
+
+
+
+
+

+ {{- if eq .Severity "CRITICAL" -}} +
{{ includeSVG "vulnerability_fill.svg" }}
+ {{- end -}} + {{- if eq .Severity "HIGH" -}} +
{{ includeSVG "vulnerability_fill.svg" }}
+ {{- end -}} + {{- if eq .Severity "MEDIUM" -}} +
{{ includeSVG "vulnerability_out.svg" }}
+ {{- end -}} + {{- if eq .Severity "LOW" -}} +
{{ includeSVG "vulnerability_out.svg" }}
+ {{- end -}} + {{- if eq .Severity "INFO" -}} +
{{ includeSVG "info.svg" }}
+ {{- end -}} + {{- .QueryName -}} +

+ Platform: {{ .Platform }} + {{ if .CWE }}CWE: {{ .CWE }}{{ end }} + {{ if .RiskScore }}Risk Score: {{ .RiskScore }}{{ end }} + Category: {{ .Category }} +
+
+ {{- if not .CISDescriptionID -}} + {{ .Description }} + {{- end -}} + {{- if .CISDescriptionID -}} + {{ .CISDescriptionIDFormatted }} + {{ .CISDescriptionTitle }} + {{ .CISDescriptionTextFormatted }} + {{- end -}} + {{ .QueryURI }} +
+
+
+ Results ({{ len .Files }}) + {{- range .Files}} + {{- $vulLine := .Line -}} +
+
+ File: {{ .FileName }} + Line {{ $vulLine }} +
+
+ Expected: {{ .KeyExpectedValue }} + Found: {{ .KeyActualValue }} +
+
+ {{- range .VulnLines -}} +
+ {{ .Position }}{{ trimSpaces .Line }} +
+ {{- end}} +
+
+ {{- end -}} +
+
+
+ {{- end -}} +
+
+ KICS is open and will always stay such. Both the scanning engine and the security queries are clear and open for the software development community. +
+
+ Spread the love: +
+
+ +
{{ includeSVG "github.svg" }}
+
+
+ +
+ + \ No newline at end of file diff --git a/test/queries_test.go b/test/queries_test.go index 88e593d30cd..52d2d045c14 100644 --- a/test/queries_test.go +++ b/test/queries_test.go @@ -3,6 +3,7 @@ package test import ( "context" "encoding/json" + "fmt" "io" "os" "path/filepath" @@ -58,6 +59,7 @@ func testRemediationQuery(t testing.TB, entry queryEntry, vulnerabilities []mode summary := &remediation.Summary{ SelectedRemediationNumber: 0, ActualRemediationDoneNumber: 0, + RemediatedFiles: []string{}, } // get remediationSets from query vulns @@ -85,26 +87,28 @@ func testRemediationQuery(t testing.TB, entry queryEntry, vulnerabilities []mode ) temporaryRemediationSets := make(map[string]interface{}) + tempToOriginal := make(map[string]string) for k := range remediationSets { tmpFilePath := filepath.Join(os.TempDir(), "temporary-remediation-"+utils.NextRandom()+filepath.Ext(k)) tmpFile := remediation.CreateTempFile(k, tmpFilePath) temporaryRemediationSets[tmpFile] = remediationSets[k] + tempToOriginal[tmpFile] = k } for filePath := range temporaryRemediationSets { fix := temporaryRemediationSets[filePath].(remediation.Set) - - err = summary.RemediateFile(filePath, fix, false, 15) + original_file_name := tempToOriginal[filePath] + err = summary.RemediateFile(filePath, original_file_name, fix, false, 15) os.Remove(filePath) if err != nil { require.NoError(t, err) } } - require.Equal(t, summary.SelectedRemediationNumber, summary.ActualRemediationDoneNumber, - "'SelectedRemediationNumber' is different from 'ActualRemediationDoneNumber'") + errorMsg := fmt.Sprintf("'SelectedRemediationNumber' is different from 'ActualRemediationDoneNumber'\nRemediated files: %v", summary.RemediatedFiles) + require.Equal(t, summary.SelectedRemediationNumber, summary.ActualRemediationDoneNumber, errorMsg) } }