This repository has been archived by the owner on Oct 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Dracon producer for AWS Security Hub findings * add securityhub finding test * lint * tidy deps * lint Co-authored-by: sheslop <[email protected]>
- Loading branch information
Showing
11 changed files
with
566 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
subinclude("//third_party/defs:docker") | ||
|
||
go_binary( | ||
name = "securityhub", | ||
srcs = [ | ||
"main.go", | ||
], | ||
deps = [ | ||
"//api/proto:v1", | ||
"//producers", | ||
"//third_party/go:aws-sdk-go-v2-securityhub", | ||
], | ||
) | ||
|
||
go_test( | ||
name = "securityhub_test", | ||
srcs = [ | ||
"main.go", | ||
"main_test.go", | ||
], | ||
data = glob(["./testcases/*.json"]), | ||
deps = [ | ||
"//api/proto:v1", | ||
"//pkg/putil", | ||
"//producers", | ||
"//third_party/go:aws-sdk-go-v2-securityhub", | ||
"//third_party/go:stretchr_testify", | ||
], | ||
) | ||
|
||
docker_image( | ||
name = "dracon-producer-securityhub", | ||
srcs = [ | ||
":securityhub", | ||
], | ||
base_image = "//build/docker:dracon-base-go", | ||
image = "dracon-producer-securityhub", | ||
visibility = ["//examples/..."], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FROM //build/docker:dracon-base-go | ||
|
||
COPY securityhub /parse | ||
|
||
ENTRYPOINT ["/parse"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package main | ||
|
||
import ( | ||
"log" | ||
|
||
v1 "github.com/thought-machine/dracon/api/proto/v1" | ||
"github.com/thought-machine/dracon/producers" | ||
|
||
securityhub "github.com/aws/aws-sdk-go-v2/service/securityhub/types" | ||
) | ||
|
||
func main() { | ||
if err := producers.ParseFlags(); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
inFile, err := producers.ReadInFile() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
var results securityHubFindings | ||
if err := producers.ParseJSON(inFile, &results); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
issues := parseIssues(&results) | ||
|
||
if err := producers.WriteDraconOut("securityhub", issues); err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
type securityHubFindings struct { | ||
Findings []securityhub.AwsSecurityFinding `json:"Findings"` | ||
} | ||
|
||
func parseIssues(shf *securityHubFindings) []*v1.Issue { | ||
severityMap := map[securityhub.SeverityLabel]v1.Severity{ | ||
securityhub.SeverityLabelInformational: v1.Severity_SEVERITY_INFO, | ||
securityhub.SeverityLabelLow: v1.Severity_SEVERITY_LOW, | ||
securityhub.SeverityLabelMedium: v1.Severity_SEVERITY_MEDIUM, | ||
securityhub.SeverityLabelHigh: v1.Severity_SEVERITY_HIGH, | ||
securityhub.SeverityLabelCritical: v1.Severity_SEVERITY_CRITICAL, | ||
} | ||
|
||
issues := make([]*v1.Issue, len(shf.Findings)) | ||
for i, r := range shf.Findings { | ||
issue := &v1.Issue{Confidence: v1.Confidence_CONFIDENCE_MEDIUM} | ||
|
||
if r.Title != nil { | ||
issue.Title = *r.Title | ||
} | ||
|
||
if r.Description != nil { | ||
issue.Description = *r.Description | ||
} | ||
|
||
if r.SourceUrl != nil { | ||
issue.Source = *r.SourceUrl | ||
} | ||
|
||
switch { | ||
case r.ProductName != nil && *r.ProductName == "Inspector" && len(r.Resources) > 0: | ||
if r.Resources[0].Details != nil && r.Resources[0].Details.AwsEc2Instance != nil { | ||
issue.Target = *r.Resources[0].Details.AwsEc2Instance.ImageId | ||
} | ||
case len(r.Resources) > 0: | ||
issue.Target = *r.Resources[0].Id | ||
case r.AwsAccountId != nil: | ||
issue.Target = *r.AwsAccountId | ||
} | ||
|
||
switch { | ||
case len(r.Types) > 0: | ||
issue.Type = r.Types[0] | ||
case r.ProductName != nil: | ||
issue.Type = *r.ProductName | ||
} | ||
|
||
switch { | ||
case r.Severity != nil: | ||
issue.Severity = severityMap[r.Severity.Label] | ||
case r.FindingProviderFields != nil && r.FindingProviderFields.Severity != nil: | ||
issue.Severity = severityMap[r.FindingProviderFields.Severity.Label] | ||
} | ||
|
||
if len(r.Vulnerabilities) > 0 { | ||
issue.Cve = *r.Vulnerabilities[0].Id | ||
|
||
highestCvss := 0.0 | ||
for _, cvss := range r.Vulnerabilities[0].Cvss { | ||
if cvss.BaseScore > highestCvss { | ||
highestCvss = cvss.BaseScore | ||
} | ||
} | ||
issue.Cvss = highestCvss | ||
} | ||
|
||
issues[i] = issue | ||
} | ||
|
||
return issues | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
"os/exec" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
v1 "github.com/thought-machine/dracon/api/proto/v1" | ||
"github.com/thought-machine/dracon/pkg/putil" | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
for i, arg := range os.Args { | ||
if arg == "--test_execution" { | ||
os.Args = append(os.Args[:i], os.Args[i+1:]...) | ||
main() | ||
return | ||
} | ||
} | ||
|
||
m.Run() | ||
} | ||
|
||
func TestMainFn(t *testing.T) { | ||
type testCase struct { | ||
inputFileName string | ||
expErr bool | ||
expStdout string | ||
expIssues []*v1.Issue | ||
} | ||
|
||
testCases := []testCase{ | ||
{ | ||
inputFileName: "no_findings.json", | ||
expErr: false, | ||
expStdout: `wrote 0 issues from to ./producers/securityhub/out.pb`, | ||
}, | ||
{ | ||
inputFileName: "empty_finding.json", | ||
expErr: false, | ||
expStdout: `wrote 1 issues from to ./producers/securityhub/out.pb`, | ||
expIssues: []*v1.Issue{ | ||
{ | ||
Target: "", | ||
Type: "", | ||
Title: "", | ||
Severity: 0, | ||
Cvss: 0.0, | ||
Confidence: 2, | ||
Description: "", | ||
Source: "unknown", | ||
Cve: "", | ||
}, | ||
}, | ||
}, | ||
{ | ||
inputFileName: "securityhub_finding.json", | ||
expErr: false, | ||
expStdout: `wrote 1 issues from to ./producers/securityhub/out.pb`, | ||
expIssues: []*v1.Issue{ | ||
{ | ||
Target: "arn:aws:ec2:eu-west-2:123456789:security-group/sg-01ef4a31dbea6f188cfbf", | ||
Type: "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark", | ||
Title: "4.3 Ensure the default security group of every VPC restricts all traffic", | ||
Severity: 3, | ||
Cvss: 0, | ||
Confidence: 2, | ||
Description: "A VPC comes with a default security group whose initial settings deny all inbound traffic, allow all outbound traffic, and allow all traffic between instances assigned to the security group. If you don't specify a security group when you launch an instance, the instance is automatically assigned to this default security group. It is recommended that the default security group restrict all traffic.", | ||
Source: "unknown", | ||
Cve: "", | ||
}, | ||
}, | ||
}, | ||
{ | ||
inputFileName: "inspector_finding.json", | ||
expErr: false, | ||
expStdout: `wrote 1 issues from to ./producers/securityhub/out.pb`, | ||
expIssues: []*v1.Issue{ | ||
{ | ||
Target: "ami-053269b2b68617f7c", | ||
Type: "Software and Configuration Checks/Vulnerabilities/CVE", | ||
Title: "CVE-2022-25315 - expat", | ||
Severity: 4, | ||
Cvss: 9.8, | ||
Confidence: 2, | ||
Description: "An integer overflow was found in expat. The issue occurs in storeRawNames() by abusing the m_buffer expansion logic to allow allocations very close to INT_MAX and out-of-bounds heap writes. This flaw can cause a denial of service or potentially arbitrary code execution.", | ||
Source: "unknown", | ||
Cve: "CVE-2022-25315", | ||
}, | ||
}, | ||
}, | ||
{ | ||
inputFileName: "bad-input", | ||
expErr: true, | ||
expStdout: `open ./producers/securityhub/testcases/bad-input: no such file or directory`, | ||
}, | ||
{ | ||
inputFileName: "unparseable.json", | ||
expErr: true, | ||
expStdout: `invalid character '.' looking for beginning of value`, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.inputFileName, func(t *testing.T) { | ||
cmd := runProducerMain(`-in=./producers/securityhub/testcases/`+tc.inputFileName, `-out=./producers/securityhub/out.pb`) | ||
stdoutStderr, err := cmd.CombinedOutput() | ||
|
||
if err != nil && !tc.expErr { | ||
assert.Fail(t, "unexpected err executing producer with input file '%s'. err: '%s", tc.inputFileName, err) | ||
} else if tc.expErr { | ||
assert.Error(t, err) | ||
} else { | ||
assert.Nil(t, err) | ||
} | ||
|
||
expMsg := "expected output from '%s' to end with '%s'. got: '%s'" | ||
stdOut := strings.TrimSpace(string(stdoutStderr)) | ||
suffixCheck := strings.HasSuffix(stdOut, tc.expStdout) | ||
|
||
assert.True(t, suffixCheck, expMsg, tc.inputFileName, tc.expStdout, stdOut) | ||
|
||
ltr, err := putil.LoadToolResponse("./producers/securityhub") | ||
assert.NoError(t, err) | ||
|
||
if tc.expIssues != nil { | ||
assert.EqualValues(t, ltr[0].Issues, tc.expIssues) | ||
} else if !tc.expErr { | ||
assert.Empty(t, ltr[0].Issues) | ||
} | ||
|
||
os.Remove("./producers/securityhub/out.pb") | ||
}) | ||
} | ||
} | ||
|
||
func runProducerMain(args ...string) *exec.Cmd { | ||
return exec.Command(os.Args[0], append([]string{"--test_execution"}, args...)...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"Findings": [{}] | ||
} |
Oops, something went wrong.