Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ test-*.yaml
/cloudlist
/terraform/
azure-readonly-config.yaml
docs/superpowers/
17 changes: 17 additions & 0 deletions pkg/providers/aws/alb.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func (ep *elbV2Provider) GetResource(ctx context.Context) (*schema.Resources, er
list := schema.NewResources()
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error

for _, region := range ep.regions.Regions {
albClients, ec2Clients := ep.getElbV2AndEc2Clients(region.RegionName)
Expand All @@ -41,6 +42,13 @@ func (ep *elbV2Provider) GetResource(ctx context.Context) (*schema.Resources, er

go func(albClient *elbv2.ELBV2, ec2Client *ec2.EC2) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
mu.Lock()
errs = append(errs, fmt.Errorf("panic in alb provider: %v", r))
mu.Unlock()
}
}()

if resources, err := ep.listELBV2Resources(albClient, ec2Client); err == nil {
mu.Lock()
Expand All @@ -51,6 +59,9 @@ func (ep *elbV2Provider) GetResource(ctx context.Context) (*schema.Resources, er
}
}
wg.Wait()
if len(errs) > 0 && len(list.Items) == 0 {
return nil, fmt.Errorf("alb: all workers failed: %v", errs)
}
return list, nil
}

Expand All @@ -63,6 +74,9 @@ func (ep *elbV2Provider) listELBV2Resources(albClient *elbv2.ELBV2, ec2Client *e
}

