Skip to content

lisa/feat/plugin-implementation#1

Merged
gusfcarvalho merged 8 commits into
mainfrom
lisa/feat/plugin-implementation
May 29, 2026
Merged

lisa/feat/plugin-implementation#1
gusfcarvalho merged 8 commits into
mainfrom
lisa/feat/plugin-implementation

Conversation

@ccf-lisa
Copy link
Copy Markdown

@ccf-lisa ccf-lisa Bot commented May 28, 2026

automated implementation by lisa.

Summary by CodeRabbit

  • New Features

    • Added an AWS Secrets Manager compliance plugin with a collector that gathers secrets, correlates CloudTrail/IAM events, and evaluates policies; includes a go-plugin entrypoint.
  • Configuration

    • New PluginConfig/account model with parsing, defaults, validation, concurrency and timeout normalization.
  • Documentation

    • README replaced with Secrets Manager–specific docs and normalization semantics.
  • Utilities

    • New input/resource record types and helper utilities for hashing, time formatting, and map merging.
  • Tests

    • Extensive tests for collection, parsing, event correlation, concurrency, and policy evaluation.
  • Chores

    • CI/release workflow and image tagging/publishing updates; added go.mod.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 940c2fca-e943-4914-955c-660618ab50c4

📥 Commits

Reviewing files that changed from the base of the PR and between 4379717 and d182393.

📒 Files selected for processing (4)
  • README.md
  • input.go
  • main.go
  • main_test.go

📝 Walkthrough

Walkthrough

This PR implements an AWS Secrets Manager compliance plugin: configuration parsing, concurrent secret collection across accounts/regions, normalization into ResourceRecords, CloudTrail correlation (Secrets Manager and IAM), Rego policy evaluation via a HashiCorp runner plugin, tests, README replacement, and CI workflow updates.

Changes

AWS Secrets Manager Compliance Plugin

Layer / File(s) Summary
Go module and utility foundation
go.mod, internal/util.go
Go module manifest and utility helpers (string/map merging, hashing, time formatting, pointer conversions) provide foundational support for the plugin implementation.
Configuration model and validation
config.go, config_test.go
PluginConfig and AccountConfig structs define plugin behavior (accounts, regions, lookback window, policy inputs/labels, concurrency, API timeout); parsePluginConfig parses and validates JSON-encoded configuration with sensible defaults and bounds checking.
Resource record normalization
input.go
Data structures (AccountContext, RegionContext, ResourceIdentity, Window, CollectionError, CollectionMetadata, NormalizedInput, ResourceRecord) and newSecretRecord helper transform raw AWS metadata into normalized records with typed fields, account/region/resource identity, config/dynamic maps, collection metadata, labels, and subject identity for policy evaluation.
AWS client factory & target resolution
collector.go
AWS client abstractions and DefaultAWSClientFactory that resolve per-account/region targets, optionally assume STS roles, resolve missing AccountID, and construct per-target SecretsManager/CloudTrail/STS clients (IAM/global CloudTrail forced to us-east-1).
Collector orchestration
collector.go
Collector.Collect runs a worker pool over resolved targets with per-target timeouts, aggregates ResourceRecords and scoped errors, and returns a combined CollectionResult.
Per-target enumeration & collection
collector.go
Lists secret ARNs (including planned-deletion), builds per-secret drafts via DescribeSecret/GetResourcePolicy/ListSecretVersionIds, performs CloudTrail lookups, correlates events to drafts, and emits ResourceRecords with lookback and recovery-window metadata.
Describe/policy parsing & version hashing
collector.go
Converts DescribeSecret outputs into normalized config with sentinel defaults, parses resource policies into normalized statements/principals and computes stable hashes; paginates ListSecretVersionIds to build version payload hashes and deprecated-count heuristics.
CloudTrail lookup & event attachment
collector.go
LookupEvents pagination and normalization for allowed event names; attach Secrets Manager events by identifier substring matching and attach IAM credential-removal events only when parsed principals contain affected IAM identifiers; deduplicate attachments and override recovery windows from DeleteSecret payloads.
Plugin Eval and policy orchestration
main.go, main_test.go, rego_fixture_test.go
CompliancePlugin implements Configure/Init/Eval: parse/clone policy inputs for thread safety, collect ResourceRecords, convert inputs for Rego, evaluate each policy path to produce evidences, and optionally create evidences via ApiHelper; tests validate nil requests, concurrent eval, default log level, and inline Rego evidence generation.
Collector & plugin tests
collector_test.go, main_test.go, rego_fixture_test.go
Extensive unit tests using in-memory fake AWS clients covering target resolution, listing/describing/policy/version pagination, CloudTrail correlation, error scoping, recovery-window derivation, duplicate-name handling, concurrency bounds, and policy evaluation fixtures.
CI/CD pipeline and workflows
.github/workflows/build-and-upload.yml, .github/workflows/release.yml, .github/workflows/test.yml
Updated action pins and settings: checkout fetch-depth=0, setup-go caching change, refactored OCI tag derivation (safe v-prefix, 128-char truncation, strict prerelease detection), lower-cased GHCR repo naming, gooci login actor/env token usage, and release job secrets/permissions changes.
Plugin documentation
README.md
Replaces ELBv2 docs with AWS Secrets Manager plugin specification: subjects/labels, configuration keys, normalized Rego input schema with sentinels, CloudTrail filtering/attribution, error scoping, and development instructions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


