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

Feature/356 checkov producer #369

Merged
merged 5 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ clean: clean-protos clean-migrations-compose

lint:
# we need to redirect stderr to stdout because Github actions don't capture the stderr lolz
@reviewdog -fail-on-error -diff="git diff origin/main" -filter-mode=added 2>&1
@reviewdog -fail-level=any -diff="git diff origin/main" -filter-mode=added 2>&1

install-lint-tools:
@go install honnef.co/go/tools/cmd/staticcheck@latest
Expand All @@ -122,7 +122,7 @@ install-lint-tools:
@go install github.com/kisielk/errcheck@latest
@go install github.com/rhysd/actionlint/cmd/actionlint@latest
@go install github.com/client9/misspell/cmd/misspell@latest
@go install github.com/bufbuild/buf/cmd/buf@v1.32.2
@go install github.com/bufbuild/buf/cmd/buf@v1.28.1
@npm ci

install-go-test-tools:
Expand Down
6 changes: 3 additions & 3 deletions components/consumers/dependency-track/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func TestUploadBomsFromRaw(t *testing.T) {
require.NoError(t, err)

client = c
issues, err := cyclonedx.ToDracon(rawSaaSBOM, "json")
issues, err := cyclonedx.ToDracon(rawSaaSBOM, "json", "")

require.NoError(t, err)
ltr := v1.LaunchToolResponse{
Expand Down Expand Up @@ -112,7 +112,7 @@ func TestUploadBomsFromEnriched(t *testing.T) {
require.NoError(t, err)

client = c
issues, err := cyclonedx.ToDracon(rawSaaSBOM, "json")
issues, err := cyclonedx.ToDracon(rawSaaSBOM, "json", "")

require.NoError(t, err)
ltr := v1.LaunchToolResponse{
Expand Down Expand Up @@ -206,7 +206,7 @@ func TestUploadBomsFromEnrichedWithOwners(t *testing.T) {
require.NoError(t, err)

client = c
issues, err := cyclonedx.ToDracon(rawSaaSBOM, "json")
issues, err := cyclonedx.ToDracon(rawSaaSBOM, "json", "")
require.NoError(t, err)

ltr := v1.LaunchToolResponse{
Expand Down
2 changes: 1 addition & 1 deletion components/enrichers/depsdev/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ func enrichIssue(i *v1.Issue) (*v1.EnrichedIssue, error) {
if err != nil {
return &enrichedIssue, err
}
originalIssue, err := cyclonedx.ToDracon(marshalled, "json")
originalIssue, err := cyclonedx.ToDracon(marshalled, "json", "")
if err != nil {
return &enrichedIssue, err
}
Expand Down
2 changes: 1 addition & 1 deletion components/producers/cdxgen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@ func main() {
}

func handleCycloneDX(inFile []byte) ([]*v1.Issue, error) {
return cyclonedx.ToDracon(inFile, "json")
return cyclonedx.ToDracon(inFile, "json", "")
}

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions components/producers/checkov/exampleData/checkov_sarif_out.pb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

 ����[checkov�
!code/cfngoat/cfngoat.yaml:891-892 CKV_SECRET_6Base64 High Entropy String :�MatchedRule: {"id":"CKV_SECRET_6","name":"Base64 High Entropy String","shortDescription":{"text":"Base64 High Entropy String"},"fullDescription":{"text":"Base64 High Entropy String"},"defaultConfiguration":{"level":"error"},"help":{"text":"Base64 High Entropy String\nResource: c00f1a6e4b20aa64691d50781b810756d6254b8e"}}
Message: Base64 High Entropy StringBunknown�
/code/cfngoat/.github/workflows/checkov.yaml:1-1
CKV2_GHA_15Ensure top-level permissions are not set to write-all :�MatchedRule: {"id":"CKV2_GHA_1","name":"Ensure top-level permissions are not set to write-all","shortDescription":{"text":"Ensure top-level permissions are not set to write-all"},"fullDescription":{"text":"Ensure top-level permissions are not set to write-all"},"defaultConfiguration":{"level":"error"},"help":{"text":"Ensure top-level permissions are not set to write-all\nResource: on(build)"}}
Message: Ensure top-level permissions are not set to write-allBunknown�
,code/cfngoat/.github/workflows/main.yaml:1-1
CKV2_GHA_15Ensure top-level permissions are not set to write-all :�MatchedRule: {"id":"CKV2_GHA_1","name":"Ensure top-level permissions are not set to write-all","shortDescription":{"text":"Ensure top-level permissions are not set to write-all"},"fullDescription":{"text":"Ensure top-level permissions are not set to write-all"},"defaultConfiguration":{"level":"error"},"help":{"text":"Ensure top-level permissions are not set to write-all\nResource: on(build)"}}
Message: Ensure top-level permissions are not set to write-allBunknown
Expand Down
2,716 changes: 2,716 additions & 0 deletions components/producers/checkov/exampleData/results_cyclonedx.json

Large diffs are not rendered by default.

639 changes: 639 additions & 0 deletions components/producers/checkov/exampleData/results_sarif.sarif

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions components/producers/checkov/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"flag"
"log"

"github.com/go-errors/errors"
northdpole marked this conversation as resolved.
Show resolved Hide resolved

draconv1 "github.com/ocurity/dracon/api/proto/v1"
"github.com/ocurity/dracon/components/producers"
"github.com/ocurity/dracon/pkg/cyclonedx"
"github.com/ocurity/dracon/pkg/sarif"
)

// the CycloneDX target override
var target string

func main() {
flag.StringVar(&target, "target", "", "The target being scanned, this will override the CycloneDX target and is useful for cases where you scan iac or a dockerfile for an application that you know it's purl")

if err := producers.ParseFlags(); err != nil {
log.Fatal(err)
}

inFile, err := producers.ReadInFile()
if err != nil {
log.Fatal(err)
}
if err := run(inFile, target); err != nil {
log.Fatal(err)
}
}

func run(inFile []byte, target string) error {
sarifResults, sarifErr := handleSarif(inFile)
cyclondxResults, cyclonedxErr := handleCycloneDX(inFile, target)
var issues []*draconv1.Issue
if sarifErr == nil {
issues = sarifResults
} else if cyclonedxErr == nil {
issues = cyclondxResults
} else {
return errors.Errorf("Could not parse input file as neither Sarif nor CycloneDX sarif error: %v, cyclonedx error: %v", sarifErr, cyclonedxErr)
}
return producers.WriteDraconOut(
"checkov",
issues,
)
}

func handleSarif(inFile []byte) ([]*draconv1.Issue, error) {
var sarifResults []*sarif.DraconIssueCollection
var draconResults []*draconv1.Issue
sarifResults, err := sarif.ToDracon(string(inFile))
if err != nil {
return draconResults, err
}
for _, result := range sarifResults {
draconResults = append(draconResults, result.Issues...)
}
return draconResults, nil
}

func handleCycloneDX(inFile []byte, target string) ([]*draconv1.Issue, error) {
return cyclonedx.ToDracon(inFile, "json", target)
}
106 changes: 106 additions & 0 deletions components/producers/checkov/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package main

import (
"encoding/json"
"os"
"path/filepath"
"slices"
"strings"
"testing"

"google.golang.org/protobuf/proto"
northdpole marked this conversation as resolved.
Show resolved Hide resolved

"github.com/CycloneDX/cyclonedx-go"
"github.com/stretchr/testify/require"

draconv1 "github.com/ocurity/dracon/api/proto/v1"
"github.com/ocurity/dracon/components/producers"
)

const (
sarifInputPath = "exampleData/results_sarif.sarif"
cyclonedxInputPath = "exampleData/results_cyclonedx.json"
)

func TestRunSarif(t *testing.T) {
workspace, err := os.MkdirTemp("", "dracon")
require.NoError(t, err)

defer require.NoError(t, os.RemoveAll(workspace))

producers.OutFile = filepath.Join(workspace, "out.pb")
input, err := os.ReadFile(sarifInputPath)
require.NoError(t, err)
require.NoError(t, run(input, ""))

_, err = os.Stat(producers.OutFile)
require.NoError(t, err)

in, err := os.ReadFile(producers.OutFile)
require.NoError(t, err)
var wrote draconv1.LaunchToolResponse
err = proto.Unmarshal(in, &wrote)
require.NoError(t, err)
expectedIssues := []*draconv1.Issue{
{
Target: "code/cfngoat/cfngoat.yaml:891-892",
Type: "CKV_SECRET_6",
Title: "Base64 High Entropy String",
Severity: draconv1.Severity_SEVERITY_HIGH,
Description: "MatchedRule: {\"id\":\"CKV_SECRET_6\",\"name\":\"Base64 High Entropy String\",\"shortDescription\":{\"text\":\"Base64 High Entropy String\"},\"fullDescription\":{\"text\":\"Base64 High Entropy String\"},\"defaultConfiguration\":{\"level\":\"error\"},\"help\":{\"text\":\"Base64 High Entropy String\\nResource: c00f1a6e4b20aa64691d50781b810756d6254b8e\"}} \n Message: Base64 High Entropy String",
}, {
Target: "code/cfngoat/.github/workflows/checkov.yaml:1-1",
Type: "CKV2_GHA_1",
Title: "Ensure top-level permissions are not set to write-all",
Severity: draconv1.Severity_SEVERITY_HIGH,
Description: "MatchedRule: {\"id\":\"CKV2_GHA_1\",\"name\":\"Ensure top-level permissions are not set to write-all\",\"shortDescription\":{\"text\":\"Ensure top-level permissions are not set to write-all\"},\"fullDescription\":{\"text\":\"Ensure top-level permissions are not set to write-all\"},\"defaultConfiguration\":{\"level\":\"error\"},\"help\":{\"text\":\"Ensure top-level permissions are not set to write-all\\nResource: on(build)\"}} \n Message: Ensure top-level permissions are not set to write-all",
}, {
Target: "code/cfngoat/.github/workflows/main.yaml:1-1",
Type: "CKV2_GHA_1",
Title: "Ensure top-level permissions are not set to write-all",
Severity: draconv1.Severity_SEVERITY_HIGH,
Description: "MatchedRule: {\"id\":\"CKV2_GHA_1\",\"name\":\"Ensure top-level permissions are not set to write-all\",\"shortDescription\":{\"text\":\"Ensure top-level permissions are not set to write-all\"},\"fullDescription\":{\"text\":\"Ensure top-level permissions are not set to write-all\"},\"defaultConfiguration\":{\"level\":\"error\"},\"help\":{\"text\":\"Ensure top-level permissions are not set to write-all\\nResource: on(build)\"}} \n Message: Ensure top-level permissions are not set to write-all",
},
}

slices.SortFunc(wrote.Issues, func(a, b *draconv1.Issue) int { return strings.Compare(a.Target, b.Target) })
slices.SortFunc(expectedIssues, func(a, b *draconv1.Issue) int { return strings.Compare(a.Target, b.Target) })
require.Equal(t, len(wrote.Issues), len(expectedIssues))
for i, expectedIssue := range expectedIssues {
require.Equal(t, expectedIssue.Title, wrote.Issues[i].Title)
require.Equal(t, expectedIssue.Description, wrote.Issues[i].Description)
require.Equal(t, expectedIssue.Target, wrote.Issues[i].Target)
require.Equal(t, expectedIssue.Severity, wrote.Issues[i].Severity)
}
}

func TestRunCyclonedx(t *testing.T) {
workspace, err := os.MkdirTemp("", "dracon")
require.NoError(t, err)
defer require.NoError(t, os.RemoveAll(workspace))

producers.OutFile = filepath.Join(workspace, "out.pb")
input, err := os.ReadFile(cyclonedxInputPath)
require.NoError(t, err)

target := "pkg:my/awesome/package"
require.NoError(t, run(input, target))

_, err = os.Stat(producers.OutFile)
require.NoError(t, err)

in, err := os.ReadFile(producers.OutFile)
require.NoError(t, err)
var wrote draconv1.LaunchToolResponse
err = proto.Unmarshal(in, &wrote)
require.NoError(t, err)
sbom := string(input)

var expectedBom cyclonedx.BOM
var actualBom cyclonedx.BOM
require.NoError(t, json.Unmarshal([]byte(sbom), &expectedBom))
require.NoError(t, json.Unmarshal([]byte(*wrote.Issues[0].CycloneDXSBOM), &actualBom))

require.Equal(t, 1, len(wrote.Issues))
require.Equal(t, expectedBom, actualBom)
}
58 changes: 58 additions & 0 deletions components/producers/checkov/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: producer-checkov
labels:
v1.dracon.ocurity.com/component: producer
v1.dracon.ocurity.com/test-type: sast
v1.dracon.ocurity.com/language: iac
spec:
description: Analyse IAC source code to look for security issues.
params:
- name: producer-checkov-cyclonedx-target-override
type: string
default: ""
volumes:
- name: scratch
emptyDir: {}
workspaces:
- name: output
description: The workspace containing the source-code to scan.
steps:
- name: run-checkov
image: bridgecrew/checkov:3.2.255
command: [/usr/local/bin/checkov]
args:
- --skip-download
- --directory
- "$(workspaces.output.path)/source-code"
- --output=cyclonedx_json
- --output=sarif
- --output-file-path
- /scratch
- --soft-fail
volumeMounts:
- mountPath: /scratch
name: scratch
- name: produce-issues-sarif
imagePullPolicy: IfNotPresent
image: '{{ default "ghcr.io/ocurity/dracon" .Values.image.registry }}/components/producers/checkov:{{ .Chart.AppVersion }}'
command: ["/app/components/producers/checkov/checkov-parser"]
args:
- "-in=/scratch/results_sarif.sarif"
- "-out=$(workspaces.output.path)/.dracon/producers/checkov-sarif.pb"
volumeMounts:
- mountPath: /scratch
name: scratch
- name: produce-issues-cyclonedx
imagePullPolicy: IfNotPresent
image: '{{ default "ghcr.io/ocurity/dracon" .Values.image.registry }}/components/producers/checkov:{{ .Chart.AppVersion }}'
command: ["/app/components/producers/checkov/checkov-parser"]
args:
- "-in=/scratch/results_cyclonedx.json"
- "-out=$(workspaces.output.path)/.dracon/producers/checkov-cyclonedx.pb"
- "-target=$(producer-checkov-cyclonedx-target-override)"
volumeMounts:
- mountPath: /scratch
name: scratch
2 changes: 1 addition & 1 deletion components/producers/docker-trivy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func handleSarif(inFile []byte) ([]*v1.Issue, error) {
}

func handleCycloneDX(inFile []byte) ([]*v1.Issue, error) {
return cyclonedx.ToDracon(inFile, "json")
return cyclonedx.ToDracon(inFile, "json", "")
}

func parseCombinedOut(results types.CombinedOut) []*v1.Issue {
Expand Down
12 changes: 12 additions & 0 deletions examples/pipelines/checkov-project/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
nameSuffix: -checkov-project
components:
- pkg:helm/dracon-oss-components/base
- pkg:helm/dracon-oss-components/git-clone
- pkg:helm/dracon-oss-components/producer-checkov
- pkg:helm/dracon-oss-components/producer-aggregator
- pkg:helm/dracon-oss-components/enricher-codeowners
- pkg:helm/dracon-oss-components/enricher-aggregator
- pkg:helm/dracon-oss-components/consumer-stdout-json
24 changes: 24 additions & 0 deletions examples/pipelines/checkov-project/pipelinerun.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: dracon-checkov-project-
namespace: dracon
spec:
pipelineRef:
name: dracon-checkov-project
params:
- name: git-clone-url
value: https://github.com/bridgecrewio/cfngoat
- name: producer-checkov-cyclonedx-target-override
value: "pkg:terraform/bridgecrewio/cfngoat"
workspaces:
- name: output
subPath: source-code
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
16 changes: 11 additions & 5 deletions pkg/cyclonedx/cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
)

// ToDracon accepts a cycloneDX bom file and transforms to an array containing a singular v1.Issue.
func ToDracon(inFile []byte, format string) ([]*v1.Issue, error) {
// revive:disable:cognitive-complexity,cyclomatic High complexity score but
func ToDracon(inFile []byte, format, targetOverride string) ([]*v1.Issue, error) {
bom := new(cdx.BOM)
var decoder cdx.BOMDecoder
var issues []*v1.Issue
Expand Down Expand Up @@ -42,10 +43,15 @@ func ToDracon(inFile []byte, format string) ([]*v1.Issue, error) {
}
result := strings.TrimSpace(buf.String())
target := ""
if bom.Metadata.Component.BOMRef != "" {
target = bom.Metadata.Component.BOMRef
} else {
target = bom.Metadata.Component.PackageURL
if bom.Metadata != nil && bom.Metadata.Component != nil {
if bom.Metadata.Component.BOMRef != "" {
target = bom.Metadata.Component.BOMRef
} else {
target = bom.Metadata.Component.PackageURL
}
}
if targetOverride != "" {
target = targetOverride
}

return []*v1.Issue{
Expand Down
Loading