Skip to content
Merged
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
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI

on:
push:
branches: ["**"]
pull_request:
branches: ["**"]

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: go vet
run: go vet ./...
- name: go test
run: go test ./...
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist/
36 changes: 36 additions & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

version: 2

before:
hooks:
- go mod tidy
- go generate ./...

builds:
- binary: plugin
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
goarch:
- amd64
- arm64

archives:
- format: tar.gz
name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "arm64" }}arm64
{{- else }}{{ .Arch }}{{ end }}

changelog:
sort: asc
filters:
exclude:
- "^docs:"
- "^test:"
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.PHONY: help test clean build run

##@ Help
help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "\033[1mUsage\033[0m\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-30s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

test: ## Run unit tests
@go test ./...

clean: ## Remove build artifacts
@rm -rf dist/*

build: clean ## Build the plugin binary
@mkdir -p dist/
@go build -o dist/plugin main.go

run: build ## Run the agent with the built plugin
@if [ ! -x ../agent/dist/concom ]; then echo "ERROR: ../agent/dist/concom not found or not executable — build the agent first"; exit 1; fi
@if [ ! -r ./.config/config.yaml ]; then echo "ERROR: ./.config/config.yaml not found — copy examples/agent-config.yaml to .config/config.yaml and fill in your settings"; exit 1; fi
@../agent/dist/./concom agent --config ./.config/config.yaml
Comment thread
coderabbitai[bot] marked this conversation as resolved.
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# plugin-aws-ecr

CCF compliance plugin for AWS Elastic Container Registry (ECR). Evaluates private ECR repositories and container image scan results against SOC2 TSC 2017 controls.

## What it checks

### CONFIG checks (per repository)

| Check | Description | Controls |
|-------|-------------|---------|
| `ecr_require_scan_on_push` | `imageScanningConfiguration.scanOnPush` must be `true` | CC5.3, CC6.8, CC7.1 |
| `ecr_require_tag_immutability` | `imageTagImmutability` must be `IMMUTABLE` | CC6.8, CC8.1 |
| `ecr_require_encryption` | Encryption type must be in `approved_encryption_types` | CC5.2 |
| `ecr_require_lifecycle_policy` | Repository must have a lifecycle policy configured | CC6.5 |
| `ecr_deny_public_access` | Resource policy must not grant `Principal: "*"` with `Effect: Allow` | CC6.8, CC8.1 |
| `ecr_require_tags` | Repository must carry all `required_repository_tags` | CC6.1 |

### CONFIG checks (per registry / account+region)

| Check | Description | Controls |
|-------|-------------|---------|
| `ecr_require_registry_scanning` | Registry-level scan type must be in `approved_registry_scan_types` | CC5.2, CC5.3, CC7.1 |

### DYNAMIC checks (per image digest, 90-day lookback)

| Check | Description | Controls |
|-------|-------------|---------|
| `ecr_require_image_scan_complete` | Image scan `status` must be `COMPLETE` | CC3.2, CC5.2, CC7.1, CC8.1 |
| `ecr_require_no_critical_image_findings` | `CRITICAL` finding count must be 0 | CC6.8, CC7.1, CC8.1 |
| `ecr_require_no_high_image_findings` | `HIGH` finding count must be ≤ `max_high_finding_count` | CC6.8, CC7.1, CC8.1 |
| `ecr_require_scan_findings_retrievable` | Scan findings with severity data must be accessible | CC6.8, CC7.1 |

## Required IAM actions

```json
{
"Effect": "Allow",
"Action": [
"ecr:DescribeRepositories",
"ecr:GetLifecyclePolicy",
"ecr:GetRepositoryPolicy",
"ecr:ListTagsForResource",
"ecr:GetRegistryScanningConfiguration",
"ecr:DescribeImages",
"ecr:DescribeImageScanFindings"
],
"Resource": "*"
}
```

## Configuration

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `regions` | `string` | Yes | Comma-separated AWS regions to scan (e.g. `"us-east-1,eu-west-1"`) |
| `accounts` | `string` | No | Comma-separated account IDs to filter on. If omitted, all repositories in the region are evaluated. |
| `policy_labels` | `string` (JSON) | No | Extra labels added to every Evidence record (e.g. `{"env":"prod"}`) |

## Policy data (overrides `data.json` defaults)

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `approved_encryption_types` | `[]string` | `["KMS"]` | Allowed ECR encryption types |
| `approved_registry_scan_types` | `[]string` | `["ENHANCED"]` | Allowed registry-level scan modes |
| `required_repository_tags` | `[]string` | `["Environment","Owner"]` | Tag keys every repository must carry |
| `required_tag_values` | `object` | `{}` | Enforce specific values for certain tags |
| `image_lookback_days` | `number` | `90` | Days back to evaluate image digests |
| `max_high_finding_count` | `number` | `0` | Maximum HIGH severity findings allowed per image |

## Local development

```bash
# Build the binary
make build

# Run unit tests
make test

# Run against a dev account (requires AWS credentials)
make run
```

See `examples/agent-config.yaml` for a full configuration reference.
59 changes: 59 additions & 0 deletions examples/agent-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# plugin-aws-ecr agent configuration
# Choose ONE source variant by setting source.type to either "local" or "oci".

plugins:
- name: plugin-aws-ecr
source:
# Local binary (development / CI)
type: local
path: /path/to/plugin-aws-ecr/dist/plugin

# OCI image (production) — replace the block above with these two lines:
# type: oci
# image: ghcr.io/compliance-framework/plugin-aws-ecr:latest

config:
# regions: comma-separated list of AWS regions to scan (required)
regions: "us-east-1,eu-west-1"

# accounts: comma-separated list of account IDs to filter on (optional)
# If omitted all repositories in each region are evaluated.
# accounts: "123456789012,987654321098"

# policy_labels: JSON object of extra labels added to every Evidence record (optional)
# Useful for tagging evidence by environment, team, or cost centre.
# policy_labels: '{"env":"prod","team":"platform"}'

policy_paths:
- /path/to/plugin-aws-ecr-policies/dist/bundle.tar.gz

# policy_data overrides the defaults in policies/data.json at runtime.
policy_data:
# approved_encryption_types: list of allowed ECR encryption types.
# Allowed values: "KMS", "AES256", "KMS_DSSE"
approved_encryption_types:
- "KMS"

# approved_registry_scan_types: list of allowed registry-level scan modes.
# Allowed values: "BASIC", "ENHANCED"
approved_registry_scan_types:
- "ENHANCED"

# required_repository_tags: tag keys that every ECR repository must carry.
required_repository_tags:
- "Environment"
- "Owner"

# required_tag_values: enforce specific values for certain tag keys (optional).
# Example: require Environment tag to be exactly "prod"
# required_tag_values:
# Environment: "prod"
required_tag_values: {}

# image_lookback_days: how many days back to evaluate image digests for DYNAMIC checks.
# Default 90 days matches the SOC2 audit window.
image_lookback_days: 90

# max_high_finding_count: maximum number of HIGH severity findings allowed per image.
# Set to 0 to require zero HIGH findings. Increase with caution.
max_high_finding_count: 0
72 changes: 72 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
module github.com/container-solutions/plugin-aws-ecr

go 1.26.3

require (
github.com/aws/aws-sdk-go-v2 v1.41.11
github.com/aws/aws-sdk-go-v2/config v1.32.19
github.com/aws/aws-sdk-go-v2/service/ecr v1.58.2
github.com/compliance-framework/agent v0.7.0
github.com/hashicorp/go-hclog v1.6.3
github.com/hashicorp/go-plugin v1.7.0
)

require (
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.18 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.27 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.24 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.1.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.18 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 // indirect
github.com/aws/smithy-go v1.27.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/compliance-framework/api v0.16.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect
github.com/defenseunicorns/go-oscal v0.7.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/yamux v0.1.2 // indirect
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
github.com/lestrrat-go/dsig v1.0.0 // indirect
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc/v3 v3.0.4 // indirect
github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/oklog/run v1.2.0 // indirect
github.com/open-policy-agent/opa v1.14.1 // indirect
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/valyala/fastjson v1.6.10 // indirect
github.com/vektah/gqlparser/v2 v2.5.32 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.52.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.37.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
Loading
Loading