🐰 A secrets collector hops through the AWS cloud so bright,
Gathers metadata, policies, and CloudTrail in the night,
Rego policies stand ready to assess each precious store,
Records and hashes, events matched, and errors scoped just right,
Now Secrets Manager secrets can be watched and kept secure!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'lisa/feat/plugin-implementation' is vague and generic, using non-descriptive branch naming conventions rather than a clear summary of the main change. Replace the branch name with a descriptive title summarizing the main change, e.g., 'Implement AWS Secrets Manager compliance plugin' or similar.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@gusfcarvalho gusfcarvalho left a comment

Choose a reason for hiding this comment

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

Findings inline. Headline: CI is blocked by a non-existent actions/checkout SHA pinned in both test and release workflows; otherwise the implementation is in good shape, with three low-severity correctness/maintainability notes on IAM event attribution and friendly-name suffix handling. Local go build, go vet, go test -race, and gofmt are all clean. The pass-1/2/3 self-review fixes (planned-deletion listing, us-east-1 CloudTrail for IAM, duplicate-friendly-name index, go mod verify in release hooks) all look correctly applied and tested.

Comment thread .github/workflows/test.yml Outdated
Comment thread .github/workflows/build-and-upload.yml Outdated
Comment thread collector.go Outdated
Comment thread collector.go
Comment thread input.go
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with 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.

Inline comments:
In `@collector.go`:
- Around line 102-104: The code in collector.go currently masks a missing AWS
region by setting regions = []string{""}; instead, detect when len(regions)==0
and return a clear configuration error (e.g., fmt.Errorf or a package
ErrInvalidConfig) from the function that constructs targets (the function that
owns the 'regions' variable) so failure is fast and descriptive; replace the
empty-string fallback with a returned error and update any callers to
propagate/handle that error accordingly.

In `@main_test.go`:
- Around line 37-43: The test currently calls p.Eval(nil, nil) and dereferences
resp without guarding for a nil response; update the nil-request test around
p.Eval to explicitly check if resp == nil after the call and t.Fatalf with a
clear message (e.g., "expected non-nil resp when error returned" or include the
err) before calling resp.GetStatus(), and only then assert resp.GetStatus() ==
proto.ExecutionStatus_FAILURE so the test cannot panic when Eval returns (nil,
err).

In `@main.go`:
- Line 227: The logger is currently created with debug enabled unconditionally
(logger := hclog.New(&hclog.LoggerOptions{Level: hclog.Debug, JSONFormat:
true})); change this to use Info as the default level and make Debug opt‑in via
configuration or an environment variable (e.g., read LOG_LEVEL or DEBUG at
startup), map that value to an hclog level (using hclog parsing or a small
switch) and pass the resolved level into hclog.New so the runtime defaults to
Info but can be elevated to Debug when explicitly set.
- Line 102: When building the Collector you must guard against a nil logger on
the CompliancePlugin receiver (l) before calling l.logger.Named; if l.logger is
nil assign a safe default no-op logger to l.logger (e.g., a zap.NewNop() or
equivalent no-op logger used in this project) and then call l.logger.Named to
construct the Collector: update the code around the Collector creation
(referencing Collector, CompliancePlugin receiver l, and l.logger.Named) to
ensure l.logger is non-nil first.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 22c450bd-1eae-4600-b4c4-40fc86c365e8

📥 Commits

Reviewing files that changed from the base of the PR and between 8a8c7db and a73516b.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (14)
  • .github/workflows/build-and-upload.yml
  • .github/workflows/release.yml
  • .github/workflows/test.yml
  • README.md
  • collector.go
  • collector_test.go
  • config.go
  • config_test.go
  • go.mod
  • input.go
  • internal/util.go
  • main.go
  • main_test.go
  • rego_fixture_test.go

Comment thread collector.go
Comment thread main_test.go
Comment thread main.go Outdated
Comment thread main.go Outdated
Copy link
Copy Markdown
Contributor

@gusfcarvalho gusfcarvalho left a comment

Choose a reason for hiding this comment

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

