Skip to content

Commit c9eff12

Browse files
feat: Add table_options support for aws_securityhub_findings table (#11955)
#### Summary This PR adds support for `table_options` for the table `aws_securityhub_findings`. This allows users to specify filters before fetching findings from Security Hub. <!--
1 parent cf9cd16 commit c9eff12

File tree

6 files changed

+122
-11
lines changed

6 files changed

+122
-11
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package tableoptions
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
7+
"github.com/aws/aws-sdk-go-v2/aws"
8+
"github.com/aws/aws-sdk-go-v2/service/securityhub"
9+
"github.com/cloudquery/plugin-sdk/v3/caser"
10+
)
11+
12+
type SecurityHubAPIs struct {
13+
GetFindingsOpts []CustomGetFindingsOpts `json:"get_findings,omitempty"`
14+
}
15+
16+
type CustomGetFindingsOpts struct {
17+
securityhub.GetFindingsInput
18+
}
19+
20+
// UnmarshalJSON implements the json.Unmarshaler interface for the CustomGetFindingsOpts type.
21+
// It is the same as default, but allows the use of underscore in the JSON field names.
22+
func (s *CustomGetFindingsOpts) UnmarshalJSON(data []byte) error {
23+
m := map[string]any{}
24+
err := json.Unmarshal(data, &m)
25+
if err != nil {
26+
return err
27+
}
28+
csr := caser.New()
29+
changeCaseForObject(m, csr.ToPascal)
30+
b, _ := json.Marshal(m)
31+
return json.Unmarshal(b, &s.GetFindingsInput)
32+
}
33+
34+
func (s *SecurityHubAPIs) validateGetFindingEvent() error {
35+
for _, opt := range s.GetFindingsOpts {
36+
if aws.ToString(opt.NextToken) != "" {
37+
return errors.New("invalid input: cannot set NextToken in GetFindings")
38+
}
39+
40+
// As per https://docs.aws.amazon.com/securityhub/1.0/APIReference/API_GetFindings.html#API_GetFindings_RequestSyntax
41+
if opt.MaxResults < 1 || opt.MaxResults > 100 {
42+
return errors.New("invalid range: MaxResults must be within range [1-100]")
43+
}
44+
}
45+
return nil
46+
}
47+
48+
func (s *SecurityHubAPIs) setDefaults() {
49+
for i := 0; i < len(s.GetFindingsOpts); i++ {
50+
if s.GetFindingsOpts[i].MaxResults == 0 {
51+
s.GetFindingsOpts[i].MaxResults = 100
52+
}
53+
}
54+
}
55+
56+
func (s *SecurityHubAPIs) Validate() error {
57+
s.setDefaults()
58+
return s.validateGetFindingEvent()
59+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package tableoptions
2+
3+
import (
4+
"testing"
5+
6+
"github.com/cloudquery/plugin-sdk/v3/faker"
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestGetFindings(t *testing.T) {
12+
u := CustomGetFindingsOpts{}
13+
require.NoError(t, faker.FakeObject(&u))
14+
api := SecurityHubAPIs{
15+
GetFindingsOpts: []CustomGetFindingsOpts{u},
16+
}
17+
// Ensure that the validation works as expected
18+
err := api.Validate()
19+
assert.EqualError(t, err, "invalid input: cannot set NextToken in GetFindings")
20+
21+
// Ensure that as soon as the validation passes that there are no unexpected empty or nil fields
22+
api.GetFindingsOpts[0].NextToken = nil
23+
err = api.Validate()
24+
assert.EqualError(t, err, "invalid range: MaxResults must be within range [1-100]")
25+
}

plugins/source/aws/client/tableoptions/table_options.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type TableOptions struct {
1212
AccessAnalyzerFindings *AccessanalyzerFindings `json:"aws_accessanalyzer_analyzer_findings,omitempty"`
1313
Inspector2Findings *Inspector2APIs `json:"aws_inspector2_findings,omitempty"`
1414
CustomCostExplorer *CostExplorerAPIs `json:"aws_alpha_costexplorer_cost_custom,omitempty"`
15+
SecurityHubFindings *SecurityHubAPIs `json:"aws_securityhub_findings,omitempty"`
1516
}
1617

1718
func (t TableOptions) Validate() error {

plugins/source/aws/client/tableoptions/table_options_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
costexplorertypes "github.com/aws/aws-sdk-go-v2/service/costexplorer/types"
1616
"github.com/aws/aws-sdk-go-v2/service/inspector2"
1717
inspector2types "github.com/aws/aws-sdk-go-v2/service/inspector2/types"
18+
"github.com/aws/aws-sdk-go-v2/service/securityhub"
19+
securityhubtypes "github.com/aws/aws-sdk-go-v2/service/securityhub/types"
1820
"github.com/stretchr/testify/require"
1921

2022
"github.com/cloudquery/plugin-sdk/v3/caser"
@@ -108,6 +110,16 @@ func TestTableOptionsUnmarshal(t *testing.T) {
108110
costexplorertypes.TagValues{},
109111
costexplorertypes.GroupDefinition{},
110112
costexplorer.GetCostAndUsageInput{},
113+
securityhub.GetFindingsInput{},
114+
securityhubtypes.AwsSecurityFindingFilters{},
115+
securityhubtypes.StringFilter{},
116+
securityhubtypes.NumberFilter{},
117+
securityhubtypes.DateFilter{},
118+
securityhubtypes.KeywordFilter{},
119+
securityhubtypes.MapFilter{},
120+
securityhubtypes.IpFilter{},
121+
securityhubtypes.BooleanFilter{},
122+
securityhubtypes.SortCriterion{},
111123
)); diff != "" {
112124
t.Fatalf("mismatch between objects after loading from snake case json: %v", diff)
113125
}

plugins/source/aws/resources/services/securityhub/findings.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/aws/aws-sdk-go-v2/service/securityhub"
88
"github.com/aws/aws-sdk-go-v2/service/securityhub/types"
99
"github.com/cloudquery/cloudquery/plugins/source/aws/client"
10+
"github.com/cloudquery/cloudquery/plugins/source/aws/client/tableoptions"
1011
"github.com/cloudquery/plugin-sdk/v3/schema"
1112
"github.com/cloudquery/plugin-sdk/v3/transformers"
1213
)
@@ -44,19 +45,29 @@ This is useful when multi region and account aggregation is enabled.`,
4445

4546
func fetchFindings(ctx context.Context, meta schema.ClientMeta, parent *schema.Resource, res chan<- any) error {
4647
cl := meta.(*client.Client)
47-
svc := cl.Services().Securityhub
48-
config := securityhub.GetFindingsInput{
49-
MaxResults: 100,
48+
var allConfigs []tableoptions.CustomGetFindingsOpts
49+
50+
if cl.Spec.TableOptions.SecurityHubFindings != nil && cl.Spec.TableOptions.SecurityHubFindings.GetFindingsOpts != nil {
51+
allConfigs = cl.Spec.TableOptions.SecurityHubFindings.GetFindingsOpts
52+
} else {
53+
allConfigs = []tableoptions.CustomGetFindingsOpts{{GetFindingsInput: securityhub.GetFindingsInput{MaxResults: 100}}}
5054
}
51-
p := securityhub.NewGetFindingsPaginator(svc, &config)
52-
for p.HasMorePages() {
53-
response, err := p.NextPage(ctx, func(o *securityhub.Options) {
54-
o.Region = cl.Region
55-
})
56-
if err != nil {
57-
return err
55+
56+
svc := cl.Services().Securityhub
57+
58+
var config securityhub.GetFindingsInput
59+
for _, w := range allConfigs {
60+
config = w.GetFindingsInput
61+
p := securityhub.NewGetFindingsPaginator(svc, &config)
62+
for p.HasMorePages() {
63+
response, err := p.NextPage(ctx, func(o *securityhub.Options) {
64+
o.Region = cl.Region
65+
})
66+
if err != nil {
67+
return err
68+
}
69+
res <- response.Findings
5870
}
59-
res <- response.Findings
6071
}
6172
return nil
6273
}

website/pages/docs/plugins/sources/aws/configuration.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ This is the (nested) spec used by the AWS source plugin.
157157
aws_inspector2_findings:
158158
list_findings:
159159
- <[ListFindings](https://docs.aws.amazon.com/inspector/v2/APIReference/API_ListFindings.html)>
160+
aws_securityhub_findings:
161+
get_findings:
162+
- <[GetFindings](https://docs.aws.amazon.com/securityhub/1.0/APIReference/API_GetFindings.html)>
160163
```
161164

162165

0 commit comments

Comments
 (0)