diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3a630c..9019d93 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,7 +24,7 @@ On a PR you can comment `@coderabbitai review` (incremental) or `@coderabbitai f ## Development guidelines -- Keep collectors **read-only** toward cloud accounts by default. `om scan workload` (and its `azure` and `gcp` subcommands) is the opt-in exception: it may create and delete snapshots in the account, and it stores package identifiers only +- Keep collectors **read-only** toward cloud accounts by default. `om scan workload aws`, `om scan workload azure`, and `om scan workload gcp` are the opt-in exception: they may create and delete snapshots in the account, and they store package identifiers only - Document graph schema changes in `docs/` - Prefer small, reviewable PRs - Add tests for graph queries and normalization logic diff --git a/README.md b/README.md index 40ad110..5bcb35b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ OpenSourceOM Core is the platform behind [opensourceom.org](https://opensourceom Traditional scanners flood you with CVEs and misconfigurations. OpenSourceOM connects the dots — showing which findings sit on paths from the internet to your sensitive data and privileged identities. -> **Status:** Phase 3 has shipped. Phase 4 is open: `om scan workload` inventories AWS, Azure, and GCE Linux root volumes. Phase 5 (secret and malware findings on that storage) is open. Further rule packs stay open for contributors ([#10](https://github.com/OpenSourceOM/core/issues/10)). See the [roadmap](./docs/ROADMAP.md). +> **Status:** Phase 3 has shipped. Phase 4 is open: `om scan workload aws`, `om scan workload azure`, and `om scan workload gcp` inventory Linux root volumes. Phase 5 (secret and malware findings on that storage) is open. Further rule packs stay open for contributors ([#10](https://github.com/OpenSourceOM/core/issues/10)). See the [roadmap](./docs/ROADMAP.md). ## Why this exists @@ -94,7 +94,7 @@ go build -o om ./cmd/om export AWS_REGION=us-east-1 ./om scan aws # On an EC2 instance in that account, as root, after the API scan: -# sudo -E ./om scan workload +# sudo -E ./om scan workload aws # On an Azure VM or a GCE instance in that region, as root: # sudo -E ./om scan workload azure # sudo -E ./om scan workload gcp @@ -138,7 +138,7 @@ An API token is created only when an admin asks for one, from the console or wit ```bash ./om scan demo # sample environment, no credentials ./om scan aws -./om scan workload # root on an EC2 worker; Linux root-volume packages +./om scan workload aws # root on an EC2 worker; Linux root-volume packages ./om scan azure # requires AZURE_SUBSCRIPTION_ID + az login ./om scan workload azure # root on an Azure VM; Linux OS-disk packages ./om scan gcp # requires GCP_PROJECT_ID + ADC @@ -170,7 +170,7 @@ Full documentation: [opensourceom.org](https://opensourceom.org) (docs at [opens | **1** | Attack path queries, CVE enrichment, web UI, Azure/GCP collectors | | **2** | CSPM rules, blast radius, K8s connector, exports | | **3** | Plugin SDK, Helm, embedded rule pack, demo graph, inventory-backed CVEs, crown-jewel datastores, attack-path findings, and cloud audit context (CloudTrail, Activity Log, Admin Activity). Shipped. | -| **4** | Package inventory from persistent workload storage. AWS, Azure, and GCE Linux root volumes ship with `om scan workload`. Boot images, container layers, function bundles, and shared filesystems are open. | +| **4** | Package inventory from persistent workload storage. AWS, Azure, and GCE Linux root volumes ship with `om scan workload aws`, `om scan workload azure`, and `om scan workload gcp`. Boot images, container layers, function bundles, and shared filesystems are open. | | **5** | Secret and malware findings on storage Phase 4 already opens. Kind and path only. Open. | Details: [docs/ROADMAP.md](./docs/ROADMAP.md) diff --git a/collectors/README.md b/collectors/README.md index 6b3d043..ae62c97 100644 --- a/collectors/README.md +++ b/collectors/README.md @@ -20,7 +20,7 @@ Cloud and platform ingestion plugins. Each collector normalizes provider APIs in The **demo** collector loads a fixed environment (internet-exposed web tier, production database, public/private buckets, admin vs app identities, public Kubernetes service). Use it to exercise CSPM packs without cloud credentials. -The **AWS** collector emits properties the CIS pack matches on: `open_ingress`, `imdsv2`, `public_ip`, S3 `encryption` / `versioning` / `public_access_block`, and IAM user `mfa` / `unused_access_keys`. It also records RDS and Aurora instances as datastores. S3 bucket tags and the RDS `TagList` copy `sensitivity` or `data-class` onto the datastore. `sensitivity` wins when both are set. The same scan reads CloudTrail management events from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. `om scan aws` does not read instance disks. `om scan workload` is the separate command that inventories packages on Linux root volumes. +The **AWS** collector emits properties the CIS pack matches on: `open_ingress`, `imdsv2`, `public_ip`, S3 `encryption` / `versioning` / `public_access_block`, and IAM user `mfa` / `unused_access_keys`. It also records RDS and Aurora instances as datastores. S3 bucket tags and the RDS `TagList` copy `sensitivity` or `data-class` onto the datastore. `sensitivity` wins when both are set. The same scan reads CloudTrail management events from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. `om scan aws` does not read instance disks. `om scan workload aws` is the separate command that inventories packages on Linux root volumes. The **Azure** collector records logical SQL servers. `om scan azure` and `om scan gcp` do not read OS disks. `om scan workload azure` and `om scan workload gcp` are the separate commands that inventory packages on those disks. The same scan reads administrative Activity Log events from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. Entra ID sign-in logs are not part of this scan. The **GCP** collector records Cloud SQL instances. The same scan reads Admin Activity audit logs from the last 24 hours and stores the ones that name an identity and a resource on an exposed path. A failed lookup omits those events. Data Access logs are not part of this scan. A workload gets `CAN_ACCESS` only when a security group, firewall, or VPC path allows it. Azure copies the crown-jewel mark from storage-account and SQL-server tags. GCP copies it from a bucket label or a Cloud SQL user label. The keys are `sensitivity` and `data-class`. A plugin may set `sensitivity` on a datastore directly. diff --git a/deploy/helm/opensourceom/values.yaml b/deploy/helm/opensourceom/values.yaml index c73debf..92907bc 100644 --- a/deploy/helm/opensourceom/values.yaml +++ b/deploy/helm/opensourceom/values.yaml @@ -58,9 +58,10 @@ resources: # collector is enabled and credentials are set (a static key, an existing # Secret, or serviceAccountAuth). Kubernetes is off unless enabled; without # a kubeconfig Secret the chart creates a read-only ServiceAccount for this -# cluster. `om scan demo` and `om scan workload` are not scheduled. +# cluster. `om scan demo` and the workload scans are not scheduled. # The workload scan mounts disks and runs on a VM in the account: EC2 for -# `om scan workload`, an Azure VM for `azure`, and a GCE instance for `gcp`. +# `om scan workload aws`, an Azure VM for `om scan workload azure`, and a +# GCE instance for `om scan workload gcp`. scan: schedule: "0 */6 * * *" successfulJobsHistoryLimit: 3 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 7620a91..b494e81 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 # Architecture -> **Status:** Phase 3 has shipped. The collector plugin SDK, Helm, the embedded rule pack, and cloud audit context from CloudTrail, Activity Log, and Admin Activity logs are in the tree, alongside Phase 2 CSPM rules, blast radius, Kubernetes ingest, and exports. Phase 4 is open: `om scan workload` inventories AWS, Azure, and GCE Linux root volumes. Phase 5 (secret and malware findings on that storage) is open. Further rule packs stay open for contributors. +> **Status:** Phase 3 has shipped. The collector plugin SDK, Helm, the embedded rule pack, and cloud audit context from CloudTrail, Activity Log, and Admin Activity logs are in the tree, alongside Phase 2 CSPM rules, blast radius, Kubernetes ingest, and exports. Phase 4 is open: `om scan workload aws`, `om scan workload azure`, and `om scan workload gcp` inventory Linux root volumes. Phase 5 (secret and malware findings on that storage) is open. Further rule packs stay open for contributors. ## Overview @@ -32,7 +32,7 @@ SPDX-License-Identifier: Apache-2.0 | Component | Location | Notes | |-----------|----------|-------| | **Collectors** | `internal/collectors/` | AWS, Azure, GCP, Kubernetes, demo; RDS, Azure SQL, and Cloud SQL are datastores; AWS emits CIS pack properties and recent CloudTrail management events; Azure emits recent Activity Log events; GCP emits recent Admin Activity audit logs | -| **Workload packages** | `internal/workload/` | `om scan workload` reads AWS, Azure, and GCE Linux root volumes and sets `packages` | +| **Workload packages** | `internal/workload/` | `om scan workload aws`, `om scan workload azure`, and `om scan workload gcp` set `packages` from Linux root volumes | | **Plugin SDK** | `sdk/collector`, `internal/plugins/` | External executables; `om scan plugin` | | **Graph store** | `internal/graph/`, `migrations/` | PostgreSQL `nodes` + `edges` | | **Path queries** | `internal/graph/query.go` | Named queries including `internet-to-datastore` and `internet-to-sensitive-datastore` | @@ -87,7 +87,7 @@ Further community rule packs (PCI and additional CIS mappings) stay open for con Phase 4 writes `packages` (CPE 2.3 names and package URLs) from persistent workload storage onto the workload node that enrichment already matches. The sources, in order, are AWS Linux root volumes, Azure VM and GCE root volumes, boot images, container image layers and function bundles, and shared filesystems (EFS, Azure Files, Filestore). The scan is opt-in. `om scan aws`, `om scan azure`, and `om scan gcp` stay read-only API collectors. -`om scan workload` is the AWS root-volume scan. `om scan workload azure` and `om scan workload gcp` are the same walk for Azure OS disks and GCE boot disks. Each runs as root on a worker in that account and region, snapshots the root disk, mounts the copy read-only, and stores package identifiers only. A later API scan keeps `packages`. Windows, LVM, and non-root disks are skipped. Boot images, container layers, function bundles, and shared filesystems are still open. See [ADR 006](./adr/006-aws-root-volume-packages.md) and [ADR 007](./adr/007-azure-gce-root-volume-packages.md). +`om scan workload aws` is the AWS root-volume scan. `om scan workload azure` and `om scan workload gcp` are the same walk for Azure OS disks and GCE boot disks. Each runs as root on a worker in that account and region, snapshots the root disk, mounts the copy read-only, and stores package identifiers only. A later API scan keeps `packages`. Windows, LVM, and non-root disks are skipped. Boot images, container layers, function bundles, and shared filesystems are still open. See [ADR 006](./adr/006-aws-root-volume-packages.md) and [ADR 007](./adr/007-azure-gce-root-volume-packages.md). Phase 5 adds a finding for a secret pattern or a malware signature on storage that Phase 4 reader already opened. The finding stores the kind and the path. Object bodies and database rows are in neither phase. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 527ae9a..6d038f7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -74,7 +74,7 @@ The path, in order: - [ ] **Container image layers and function bundles** — layers for images already stored on Kubernetes workloads, plus Lambda deployment packages and the Azure and GCP equivalents. No volume attach — [#105](https://github.com/OpenSourceOM/core/issues/105) - [ ] **Shared filesystems** — EFS, Azure Files, and Filestore, when the application is installed there and a root-volume scan would miss it — [#106](https://github.com/OpenSourceOM/core/issues/106) -`om scan workload` reads Linux root volumes in the current AWS account. It runs as root on an EC2 instance in that account, snapshots the root volume, mounts the copy read-only, and writes CPE 2.3 names and package URLs onto `packages`. `om scan workload azure` does the same for managed OS disks from an Azure VM in that region, and `om scan workload gcp` does it for GCE boot disks from an instance in that region. A later `om scan aws`, `om scan azure`, or `om scan gcp` leaves `packages` in place. Windows, LVM, and disks other than the root volume are skipped. +`om scan workload aws` reads Linux root volumes in the current AWS account. It runs as root on an EC2 instance in that account, snapshots the root volume, mounts the copy read-only, and writes CPE 2.3 names and package URLs onto `packages`. `om scan workload azure` does the same for managed OS disks from an Azure VM in that region, and `om scan workload gcp` does it for GCE boot disks from an instance in that region. A later `om scan aws`, `om scan azure`, or `om scan gcp` leaves `packages` in place. Windows, LVM, and disks other than the root volume are skipped. Object bodies and database rows stay out. They are customer data, not the installed software. A public bucket or database is already an exposure finding from the API scan. diff --git a/docs/adr/006-aws-root-volume-packages.md b/docs/adr/006-aws-root-volume-packages.md index ca48b9f..bc412c0 100644 --- a/docs/adr/006-aws-root-volume-packages.md +++ b/docs/adr/006-aws-root-volume-packages.md @@ -17,7 +17,7 @@ Reading a volume means creating a snapshot and attaching a copy. That is a write ## Decision -`om scan workload` is a separate command. It runs as root on an EC2 instance in the account and region being scanned. +`om scan workload aws` is a separate command. It runs as root on an EC2 instance in the account and region being scanned. For each Linux instance with an EBS root volume, the command: diff --git a/docs/adr/007-azure-gce-root-volume-packages.md b/docs/adr/007-azure-gce-root-volume-packages.md index 3e06ebd..8e50dc2 100644 --- a/docs/adr/007-azure-gce-root-volume-packages.md +++ b/docs/adr/007-azure-gce-root-volume-packages.md @@ -11,13 +11,13 @@ SPDX-License-Identifier: Apache-2.0 ## Context -[ADR 006](./006-aws-root-volume-packages.md) defined `packages` and `om scan workload` for AWS Linux root volumes. `om scan azure` and `om scan gcp` still record the virtual machine from the cloud API and do not read the OS disk, so a package installed on an Azure VM or a GCE instance never reaches `packages`. +[ADR 006](./006-aws-root-volume-packages.md) defined `packages` and `om scan workload aws` for AWS Linux root volumes. `om scan azure` and `om scan gcp` still record the virtual machine from the cloud API and do not read the OS disk, so a package installed on an Azure VM or a GCE instance never reaches `packages`. The filesystem walk is the same. The snapshot and attach APIs are not. ## Decision -`om scan workload azure` and `om scan workload gcp` are subcommands of the same opt-in scan. `om scan azure` and `om scan gcp` stay read-only. +`om scan workload aws`, `om scan workload azure`, and `om scan workload gcp` are subcommands of the same opt-in scan. `om scan azure` and `om scan gcp` stay read-only. `om scan workload azure` runs as root on an Azure VM in the subscription. For each Linux VM in that region with a managed OS disk, it creates a snapshot and a disk in the worker's resource group and zone, attaches the disk to the worker, and mounts it read-only. `om scan workload gcp` runs as root on a GCE instance in the project. For each Linux instance in that region with a persistent boot disk, it creates a snapshot, creates a disk in the worker's zone, and attaches that disk read-only. diff --git a/internal/cmd/scan_workload.go b/internal/cmd/scan_workload.go index fe73b12..0699859 100644 --- a/internal/cmd/scan_workload.go +++ b/internal/cmd/scan_workload.go @@ -3,117 +3,16 @@ package cmd -import ( - "fmt" - "os" - "strings" - "time" - - awscol "github.com/OpenSourceOM/core/internal/collectors/aws" - "github.com/OpenSourceOM/core/internal/workload" - awsv "github.com/OpenSourceOM/core/internal/workload/aws" - "github.com/aws/aws-sdk-go-v2/config" - "github.com/aws/aws-sdk-go-v2/service/ec2" - "github.com/spf13/cobra" -) - -var ( - workloadTimeout time.Duration - workloadInstances []string -) +import "github.com/spf13/cobra" var scanWorkloadCmd = &cobra.Command{ Use: "workload", - Short: "Inventory packages on AWS Linux root volumes", - Long: workloadLong(), - RunE: runScanWorkload, -} - -func workloadLong() string { - return `Inventory packages on Linux root volumes in the current AWS account. - -Run it as root on an EC2 instance in that account. The command snapshots each root volume, creates a volume from the snapshot in this instance's availability zone, attaches it, and mounts it read-only. It writes CPE 2.3 names and package URLs onto the workload node from om scan aws, then deletes the snapshot and the volume, including when a read fails. File contents stay on the volume. - -om scan aws stays a read-only API scan. A later om scan aws keeps packages already stored on the workload. Snapshots and volumes are tagged opensourceom:scan=workload, which is how to find one left behind by a killed process. - -This command reads Linux root volumes. Windows instances are skipped. LVM and disks other than the root volume are skipped. Azure OS disks are "om scan workload azure". GCE boot disks are "om scan workload gcp". Boot images, container layers, function bundles, and shared filesystems are later scans. - -IAM actions: - ` + strings.Join(awsv.Permissions, "\n ") -} - -func runScanWorkload(cmd *cobra.Command, args []string) error { - cfg := loadConfig() - ctx := cmd.Context() - worker, err := awsv.LookupWorker(ctx) - if err != nil { - return err - } - if err := awsv.CheckRegion(worker.Region, cfg.AWSRegion); err != nil { - return err - } - if os.Geteuid() != 0 { - return fmt.Errorf("om scan workload mounts the snapshot and must run as root on the worker") - } - - store, err := openGraphStore(ctx, cfg) - if err != nil { - return err - } - defer store.Close() - - collector, err := awscol.NewCollector(ctx, cfg.AWSRegion) - if err != nil { - return err - } - awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.AWSRegion)) - if err != nil { - return fmt.Errorf("load aws config: %w", err) - } - - scanner := &awsv.Scanner{ - API: ec2.NewFromConfig(awsCfg), - Worker: awsv.Worker{ - InstanceID: worker.InstanceID, - AvailabilityZone: worker.AvailabilityZone, - Region: worker.Region, - }, - Only: workloadInstances, - Timeout: workloadTimeout, - Mount: awsv.NewGuest().MountReadOnly, - Walk: workload.Walk, - OnStart: func(instanceID, name string) { - fmt.Printf("%s: snapshotting the root volume\n", workloadLabel(instanceID, name)) - }, - } - wrote := 0 - err = scanner.Scan(ctx, func(res awsv.Result) error { - label := workloadLabel(res.InstanceID, res.Name) - if res.Skipped != "" { - fmt.Printf("%s: skipped (%s)\n", label, res.Skipped) - return nil - } - nodeID := awscol.WorkloadNodeID(collector.AccountID, cfg.AWSRegion, res.InstanceID) - if err := store.SetWorkloadPackages(ctx, nodeID, res.Packages); err != nil { - return err - } - fmt.Printf("%s: %d package identifiers\n", label, len(res.Packages)) - wrote++ - return nil - }) - fmt.Printf("Stored packages on %d workloads.\n", wrote) - return err -} + Short: "Inventory packages on Linux root volumes", + Long: `Inventory packages on Linux root volumes in the current account. -func workloadLabel(instanceID, name string) string { - if name != "" && name != instanceID { - return instanceID + " (" + name + ")" - } - return instanceID +Each cloud is its own command: "om scan workload aws", "om scan workload azure", and "om scan workload gcp". Each runs as root on a worker in that account and region. om scan aws, om scan azure, and om scan gcp stay read-only.`, } func init() { - scanWorkloadCmd.Flags().DurationVar(&workloadTimeout, "timeout", 30*time.Minute, "time limit for each instance") - scanWorkloadCmd.Flags().StringArrayVar(&workloadInstances, "instance", nil, "EC2 instance to scan (repeat to scan several; the default is every instance in the region)") scanCmd.AddCommand(scanWorkloadCmd) } diff --git a/internal/cmd/scan_workload_aws.go b/internal/cmd/scan_workload_aws.go new file mode 100644 index 0000000..f4ae01c --- /dev/null +++ b/internal/cmd/scan_workload_aws.go @@ -0,0 +1,119 @@ +// Copyright 2026 OpenSourceOM +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "fmt" + "os" + "strings" + "time" + + awscol "github.com/OpenSourceOM/core/internal/collectors/aws" + "github.com/OpenSourceOM/core/internal/workload" + awsv "github.com/OpenSourceOM/core/internal/workload/aws" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/spf13/cobra" +) + +var ( + awsWorkloadTimeout time.Duration + awsWorkloadInstances []string +) + +var scanWorkloadAWSCmd = &cobra.Command{ + Use: "aws", + Short: "Inventory packages on AWS Linux root volumes", + Long: workloadAWSLong(), + RunE: runScanWorkloadAWS, +} + +func workloadAWSLong() string { + return `Inventory packages on Linux root volumes in the current AWS account. + +Run it as root on an EC2 instance in that account. The command snapshots each root volume, creates a volume from the snapshot in this instance's availability zone, attaches it, and mounts it read-only. It writes CPE 2.3 names and package URLs onto the workload node from om scan aws, then deletes the snapshot and the volume, including when a read fails. File contents stay on the volume. + +om scan aws stays a read-only API scan. A later om scan aws keeps packages already stored on the workload. Snapshots and volumes are tagged opensourceom:scan=workload, which is how to find one left behind by a killed process. + +This command reads Linux root volumes. Windows instances are skipped. LVM and disks other than the root volume are skipped. Boot images, container layers, function bundles, and shared filesystems are later scans. + +IAM actions: + ` + strings.Join(awsv.Permissions, "\n ") +} + +func runScanWorkloadAWS(cmd *cobra.Command, args []string) error { + cfg := loadConfig() + ctx := cmd.Context() + worker, err := awsv.LookupWorker(ctx) + if err != nil { + return err + } + if err := awsv.CheckRegion(worker.Region, cfg.AWSRegion); err != nil { + return err + } + if os.Geteuid() != 0 { + return fmt.Errorf("om scan workload aws mounts the snapshot and must run as root on the worker") + } + + store, err := openGraphStore(ctx, cfg) + if err != nil { + return err + } + defer store.Close() + + collector, err := awscol.NewCollector(ctx, cfg.AWSRegion) + if err != nil { + return err + } + awsCfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(cfg.AWSRegion)) + if err != nil { + return fmt.Errorf("load aws config: %w", err) + } + + scanner := &awsv.Scanner{ + API: ec2.NewFromConfig(awsCfg), + Worker: awsv.Worker{ + InstanceID: worker.InstanceID, + AvailabilityZone: worker.AvailabilityZone, + Region: worker.Region, + }, + Only: awsWorkloadInstances, + Timeout: awsWorkloadTimeout, + Mount: awsv.NewGuest().MountReadOnly, + Walk: workload.Walk, + OnStart: func(instanceID, name string) { + fmt.Printf("%s: snapshotting the root volume\n", workloadLabel(instanceID, name)) + }, + } + wrote := 0 + err = scanner.Scan(ctx, func(res awsv.Result) error { + label := workloadLabel(res.InstanceID, res.Name) + if res.Skipped != "" { + fmt.Printf("%s: skipped (%s)\n", label, res.Skipped) + return nil + } + nodeID := awscol.WorkloadNodeID(collector.AccountID, cfg.AWSRegion, res.InstanceID) + if err := store.SetWorkloadPackages(ctx, nodeID, res.Packages); err != nil { + return err + } + fmt.Printf("%s: %d package identifiers\n", label, len(res.Packages)) + wrote++ + return nil + }) + fmt.Printf("Stored packages on %d workloads.\n", wrote) + return err +} + +func workloadLabel(instanceID, name string) string { + if name != "" && name != instanceID { + return instanceID + " (" + name + ")" + } + return instanceID +} + +func init() { + scanWorkloadAWSCmd.Flags().DurationVar(&awsWorkloadTimeout, "timeout", 30*time.Minute, "time limit for each instance") + scanWorkloadAWSCmd.Flags().StringArrayVar(&awsWorkloadInstances, "instance", nil, "EC2 instance to scan (repeat to scan several; the default is every instance in the region)") + scanWorkloadCmd.AddCommand(scanWorkloadAWSCmd) +} diff --git a/internal/cmd/scan_workload_test.go b/internal/cmd/scan_workload_test.go index fb433ef..b694fda 100644 --- a/internal/cmd/scan_workload_test.go +++ b/internal/cmd/scan_workload_test.go @@ -24,9 +24,13 @@ func TestScanWorkloadRegistered(t *testing.T) { if workloadCmd == nil { t.Fatal("om scan workload is not registered") } + if workloadCmd.Runnable() { + t.Fatal("om scan workload runs a scan; name the cloud") + } + aws := commandByName(t, workloadCmd, "aws") for _, action := range awsv.Permissions { - if !strings.Contains(workloadCmd.Long, action) { - t.Errorf("help missing %s", action) + if !strings.Contains(aws.Long, action) { + t.Errorf("aws help missing %s", action) } } azure := commandByName(t, workloadCmd, "azure") diff --git a/internal/workload/aws/identity.go b/internal/workload/aws/identity.go index 8a47580..51a6f38 100644 --- a/internal/workload/aws/identity.go +++ b/internal/workload/aws/identity.go @@ -22,7 +22,7 @@ func LookupWorker(ctx context.Context) (Worker, error) { client := imds.NewFromConfig(cfg) id, err := metadata(ctx, client, "instance-id") if err != nil { - return Worker{}, fmt.Errorf("om scan workload runs on an EC2 instance in this account: %w", err) + return Worker{}, fmt.Errorf("om scan workload aws runs on an EC2 instance in this account: %w", err) } az, err := metadata(ctx, client, "placement/availability-zone") if err != nil { diff --git a/internal/workload/aws/scan.go b/internal/workload/aws/scan.go index 128d6b9..9774a66 100644 --- a/internal/workload/aws/scan.go +++ b/internal/workload/aws/scan.go @@ -16,7 +16,7 @@ import ( "github.com/aws/smithy-go" ) -// Permissions are the IAM actions om scan workload adds. +// Permissions are the IAM actions om scan workload aws adds. // kms:CreateGrant and kms:Decrypt are the grant an encrypted root volume needs. var Permissions = []string{ "ec2:DescribeInstances",