Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions PROVIDERS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Providers

## Service filtering

Every provider enumerates all of its supported services by default. Two optional, additive keys narrow that set (also available on the CLI as `-s`/`--service` and `-es`/`--exclude-service`):

- `services` (list): allowlist. Only the listed services are enumerated. Unknown values are ignored.
- `exclude_services` (list): blocklist applied after `services` (or after the default-all set when `services` is omitted). Unknown values are ignored.

```yaml
- provider: gcp
exclude_services:
- cloud-function
project_ids:
- my-project
```

### Amazon Web Services (AWS)

Amazon Web Services can be integrated by using the following configuration block.
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,13 @@ CONFIGURATION:
-pc, -provider-config string provider config file (default "$HOME/.config/cloudlist/provider-config.yaml")

FILTERS:
-p, -provider value display results for given providers (comma-separated) (default linode,fastly,heroku,terraform,digitalocean,consul,cloudflare,hetzner,nomad,do,scw,openstack,alibaba,aws,gcp,namecheap,kubernetes,azure, custom)
-id string[] display results for given ids (comma-separated)
-host display only hostnames in results
-ip display only ips in results
-s, -service value query and display results from given service (comma-separated)) (default cloudfront,gke,domain,compute,ec2,instance,cloud-function,app,eks,custom,consul,droplet,vm,ecs,fastly,alb,s3,lambda,elb,cloud-run,route53,publicip,dns,service,nomad,lightsail,ingress,apigateway)
-ep, -exclude-private exclude private ips in cli output
-p, -provider value display results for given providers (comma-separated) (default linode,fastly,heroku,terraform,digitalocean,consul,cloudflare,hetzner,nomad,do,scw,openstack,alibaba,aws,gcp,namecheap,kubernetes,azure, custom)
-id string[] display results for given ids (comma-separated)
-host display only hostnames in results
-ip display only ips in results
-s, -service value query and display results from given service (comma-separated) (default cloudfront,gke,domain,compute,ec2,instance,cloud-function,app,eks,custom,consul,droplet,vm,ecs,fastly,alb,s3,lambda,elb,cloud-run,route53,publicip,dns,service,nomad,lightsail,ingress,apigateway)
-es, -exclude-service value services to skip for a provider (comma-separated)
-ep, -exclude-private exclude private ips in cli output

