diff --git a/checker/raw_result.go b/checker/raw_result.go index d494d42c16b..5019ad97bb0 100644 --- a/checker/raw_result.go +++ b/checker/raw_result.go @@ -286,6 +286,8 @@ const ( PysaWorkflow SASTWorkflowType = "Pysa" // QodanaWorkflow represents a workflow that runs Qodana. QodanaWorkflow SASTWorkflowType = "Qodana" + // TrivyWorkflow represents a workflow that runs Trivy. + TrivyWorkflow SASTWorkflowType = "Trivy" ) // SASTWorkflow represents a SAST workflow. diff --git a/checks/evaluation/sast_test.go b/checks/evaluation/sast_test.go index 65f726d63ae..5e04e08d410 100644 --- a/checks/evaluation/sast_test.go +++ b/checks/evaluation/sast_test.go @@ -160,6 +160,25 @@ func TestSAST(t *testing.T) { NumberOfInfo: 2, }, }, + { + name: "Trivy is installed, no other SAST tools are installed", + findings: []finding.Finding{ + tool(checker.TrivyWorkflow), + { + Probe: sastToolRunsOnAllCommits.Probe, + Outcome: finding.OutcomeTrue, + Values: map[string]string{ + sastToolRunsOnAllCommits.AnalyzedPRsKey: "1", + sastToolRunsOnAllCommits.TotalPRsKey: "3", + }, + }, + }, + result: scut.TestReturn{ + Score: 10, + NumberOfWarn: 0, + NumberOfInfo: 2, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/checks/raw/sast.go b/checks/raw/sast.go index e12096ae553..f7d6e134e01 100644 --- a/checks/raw/sast.go +++ b/checks/raw/sast.go @@ -87,6 +87,12 @@ func SAST(c *checker.CheckRequest) (checker.SASTData, error) { } data.Workflows = append(data.Workflows, qodanaWorkflows...) + trivyWorkflows, err := getSastUsesWorkflows(c, "^aquasecurity/trivy-action$", checker.TrivyWorkflow) + if err != nil { + return data, err + } + data.Workflows = append(data.Workflows, trivyWorkflows...) + return data, nil } diff --git a/checks/raw/sast_test.go b/checks/raw/sast_test.go index 0a6763fcdd5..7d8159afc24 100644 --- a/checks/raw/sast_test.go +++ b/checks/raw/sast_test.go @@ -248,6 +248,29 @@ func TestSAST(t *testing.T) { }, }, }, + { + name: "Has Trivy", + files: []string{".github/workflows/github-trivy-workflow.yaml"}, + commits: []clients.Commit{ + { + AssociatedMergeRequest: clients.PullRequest{ + Number: 1, + }, + }, + }, + expected: checker.SASTData{ + Workflows: []checker.SASTWorkflow{ + { + Type: checker.TrivyWorkflow, + File: checker.File{ + Path: ".github/workflows/github-trivy-workflow.yaml", + Offset: checker.OffsetDefault, + Type: finding.FileTypeSource, + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/checks/raw/testdata/.github/workflows/github-trivy-workflow.yaml b/checks/raw/testdata/.github/workflows/github-trivy-workflow.yaml new file mode 100644 index 00000000000..3d5772ad164 --- /dev/null +++ b/checks/raw/testdata/.github/workflows/github-trivy-workflow.yaml @@ -0,0 +1,24 @@ +name: build +on: + push: + branches: + - main + pull_request: +jobs: + build: + name: Build + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Build an image from Dockerfile + run: docker build -t docker.io/my-organization/my-app:${{ github.sha }} . + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: 'docker.io/my-organization/my-app:${{ github.sha }}' + format: 'table' + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' \ No newline at end of file diff --git a/checks/sast_test.go b/checks/sast_test.go index 2718e7dda7a..83b7749c353 100644 --- a/checks/sast_test.go +++ b/checks/sast_test.go @@ -76,6 +76,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "completed", @@ -101,6 +105,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "completed", @@ -126,6 +134,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "completed", @@ -152,6 +164,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "completed", @@ -178,7 +194,11 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, - path: ".github/workflows/airflow-codeql-workflow.yaml", + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, + path: ".github/workflows/airflow-codeql-workflow.yaml", expected: scut.TestReturn{ Score: 7, NumberOfWarn: 1, @@ -196,6 +216,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "completed", @@ -231,6 +255,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "completed", @@ -271,6 +299,10 @@ func Test_SAST(t *testing.T) { }, }, searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/codeql-action/analyze", + Path: "/.github/workflows", + }, checkRuns: []clients.CheckRun{ { Status: "notCompletedForTestingOnly", @@ -294,12 +326,51 @@ func Test_SAST(t *testing.T) { NumberOfInfo: 1, }, }, + { + name: `Trivy workflow with commits`, + err: nil, + commits: []clients.Commit{ + { + AssociatedMergeRequest: clients.PullRequest{ + MergedAt: time.Now().Add(time.Hour - 1), + }, + }, + { + AssociatedMergeRequest: clients.PullRequest{ + MergedAt: time.Now().Add(time.Hour - 2), + }, + }, + }, + searchresult: clients.SearchResponse{}, + searchRequest: clients.SearchRequest{ + Query: "github/aquasecurity/trivy-action", + Path: "/.github/workflows", + }, + checkRuns: []clients.CheckRun{ + { + Status: "completed", + Conclusion: "success", + App: clients.CheckRunApp{ + Slug: "aqua-security-trivy", + }, + }, + { + Status: "completed", + Conclusion: "success", + App: clients.CheckRunApp{ + Slug: "aqua-security-trivy", + }, + }, + }, + path: ".github/workflows/github-trivy-workflow.yaml", + expected: scut.TestReturn{ + Score: checker.MaxResultScore, + NumberOfDebug: 2, + NumberOfInfo: 2, + }, + }, } for _, tt := range tests { - searchRequest := clients.SearchRequest{ - Query: "github/codeql-action/analyze", - Path: "/.github/workflows", - } t.Run(tt.name, func(t *testing.T) { t.Parallel() ctrl := gomock.NewController(t) @@ -311,7 +382,7 @@ func Test_SAST(t *testing.T) { return tt.commits, tt.err }) mockRepoClient.EXPECT().ListCheckRunsForRef("").Return(tt.checkRuns, nil).AnyTimes() - mockRepoClient.EXPECT().Search(searchRequest).Return(tt.searchresult, nil).AnyTimes() + mockRepoClient.EXPECT().Search(tt.searchRequest).Return(tt.searchresult, nil).AnyTimes() mockRepoClient.EXPECT().ListFiles(gomock.Any()).DoAndReturn( func(predicate func(string) (bool, error)) ([]string, error) { if strings.Contains(tt.path, "pom") { diff --git a/checks/testdata/.github/workflows/github-trivy-workflow.yaml b/checks/testdata/.github/workflows/github-trivy-workflow.yaml new file mode 100644 index 00000000000..3d5772ad164 --- /dev/null +++ b/checks/testdata/.github/workflows/github-trivy-workflow.yaml @@ -0,0 +1,24 @@ +name: build +on: + push: + branches: + - main + pull_request: +jobs: + build: + name: Build + runs-on: ubuntu-24.04 + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Build an image from Dockerfile + run: docker build -t docker.io/my-organization/my-app:${{ github.sha }} . + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@0.28.0 + with: + image-ref: 'docker.io/my-organization/my-app:${{ github.sha }}' + format: 'table' + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' \ No newline at end of file diff --git a/probes/sastToolConfigured/impl_test.go b/probes/sastToolConfigured/impl_test.go index 587415b276e..57fcd8c2e32 100644 --- a/probes/sastToolConfigured/impl_test.go +++ b/probes/sastToolConfigured/impl_test.go @@ -67,6 +67,9 @@ func Test_Run(t *testing.T) { { Type: checker.PysaWorkflow, }, + { + Type: checker.TrivyWorkflow, + }, }, }, }, @@ -74,6 +77,7 @@ func Test_Run(t *testing.T) { finding.OutcomeTrue, finding.OutcomeTrue, finding.OutcomeTrue, + finding.OutcomeTrue, }, }, } @@ -148,6 +152,19 @@ func Test_Run_tools(t *testing.T) { }, tools: []string{"Sonar", "Pysa"}, }, + { + name: "Trivy", + raw: &checker.RawResults{ + SASTResults: checker.SASTData{ + Workflows: []checker.SASTWorkflow{ + { + Type: checker.TrivyWorkflow, + }, + }, + }, + }, + tools: []string{"Trivy"}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {