Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add output analytics for GH integration #270

Merged
merged 9 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
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>
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
Loading