Skip to content

Commit

Permalink
Add output analytics for GH integration (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
attiasas authored Dec 25, 2024
1 parent d0f1598 commit 74520fe
Show file tree
Hide file tree
Showing 20 changed files with 334 additions and 99 deletions.
1 change: 1 addition & 0 deletions commands/scan/buildscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ func (bsc *BuildScanCommand) runBuildScanAndPrintResults(xrayManager *xray.XrayS

resultsPrinter := output.NewResultsWriter(cmdResults).
SetOutputFormat(bsc.outputFormat).
SetPlatformUrl(bsc.serverDetails.Url).
SetHasViolationContext(true).
SetIncludeVulnerabilities(bsc.includeVulnerabilities).
SetIncludeLicenses(false).
Expand Down
1 change: 1 addition & 0 deletions commands/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ func (scanCmd *ScanCommand) RunAndRecordResults(cmdType utils.CommandType, recor

if err = output.NewResultsWriter(cmdResults).
SetOutputFormat(scanCmd.outputFormat).
SetPlatformUrl(scanCmd.serverDetails.Url).
SetHasViolationContext(scanCmd.hasViolationContext()).
SetIncludeVulnerabilities(scanCmd.includeVulnerabilities).
SetIncludeLicenses(scanCmd.includeLicenses).
Expand Down
14 changes: 7 additions & 7 deletions jas/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ func TestAddScoreToRunRules(t *testing.T) {
sarifutils.CreateResultWithOneLocation("file", 0, 0, 0, 0, "snippet", "rule2", "warning"),
),
expectedOutput: []*sarif.ReportingDescriptor{
sarif.NewRule("rule1").WithProperties(sarif.Properties{"security-severity": "6.9"}),
sarif.NewRule("rule2").WithProperties(sarif.Properties{"security-severity": "6.9"}),
sarifutils.CreateDummyRuleWithProperties("rule1", sarif.Properties{"security-severity": "6.9"}),
sarifutils.CreateDummyRuleWithProperties("rule2", sarif.Properties{"security-severity": "6.9"}),
},
},
{
Expand All @@ -78,11 +78,11 @@ func TestAddScoreToRunRules(t *testing.T) {
sarifutils.CreateResultWithOneLocation("file", 0, 0, 0, 0, "snippet", "rule5", "error"),
),
expectedOutput: []*sarif.ReportingDescriptor{
sarif.NewRule("rule1").WithProperties(sarif.Properties{"security-severity": "0.0"}),
sarif.NewRule("rule2").WithProperties(sarif.Properties{"security-severity": "3.9"}),
sarif.NewRule("rule3").WithProperties(sarif.Properties{"security-severity": "6.9"}),
sarif.NewRule("rule4").WithProperties(sarif.Properties{"security-severity": "6.9"}),
sarif.NewRule("rule5").WithProperties(sarif.Properties{"security-severity": "8.9"}),
sarifutils.CreateDummyRuleWithProperties("rule1", sarif.Properties{"security-severity": "0.0"}),
sarifutils.CreateDummyRuleWithProperties("rule2", sarif.Properties{"security-severity": "3.9"}),
sarifutils.CreateDummyRuleWithProperties("rule3", sarif.Properties{"security-severity": "6.9"}),
sarifutils.CreateDummyRuleWithProperties("rule4", sarif.Properties{"security-severity": "6.9"}),
sarifutils.CreateDummyRuleWithProperties("rule5", sarif.Properties{"security-severity": "8.9"}),
},
},
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<pre>44 Security issues are grouped by CVE number:&Tab;44 SCA<br><br><div style="display: flex; align-items: center; text-align: center">❗️ 33 Critical</div><br><div style="display: flex; align-items: center; text-align: center">🟡 11 Low</div><br><a href="https://test-platform-url.jfrog.io/ui/onDemandScanning/3d90ec4b-cf33-4846-6831-4bf9576f2235?gh_job_id=some-job-id&gh_section=on_demand_scan">See the results of the scan in JFrog</a></pre>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<pre>24 Security Issues:&Tab;24 SCA<br><br><div style="display: flex; align-items: center; text-align: center">🔴 3 High</div><br><div style="display: flex; align-items: center; text-align: center">🟠 1 Medium</div><br><div style="display: flex; align-items: center; text-align: center">⚪️ 20 Unknown</div><br><a href="https://test-more-info-url.jfrog.io/?gh_job_id=some-job-id&gh_section=build">See the results of the scan in JFrog</a></pre>
2 changes: 1 addition & 1 deletion tests/testdata/output/jobSummary/violations.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pre>watches: <br>watch1, watch2, watch3, watch4<br>watch5</pre><br><pre>23 Policy Violations:&Tab;17 Security&Tab;2 Operational&Tab;1 License&Tab;3 Secrets<br><br><div style="display: flex; align-items: center; text-align: center">❗️ 8 Critical (2 Not Applicable)</div><br><div style="display: flex; align-items: center; text-align: center">🔴 6 High</div><br><div style="display: flex; align-items: center; text-align: center">🟠 3 Medium</div><br><div style="display: flex; align-items: center; text-align: center">🟡 5 Low (3 Not Applicable)</div><br><div style="display: flex; align-items: center; text-align: center">⚪️ 1 Unknown</div><br><a href="https://test-more-info-url.jfrog.io/">See the results of the scan in JFrog</a></pre>
<pre>watches: <br>watch1, watch2, watch3, watch4<br>watch5</pre><br><pre>23 Policy Violations:&Tab;17 Security&Tab;2 Operational&Tab;1 License&Tab;3 Secrets<br><br><div style="display: flex; align-items: center; text-align: center">❗️ 8 Critical (2 Not Applicable)</div><br><div style="display: flex; align-items: center; text-align: center">🔴 6 High</div><br><div style="display: flex; align-items: center; text-align: center">🟠 3 Medium</div><br><div style="display: flex; align-items: center; text-align: center">🟡 5 Low (3 Not Applicable)</div><br><div style="display: flex; align-items: center; text-align: center">⚪️ 1 Unknown</div><br><a href="https://test-more-info-url.jfrog.io/scan-descendants/master?repoId=10">See the results of the scan in JFrog</a></pre>
1 change: 1 addition & 0 deletions tests/testdata/output/jobSummary/violations_analytics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<pre>watches: <br>watch1, watch2, watch3, watch4<br>watch5</pre><br><pre>23 Policy Violations:&Tab;17 Security&Tab;2 Operational&Tab;1 License&Tab;3 Secrets<br><br><div style="display: flex; align-items: center; text-align: center">❗️ 8 Critical (2 Not Applicable)</div><br><div style="display: flex; align-items: center; text-align: center">🔴 6 High</div><br><div style="display: flex; align-items: center; text-align: center">🟠 3 Medium</div><br><div style="display: flex; align-items: center; text-align: center">🟡 5 Low (3 Not Applicable)</div><br><div style="display: flex; align-items: center; text-align: center">⚪️ 1 Unknown</div><br><a href="https://test-more-info-url.jfrog.io/scan-descendants/master?repoId=10gh_job_id=some-job-id&gh_section=on_demand_scan">See the results of the scan in JFrog</a></pre>
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pre>watch: watch1</pre><br><pre>26 Policy Violations:&Tab;20 Security&Tab;2 Operational&Tab;1 License&Tab;3 Secrets<br><br><a href="https://jfrog.com/help/access?xinfo:appid=csh-gen-gitbook">🐸 Unlock detailed findings</a></pre>
<pre>watch: watch1</pre><br><pre>23 Policy Violations:&Tab;17 Security&Tab;2 Operational&Tab;1 License&Tab;3 Secrets<br><br><a href="https://jfrog.com/help/access?xinfo:appid=csh-gen-gitbook">🐸 Unlock detailed findings</a></pre>
24 changes: 24 additions & 0 deletions utils/formats/sarifutils/sarifutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,30 @@ func SetRuleShortDescriptionText(value string, rule *sarif.ReportingDescriptor)
rule.ShortDescription.Text = &value
}

func SetRuleHelp(msg, markdown string, rule *sarif.ReportingDescriptor) {
if rule.Help == nil {
rule.Help = &sarif.MultiformatMessageString{
Text: &msg,
Markdown: &markdown,
}
return
}
rule.Help.Markdown = &markdown
rule.Help.Text = &msg
}

func SetRuleFullDescription(msg, markdown string, rule *sarif.ReportingDescriptor) {
if rule.FullDescription == nil {
rule.FullDescription = &sarif.MultiformatMessageString{
Text: &msg,
Markdown: &markdown,
}
return
}
rule.FullDescription.Markdown = &markdown
rule.FullDescription.Text = &msg
}

func GetRuleShortDescriptionText(rule *sarif.ReportingDescriptor) string {
if rule.ShortDescription != nil && rule.ShortDescription.Text != nil {
return *rule.ShortDescription.Text
Expand Down
13 changes: 9 additions & 4 deletions utils/formats/sarifutils/sarifutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ func TestGetRunRules(t *testing.T) {
run: CreateRunWithDummyResults(
CreateDummyPassingResult("rule1"),
),
expectedOutput: []*sarif.ReportingDescriptor{sarif.NewRule("rule1")},
expectedOutput: []*sarif.ReportingDescriptor{sarif.NewRule("rule1").WithShortDescription(sarif.NewMultiformatMessageString("")).WithFullDescription(sarif.NewMarkdownMultiformatMessageString("rule-markdown").WithText("rule-msg"))},
},
{
run: CreateRunWithDummyResults(
Expand All @@ -576,12 +576,17 @@ func TestGetRunRules(t *testing.T) {
CreateDummyPassingResult("rule3"),
CreateDummyPassingResult("rule2"),
),
expectedOutput: []*sarif.ReportingDescriptor{sarif.NewRule("rule1"), sarif.NewRule("rule2"), sarif.NewRule("rule3")},
expectedOutput: []*sarif.ReportingDescriptor{
sarif.NewRule("rule1").WithShortDescription(sarif.NewMultiformatMessageString("")).WithFullDescription(sarif.NewMarkdownMultiformatMessageString("rule-markdown").WithText("rule-msg")),
sarif.NewRule("rule2").WithShortDescription(sarif.NewMultiformatMessageString("")).WithFullDescription(sarif.NewMarkdownMultiformatMessageString("rule-markdown").WithText("rule-msg")),
sarif.NewRule("rule3").WithShortDescription(sarif.NewMultiformatMessageString("")).WithFullDescription(sarif.NewMarkdownMultiformatMessageString("rule-markdown").WithText("rule-msg")),
},
},
}

for _, test := range tests {
assert.Equal(t, test.expectedOutput, GetRunRules(test.run))
rules := GetRunRules(test.run)
assert.Equal(t, test.expectedOutput, rules)
}
}

Expand Down Expand Up @@ -635,7 +640,7 @@ func TestGetResultFingerprint(t *testing.T) {
},
{
name: "Results with fingerprint field",
result: CreateDummyResultWithFingerprint("some_markdown", "masg", jasutils.SastFingerprintKey, "sast_fingerprint"),
result: CreateDummyResultWithFingerprint("some_markdown", "msg", jasutils.SastFingerprintKey, "sast_fingerprint"),
expectedOutput: "sast_fingerprint",
},
}
Expand Down
55 changes: 46 additions & 9 deletions utils/formats/sarifutils/test_sarifutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,24 @@ import (
"github.com/owenrumney/go-sarif/v2/sarif"
)

// TODO: Create a Builder struct (with dynamic setters) and refactor sarif tests for better maintenance

func CreateRunWithDummyResultsWithRuleInformation(toolName, ruleShortTxtDescription, ruleTxtDescription, ruleMarkdownDescription, ruleHelpMsg, ruleHelpMarkdown, wd string, results ...*sarif.Result) *sarif.Run {
run := createRunWithDummyResults(toolName, ruleShortTxtDescription, ruleTxtDescription, ruleMarkdownDescription, ruleHelpMsg, ruleHelpMarkdown, results...)
run.Invocations = []*sarif.Invocation{sarif.NewInvocation().WithWorkingDirectory(sarif.NewSimpleArtifactLocation(wd))}
return run
}

func CreateRunWithDummyResultsInWdWithHelp(helpMsg, helpMarkdown, wd string, results ...*sarif.Result) *sarif.Run {
return createRunWithDummyResults("", "", "rule-msg", "rule-markdown", helpMsg, helpMarkdown, results...).WithInvocations([]*sarif.Invocation{sarif.NewInvocation().WithWorkingDirectory(sarif.NewSimpleArtifactLocation(wd))})
}

func CreateRunWithDummyResultsInWd(wd string, results ...*sarif.Result) *sarif.Run {
return createRunWithDummyResults("", results...).WithInvocations([]*sarif.Invocation{sarif.NewInvocation().WithWorkingDirectory(sarif.NewSimpleArtifactLocation(wd))})
return createRunWithDummyResults("", "", "rule-msg", "rule-markdown", "", "", results...).WithInvocations([]*sarif.Invocation{sarif.NewInvocation().WithWorkingDirectory(sarif.NewSimpleArtifactLocation(wd))})
}

func CreateRunWithDummyResults(results ...*sarif.Result) *sarif.Run {
return createRunWithDummyResults("", results...)
return createRunWithDummyResults("", "", "rule-msg", "rule-markdown", "", "", results...)
}

func CreateDummyDriver(toolName string, rules ...*sarif.ReportingDescriptor) *sarif.ToolComponent {
Expand All @@ -20,20 +32,36 @@ func CreateDummyDriver(toolName string, rules ...*sarif.ReportingDescriptor) *sa
}

func CreateRunNameWithResults(toolName string, results ...*sarif.Result) *sarif.Run {
return createRunWithDummyResults(toolName, results...)
return createRunWithDummyResults(toolName, "", "rule-msg", "rule-markdown", "", "", results...)
}

func createRunWithDummyResults(toolName string, results ...*sarif.Result) *sarif.Run {
func createRunWithDummyResults(toolName, ruleShortTxtDescription, ruleMsg, ruleMarkdown, ruleHelpMsg, ruleHelpMarkdown string, results ...*sarif.Result) *sarif.Run {
run := sarif.NewRun(*sarif.NewSimpleTool(toolName))
for _, result := range results {
if result.RuleID != nil {
run.AddRule(*result.RuleID)
rule := run.AddRule(*result.RuleID)
SetRuleFullDescription(ruleMsg, ruleMarkdown, rule)
if ruleHelpMsg != "" || ruleHelpMarkdown != "" {
SetRuleHelp(ruleHelpMsg, ruleHelpMarkdown, rule)
}
SetRuleShortDescriptionText(ruleShortTxtDescription, rule)
}
run.AddResult(result)
}
return run
}

func CreateRunWithDummyResultAndRuleInformation(result *sarif.Result, ruleHelpMsg, ruleHelpMarkdown string, properties, values []string) *sarif.Run {
run := CreateRunWithDummyResultAndRuleProperties(result, properties, values)
if run != nil {
rule := GetRuleById(run, GetResultRuleId(result))
if rule != nil {
SetRuleHelp(ruleHelpMsg, ruleHelpMarkdown, rule)
}
}
return run
}

func CreateRunWithDummyResultAndRuleProperties(result *sarif.Result, properties, values []string) *sarif.Run {
if len(properties) != len(values) {
return nil
Expand All @@ -56,7 +84,7 @@ func CreateDummyResultInPath(fileName string) *sarif.Result {

func CreateDummyResult(markdown, msg, ruleId, level string) *sarif.Result {
return &sarif.Result{
Message: *sarif.NewTextMessage(msg).WithMarkdown(markdown),
Message: sarif.Message{Text: &msg, Markdown: &markdown},
Level: &level,
RuleID: &ruleId,
}
Expand All @@ -83,7 +111,7 @@ func CreateResultWithDummyLocationAmdProperty(fileName, property, value string)
}

func CreateResultWithLocations(msg, ruleId, level string, locations ...*sarif.Location) *sarif.Result {
result := CreateDummyResult("", msg, ruleId, level)
result := CreateDummyResult("result-markdown", msg, ruleId, level)
result.Locations = locations
return result
}
Expand All @@ -103,7 +131,7 @@ func newUintPtr(v uint) *uint {
}

func CreateDummyResultWithPathAndLogicalLocation(fileName, logicalName, kind, property, value string) *sarif.Result {
result := CreateDummyResult("", "", "rule", "level")
result := CreateDummyResult("result-markdown", "result-msg", "rule", "level")
result.Locations = append(result.Locations, CreateDummyLocationWithPathAndLogicalLocation(fileName, logicalName, kind, property, value))
return result
}
Expand Down Expand Up @@ -146,7 +174,7 @@ func CreateDummyPassingResult(ruleId string) *sarif.Result {
}

func CreateResultWithOneLocation(fileName string, startLine, startCol, endLine, endCol int, snippet, ruleId, level string) *sarif.Result {
return CreateResultWithLocations("", ruleId, level, CreateLocation(fileName, startLine, startCol, endLine, endCol, snippet))
return CreateResultWithLocations("result-msg", ruleId, level, CreateLocation(fileName, startLine, startCol, endLine, endCol, snippet))
}

func CreateCodeFlow(threadFlows ...*sarif.ThreadFlow) *sarif.CodeFlow {
Expand All @@ -164,3 +192,12 @@ func CreateThreadFlow(locations ...*sarif.Location) *sarif.ThreadFlow {
}
return stackStrace
}

func CreateDummyRuleWithProperties(id string, properties sarif.Properties) *sarif.ReportingDescriptor {
return &sarif.ReportingDescriptor{
ID: id,
Properties: properties,
ShortDescription: sarif.NewMultiformatMessageString(""),
FullDescription: sarif.NewMarkdownMultiformatMessageString("rule-markdown").WithText("rule-msg"),
}
}
5 changes: 3 additions & 2 deletions utils/results/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ func TestGetApplicableCveValue(t *testing.T) {
cves: []services.Cve{{Id: "testCve2"}},
expectedResult: jasutils.Applicable,
expectedCves: []formats.CveRow{{Id: "testCve2", Applicability: &formats.Applicability{Status: string(jasutils.Applicable), Evidence: []formats.Evidence{{
Reason: "result-msg",
Location: formats.Location{
File: "fileName2",
StartLine: 1,
Expand Down Expand Up @@ -456,7 +457,7 @@ func TestGetApplicableCveValue(t *testing.T) {
expectedCves: []formats.CveRow{
{Id: "testCve1", Applicability: &formats.Applicability{Status: string(jasutils.NotApplicable)}},
{Id: "testCve2", Applicability: &formats.Applicability{Status: string(jasutils.Applicable),
Evidence: []formats.Evidence{{Location: formats.Location{File: "fileName4", StartLine: 1, Snippet: "snippet"}}},
Evidence: []formats.Evidence{{Reason: "result-msg", Location: formats.Location{File: "fileName4", StartLine: 1, Snippet: "snippet"}}},
}},
},
},
Expand Down Expand Up @@ -558,7 +559,7 @@ func TestGetApplicableCveValue(t *testing.T) {
expectedResult: jasutils.Applicable,
expectedCves: []formats.CveRow{
{Id: "testCve1", Applicability: &formats.Applicability{Status: string(jasutils.NotApplicable)}},
{Id: "testCve2", Applicability: &formats.Applicability{Status: string(jasutils.Applicable), Evidence: []formats.Evidence{{Location: formats.Location{File: "fileName4", StartLine: 1, Snippet: "snippet"}}}}},
{Id: "testCve2", Applicability: &formats.Applicability{Status: string(jasutils.Applicable), Evidence: []formats.Evidence{{Reason: "result-msg", Location: formats.Location{File: "fileName4", StartLine: 1, Snippet: "snippet"}}}}},
},
},
}
Expand Down
4 changes: 3 additions & 1 deletion utils/results/conversion/convertor.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type ResultConvertParams struct {
SimplifiedOutput bool
// Convert the results to a pretty format if supported (Table and SimpleJson only)
Pretty bool
// The JFrog platform URL to be used in the results (Sarif only - GitHub integration)
PlatformUrl string
}

func NewCommandResultsConvertor(params ResultConvertParams) *CommandResultsConvertor {
Expand Down Expand Up @@ -67,7 +69,7 @@ func (c *CommandResultsConvertor) ConvertToSimpleJson(cmdResults *results.Securi
}

func (c *CommandResultsConvertor) ConvertToSarif(cmdResults *results.SecurityCommandResults) (sarifReport *sarif.Report, err error) {
parser := sarifparser.NewCmdResultsSarifConverter(c.Params.IncludeVulnerabilities, c.Params.HasViolationContext, c.Params.PatchBinaryPaths)
parser := sarifparser.NewCmdResultsSarifConverter(c.Params.PlatformUrl, c.Params.IncludeVulnerabilities, c.Params.HasViolationContext, c.Params.PatchBinaryPaths)
return parseCommandResults(c.Params, parser, cmdResults)
}

Expand Down
Loading

0 comments on commit 74520fe

Please sign in to comment.