UPDATE:
-up, -update update cloudlist to latest version
Expand Down
10 changes: 6 additions & 4 deletions internal/runner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ type Options struct {
Version bool // Version returns the version of the tool.
Verbose bool // Verbose prints verbose output.
Hosts bool // Hosts specifies to fetch only DNS Names
IPAddress bool // IPAddress specifes to fetch only IP Addresses
IPAddress bool // IPAddress specifies to fetch only IP Addresses
Config string // Config is the location of the config file.
Output string // Output is the file to write found results too.
ExcludePrivate bool // ExcludePrivate excludes private IPs from results
Providers goflags.StringSlice // Providers specifies what providers to fetch assets for.
Id goflags.StringSlice // Id specifies what id's to fetch assets for.
Services goflags.StringSlice // Services specifies what services to fetch assets for a provider.
ExcludeServices goflags.StringSlice // ExcludeServices specifies what services to skip for a provider.
ExtendedMetadata bool // ExtendedMetadata enables extended metadata for providers.
ProviderConfig string // ProviderConfig is the location of the provider config file.
DisableUpdateCheck bool // DisableUpdateCheck disable automatic update check
Expand All @@ -40,7 +41,7 @@ type Options struct {
var (
defaultConfigLocation = filepath.Join(userHomeDir(), ".config/cloudlist/config.yaml")
defaultProviderConfigLocation = filepath.Join(userHomeDir(), ".config/cloudlist/provider-config.yaml")
defaultProviders, defaultServies = []string{}, []string{}
defaultProviders, defaultServices = []string{}, []string{}
allowedProviders, allowedServices = []string{}, []string{}
)

Expand All @@ -51,7 +52,7 @@ func init() {
}

for _, service := range inventory.GetServices() {
defaultServies = append(defaultServies, service)
defaultServices = append(defaultServices, service)
allowedServices = append(allowedServices, service)
}
}
Expand Down Expand Up @@ -84,7 +85,8 @@ func ParseOptions() *Options {
flagSet.BoolVar(&options.Hosts, "host", false, "display only hostnames in results"),
flagSet.BoolVar(&options.IPAddress, "ip", false, "display only ips in results"),
flagSet.BoolVar(&options.ExtendedMetadata, "extended-metadata", false, "enable extended metadata for providers"),
flagSet.StringSliceVarP(&options.Services, "service", "s", nil, "query and display results from given service (comma-separated)) (default "+strings.Join(defaultServies, ",")+")", goflags.CommaSeparatedStringSliceOptions),
flagSet.StringSliceVarP(&options.Services, "service", "s", nil, "query and display results from given service (comma-separated) (default "+strings.Join(defaultServices, ",")+")", goflags.CommaSeparatedStringSliceOptions),
flagSet.StringSliceVarP(&options.ExcludeServices, "exclude-service", "es", nil, "services to skip for a provider (comma-separated)", goflags.CommaSeparatedStringSliceOptions),
flagSet.BoolVarP(&options.ExcludePrivate, "exclude-private", "ep", false, "exclude private ips in cli output"),
)
flagSet.CreateGroup("update", "Update",
Expand Down
12 changes: 11 additions & 1 deletion internal/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ func New(options *Options) (*Runner, error) {
if len(options.Services) == 0 {
options.Services = append(options.Services, config.GetServiceNames()...)
}
if len(options.ExcludeServices) == 0 {
options.ExcludeServices = append(options.ExcludeServices, config.GetExcludeServiceNames()...)
}

// assign default services if not provided
if len(options.Services) == 0 {
options.Services = append(options.Services, defaultServies...)
options.Services = append(options.Services, defaultServices...)
}
if len(options.Providers) == 0 {
options.Providers = append(options.Providers, defaultProviders...)
Expand All @@ -57,6 +60,10 @@ func (r *Runner) Enumerate() {
if r.options.Services != nil {
services = r.options.Services
}
excludeServices := []string{}
if r.options.ExcludeServices != nil {
excludeServices = r.options.ExcludeServices
}

for _, item := range r.config {
if item == nil {
Expand All @@ -68,6 +75,9 @@ func (r *Runner) Enumerate() {
if len(services) > 0 {
item["services"] = strings.Join(services, ",")
}
if len(excludeServices) > 0 {
item["exclude_services"] = strings.Join(excludeServices, ",")
}
if r.options.ExtendedMetadata {
item["extended_metadata"] = "true"
}
Expand Down
19 changes: 1 addition & 18 deletions pkg/providers/alibaba/alibaba.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package alibaba

import (
"context"
"strings"

"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/projectdiscovery/cloudlist/pkg/schema"
Expand Down Expand Up @@ -42,23 +41,7 @@ func New(options schema.OptionBlock) (*Provider, error) {
id, _ := options.GetMetadata("id")
provider := &Provider{id: id}

supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}
services := make(schema.ServiceMap)
if ss, ok := options.GetMetadata("services"); ok {
for _, s := range strings.Split(ss, ",") {
if _, ok := supportedServicesMap[s]; ok {
services[s] = struct{}{}
}
}
}
if len(services) == 0 {
for _, s := range Services {
services[s] = struct{}{}
}
}
services := options.ResolveServices(Services)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve an explicit empty allowlist instead of defaulting to all services.

ResolveServices currently defaults to all supported services whenever the parsed allowlist is empty. With this shared call, services: typo or an allowlist containing only unknown values enumerates every supported service, instead of using the provided allowlist with unknowns ignored.

Suggested fix in pkg/schema/schema.go
 services := make(ServiceMap)
 if allow, ok := o.GetMetadata("services"); ok {
 	for _, s := range strings.Split(allow, ",") {
 		s = strings.TrimSpace(s)
 		if _, ok := supportedSet[s]; ok {
 			services[s] = struct{}{}
 		}
 	}
-}
-
-// default to all supported services when no allowlist is provided
-if len(services) == 0 {
+} else {
 	for _, s := range supported {
 		services[s] = struct{}{}
 	}
 }

As per coding guidelines, Respect service filtering: Services() must list supported services, and providers should honor -s filters when gathering resources.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/providers/alibaba/alibaba.go` at line 44, Preserve the caller’s explicit
allowlist in the Alibaba provider: the current use of ResolveServices(Services)
causes empty or fully invalid filters to fall back to every supported service,
which breaks service filtering. Update the service resolution path around
Services() and the provider’s services assignment so unknown entries are ignored
but an explicitly empty result stays empty, and only default to all services
when no filter was supplied at all.

Source: Coding guidelines

provider.services = services

if services.Has("instance") {
Expand Down
20 changes: 1 addition & 19 deletions pkg/providers/arvancloud/arvancloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package arvancloud

import (
"context"
"strings"

r1c "git.arvancloud.ir/arvancloud/cdn-go-sdk"
"github.com/projectdiscovery/cloudlist/pkg/schema"
Expand Down Expand Up @@ -35,24 +34,7 @@ func New(options schema.OptionBlock) (*Provider, error) {
// Construct a new API object
api := r1c.NewAPIClient(configuration)

supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}

services := make(schema.ServiceMap)
if ss, ok := options.GetMetadata("services"); ok {
for _, s := range strings.Split(ss, ",") {
if _, ok := supportedServicesMap[s]; ok {
services[s] = struct{}{}
}
}
}
if len(services) == 0 {
for _, s := range Services {
services[s] = struct{}{}
}
}
services := options.ResolveServices(Services)

return &Provider{id: id, client: api, services: services}, nil
}
Expand Down
20 changes: 1 addition & 19 deletions pkg/providers/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,7 @@ func (p *ProviderOptions) ParseOptionBlock(block schema.OptionBlock) error {
p.OrgDiscoveryRoleArn = orgRoleArn
}

supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}
services := make(schema.ServiceMap)
if ss, ok := block.GetMetadata("services"); ok {
for _, s := range strings.Split(ss, ",") {
if _, ok := supportedServicesMap[s]; ok {
services[s] = struct{}{}
}
}
}
// if no services provided from -service flag, includes all services
if len(services) == 0 {
for _, s := range Services {
services[s] = struct{}{}
}
}
p.Services = services
p.Services = block.ResolveServices(Services)

if extendedMetadata, ok := block.GetMetadata("extended_metadata"); ok {
p.ExtendedMetadata = extendedMetadata == "true"
Expand Down
48 changes: 48 additions & 0 deletions pkg/providers/aws/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,54 @@ import (
"github.com/stretchr/testify/require"
)

// TestParseOptionBlockExcludeServices covers exclude_services resolution:
// the excluded entries are dropped from the effective set, exclusion composes
// with the services allowlist, and unknown values are ignored.
func TestParseOptionBlockExcludeServices(t *testing.T) {
tests := []struct {
name string
block schema.OptionBlock
wantPresent []string
wantAbsent []string
}{
{
name: "exclude from default-all set",
block: schema.OptionBlock{"exclude_services": "s3,route53"},
wantPresent: []string{"ec2", "lambda"},
wantAbsent: []string{"s3", "route53"},
},
{
name: "exclude composes with services allowlist",
block: schema.OptionBlock{"services": "ec2,s3,lambda", "exclude_services": "s3"},
wantPresent: []string{"ec2", "lambda"},
wantAbsent: []string{"s3"},
},
{
name: "whitespace is trimmed in both lists",
block: schema.OptionBlock{"services": "ec2, s3, lambda", "exclude_services": "s3, lambda"},
wantPresent: []string{"ec2"},
wantAbsent: []string{"s3", "lambda"},
},
{
name: "unknown exclude value is ignored",
block: schema.OptionBlock{"exclude_services": "not-a-service"},
wantPresent: []string{"ec2", "s3"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var opts ProviderOptions
require.NoError(t, opts.ParseOptionBlock(tt.block))
for _, s := range tt.wantPresent {
require.True(t, opts.Services.Has(s), "expected service %q to be present", s)
}
for _, s := range tt.wantAbsent {
require.False(t, opts.Services.Has(s), "expected service %q to be excluded", s)
}
})
}
}

// TestParseOptionBlock covers the static-credential pair validation that backs
// keyless authentication: both keys present is valid, both omitted is valid
// (keyless), and a half-configured pair is rejected.
Expand Down
19 changes: 1 addition & 18 deletions pkg/providers/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package azure
import (
"context"
"fmt"
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/azcore"
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions"
Expand Down Expand Up @@ -44,23 +43,7 @@ func New(options schema.OptionBlock) (*Provider, error) {
}

// Parse services
supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}
services := make(schema.ServiceMap)
if ss, ok := options.GetMetadata("services"); ok {
for _, s := range strings.Split(ss, ",") {
if _, ok := supportedServicesMap[s]; ok {
services[s] = struct{}{}
}
}
}
if len(services) == 0 {
for _, s := range Services {
services[s] = struct{}{}
}
}
services := options.ResolveServices(Services)

provider := &Provider{
Credential: credential, // Track 2: use credential instead of authorizer
Expand Down
20 changes: 1 addition & 19 deletions pkg/providers/cloudflare/cloudflare.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cloudflare
import (
"context"
"fmt"
"strings"

"github.com/cloudflare/cloudflare-go"
"github.com/projectdiscovery/cloudlist/pkg/schema"
Expand All @@ -24,24 +23,7 @@ type Provider struct {
func New(options schema.OptionBlock) (*Provider, error) {
id, _ := options.GetMetadata("id")

supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}

services := make(schema.ServiceMap)
if ss, ok := options.GetMetadata("services"); ok {
for _, s := range strings.Split(ss, ",") {
if _, ok := supportedServicesMap[s]; ok {
services[s] = struct{}{}
}
}
}
if len(services) == 0 {
for _, s := range Services {
services[s] = struct{}{}
}
}
services := options.ResolveServices(Services)

// Parse extended metadata option
extendedMetadata := false
Expand Down
30 changes: 2 additions & 28 deletions pkg/providers/custom/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,8 @@ func New(block schema.OptionBlock) (*Provider, error) {
return nil, err
}

supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}

services := make(schema.ServiceMap)
for _, s := range Services {
services[s] = struct{}{}
}
client := retryablehttp.NewClient(retryablehttp.DefaultOptionsSingle)
return &Provider{client: client, id: options.Id, urlList: options.URLs, headerList: options.Headers, services: services}, nil
return &Provider{client: client, id: options.Id, urlList: options.URLs, headerList: options.Headers, services: options.Services}, nil
}

// Name returns the name of the provider
Expand Down Expand Up @@ -84,24 +75,7 @@ func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) {
func (p *ProviderOptions) ParseOptionBlock(block schema.OptionBlock) error {
p.Id, _ = block.GetMetadata("id")

supportedServicesMap := make(map[string]struct{})
for _, s := range Services {
supportedServicesMap[s] = struct{}{}
}
services := make(schema.ServiceMap)
if ss, ok := block.GetMetadata("services"); ok {
for _, s := range strings.Split(ss, ",") {
if _, ok := supportedServicesMap[s]; ok {
services[s] = struct{}{}
}
}
}
// if no services provided from -service flag, includes all services
if len(services) == 0 {
for _, s := range Services {
services[s] = struct{}{}
}
}
p.Services = block.ResolveServices(Services)

np, err := networkpolicy.New(networkpolicy.DefaultOptions)
if err != nil {
Expand Down
Loading
Loading