Skip to content

Commit 728d659

Browse files
authored
Merge pull request #47845 from hashicorp/td-lister-describe-with-callback-yield-err
Technical debt: Add and fix semgrep rule `list-iterator-calls-stopped-yield`
2 parents 69c0695 + db9d079 commit 728d659

6 files changed

Lines changed: 46 additions & 14 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Copyright IBM Corp. 2014, 2026
2+
# "SPDX-License-Identifier: MPL-2.0"
3+
4+
rules:
5+
- id: list-iterator-calls-stopped-yield
6+
message: >-
7+
List iterator function uses callback-based paginated finder (lastPage pattern)
8+
without ensuring that a stopped yield isn't called on error.
9+
languages: [go]
10+
severity: WARNING
11+
patterns:
12+
- pattern: |
13+
$ERR := $PAGES_FN($...PARGS, func($PAGE $PTYPE, lastPage bool) bool {
14+
$...BODY
15+
})
16+
- pattern-inside: |
17+
return func(yield func($T, error) bool) {
18+
$...ALL
19+
}
20+
- pattern-not-inside: |
21+
var stopped bool
22+
$...REST

internal/service/apigatewayv2/api_list.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,19 +95,21 @@ type listAPIModel struct {
9595

9696
func listAPIs(ctx context.Context, conn *apigatewayv2.Client, input *apigatewayv2.GetApisInput) iter.Seq2[awstypes.Api, error] {
9797
return func(yield func(awstypes.Api, error) bool) {
98+
var stopped bool
9899
err := getAPIsPages(ctx, conn, input, func(page *apigatewayv2.GetApisOutput, lastPage bool) bool {
99100
if page == nil {
100101
return !lastPage
101102
}
102103
for _, item := range page.Items {
103104
if !yield(item, nil) {
105+
stopped = true
104106
return false
105107
}
106108
}
107109
return !lastPage
108110
})
109-
if err != nil {
110-
yield(awstypes.Api{}, fmt.Errorf("listing API Gateway V2 API resources: %w", err))
111+
if !stopped && err != nil {
112+
yield(inttypes.Zero[awstypes.Api](), fmt.Errorf("listing API Gateway V2 APIs: %w", err))
111113
}
112114
}
113115
}

internal/service/apigatewayv2/route_list.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,19 +115,21 @@ type listRouteModel struct {
115115

116116
func listRoutes(ctx context.Context, conn *apigatewayv2.Client, input *apigatewayv2.GetRoutesInput) iter.Seq2[awstypes.Route, error] {
117117
return func(yield func(awstypes.Route, error) bool) {
118+
var stopped bool
118119
err := getRoutesPages(ctx, conn, input, func(page *apigatewayv2.GetRoutesOutput, lastPage bool) bool {
119120
if page == nil {
120121
return !lastPage
121122
}
122123
for _, item := range page.Items {
123124
if !yield(item, nil) {
125+
stopped = true
124126
return false
125127
}
126128
}
127129
return !lastPage
128130
})
129-
if err != nil {
130-
yield(awstypes.Route{}, fmt.Errorf("listing API Gateway V2 Route resources: %w", err))
131+
if !stopped && err != nil {
132+
yield(inttypes.Zero[awstypes.Route](), fmt.Errorf("listing API Gateway V2 Routes: %w", err))
131133
}
132134
}
133135
}

internal/service/ec2/find.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5365,21 +5365,23 @@ func findTransitGatewayMeteringPolicies(ctx context.Context, conn *ec2.Client, i
53655365

53665366
func listTransitGatewayMeteringPolicies(ctx context.Context, conn *ec2.Client, input *ec2.DescribeTransitGatewayMeteringPoliciesInput) iter.Seq2[awstypes.TransitGatewayMeteringPolicy, error] {
53675367
return func(yield func(awstypes.TransitGatewayMeteringPolicy, error) bool) {
5368+
var stopped bool
53685369
err := describeTransitGatewayMeteringPoliciesPages(ctx, conn, input, func(page *ec2.DescribeTransitGatewayMeteringPoliciesOutput, lastPage bool) bool {
53695370
if page == nil {
53705371
return !lastPage
53715372
}
53725373

53735374
for _, v := range page.TransitGatewayMeteringPolicies {
53745375
if !yield(v, nil) {
5376+
stopped = true
53755377
return false
53765378
}
53775379
}
53785380

53795381
return !lastPage
53805382
})
53815383

5382-
if err != nil {
5384+
if !stopped && err != nil {
53835385
yield(inttypes.Zero[awstypes.TransitGatewayMeteringPolicy](), fmt.Errorf("listing EC2 Transit Gateway Metering Policies: %w", err))
53845386
return
53855387
}

internal/service/events/rule_list.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,21 @@ type listRuleModel struct {
100100

101101
func listRules(ctx context.Context, conn *eventbridge.Client, input *eventbridge.ListRulesInput) iter.Seq2[awstypes.Rule, error] {
102102
return func(yield func(awstypes.Rule, error) bool) {
103+
var stopped bool
103104
err := listRulesPages(ctx, conn, input, func(page *eventbridge.ListRulesOutput, lastPage bool) bool {
105+
if page == nil {
106+
return !lastPage
107+
}
104108
for _, item := range page.Rules {
105109
if !yield(item, nil) {
106-
return !lastPage
110+
stopped = true
111+
return false
107112
}
108113
}
109114
return !lastPage
110115
})
111-
if err != nil {
112-
yield(awstypes.Rule{}, fmt.Errorf("listing EventBridge Rule resources: %w", err))
116+
if !stopped && err != nil {
117+
yield(inttypes.Zero[awstypes.Rule](), fmt.Errorf("listing EventBridge Rules: %w", err))
113118
return
114119
}
115120
}

internal/service/events/target_list.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,22 +115,21 @@ type listTargetModel struct {
115115

116116
func listTargets(ctx context.Context, conn *eventbridge.Client, input *eventbridge.ListTargetsByRuleInput) iter.Seq2[awstypes.Target, error] {
117117
return func(yield func(awstypes.Target, error) bool) {
118+
var stopped bool
118119
err := listTargetsByRulePages(ctx, conn, input, func(page *eventbridge.ListTargetsByRuleOutput, lastPage bool) bool {
119120
if page == nil {
120121
return !lastPage
121122
}
122-
123123
for _, item := range page.Targets {
124124
if !yield(item, nil) {
125-
return !lastPage
125+
stopped = true
126+
return false
126127
}
127128
}
128-
129129
return !lastPage
130130
})
131-
132-
if err != nil {
133-
yield(awstypes.Target{}, fmt.Errorf("listing EventBridge Target resources: %w", err))
131+
if !stopped && err != nil {
132+
yield(inttypes.Zero[awstypes.Target](), fmt.Errorf("listing EventBridge Targets: %w", err))
134133
return
135134
}
136135
}

0 commit comments

Comments
 (0)