Approving. All five of my prior threads are addressed (CI checkout SHA, one-way principal match, role-side IAM events, friendly-name suffix), and the four CodeRabbit threads from the previous cycle are now resolved with code in commit 4379717: regions-resolution returns a clear error instead of falling through to an empty region; Eval guards a nil logger; main reads LOG_LEVEL (defaulting to info); the nil-request test guards resp before GetStatus. Three new focused tests (TestDefaultAWSClientFactoryResolveTargetsRequiresRegion, TestEvalNilLoggerDoesNotPanic, TestDefaultLogLevel) cover those changes. CI is green (test / test passing on both runs), and local go build, go vet, go test -race, and gofmt -l are clean on commit 4379717.

@ccf-lisa
Copy link
Copy Markdown
Author

ccf-lisa Bot commented May 28, 2026

PR approved. Marking as ready for e2e.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
.github/workflows/build-and-upload.yml (2)

17-20: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Pin the GoReleaser CLI to an exact release.

The action is SHA-pinned, but version: "~> v2" still downloads a moving GoReleaser binary. The action docs allow a fixed version, and using a range here means release behavior can change without any workflow diff. Pin the exact v2.x.y you have validated in CI. (github.com)

🤖 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 @.github/workflows/build-and-upload.yml around lines 17 - 20, The workflow
uses the goreleaser action 'goreleaser/goreleaser-action' with a moving version
specifier version: "~> v2"; change this to an exact validated tag (e.g. v2.x.y)
so the action installs a fixed GoReleaser binary and release behavior remains
deterministic—update the version field in the same step that references
goreleaser/goreleaser-action and keep args: release --clean untouched.

17-22: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add explicit permissions for the reusable build-and-upload job (contents + packages)

This is a workflow_call reusable workflow and it currently has no job/workflow permissions, so the GITHUB_TOKEN scopes depend on the caller/default. The steps using goreleaser/goreleaser-action and gooci to push to GHCR need contents: write and packages: write for the provided GITHUB_TOKEN to publish successfully.

Suggested change
 jobs:
   build-and-upload:
     runs-on: ubuntu-latest
+    permissions:
+      contents: write
+      packages: write
     steps:

Pin goreleaser/goreleaser-action’s with.version to an exact GoReleaser v2.x.y release (avoid the floating ~> v2) to improve release reproducibility.

🤖 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 @.github/workflows/build-and-upload.yml around lines 17 - 22, This reusable
workflow needs explicit workflow permissions and a fixed GoReleaser action
version: add a top-level permissions block granting contents: write and
packages: write for the workflow_call so the GITHUB_TOKEN can push to GHCR, and
replace the floating with: "~> v2" in the goreleaser/goreleaser-action step's
with.version with an exact v2.x.y tag (pin to a concrete GoReleaser v2 release)
to ensure reproducible releases; locate the goreleaser step
(goreleaser/goreleaser-action and its with.version) and the workflow declaration
to add the permissions.
.github/workflows/test.yml (1)

10-18: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restrict the reusable test job to read-only GITHUB_TOKEN permissions.

.github/workflows/test.yml is invoked via workflow_call, and jobs.test has no permissions block; add permissions: contents: read so the workflow doesn’t inherit broader repository defaults.

Suggested change
 jobs:
   test:
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     steps:
🤖 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 @.github/workflows/test.yml around lines 10 - 18, The reusable workflow's
test job lacks a permissions block, so add a permissions entry for the job named
"test" (jobs.test) to restrict GITHUB_TOKEN to read-only access: add a
permissions section with contents: read under jobs.test in
.github/workflows/test.yml so the workflow_call cannot inherit broader repo
permissions; update the workflow YAML to include this permissions block adjacent
to the job definition that contains the steps (actions/checkout, setup-go, go
mod download/verify, go test).
🤖 Prompt for all review comments with 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.

Outside diff comments:
In @.github/workflows/build-and-upload.yml:
- Around line 17-20: The workflow uses the goreleaser action
'goreleaser/goreleaser-action' with a moving version specifier version: "~> v2";
change this to an exact validated tag (e.g. v2.x.y) so the action installs a
fixed GoReleaser binary and release behavior remains deterministic—update the
version field in the same step that references goreleaser/goreleaser-action and
keep args: release --clean untouched.
- Around line 17-22: This reusable workflow needs explicit workflow permissions
and a fixed GoReleaser action version: add a top-level permissions block
granting contents: write and packages: write for the workflow_call so the
GITHUB_TOKEN can push to GHCR, and replace the floating with: "~> v2" in the
goreleaser/goreleaser-action step's with.version with an exact v2.x.y tag (pin
to a concrete GoReleaser v2 release) to ensure reproducible releases; locate the
goreleaser step (goreleaser/goreleaser-action and its with.version) and the
workflow declaration to add the permissions.

