From f454c20c3fd5204fcb089666a49239f401d38fea Mon Sep 17 00:00:00 2001 From: James Salt Date: Thu, 4 Jun 2026 17:31:25 +0100 Subject: [PATCH 1/6] BCH-1296: Remove WithUndefinedMappedTo fallback from policy behavior setup The three explicit behavior mappings cover all supported bundle names. A silent fallback to repository/registry for unrecognised bundle names is more surprising than leaving them uncovered. Co-Authored-By: Claude Sonnet 4.6 --- main.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/main.go b/main.go index 3fcce6c..266a255 100644 --- a/main.go +++ b/main.go @@ -111,9 +111,7 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api // Single all-in-one bundle: cover every behavior so it works out of the box. "plugin-aws-ecr-policies": {"repository", "registry", "image"}, } - policyEval := request. - WithDefaultPolicyBehavior(defaultBehaviorMapping). - WithUndefinedMappedTo([]string{"repository", "registry"}) + policyEval := request.WithDefaultPolicyBehavior(defaultBehaviorMapping) repositoryPaths := policyEval.PolicyPathsForBehavior("repository") registryPaths := policyEval.PolicyPathsForBehavior("registry") From 14ae6a5245523375efb11f9462cbb8c0e89baf8f Mon Sep 17 00:00:00 2001 From: James Salt Date: Fri, 5 Jun 2026 08:49:54 +0100 Subject: [PATCH 2/6] BCH-1296: Restructure GitHub Actions workflows to match ACM plugin pattern Split monolithic ci.yml into reusable test.yml + push.yml trigger. Extract goreleaser/gooci steps into reusable build-and-upload.yml with release.yml as a thin tag trigger, matching the four-file structure established in plugin-aws-acm. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build-and-upload.yml | 34 ++++++++++++++++++++++++++ .github/workflows/push.yml | 13 ++++++++++ .github/workflows/release.yml | 25 +------------------ .github/workflows/{ci.yml => test.yml} | 21 ++++++++-------- 4 files changed, 58 insertions(+), 35 deletions(-) create mode 100644 .github/workflows/build-and-upload.yml create mode 100644 .github/workflows/push.yml rename .github/workflows/{ci.yml => test.yml} (53%) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml new file mode 100644 index 0000000..a17b7c6 --- /dev/null +++ b/.github/workflows/build-and-upload.yml @@ -0,0 +1,34 @@ +name: Build and Upload Artifacts + +on: + workflow_call: + +jobs: + release: + runs-on: ubuntu-latest + permissions: + packages: write + contents: write + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Run GoReleaser + uses: goreleaser/goreleaser-action@v6 + with: + version: '~> v2' + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Install gooci cli + run: go install github.com/compliance-framework/gooci@v0.0.7 + - name: Authenticate gooci cli + run: gooci login ghcr.io --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} + - name: gooci Upload Version + run: gooci upload dist/ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.ref_name }} + - name: gooci Upload Latest + if: "!contains(github.ref_name, '-')" + run: gooci upload dist/ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml new file mode 100644 index 0000000..c6b883f --- /dev/null +++ b/.github/workflows/push.yml @@ -0,0 +1,13 @@ +name: Push + +on: + pull_request: + push: + branches: + - '**' + +jobs: + test: + permissions: + contents: read + uses: ./.github/workflows/test.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a84b419..6891a26 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,30 +7,7 @@ on: jobs: release: - runs-on: ubuntu-latest permissions: packages: write contents: write - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - with: - persist-credentials: false - - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff - with: - go-version-file: go.mod - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a - with: - version: '~> v2' - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Install gooci cli - run: go install github.com/compliance-framework/gooci@v0.0.7 - - name: Authenticate gooci cli - run: gooci login ghcr.io --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} - - name: gooci Upload Version - run: gooci upload dist/ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.ref_name }} - - name: gooci Upload Latest - if: "!contains(github.ref_name, '-')" - run: gooci upload dist/ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest + uses: ./.github/workflows/build-and-upload.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/test.yml similarity index 53% rename from .github/workflows/ci.yml rename to .github/workflows/test.yml index b6b1fa0..9bda56f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/test.yml @@ -1,23 +1,22 @@ -name: CI +name: Go Test on: - push: - branches: ["**"] - pull_request: - branches: ["**"] - -permissions: - contents: read + workflow_call: jobs: test: runs-on: ubuntu-latest + + permissions: + contents: read + steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-go@v5 with: go-version-file: go.mod - - name: go vet - run: go vet ./... - - name: go test + + - name: Test run: go test ./... From 70a5fe75505566fe80e7a4c89b507c7fdb28830f Mon Sep 17 00:00:00 2001 From: James Salt Date: Fri, 5 Jun 2026 09:20:07 +0100 Subject: [PATCH 3/6] BCH-1296: Add behavior mapping entries for split policy repos Maps ecr-repository-policy, ecr-registry-policy, and ecr-image-policy bundle name substrings to their respective resource type behaviors. Legacy combined bundle keys retained for backward compatibility. Co-Authored-By: Claude Sonnet 4.6 --- main.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 266a255..5b0e697 100644 --- a/main.go +++ b/main.go @@ -106,9 +106,12 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api // continues to work unchanged; operators may override by supplying two separate bundles // and configuring PolicyBehavior in the agent config. defaultBehaviorMapping := map[string][]string{ + "ecr-repository-policy": {"repository"}, + "ecr-registry-policy": {"registry"}, + "ecr-image-policy": {"image"}, + // Legacy combined bundle — kept for backward compatibility. "ecr-repository-policies": {"repository", "registry"}, "ecr-image-policies": {"image"}, - // Single all-in-one bundle: cover every behavior so it works out of the box. "plugin-aws-ecr-policies": {"repository", "registry", "image"}, } policyEval := request.WithDefaultPolicyBehavior(defaultBehaviorMapping) From 5f356cf33b3302237c4263651d2b8d69f695d0d7 Mon Sep 17 00:00:00 2001 From: James Salt Date: Fri, 5 Jun 2026 09:22:24 +0100 Subject: [PATCH 4/6] BCH-1296: Fix behavior mapping keys after policy repo rename to -policies Rename ecr-{repository,registry,image}-policy keys to -policies to match the repository naming convention. Removes duplicate map entries introduced by the mechanical rename. Co-Authored-By: Claude Sonnet 4.6 --- main.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/main.go b/main.go index 5b0e697..eadaaec 100644 --- a/main.go +++ b/main.go @@ -106,12 +106,10 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api // continues to work unchanged; operators may override by supplying two separate bundles // and configuring PolicyBehavior in the agent config. defaultBehaviorMapping := map[string][]string{ - "ecr-repository-policy": {"repository"}, - "ecr-registry-policy": {"registry"}, - "ecr-image-policy": {"image"}, - // Legacy combined bundle — kept for backward compatibility. - "ecr-repository-policies": {"repository", "registry"}, + "ecr-repository-policies": {"repository"}, + "ecr-registry-policies": {"registry"}, "ecr-image-policies": {"image"}, + // Legacy all-in-one bundle — kept for backward compatibility. "plugin-aws-ecr-policies": {"repository", "registry", "image"}, } policyEval := request.WithDefaultPolicyBehavior(defaultBehaviorMapping) From 485d3551aa2e806fab213b8fb2a93e6365a8866d Mon Sep 17 00:00:00 2001 From: James Salt Date: Fri, 5 Jun 2026 09:24:25 +0100 Subject: [PATCH 5/6] BCH-1296: Remove legacy plugin-aws-ecr-policies behavior mapping entry The single combined policy repo was never released. Only the three per-resource-type bundles are supported. Co-Authored-By: Claude Sonnet 4.6 --- main.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/main.go b/main.go index eadaaec..b8b570d 100644 --- a/main.go +++ b/main.go @@ -100,17 +100,12 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api } // Scope policy paths to each resource type using behavior mapping. - // Default: a bundle named "*ecr-repository-policies*" covers repository/registry checks; - // one named "*ecr-image-policies*" covers image checks. A single bundle containing all - // policies (e.g. plugin-aws-ecr-policies) is mapped to all three behaviors so it - // continues to work unchanged; operators may override by supplying two separate bundles - // and configuring PolicyBehavior in the agent config. + // Bundles are matched by substring against the policy path, so a bundle + // named "*ecr-repository-policies*" maps to repository checks, etc. defaultBehaviorMapping := map[string][]string{ "ecr-repository-policies": {"repository"}, "ecr-registry-policies": {"registry"}, "ecr-image-policies": {"image"}, - // Legacy all-in-one bundle — kept for backward compatibility. - "plugin-aws-ecr-policies": {"repository", "registry", "image"}, } policyEval := request.WithDefaultPolicyBehavior(defaultBehaviorMapping) From afacc4db03a31c496a936ce6f1d62f7a91efa24e Mon Sep 17 00:00:00 2001 From: James Salt Date: Fri, 5 Jun 2026 10:53:37 +0100 Subject: [PATCH 6/6] =?UTF-8?q?BCH-1296:=20Address=20PR=20review=20comment?= =?UTF-8?q?s=20=E2=80=94=20workflows=20and=20FetchRepositories=20guard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - test.yml: pin actions/checkout and actions/setup-go to commit SHAs; add go vet ./... step before go test ./... - build-and-upload.yml: pin checkout/setup-go SHAs; add cache:"none" to setup-go for deterministic release builds; move github.actor, repository_owner, event.repository.name and ref_name template expansions into step env vars to prevent shell injection - release.yml: remove redundant permissions block (already provided by the called reusable workflow) - main.go: guard FetchRepositories behind repositoryPaths/imagePaths check to avoid unnecessary DescribeRepositories calls when only registry checks are configured Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/build-and-upload.yml | 22 +++++++++++++++++----- .github/workflows/release.yml | 3 --- .github/workflows/test.yml | 7 +++++-- main.go | 15 ++++++++------- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index a17b7c6..abe17b2 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -1,5 +1,6 @@ name: Build and Upload Artifacts +# SHA pins below are managed by Dependabot/Renovate — do not replace with tags. on: workflow_call: @@ -10,12 +11,13 @@ jobs: packages: write contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - uses: actions/setup-go@v5 + - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: go-version-file: go.mod + cache: "none" - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: @@ -26,9 +28,19 @@ jobs: - name: Install gooci cli run: go install github.com/compliance-framework/gooci@v0.0.7 - name: Authenticate gooci cli - run: gooci login ghcr.io --username ${{ github.actor }} --password ${{ secrets.GITHUB_TOKEN }} + env: + GOOCI_ACTOR: ${{ github.actor }} + GOOCI_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gooci login ghcr.io --username "$GOOCI_ACTOR" --password "$GOOCI_TOKEN" - name: gooci Upload Version - run: gooci upload dist/ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.ref_name }} + env: + UPLOAD_OWNER: ${{ github.repository_owner }} + UPLOAD_REPO: ${{ github.event.repository.name }} + UPLOAD_REF: ${{ github.ref_name }} + run: gooci upload dist/ "ghcr.io/${UPLOAD_OWNER}/${UPLOAD_REPO}:${UPLOAD_REF}" - name: gooci Upload Latest if: "!contains(github.ref_name, '-')" - run: gooci upload dist/ ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:latest + env: + OWNER: ${{ github.repository_owner }} + REPO: ${{ github.event.repository.name }} + run: gooci upload dist/ "ghcr.io/${OWNER}/${REPO}:latest" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6891a26..64a1faf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,4 @@ on: jobs: release: - permissions: - packages: write - contents: write uses: ./.github/workflows/build-and-upload.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9bda56f..ff021be 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,12 +11,15 @@ jobs: contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false - - uses: actions/setup-go@v5 + - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: go-version-file: go.mod + - name: Vet + run: go vet ./... + - name: Test run: go test ./... diff --git a/main.go b/main.go index b8b570d..44158ed 100644 --- a/main.go +++ b/main.go @@ -120,15 +120,16 @@ func (l *CompliancePlugin) Eval(request *proto.EvalRequest, apiHelper runner.Api var evalErrors error for _, region := range l.config.Regions { - // CONFIG — repository checks - repos, err := dataFetcher.FetchRepositories(ctx, region) - if err != nil { - return &proto.EvalResponse{Status: proto.ExecutionStatus_FAILURE}, fmt.Errorf("region %s: fetching repositories: %w", region, err) + // Fetch repositories only when at least one bundle covers repository or image checks. + var repos []internal.RepositoryContext + if len(repositoryPaths) > 0 || len(imagePaths) > 0 { + r, err := dataFetcher.FetchRepositories(ctx, region) + if err != nil { + return &proto.EvalResponse{Status: proto.ExecutionStatus_FAILURE}, fmt.Errorf("region %s: fetching repositories: %w", region, err) + } + repos = internal.FilterByAccounts(r, l.config.Accounts) } - // Filter to configured accounts if any are specified. - repos = internal.FilterByAccounts(repos, l.config.Accounts) - for _, repo := range repos { evidences, err := policyEvaluator.EvalRepository(ctx, repo, repositoryPaths, l.policyData, l.config.PolicyLabels) allEvidences = append(allEvidences, evidences...)