Skip to content

Commit

Permalink
feat: ast-70404 include rules in sarif (#252)
Browse files Browse the repository at this point in the history
<!--
Thanks for contributing to 2ms by offering a pull request.
-->

Closes #

**Proposed Changes**

<!--
Adding rules for which results were found to Sarif report. Added new
field in JSON and YAML reports, ruleDescription.
-->

**Checklist**

- [ ] I covered my changes with tests.
- [ ] I Updated the documentation that is affected by my changes:
  - [ ] Change in the CLI arguments
  - [ ] Change in the configuration file

I submit this contribution under the Apache-2.0 license.
  • Loading branch information
diogo-fjrocha authored Oct 23, 2024
1 parent 3208f45 commit 0b69901
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 15 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RUN go build -o /app/2ms .
# Runtime image
FROM cgr.dev/chainguard/git@sha256:02660563e96b553d6aeb4093e3fcc3e91b2ad3a86e05c65b233f37f035e5044e

RUN apk add --no-cache bash=5.2.21-r1 git=2.45.1-r0 git-lfs=3.5.1-r8 libcurl-openssl4=8.10.0-r0 glibc=2.39-r5 glibc-locale-posix=2.39-r5 ld-linux==2.39-r5 libcrypt1=2.39-r5 && git config --global --add safe.directory /repo
RUN apk add --no-cache bash=5.2.21-r1 git=2.45.1-r0 git-lfs=3.5.1-r8 libcurl-openssl4=8.10.0-r0 glibc=2.39-r5 glibc-locale-posix=2.39-r5 ld-linux==2.39-r5 libcrypt1=2.39-r5 libcrypto3=3.3.2-r2 libssl3=3.3.2-r2 && git config --global --add safe.directory /repo

COPY --from=builder /app/2ms .

Expand Down
19 changes: 10 additions & 9 deletions engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,16 @@ func (e *Engine) Detect(item plugins.ISourceItem, secretsChannel chan *secrets.S
endLine = value.EndLine
}
secret := &secrets.Secret{
ID: itemId,
Source: item.GetSource(),
RuleID: value.RuleID,
StartLine: startLine,
StartColumn: value.StartColumn,
EndLine: endLine,
EndColumn: value.EndColumn,
Value: value.Secret,
LineContent: value.Line,
ID: itemId,
Source: item.GetSource(),
RuleID: value.RuleID,
StartLine: startLine,
StartColumn: value.StartColumn,
EndLine: endLine,
EndColumn: value.EndColumn,
Value: value.Secret,
LineContent: value.Line,
RuleDescription: value.Description,
}
if !isSecretIgnored(secret, &e.ignoredIds, &e.allowedValues) {
secretsChannel <- secret
Expand Down
271 changes: 271 additions & 0 deletions lib/reporting/report_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,172 @@
package reporting

import (
"encoding/json"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"

"github.com/checkmarx/2ms/lib/config"
"github.com/checkmarx/2ms/lib/secrets"
"github.com/stretchr/testify/assert"
)

// test input results
var (
ruleID1 = "ruleID1"
ruleID2 = "ruleID2"
result1 = &secrets.Secret{
ID: "ID1",
Source: "file1",
RuleID: ruleID1,
StartLine: 150,
EndLine: 150,
LineContent: "line content",
StartColumn: 31,
EndColumn: 150,
Value: "value",
ValidationStatus: secrets.ValidResult,
RuleDescription: "Rule Description",
}
// this result has a different rule than result1
result2 = &secrets.Secret{
ID: "ID2",
Source: "file2",
RuleID: ruleID2,
StartLine: 10,
EndLine: 10,
LineContent: "line content2",
StartColumn: 41,
EndColumn: 160,
Value: "value 2",
ValidationStatus: secrets.InvalidResult,
RuleDescription: "Rule Description2",
}
// this result has the same rule as result1
result3 = &secrets.Secret{
ID: "ID3",
Source: "file3",
RuleID: ruleID1,
StartLine: 16,
EndLine: 16,
LineContent: "line content3",
StartColumn: 11,
EndColumn: 130,
Value: "value 3",
ValidationStatus: secrets.UnknownResult,
RuleDescription: "Rule Description",
}
)

// test expected outputs
var (
// sarif rules
rule1Sarif = &SarifRule{
ID: ruleID1,
FullDescription: &Message{
Text: result1.RuleDescription,
},
}
rule2Sarif = &SarifRule{
ID: ruleID2,
FullDescription: &Message{
Text: result2.RuleDescription,
},
}
// sarif results
result1Sarif = Results{
Message: Message{
Text: messageText(result1.RuleID, result1.Source),
},
RuleId: ruleID1,
Locations: []Locations{
{
PhysicalLocation: PhysicalLocation{
ArtifactLocation: ArtifactLocation{
URI: result1.Source,
},
Region: Region{
StartLine: result1.StartLine,
StartColumn: result1.StartColumn,
EndLine: result1.EndLine,
EndColumn: result1.EndColumn,
Snippet: Snippet{
Text: result1.Value,
Properties: Properties{
"lineContent": strings.TrimSpace(result1.LineContent),
},
},
},
},
},
},
Properties: Properties{
"validationStatus": string(result1.ValidationStatus),
},
}
result2Sarif = Results{
Message: Message{
Text: messageText(result2.RuleID, result2.Source),
},
RuleId: ruleID2,
Locations: []Locations{
{
PhysicalLocation: PhysicalLocation{
ArtifactLocation: ArtifactLocation{
URI: result2.Source,
},
Region: Region{
StartLine: result2.StartLine,
StartColumn: result2.StartColumn,
EndLine: result2.EndLine,
EndColumn: result2.EndColumn,
Snippet: Snippet{
Text: result2.Value,
Properties: Properties{
"lineContent": strings.TrimSpace(result2.LineContent),
},
},
},
},
},
},
Properties: Properties{
"validationStatus": string(result2.ValidationStatus),
},
}
result3Sarif = Results{
Message: Message{
Text: messageText(result3.RuleID, result3.Source),
},
RuleId: ruleID1,
Locations: []Locations{
{
PhysicalLocation: PhysicalLocation{
ArtifactLocation: ArtifactLocation{
URI: result3.Source,
},
Region: Region{
StartLine: result3.StartLine,
StartColumn: result3.StartColumn,
EndLine: result3.EndLine,
EndColumn: result3.EndColumn,
Snippet: Snippet{
Text: result3.Value,
Properties: Properties{
"lineContent": strings.TrimSpace(result3.LineContent),
},
},
},
},
},
},
Properties: Properties{
"validationStatus": string(result3.ValidationStatus),
},
}
)

func TestAddSecretToFile(t *testing.T) {
Expand Down Expand Up @@ -57,3 +216,115 @@ func TestWriteReportInNonExistingDir(t *testing.T) {

os.RemoveAll(filepath.Join(tempDir, "test_temp_dir"))
}

func TestGetOutputSarif(t *testing.T) {
tests := []struct {
name string
arg Report
want []Runs
wantErr bool
}{
{
name: "two_results_same_rule_want_one_rule_in_report",
arg: Report{
TotalItemsScanned: 2,
TotalSecretsFound: 2,
Results: map[string][]*secrets.Secret{
"secret1": {result1},
"secret3": {result3},
},
},
wantErr: false,
want: []Runs{
{
Tool: Tool{
Driver: Driver{
Name: "report",
SemanticVersion: "1",
Rules: []*SarifRule{
rule1Sarif,
},
},
},
Results: []Results{
result1Sarif,
result3Sarif,
},
},
},
},
{
name: "two_results_two_rules_want_two_rules_in_report",
arg: Report{
TotalItemsScanned: 2,
TotalSecretsFound: 2,
Results: map[string][]*secrets.Secret{
"secret1": {result1},
"secret2": {result2},
},
},
wantErr: false,
want: []Runs{
{
Tool: Tool{
Driver: Driver{
Name: "report",
SemanticVersion: "1",
Rules: []*SarifRule{
rule1Sarif,
rule2Sarif,
},
},
},
Results: []Results{
result1Sarif,
result2Sarif,
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.arg.getOutput(sarifFormat, &config.Config{Name: "report", Version: "1"})
if tt.wantErr {
assert.NotNil(t, err)
return
}
var gotReport Sarif
err = json.Unmarshal([]byte(got), &gotReport)
assert.Nil(t, err)
SortSarifReports(&gotReport, &Sarif{Runs: tt.want})
assert.Equal(t, tt.want, gotReport.Runs)
})
}
}

// SortProject Sorts two sarif reports
func SortSarifReports(run1, run2 *Sarif) {
// Sort Rules
SortRules(run1.Runs[0].Tool.Driver.Rules, run2.Runs[0].Tool.Driver.Rules)
SortResults(run1.Runs[0].Results, run2.Runs[0].Results)

}

func SortRules(rules1, rules2 []*SarifRule) {
// Sort both slices
sort.Slice(rules1, func(i, j int) bool {
return rules1[i].ID < rules1[j].ID
})
sort.Slice(rules2, func(i, j int) bool {
return rules2[i].ID < rules2[j].ID
})
}

func SortResults(results1, results2 []Results) {
// Sort both slices
sort.Slice(results1, func(i, j int) bool {
return results1[i].Message.Text < results1[j].Message.Text
})
sort.Slice(results2, func(i, j int) bool {
return results2[i].Message.Text < results2[j].Message.Text
})
}
Loading

0 comments on commit 0b69901

Please sign in to comment.