for _, lb := range loadBalancers {
if lb.DNSName == nil || lb.LoadBalancerName == nil {
continue
}
albDNS := *lb.DNSName

// Extract metadata for this load balancer
Expand Down Expand Up @@ -101,6 +115,9 @@ func (ep *elbV2Provider) listELBV2Resources(albClient *elbv2.ELBV2, ec2Client *e
}

for _, target := range targets.TargetHealthDescriptions {
if target.Target == nil || target.Target.Id == nil {
continue
}
instanceID := *target.Target.Id
instanceOutput, err := ec2Client.DescribeInstances(&ec2.DescribeInstancesInput{
InstanceIds: []*string{&instanceID},
Expand Down
109 changes: 109 additions & 0 deletions pkg/providers/aws/alb_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package aws

import (
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elbv2"
"github.com/projectdiscovery/cloudlist/pkg/schema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// processLoadBalancersForTest mirrors the nil-safe loop in listELBV2Resources
// so we can exercise the guard logic without a live AWS client.
func processLoadBalancersForTest(lbs []*elbv2.LoadBalancer) *schema.Resources {
list := schema.NewResources()
for _, lb := range lbs {
if lb.DNSName == nil || lb.LoadBalancerName == nil {
continue
}
list.Append(&schema.Resource{
Provider: "aws",
ID: *lb.LoadBalancerName,
DNSName: *lb.DNSName,
Public: true,
Service: "alb",
})
}
return list
}

// processTargetsForTest mirrors the nil-safe target loop in listELBV2Resources.
func processTargetsForTest(targets []*elbv2.TargetHealthDescription) []string {
var ids []string
for _, target := range targets {
if target.Target == nil || target.Target.Id == nil {
continue
}
ids = append(ids, *target.Target.Id)
}
return ids
}

func TestListELBV2Resources_NilDNSName(t *testing.T) {
t.Parallel()

lbs := []*elbv2.LoadBalancer{
{
DNSName: nil,
LoadBalancerName: aws.String("internal-lb"),
LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/internal-lb/abc"),
},
{
DNSName: aws.String(""),
LoadBalancerName: nil,
},
}

require.NotPanics(t, func() {
resources := processLoadBalancersForTest(lbs)
assert.Equal(t, 0, len(resources.Items), "nil DNSName or LoadBalancerName LBs must be skipped")
})
}

func TestListELBV2Resources_ValidLB(t *testing.T) {
t.Parallel()

lbs := []*elbv2.LoadBalancer{
{
DNSName: aws.String("my-lb-1234567890.us-east-1.elb.amazonaws.com"),
LoadBalancerName: aws.String("my-lb"),
LoadBalancerArn: aws.String("arn:aws:elasticloadbalancing:us-east-1:123456789:loadbalancer/app/my-lb/abc"),
},
{
DNSName: nil,
LoadBalancerName: aws.String("broken-lb"),
},
}

resources := processLoadBalancersForTest(lbs)
require.Equal(t, 1, len(resources.Items))
assert.Equal(t, "my-lb-1234567890.us-east-1.elb.amazonaws.com", resources.Items[0].DNSName)
assert.Equal(t, "my-lb", resources.Items[0].ID)
}

func TestListELBV2Resources_NilTargetId(t *testing.T) {
t.Parallel()

targets := []*elbv2.TargetHealthDescription{
{
Target: nil,
},
{
Target: &elbv2.TargetDescription{
Id: nil,
},
},
{
Target: &elbv2.TargetDescription{
Id: aws.String("i-0abc123def456"),
},
},
}

require.NotPanics(t, func() {
ids := processTargetsForTest(targets)
assert.Equal(t, []string{"i-0abc123def456"}, ids)
})
}
14 changes: 14 additions & 0 deletions pkg/providers/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,11 @@ type result struct {
type getResourcesFunc func(context.Context) (*schema.Resources, error)

func worker(ctx context.Context, fn getResourcesFunc, ch chan<- result) {
defer func() {
if r := recover(); r != nil {
ch <- result{resources: nil, err: fmt.Errorf("panic in provider worker: %v", r)}
}
}()
resources, err := fn(ctx)
ch <- result{resources, err}
}
Expand Down Expand Up @@ -497,17 +502,26 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) {
assignWorker(cloudfrontProvider.GetResource)
}


go func() {
workersWaitGroup.Wait()
close(results)
}()

var errs []error
successfulWorkers := 0
for result := range results {
if result.err != nil {
gologger.Warning().Msgf("provider worker error: %v", result.err)
errs = append(errs, result.err)
continue
}
successfulWorkers++
finalResources.Merge(result.resources)
}
if len(errs) > 0 && successfulWorkers == 0 {
return finalResources, fmt.Errorf("all provider workers failed: %v", errs)
}
return finalResources, nil
}

Expand Down
11 changes: 11 additions & 0 deletions pkg/providers/aws/cloudfront.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,20 @@ func (cp *cloudfrontProvider) GetResource(ctx context.Context) (*schema.Resource
list := schema.NewResources()
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error

for _, client := range cp.getCloudfrontClients() {
wg.Add(1)

go func(cloudfrontClient *cloudfront.CloudFront) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
mu.Lock()
errs = append(errs, fmt.Errorf("panic in cloudfront provider: %v", r))
mu.Unlock()
}
}()

if resources, err := cp.listCloudFrontResources(cloudfrontClient); err == nil {
mu.Lock()
Expand All @@ -46,6 +54,9 @@ func (cp *cloudfrontProvider) GetResource(ctx context.Context) (*schema.Resource
}(client)
}
wg.Wait()
if len(errs) > 0 && len(list.Items) == 0 {
return nil, fmt.Errorf("cloudfront: all workers failed: %v", errs)
}
return list, nil
}

Expand Down
11 changes: 11 additions & 0 deletions pkg/providers/aws/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func (ep *ecsProvider) GetResource(ctx context.Context) (*schema.Resources, erro
list := schema.NewResources()
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error

for _, region := range ep.regions.Regions {
ecsClients, ec2Clients := ep.getEcsAndEc2Clients(region.RegionName)
Expand All @@ -41,6 +42,13 @@ func (ep *ecsProvider) GetResource(ctx context.Context) (*schema.Resources, erro

go func(ecsClient *ecs.ECS, ec2Client *ec2.EC2) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
mu.Lock()
errs = append(errs, fmt.Errorf("panic in ecs provider: %v", r))
mu.Unlock()
}
}()
if resources, err := ep.listECSResources(ecsClient, ec2Client); err == nil {
mu.Lock()
list.Merge(resources)
Expand All @@ -50,6 +58,9 @@ func (ep *ecsProvider) GetResource(ctx context.Context) (*schema.Resources, erro
}
}
wg.Wait()
if len(errs) > 0 && len(list.Items) == 0 {
return nil, fmt.Errorf("ecs: all workers failed: %v", errs)
}
return list, nil
}

Expand Down
11 changes: 11 additions & 0 deletions pkg/providers/aws/eks.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,20 @@ func (ep *eksProvider) GetResource(ctx context.Context) (*schema.Resources, erro
list := schema.NewResources()
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error
for _, region := range ep.regions.Regions {
for _, eksClient := range ep.getEksClients(region.RegionName) {
wg.Add(1)

go func(client *eks.EKS) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
mu.Lock()
errs = append(errs, fmt.Errorf("panic in eks provider: %v", r))
mu.Unlock()
}
}()
if resources, err := ep.listEKSResources(client); err == nil {
mu.Lock()
list.Merge(resources)
Expand All @@ -54,6 +62,9 @@ func (ep *eksProvider) GetResource(ctx context.Context) (*schema.Resources, erro
}
}
wg.Wait()
if len(errs) > 0 && len(list.Items) == 0 {
return nil, fmt.Errorf("eks: all workers failed: %v", errs)
}
return list, nil
}

Expand Down
19 changes: 18 additions & 1 deletion pkg/providers/aws/elb.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func (ep *elbProvider) GetResource(ctx context.Context) (*schema.Resources, erro
list := schema.NewResources()
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error

for _, region := range ep.regions.Regions {
elbClients, ec2Clients := ep.getElbAndEc2Clients(region.RegionName)
Expand All @@ -41,6 +42,13 @@ func (ep *elbProvider) GetResource(ctx context.Context) (*schema.Resources, erro

go func(elbClient *elb.ELB, ec2Client *ec2.EC2) {
defer wg.Done()
defer func() {
if r := recover(); r != nil {
mu.Lock()
errs = append(errs, fmt.Errorf("panic in elb provider: %v", r))
mu.Unlock()
}
}()

if resources, err := ep.listELBResources(elbClient, ec2Client); err == nil {
mu.Lock()
Expand All @@ -51,6 +59,9 @@ func (ep *elbProvider) GetResource(ctx context.Context) (*schema.Resources, erro
}
}
wg.Wait()
if len(errs) > 0 && len(list.Items) == 0 {
return nil, fmt.Errorf("elb: all workers failed: %v", errs)
}
return list, nil
}

Expand All @@ -63,6 +74,9 @@ func (ep *elbProvider) listELBResources(elbClient *elb.ELB, ec2Client *ec2.EC2)
}

for _, lb := range loadBalancerDescriptions {
if lb.DNSName == nil || lb.LoadBalancerName == nil {
continue
}
elbDNS := *lb.DNSName

// Extract metadata for this load balancer
Expand All @@ -81,11 +95,14 @@ func (ep *elbProvider) listELBResources(elbClient *elb.ELB, ec2Client *ec2.EC2)
}
list.Append(resource)

if ep.elbClient == nil {
if ec2Client == nil {
continue
}
// Describe Instances for the Load Balancer
for _, instance := range lb.Instances {
if instance.InstanceId == nil {
continue
}
instanceID := *instance.InstanceId
instanceOutput, err := ec2Client.DescribeInstances(&ec2.DescribeInstancesInput{
InstanceIds: []*string{&instanceID},
Expand Down
Loading
Loading