Skip to content

Commit 29dade6

Browse files
tas50claude
andcommitted
🐛 Fix error handling, perf, and logic issues in Security Hub resources
Fix duplicate arn/productArn on findings (arn was incorrectly set to ProductArn), swallowed error in insight filters, by-value copy of large finding struct in hot loop, off-by-one in 1000-finding cap, and misleading standard name (was raw ARN). Replace custom strPtr/int32Ptr with sdk aws.String/aws.Int32. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent de936f1 commit 29dade6

File tree

4 files changed

+32
-32
lines changed

4 files changed

+32
-32
lines changed

providers/aws/resources/aws.lr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3087,8 +3087,6 @@ private aws.securityhub.standardControl @defaults("controlId title controlStatus
30873087
private aws.securityhub.finding @defaults("title severity workflowStatus") {
30883088
// Finding identifier
30893089
id string
3090-
// Finding ARN
3091-
arn string
30923090
// Finding title
30933091
title string
30943092
// Finding description

providers/aws/resources/aws.lr.go

Lines changed: 0 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

providers/aws/resources/aws.lr.versions

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4377,7 +4377,6 @@ aws.securityhub.automationRule.ruleStatus 13.12.1
43774377
aws.securityhub.automationRule.updatedAt 13.12.1
43784378
aws.securityhub.finding 13.12.1
43794379
aws.securityhub.finding.accountId 13.12.1
4380-
aws.securityhub.finding.arn 13.12.1
43814380
aws.securityhub.finding.complianceStatus 13.12.1
43824381
aws.securityhub.finding.createdAt 13.12.1
43834382
aws.securityhub.finding.description 13.12.1

providers/aws/resources/aws_securityhub.go

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import (
77
"context"
88
"errors"
99
"fmt"
10+
"strings"
1011

12+
"github.com/aws/aws-sdk-go-v2/aws"
1113
"github.com/aws/aws-sdk-go-v2/service/securityhub"
1214
"github.com/aws/aws-sdk-go-v2/service/securityhub/types"
1315
"github.com/rs/zerolog/log"
@@ -132,8 +134,7 @@ func (a *mqlAwsSecurityhubHub) standardSubscriptions() ([]any, error) {
132134
return nil, err
133135
}
134136
for _, std := range page.StandardsSubscriptions {
135-
// Extract a readable name from the standard ARN
136-
name := convert.ToValue(std.StandardsArn)
137+
name := standardNameFromArn(convert.ToValue(std.StandardsArn))
137138

138139
mqlStd, err := CreateResource(a.MqlRuntime, "aws.securityhub.standardSubscription",
139140
map[string]*llx.RawData{
@@ -219,17 +220,17 @@ func (a *mqlAwsSecurityhubHub) findings() ([]any, error) {
219220
RecordState: []types.StringFilter{
220221
{
221222
Comparison: types.StringFilterComparisonEquals,
222-
Value: strPtr("ACTIVE"),
223+
Value: aws.String("ACTIVE"),
223224
},
224225
},
225226
WorkflowStatus: []types.StringFilter{
226227
{
227228
Comparison: types.StringFilterComparisonNotEquals,
228-
Value: strPtr("SUPPRESSED"),
229+
Value: aws.String("SUPPRESSED"),
229230
},
230231
},
231232
},
232-
MaxResults: int32Ptr(100),
233+
MaxResults: aws.Int32(100),
233234
})
234235

235236
// Limit to 1000 findings to avoid unbounded API calls
@@ -242,8 +243,11 @@ func (a *mqlAwsSecurityhubHub) findings() ([]any, error) {
242243
}
243244
return nil, err
244245
}
245-
for _, finding := range page.Findings {
246-
mqlFinding, err := newMqlSecurityHubFinding(a.MqlRuntime, finding, region)
246+
for i := range page.Findings {
247+
if len(res) >= maxFindings {
248+
break
249+
}
250+
mqlFinding, err := newMqlSecurityHubFinding(a.MqlRuntime, &page.Findings[i], region)
247251
if err != nil {
248252
return nil, err
249253
}
@@ -253,7 +257,7 @@ func (a *mqlAwsSecurityhubHub) findings() ([]any, error) {
253257
return res, nil
254258
}
255259

256-
func newMqlSecurityHubFinding(runtime *plugin.Runtime, finding types.AwsSecurityFinding, region string) (*mqlAwsSecurityhubFinding, error) {
260+
func newMqlSecurityHubFinding(runtime *plugin.Runtime, finding *types.AwsSecurityFinding, region string) (*mqlAwsSecurityhubFinding, error) {
257261
var severity string
258262
var severityScore float64
259263
if finding.Severity != nil {
@@ -273,6 +277,8 @@ func newMqlSecurityHubFinding(runtime *plugin.Runtime, finding types.AwsSecurity
273277
workflowStatus = string(finding.Workflow.Status)
274278
}
275279

280+
// A finding can reference multiple resources, but we expose only the first
281+
// (primary) one. Most findings have exactly one resource.
276282
var resourceType, resourceId, resourceRegion string
277283
if len(finding.Resources) > 0 {
278284
resourceType = convert.ToValue(finding.Resources[0].Type)
@@ -295,7 +301,6 @@ func newMqlSecurityHubFinding(runtime *plugin.Runtime, finding types.AwsSecurity
295301
map[string]*llx.RawData{
296302
"__id": llx.StringData(fmt.Sprintf("securityhub/finding/%s/%s", region, convert.ToValue(finding.Id))),
297303
"id": llx.StringDataPtr(finding.Id),
298-
"arn": llx.StringDataPtr(finding.ProductArn),
299304
"title": llx.StringDataPtr(finding.Title),
300305
"description": llx.StringDataPtr(finding.Description),
301306
"severity": llx.StringData(severity),
@@ -396,7 +401,10 @@ func (a *mqlAwsSecurityhubHub) insights() ([]any, error) {
396401
return nil, err
397402
}
398403
for _, insight := range page.Insights {
399-
filters, _ := convert.JsonToDict(insight.Filters)
404+
filters, err := convert.JsonToDict(insight.Filters)
405+
if err != nil {
406+
return nil, err
407+
}
400408

401409
mqlInsight, err := CreateResource(a.MqlRuntime, "aws.securityhub.insight",
402410
map[string]*llx.RawData{
@@ -466,11 +474,18 @@ func (a *mqlAwsSecurityhubInsightResult) id() (string, error) {
466474
return a.__id, nil
467475
}
468476

469-
// Helper functions
470-
func strPtr(s string) *string {
471-
return &s
472-
}
473-
474-
func int32Ptr(i int32) *int32 {
475-
return &i
477+
// standardNameFromArn extracts a human-readable name from a Security Hub standard ARN.
478+
// e.g. "arn:aws:securityhub:::standards/aws-foundational-security-best-practices/v/1.0.0"
479+
// becomes "aws-foundational-security-best-practices".
480+
func standardNameFromArn(arn string) string {
481+
const prefix = "standards/"
482+
idx := strings.Index(arn, prefix)
483+
if idx == -1 {
484+
return arn
485+
}
486+
name := arn[idx+len(prefix):]
487+
if slash := strings.Index(name, "/"); slash != -1 {
488+
name = name[:slash]
489+
}
490+
return name
476491
}

0 commit comments

Comments
 (0)