In @.github/workflows/test.yml:
- Around line 10-18: The reusable workflow's test job lacks a permissions block,
so add a permissions entry for the job named "test" (jobs.test) to restrict
GITHUB_TOKEN to read-only access: add a permissions section with contents: read
under jobs.test in .github/workflows/test.yml so the workflow_call cannot
inherit broader repo permissions; update the workflow YAML to include this
permissions block adjacent to the job definition that contains the steps
(actions/checkout, setup-go, go mod download/verify, go test).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 70c8fab3-324f-4870-af9f-bc508e6966b6

📥 Commits

Reviewing files that changed from the base of the PR and between a73516b and 420c8cf.

📒 Files selected for processing (5)
  • .github/workflows/build-and-upload.yml
  • .github/workflows/test.yml
  • README.md
  • collector.go
  • collector_test.go

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
collector_test.go (2)

204-218: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid order-dependent assertions for record content.

Line 204 assumes result.Records[0] is the arn1 record. If record ordering changes, this test can become flaky.

Suggested fix
-	rec := result.Records[0]
+	var rec ResourceRecord
+	found := false
+	for _, r := range result.Records {
+		if r.Labels["resource_arn"] == arn1 {
+			rec = r
+			found = true
+			break
+		}
+	}
+	if !found {
+		t.Fatalf("record for %s not found", arn1)
+	}
🤖 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 `@collector_test.go` around lines 204 - 218, The test is making order-dependent
assertions by indexing result.Records[0]; modify the test to find the specific
record for "arn1" instead of assuming position: iterate result.Records to locate
the record whose identifier (e.g., rec.Input.ARN or rec.Input.Arn) equals the
expected arn string, assign that to rec, and then run the existing checks
against rec.Input.Config["kms_key_id"], ["deprecated_version_count"],
["replication_status"], and
rec.Input.Dynamic["cloudtrail_events"]/["iam_credential_removal_events"] so the
assertions are stable regardless of record ordering.

298-300: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make deleted_date assertion type-safe to avoid false positives.

Line 298 currently passes if deleted_date is missing (nil), because nil != "". This can hide regressions.

Suggested fix
-	if rec.Input.Config["deleted_date"] == "" {
-		t.Fatalf("deleted_date not exposed")
-	}
+	deletedDate, ok := rec.Input.Config["deleted_date"].(string)
+	if !ok || deletedDate == "" {
+		t.Fatalf("deleted_date not exposed: %#v", rec.Input.Config["deleted_date"])
+	}
🤖 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 `@collector_test.go` around lines 298 - 300, The current test compares
rec.Input.Config["deleted_date"] to "" which passes if the key is missing or
nil; change the assertion to be type-safe by first checking the key exists and
the value is a string: retrieve v, ok := rec.Input.Config["deleted_date"], fail
if !ok; then assert s, ok := v.(string) and fail if !ok or s == "" so you ensure
the key is present and holds a non-empty string (use the same rec.Input.Config
lookup and "deleted_date" identifier).
🤖 Prompt for all review comments with 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.

Outside diff comments:
In `@collector_test.go`:
- Around line 204-218: The test is making order-dependent assertions by indexing
result.Records[0]; modify the test to find the specific record for "arn1"
instead of assuming position: iterate result.Records to locate the record whose
identifier (e.g., rec.Input.ARN or rec.Input.Arn) equals the expected arn
string, assign that to rec, and then run the existing checks against
rec.Input.Config["kms_key_id"], ["deprecated_version_count"],
["replication_status"], and
rec.Input.Dynamic["cloudtrail_events"]/["iam_credential_removal_events"] so the
assertions are stable regardless of record ordering.
- Around line 298-300: The current test compares
rec.Input.Config["deleted_date"] to "" which passes if the key is missing or
nil; change the assertion to be type-safe by first checking the key exists and
the value is a string: retrieve v, ok := rec.Input.Config["deleted_date"], fail
if !ok; then assert s, ok := v.(string) and fail if !ok or s == "" so you ensure
the key is present and holds a non-empty string (use the same rec.Input.Config
lookup and "deleted_date" identifier).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2f2b6552-2fda-4af9-afb9-f22418fbec1d

📥 Commits

Reviewing files that changed from the base of the PR and between 420c8cf and 4379717.

📒 Files selected for processing (5)
  • README.md
  • collector.go
  • collector_test.go
  • main.go
  • main_test.go

Comment thread main.go Outdated
@ccf-lisa ccf-lisa Bot removed the ready-for-e2e label May 29, 2026
@ccf-lisa
Copy link
Copy Markdown
Author

ccf-lisa Bot commented May 29, 2026

No new automated feedback in the past 62 minutes. Waiting for a human review before continuing.

@gusfcarvalho gusfcarvalho merged commit 0a1bb21 into main May 29, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant