Skip to content

Conversation

@audacioustux
Copy link

@audacioustux audacioustux commented Nov 27, 2025

Summary

Adds CloudEvents v1.0 webhook handler to support AWS ECR push events via EventBridge.

Motivation

Instead of adding registry-specific webhook handlers for each cloud provider (ECR, GCR, ACR), this uses the CloudEvents specification to provide a standardized approach. AWS EventBridge can transform native ECR events to CloudEvents format using input transformers.

Changes

  • Add - CloudEvents webhook handler
  • Add - Comprehensive test suite (60+ tests)
  • Add flag to webhook/run commands
  • Add documentation in docs/configuration/webhook.md
  • Add Terraform example for EventBridge setup in config/examples/cloudevents/
  • Add trace logging for debugging webhook payloads

Testing

All tests pass:

go test ./pkg/webhook -run CloudEvents -v  # 60+ tests PASS
go test ./pkg/webhook -v                    # All webhook tests PASS
go test ./cmd -v                            # Integration tests PASS

Terraform validated:

terraform validate  # Success!

Example Usage

argocd-image-updater webhook --cloudevents-webhook-secret=$SECRET

EventBridge transforms ECR events to:

{
  "specversion": "1.0",
  "type": "com.amazon.ecr.image.push",
  "source": "urn:aws:ecr:us-east-1:123456789012:repository/my-repo",
  "subject": "my-repo:v1.0.0",
  "data": {
    "repositoryName": "my-repo",
    "imageTag": "v1.0.0",
    "imageDigest": "sha256:..."
  }
}

Summary by CodeRabbit

  • New Features

    • Support for AWS ECR image-push events via CloudEvents/EventBridge and a CloudEvents webhook with optional secret validation.
    • New CLI flag and environment variable to supply the CloudEvents webhook secret.
  • Documentation

    • Added CloudEvents setup guide, Terraform example (with outputs/variables), usage quickstart, and an end-to-end test script.
  • Tests

    • Added comprehensive tests for CloudEvents validation, parsing, and registry/source extraction.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Walkthrough

Adds CloudEvents webhook support: a new CloudEventsSecret config/CLI flag, a CloudEvents webhook handler that validates and parses CloudEvents (including AWS ECR/EventBridge shapes), example Terraform and test scripts, and documentation updates for CloudEvents/ECR integration.

Changes

Cohort / File(s) Summary
CLI configuration
cmd/common.go, cmd/run.go, cmd/webhook.go
Add CloudEventsSecret string to WebhookConfig; bind --cloudevents-webhook-secret (env CLOUDEVENTS_WEBHOOK_SECRET) and update webhook help text to reference AWS ECR (EventBridge/CloudEvents).
CloudEvents webhook implementation & tests
pkg/webhook/cloudevents.go, pkg/webhook/cloudevents_test.go
New CloudEventsWebhook type with NewCloudEventsWebhook, GetRegistryType, Validate, Parse, and helpers to extract registry URL; supports optional secret via header/query, validates POST and CloudEvents JSON, parses AWS ECR and generic container/image events; comprehensive unit tests.
Examples: Terraform & testing
config/examples/cloudevents/README.md, config/examples/cloudevents/terraform/main.tf, config/examples/cloudevents/terraform/variables.tf, config/examples/cloudevents/terraform/outputs.tf, config/examples/cloudevents/test-webhook.sh
Add EventBridge → API Destination Terraform example (ECR push → CloudEvents webhook) with variables/outputs, README, and a bash test script exercising CloudEvents scenarios and secret handling.
Documentation
docs/configuration/webhook.md
Document AWS ECR via EventBridge (CloudEvents) support, prerequisites, example setup, CloudEvents payload formats, and CLOUDEVENTS_WEBHOOK_SECRET environment/secret mapping.
sequenceDiagram
    autonumber
    participant ECR as AWS ECR
    participant EB as EventBridge
    participant API as API Destination
    participant Webhook as Webhook Server
    participant Parser as CloudEventsWebhook

    ECR->>EB: emit image.push event
    EB->>API: rule matches → invoke API Destination (CloudEvent)
    API->>Webhook: POST CloudEvents JSON (header or ?secret)
    Webhook->>Webhook: validate method, Content-Type, secret
    Webhook->>Parser: pass request body
    Parser->>Parser: parse CloudEvent fields (specversion,type,source,subject,data)
    Parser->>Webhook: return WebhookEvent {RegistryURL, Repository, Tag, Digest}
Loading

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding CloudEvents webhook support for AWS ECR, which aligns with the core functionality introduced across multiple files.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08d544e and 0b0f8c2.

📒 Files selected for processing (1)
  • cmd/common.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
cmd/common.go (1)
pkg/webhook/cloudevents.go (1)
  • NewCloudEventsWebhook (23-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build_image
  • GitHub Check: Ensure registry-scanner Go modules synchronicity and run tests
  • GitHub Check: Ensure unit tests are passing
  • GitHub Check: Ensure code is correctly linted
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
cmd/common.go (2)

24-37: WebhookConfig field comments and CloudEventsSecret addition look correct

The new/updated comments accurately describe the semantics of the webhook secrets and the hourly rate‑limit field; the CloudEventsSecret field is clearly documented and fits well with the existing secret fields. No functional or documentation issues here.


123-129: CloudEvents webhook registration wiring looks good; confirm empty‑secret behavior

Registering webhook.NewCloudEventsWebhook(webhookCfg.CloudEventsSecret) alongside the existing registry webhooks correctly wires the new CloudEvents handler using the configured secret and matches the constructor shape in pkg/webhook/cloudevents.go. Please double‑check that the behavior when CloudEventsSecret is left empty (handler still registered with an empty secret) matches your intended default (e.g., “no validation but still accept CloudEvents” vs “require a secret”).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov-commenter
Copy link

codecov-commenter commented Nov 27, 2025

Codecov Report

❌ Patch coverage is 95.31250% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.47%. Comparing base (73263e7) to head (0b0f8c2).

Files with missing lines Patch % Lines
pkg/webhook/cloudevents.go 95.12% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1370      +/-   ##
==========================================
+ Coverage   70.80%   71.47%   +0.66%     
==========================================
  Files          49       50       +1     
  Lines        4528     4655     +127     
==========================================
+ Hits         3206     3327     +121     
- Misses       1125     1128       +3     
- Partials      197      200       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@audacioustux audacioustux force-pushed the feat/cloudevents-webhook branch from 6017d44 to f66440c Compare November 27, 2025 16:11
Copy link

@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: 2

🧹 Nitpick comments (3)
pkg/webhook/cloudevents_test.go (1)

124-136: Consider adding test case for tag containing colon.

The subject parsing logic at line 131-134 in cloudevents.go uses strings.Split(payload.Subject, ":") with len(parts) == 2. Tags like sha256:abc123 or timestamps like 2025-01-01:latest would not parse correctly from the subject. Consider adding a test case to document this behavior or verify the data fields take precedence.

Example test case to add:

{
    name: "ECR event with colon in tag uses data fields",
    payload: map[string]interface{}{
        "specversion":     "1.0",
        "id":              "test-id",
        "type":            "com.amazon.ecr.image.push",
        "source":          "urn:aws:ecr:us-east-1:123456789012:repository/myrepo",
        "subject":         "myrepo:v1:latest", // malformed subject
        "time":            "2025-11-27T10:00:00Z",
        "datacontenttype": "application/json",
        "data": map[string]interface{}{
            "repositoryName": "myrepo",
            "imageDigest":    "sha256:abc",
            "imageTag":       "v1:latest",
            "registryId":     "123456789012",
        },
    },
    wantRegistryURL: "123456789012.dkr.ecr.us-east-1.amazonaws.com",
    wantRepository:  "myrepo",
    wantTag:         "v1:latest",
    wantDigest:      "sha256:abc",
    wantErr:         false,
},
pkg/webhook/cloudevents.go (2)

130-135: Subject parsing is fragile for tags containing colons.

The split on : with len(parts) == 2 check will fail for tags containing colons (e.g., repo:v1:hotfix or timestamps). Since data fields override subject anyway, consider using strings.SplitN with a limit of 2 to handle this edge case gracefully.

-	if parts := strings.Split(payload.Subject, ":"); len(parts) == 2 {
-		repository = parts[0]
-		tag = parts[1]
+	if parts := strings.SplitN(payload.Subject, ":", 2); len(parts) == 2 {
+		repository = parts[0]
+		tag = parts[1]
 		logCtx.Tracef("Extracted from subject: repository=%s, tag=%s", repository, tag)
 	}

227-252: Simplify extractECRRegistryURL by removing duplicate parsing.

The fallback block at lines 239-248 re-parses the source even though the primary block at lines 229-236 already extracts both region and account. If the primary parsing succeeds, the fallback is never needed. If it fails, the fallback with registryId still requires a valid source prefix to get the region, making it partially redundant.

 func (c *CloudEventsWebhook) extractECRRegistryURL(source string, data map[string]interface{}) string {
 	// Try to extract from source URN
 	if strings.HasPrefix(source, "urn:aws:ecr:") {
 		parts := strings.Split(source, ":")
 		if len(parts) >= 5 {
 			region := parts[3]
 			account := parts[4]
+			// Use registryId from data if available, otherwise use account from source
+			if registryID, ok := data["registryId"].(string); ok && registryID != "" {
+				account = registryID
+			}
 			return fmt.Sprintf("%s.dkr.ecr.%s.amazonaws.com", account, region)
 		}
 	}
-
-	// Fallback: try to extract from data
-	if registryID, ok := data["registryId"].(string); ok {
-		// If we have registryId, we need region to construct the URL
-		// Try to extract region from source
-		if strings.HasPrefix(source, "urn:aws:ecr:") {
-			parts := strings.Split(source, ":")
-			if len(parts) >= 4 {
-				region := parts[3]
-				return fmt.Sprintf("%s.dkr.ecr.%s.amazonaws.com", registryID, region)
-			}
-		}
-	}
-
 	return ""
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 73263e7 and f66440c.

📒 Files selected for processing (11)
  • cmd/common.go (2 hunks)
  • cmd/run.go (1 hunks)
  • cmd/webhook.go (2 hunks)
  • config/examples/cloudevents/README.md (1 hunks)
  • config/examples/cloudevents/terraform/main.tf (1 hunks)
  • config/examples/cloudevents/terraform/outputs.tf (1 hunks)
  • config/examples/cloudevents/terraform/variables.tf (1 hunks)
  • config/examples/cloudevents/test-webhook.sh (1 hunks)
  • docs/configuration/webhook.md (5 hunks)
  • pkg/webhook/cloudevents.go (1 hunks)
  • pkg/webhook/cloudevents_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
cmd/run.go (1)
registry-scanner/pkg/env/env.go (1)
  • GetStringVal (31-37)
cmd/common.go (1)
pkg/webhook/cloudevents.go (1)
  • NewCloudEventsWebhook (22-26)
cmd/webhook.go (1)
registry-scanner/pkg/env/env.go (1)
  • GetStringVal (31-37)
pkg/webhook/cloudevents_test.go (2)
pkg/webhook/cloudevents.go (1)
  • NewCloudEventsWebhook (22-26)
registry-scanner/pkg/registry/mocks/Repository.go (1)
  • Repository (15-17)
pkg/webhook/cloudevents.go (2)
registry-scanner/pkg/log/log.go (4)
  • NewContext (34-40)
  • Tracef (159-162)
  • Errorf (183-186)
  • LogContext (26-31)
pkg/argocd/types.go (1)
  • WebhookEvent (196-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Ensure code is correctly linted
  • GitHub Check: Ensure unit tests are passing
  • GitHub Check: Ensure registry-scanner Go modules synchronicity and run tests
  • GitHub Check: Analyze (go)
  • GitHub Check: build_image
🔇 Additional comments (16)
cmd/common.go (2)

29-29: LGTM!

The new CloudEventsSecret field follows the established pattern for other webhook secrets in the struct.


121-121: LGTM!

The CloudEvents webhook handler registration follows the same pattern as existing handlers (Docker, GHCR, Harbor, Quay), maintaining consistency.

config/examples/cloudevents/README.md (1)

1-35: LGTM!

Clear and well-structured documentation for the CloudEvents webhook example. The quick start guide, file listing, and documentation link provide a good onboarding experience.

cmd/run.go (1)

307-307: LGTM!

The new CLI flag follows the established pattern for other webhook secrets, with consistent naming and environment variable binding.

config/examples/cloudevents/test-webhook.sh (1)

14-92: LGTM!

Good test coverage with four distinct scenarios: successful ECR push, generic container push with CloudEvents content-type, missing secret validation, and invalid event type handling. The || echo "Expected failure" pattern correctly handles expected failures with set -e.

config/examples/cloudevents/terraform/outputs.tf (1)

1-19: LGTM!

Well-structured Terraform outputs with clear descriptions. The outputs expose useful ARNs for debugging and integration purposes.

cmd/webhook.go (2)

44-45: LGTM!

Help text correctly updated to include Harbor and AWS ECR via EventBridge CloudEvents in the list of supported registries.


93-93: LGTM!

The CLI flag binding is consistent with the run command (cmd/run.go, line 307) and follows the established pattern for other webhook secrets.

docs/configuration/webhook.md (2)

12-12: Documentation is clear and comprehensive.

The AWS ECR via EventBridge CloudEvents section provides thorough setup instructions, prerequisites, and example payloads. The documentation follows the existing patterns for other registries.

Also applies to: 88-159


161-162: Unable to verify directory existence due to repository access issues.

The sandbox environment is experiencing persistent failures when attempting to clone the repository, preventing verification of whether the config/examples/cloudevents/kubernetes/ directory exists in the PR. Manual verification of this directory in the repository is required to confirm whether the documentation reference is accurate or if the directory needs to be added.

config/examples/cloudevents/terraform/main.tf (2)

1-141: Well-structured Terraform configuration following AWS best practices.

The module correctly implements:

  • Least-privilege IAM with scoped InvokeApiDestination permission
  • API_KEY authentication via header
  • Configurable repository filtering
  • Retry policy for resilience

118-134: Based on the web search verification results, I have sufficient information to assess the review comment.

The review comment is substantively correct and identifies a real issue.

The web search confirms:

  1. ECR does send events without image-tag for untagged/manifest-only pushes — the event detail will either omit the field entirely or have it as null/empty
  2. EventBridge InputTransformer with missing source paths — when the <tag> placeholder references a path that doesn't exist in the source event, it will either fail to substitute or produce invalid JSON
  3. The current template is vulnerable — using "imageTag": "<tag>" means untagged image pushes will either cause the transformer to fail or produce malformed CloudEvents

The concern about handling manifest-only ECR pushes (which lack tags) is legitimate and should be addressed through either:

  • Adding filtering rules to exclude untagged images
  • Making the imageTag field optional in the CloudEvents template
  • Documenting this limitation
**Input template will fail or produce invalid JSON for untagged ECR images due to missing `image-tag` field.**

ECR manifest-only pushes (untagged images) do not include an `image-tag` in the EventBridge event detail. The current template uses `"imageTag": "<tag>"` which will cause the InputTransformer to fail when this field is absent. Either filter these events in the rule or modify the template to handle missing tags gracefully.
config/examples/cloudevents/terraform/variables.tf (1)

1-32: Variables are well-defined with appropriate types and sensitivity markers.

The webhook_secret is correctly marked as sensitive = true, preventing it from appearing in logs or plan outputs.

pkg/webhook/cloudevents_test.go (2)

19-122: Comprehensive validation test coverage.

The test suite covers key validation scenarios including HTTP method, content types, query/header secrets, and missing/invalid secrets.


267-420: Good coverage of generic CloudEvents parsing.

Tests appropriately cover alternate field names, source URL extraction, and error cases for missing specversion/type.

pkg/webhook/cloudevents.go (1)

14-31: Clean implementation following established webhook patterns.

The struct design, constructor, and parsing logic are well-organized. Trace logging for debugging webhook payloads is a good addition for troubleshooting.

Also applies to: 74-115

@audacioustux audacioustux force-pushed the feat/cloudevents-webhook branch 2 times, most recently from 9cc93c0 to b3cac08 Compare November 27, 2025 16:39
Copy link

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
docs/configuration/webhook.md (1)

247-256: Fix GHCR flag name; CloudEvents env mapping looks correct

The new row mapping CLOUDEVENTS_WEBHOOK_SECRET--cloudevents-webhook-secret is consistent with cmd/webhook.go. However, the GHCR row above documents --gchr-webhook-secret, while the actual flag in cmd/webhook.go is --ghcr-webhook-secret. Consider correcting that flag name in this table while touching this area.

🧹 Nitpick comments (2)
config/examples/cloudevents/test-webhook.sh (1)

14-90: Consider enforcing expected success/failure via curl exit codes

Right now the script just prints HTTP status codes, and the “expected to fail” tests rely on curl exiting non‑zero, which won’t happen for HTTP 4xx/5xx without --fail/--fail-with-body. To make this a more reliable e2e check, consider adding --fail-with-body (or parsing %{http_code}) for tests 1–4 and explicitly asserting on success vs failure instead of relying on manual inspection.

pkg/webhook/cloudevents.go (1)

188-195: Make generic registry URL extraction robust to blank values

In the generic container/image path, registryUrl/registry are treated as authoritative as long as the key exists, even if the value is an empty string, which prevents falling back to extractRegistryFromSource and leads straight to the “registry URL not found” error. If you want to be resilient to partially-populated data, consider also checking for non-empty strings before assignment, e.g.:

-    if regURL, ok := payload.Data["registryUrl"].(string); ok {
+    if regURL, ok := payload.Data["registryUrl"].(string); ok && regURL != "" {
         registryURL = regURL
-    } else if regURL, ok := payload.Data["registry"].(string); ok {
+    } else if regURL, ok := payload.Data["registry"].(string); ok && regURL != "" {
         registryURL = regURL
     } else {
         registryURL = c.extractRegistryFromSource(payload.Source)
     }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fa632b and b3cac08.

📒 Files selected for processing (11)
  • cmd/common.go (2 hunks)
  • cmd/run.go (1 hunks)
  • cmd/webhook.go (2 hunks)
  • config/examples/cloudevents/README.md (1 hunks)
  • config/examples/cloudevents/terraform/main.tf (1 hunks)
  • config/examples/cloudevents/terraform/outputs.tf (1 hunks)
  • config/examples/cloudevents/terraform/variables.tf (1 hunks)
  • config/examples/cloudevents/test-webhook.sh (1 hunks)
  • docs/configuration/webhook.md (5 hunks)
  • pkg/webhook/cloudevents.go (1 hunks)
  • pkg/webhook/cloudevents_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • cmd/common.go
  • config/examples/cloudevents/terraform/outputs.tf
  • pkg/webhook/cloudevents_test.go
  • config/examples/cloudevents/terraform/main.tf
  • config/examples/cloudevents/terraform/variables.tf
  • cmd/run.go
🧰 Additional context used
🧬 Code graph analysis (2)
cmd/webhook.go (1)
registry-scanner/pkg/env/env.go (1)
  • GetStringVal (31-37)
pkg/webhook/cloudevents.go (3)
registry-scanner/pkg/log/log.go (4)
  • NewContext (34-40)
  • Tracef (159-162)
  • Errorf (183-186)
  • LogContext (26-31)
pkg/argocd/types.go (1)
  • WebhookEvent (196-205)
registry-scanner/pkg/registry/mocks/Repository.go (1)
  • Repository (15-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Analyze (go)
  • GitHub Check: Ensure unit tests are passing
  • GitHub Check: Ensure registry-scanner Go modules synchronicity and run tests
  • GitHub Check: Ensure code is correctly linted
  • GitHub Check: build_image
🔇 Additional comments (3)
docs/configuration/webhook.md (1)

102-164: CloudEvents/ECR documentation aligns with implementation

The new “AWS ECR via EventBridge CloudEvents” section (payload shape, webhook URL with type=cloudevents, and secret handling via query or X-Webhook-Secret) matches the CloudEvents handler and examples. This gives users a clear, end‑to‑end path from EventBridge to the webhook.

cmd/webhook.go (1)

40-46: CloudEvents webhook CLI wiring is consistent

The updated help text and the new --cloudevents-webhook-secret flag (backed by CLOUDEVENTS_WEBHOOK_SECRET and webhookCfg.CloudEventsSecret) line up with the documentation and secret configuration model used for other registries. No issues here.

Also applies to: 88-94

pkg/webhook/cloudevents.go (1)

34-73: CloudEvents validation and secret handling look correct

The handler enforces POST, performs a sensible content-type check for structured CloudEvents JSON, and supports a configurable secret via query parameter or X-Webhook-Secret, returning clear errors for missing/invalid secrets. This fits the documented contract and doesn’t conflict with body parsing later on.

@audacioustux audacioustux force-pushed the feat/cloudevents-webhook branch from b3cac08 to ff24c38 Compare November 27, 2025 16:57
Adds CloudEvents v1.0 webhook handler to support AWS ECR push events
via EventBridge, using the CloudEvents specification for a standardized
approach instead of registry-specific handlers.

Changes:
- Add pkg/webhook/cloudevents.go with CloudEvents webhook handler
- Add pkg/webhook/cloudevents_test.go with comprehensive test suite (60+ tests)
- Add --cloudevents-webhook-secret flag to webhook/run commands
- Add documentation in docs/configuration/webhook.md
- Add Terraform example for EventBridge setup
- Add trace logging for debugging webhook payloads
- Use crypto/subtle.ConstantTimeCompare for secret validation

Signed-off-by: Tanjim Hossain <[email protected]>
@audacioustux audacioustux force-pushed the feat/cloudevents-webhook branch from ff24c38 to aaa02b5 Compare November 27, 2025 17:07
Copy link

@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: 1

🧹 Nitpick comments (5)
config/examples/cloudevents/test-webhook.sh (1)

58-74: set -e may not behave as expected for failure tests.

With set -e enabled (line 5), the script will exit on any command failure. However, curl returns exit code 0 even for HTTP 4xx/5xx responses unless --fail is used. The || echo "Expected failure" fallback won't trigger since curl succeeds at the transport level.

Consider adding -f or --fail to make curl return non-zero on HTTP errors, or remove set -e for a more forgiving test script that reports all results regardless of individual test outcomes.

docs/configuration/webhook.md (1)

151-155: Consider clarifying the recommended authentication method.

The documentation shows the secret in the query parameter (?type=cloudevents&secret=<YOUR_SECRET>) but then recommends using the X-Webhook-Secret header. Consider leading with the header-based approach since it's more secure and recommended for EventBridge Connection authentication.

pkg/webhook/cloudevents.go (3)

34-70: Avoid using query parameters for webhook secrets where possible.

The validation logic is sound and the constant‑time comparison is good, but accepting the secret via ?secret= risks exposure through access logs, proxies, and browser history. I’d recommend:

  • Prefer the X-Webhook-Secret header path in docs and examples.
  • Long‑term, consider deprecating or feature‑flagging the query‑param option to reduce accidental leakage.

75-104: Trace logging full payloads can be noisy; consider size limits.

Logging the entire request body at trace level is useful for debugging, but if events can grow large this may bloat logs or exceed log size limits. Consider truncating the payload (e.g., first N KB) or guarding the log behind an additional debug flag if you expect large events in some deployments.


118-201: Make ECR registry URL extraction more tolerant of different CloudEvents mappings.

parseEvent’s ECR branch currently depends on extractECRRegistryURL, which assumes the CloudEvents source has the fixed URN shape urn:aws:ecr:<region>:<account>:repository/<repo> and only uses data["registryId"] as an override. If a user tweaks the EventBridge → CloudEvents transform (for example, populating data.registryUrl directly or using a different source format), registryURL will end up empty and the event will be rejected.

To make this path more robust without breaking the happy path:

  • Prefer an explicit registryUrl field when present, and only fall back to deriving from source/registryId.
  • Optionally, extend extractECRRegistryURL to also look at additional fields (e.g., data["region"] / data["account"] / data["awsAccountId"]) if you expect those from alternative transforms.

Illustrative change:

 func (c *CloudEventsWebhook) parseEvent(payload *struct {
   ...
 }, logCtx *log.LogContext) (*argocd.WebhookEvent, error) {
   ...
   switch {
   case strings.HasPrefix(payload.Type, "com.amazon.ecr."):
     ...
-    // Extract registry URL from source
-    // Source format: urn:aws:ecr:<region>:<account>:repository/<repo>
-    registryURL = c.extractECRRegistryURL(payload.Source, payload.Data)
+    // Prefer explicit registryUrl in data, then fall back to source‑based derivation
+    if regURL, ok := payload.Data["registryUrl"].(string); ok && regURL != "" {
+      registryURL = regURL
+    } else {
+      // Source format: urn:aws:ecr:<region>:<account>:repository/<repo>
+      registryURL = c.extractECRRegistryURL(payload.Source, payload.Data)
+    }
 func (c *CloudEventsWebhook) extractECRRegistryURL(source string, data map[string]interface{}) string {
-  // Try to extract from source URN
+  // If the transform already provided a full registry URL, just use it
+  if regURL, ok := data["registryUrl"].(string); ok && regURL != "" {
+    return regURL
+  }
+
+  // Otherwise, try to extract from source URN
   if strings.HasPrefix(source, "urn:aws:ecr:") {
     ...
   }
   return ""
 }

This keeps your current Terraform example working, but makes the handler resilient to other CloudEvents mappings users might adopt.

Also applies to: 227-247

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3cac08 and aaa02b5.

📒 Files selected for processing (11)
  • cmd/common.go (2 hunks)
  • cmd/run.go (1 hunks)
  • cmd/webhook.go (2 hunks)
  • config/examples/cloudevents/README.md (1 hunks)
  • config/examples/cloudevents/terraform/main.tf (1 hunks)
  • config/examples/cloudevents/terraform/outputs.tf (1 hunks)
  • config/examples/cloudevents/terraform/variables.tf (1 hunks)
  • config/examples/cloudevents/test-webhook.sh (1 hunks)
  • docs/configuration/webhook.md (5 hunks)
  • pkg/webhook/cloudevents.go (1 hunks)
  • pkg/webhook/cloudevents_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • config/examples/cloudevents/README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • config/examples/cloudevents/terraform/main.tf
  • cmd/run.go
  • config/examples/cloudevents/terraform/outputs.tf
🧰 Additional context used
🧬 Code graph analysis (4)
cmd/common.go (1)
pkg/webhook/cloudevents.go (1)
  • NewCloudEventsWebhook (23-27)
pkg/webhook/cloudevents_test.go (3)
pkg/webhook/cloudevents.go (1)
  • NewCloudEventsWebhook (23-27)
test/utils/utils.go (1)
  • Run (50-67)
registry-scanner/pkg/registry/mocks/Repository.go (1)
  • Repository (15-17)
cmd/webhook.go (1)
registry-scanner/pkg/env/env.go (1)
  • GetStringVal (31-37)
pkg/webhook/cloudevents.go (2)
registry-scanner/pkg/log/log.go (4)
  • NewContext (34-40)
  • Tracef (159-162)
  • Errorf (183-186)
  • LogContext (26-31)
pkg/argocd/types.go (1)
  • WebhookEvent (196-205)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build_image
  • GitHub Check: Ensure registry-scanner Go modules synchronicity and run tests
  • GitHub Check: Ensure unit tests are passing
  • GitHub Check: Ensure code is correctly linted
  • GitHub Check: Analyze (go)
🔇 Additional comments (16)
config/examples/cloudevents/terraform/variables.tf (2)

1-32: Well-structured Terraform variables with appropriate security handling.

The variable declarations are clear, well-documented, and follow Terraform best practices. The sensitive = true flag is correctly applied to webhook_secret to protect credentials from accidental logging.


19-23: I'll help you verify that the ecr_repository_filter empty list behavior is properly implemented downstream. Let me search the codebase for the relevant implementation.
<function_calls>

#!/bin/bash

Search for ecr_repository_filter usage in terraform examples

rg -n "repository_filter|ecr_repository_filter" config/examples/cloudevents/terraform/ -A 3 -B 1


</function_calls>
<function_calls>

#!/bin/bash

List terraform files in the cloudevents example

ls -la config/examples/cloudevents/terraform/


</function_calls>

<function_calls>

#!/bin/bash

Check if main.tf exists and read its content

if [ -f config/examples/cloudevents/terraform/main.tf ]; then
wc -l config/examples/cloudevents/terraform/main.tf
fi


</function_calls>

config/examples/cloudevents/test-webhook.sh (1)

1-92: Test script provides good coverage of CloudEvents webhook scenarios.

The script tests ECR-specific events, generic container events, missing authentication, and invalid event types. The payloads align with the CloudEvents v1.0 specification and match the formats documented in the PR.

cmd/common.go (2)

29-29: LGTM!

The CloudEventsSecret field follows the established pattern used for other webhook secrets in the struct.


121-121: LGTM!

Registration of the CloudEvents webhook handler is consistent with the existing webhook handler registrations (Docker, GHCR, Harbor, Quay).

cmd/webhook.go (2)

44-45: LGTM!

Help text appropriately updated to include AWS ECR via EventBridge CloudEvents in the list of supported registries.


93-93: LGTM!

The CLI flag follows the established pattern for webhook secrets, with consistent naming (--cloudevents-webhook-secret) and environment variable binding (CLOUDEVENTS_WEBHOOK_SECRET).

docs/configuration/webhook.md (2)

12-12: Documentation updates are comprehensive and well-structured.

The AWS ECR via CloudEvents section provides clear prerequisites, setup steps, Terraform references, and payload format examples. Environment variables and secrets are properly documented alongside existing webhook configurations.

Also applies to: 88-99, 102-163, 177-177, 214-214, 255-255


160-163: The kubernetes directory reference cannot be verified with available tools.

The shell script execution failed due to a repository clone error, preventing direct verification of whether config/examples/cloudevents/kubernetes/ exists in the codebase. Without access to the repository filesystem, I cannot confirm whether the documentation's reference to this directory is accurate or if the directory exists with actual manifest files.

Manual verification is needed: Check if config/examples/cloudevents/kubernetes/ exists in the repository and contains Kubernetes manifest examples as referenced in the documentation.

pkg/webhook/cloudevents_test.go (5)

1-17: LGTM!

Clean imports and straightforward registry type test.


19-122: Comprehensive validation test coverage.

The table-driven tests cover all validation paths: valid requests with/without secrets, CloudEvents content type, query and header secret validation, and error cases for invalid methods, content types, and secrets.


124-265: Good ECR parsing test coverage.

Tests cover standard ECR events, namespaced repositories, subject fallback when data fields are missing, and error cases for missing repository and tag fields. The expected registry URL construction (<account>.dkr.ecr.<region>.amazonaws.com) is properly validated.


267-420: Solid generic CloudEvents parsing tests.

Tests validate parsing with different field naming conventions, source URL extraction, and proper error handling for missing specversion, type, and unsupported event types. The conditional digest assertion (line 414-416) correctly handles optional digest fields.


422-495: LGTM!

Helper function tests for extractECRRegistryURL and extractRegistryFromSource cover valid ECR URNs, fallback behavior, and various source URL formats including edge cases.

pkg/webhook/cloudevents.go (2)

1-32: CloudEventsWebhook type and constructor look consistent and minimal.

Struct, constructor, and GetRegistryType are straightforward and align with the existing webhook abstraction; no issues from a correctness or API‑surface perspective.


249-268: extractRegistryFromSource is simple and effective; consider url.Parse only if future needs grow.

The current string‑based extraction covers typical https://host/... and host.domain/path patterns without extra allocations or error handling. Unless you anticipate more exotic URI shapes, there’s no need to complicate this with net/url; the helper is fine as‑is.

@audacioustux audacioustux force-pushed the feat/cloudevents-webhook branch from 3b5c94b to 8d15807 Compare November 27, 2025 17:18
@audacioustux
Copy link
Author

@coderabbitai generate docstrings

@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Warning

Docstrings generation is disabled for your repository or organization.

@coderabbitai
Copy link

coderabbitai bot commented Nov 27, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the main CloudEvents webhook implementation
cat pkg/webhook/cloudevents.go

Length of output: 9040


🏁 Script executed:

#!/bin/bash
# Fetch cmd files with new exports
cat cmd/common.go

Length of output: 4438


🏁 Script executed:

#!/bin/bash
# Fetch cmd/webhook.go to see the new fields
cat cmd/webhook.go

Length of output: 6734


🏁 Script executed:

#!/bin/bash
# Fetch cmd/run.go
cat cmd/run.go

Length of output: 19303


@audacioustux Great news! I've reviewed the code and found that all exported types and functions already have proper Go docstrings. The CloudEvents implementation in pkg/webhook/cloudevents.go is well-documented.

However, I can suggest adding inline field comments for the WebhookConfig struct to improve documentation completeness:

// WebhookConfig holds the options for the webhook server
type WebhookConfig struct {
	// Port is the port number for the webhook server to listen on
	Port int
	// DockerSecret is the secret for validating Docker Hub webhooks
	DockerSecret string
	// GHCRSecret is the secret for validating GitHub Container Registry webhooks
	GHCRSecret string
	// QuaySecret is the secret for validating Quay webhooks
	QuaySecret string
	// HarborSecret is the secret for validating Harbor webhooks
	HarborSecret string
	// CloudEventsSecret is the secret for validating CloudEvents webhooks
	CloudEventsSecret string
	// RateLimitNumAllowedRequests is the number of allowed requests per hour for rate limiting
	RateLimitNumAllowedRequests int
}

The rest of your code already follows Go documentation best practices:

  • ✅ All exported types have docstrings
  • ✅ All exported functions/methods have docstrings
  • ✅ Comments explain the "why" not just the "what"
  • ✅ CloudEvents spec reference included
  • ✅ Complex logic (like ECR URL extraction) is well-commented

The existing documentation in pkg/webhook/cloudevents.go is excellent and requires no changes!

Improves documentation completeness by adding inline comments
to each field in the WebhookConfig struct, following Go
documentation best practices.

Signed-off-by: audacioustux <[email protected]>
Copy link

@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

🧹 Nitpick comments (1)
cmd/common.go (1)

24-37: WebhookConfig field docs look good; consider documenting “0 disables rate limiting”.

The new/updated comments for all webhook secrets and CloudEventsSecret are clear and consistent. Given that lines 133–135 treat RateLimitNumAllowedRequests <= 0 as “no rate limiting”, you might make that explicit in the comment to help operators understand config behavior:

-	// RateLimitNumAllowedRequests is the number of allowed requests per hour for rate limiting
+	// RateLimitNumAllowedRequests is the number of allowed requests per hour for rate limiting (0 disables rate limiting)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aaa02b5 and 08d544e.

📒 Files selected for processing (2)
  • cmd/common.go (2 hunks)
  • docs/configuration/webhook.md (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/configuration/webhook.md
🧰 Additional context used
🧬 Code graph analysis (1)
cmd/common.go (1)
pkg/webhook/cloudevents.go (1)
  • NewCloudEventsWebhook (23-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Ensure registry-scanner Go modules synchronicity and run tests
  • GitHub Check: Ensure unit tests are passing
  • GitHub Check: Ensure code is correctly linted
  • GitHub Check: build_image
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
cmd/common.go (1)

123-129: CloudEvents handler registration is consistent with existing webhook handlers.

Registering webhook.NewCloudEventsWebhook(webhookCfg.CloudEventsSecret) alongside the other registry-specific handlers follows the same pattern and matches the constructor signature from pkg/webhook/cloudevents.go. No issues spotted here.

Updates the RateLimitNumAllowedRequests field comment to explicitly
state that setting it to 0 disables rate limiting, helping operators
understand the configuration behavior.

Signed-off-by: audacioustux <[email protected]>
@audacioustux
Copy link
Author

@dkarpele @chengfang

Hi, are you available to review this PR?

This change adds support for a generic webhook endpoint. This avoids the need to maintain individual logic for every container registry (like AWS ECR). Instead, we can rely on external transformation (e.g., ECR -> EventBridge Input Transformation -> ArgoCD IU).

This allows users of any cloud platform to map their events to the CloudEvents schema using serverless compute or transformation rules.

I have pushed an image here for testing purposes: https://github.com/audacioustux/argocd-image-updater/pkgs/container/argocd-image-updater

Thank you!

@dkarpele
Copy link
Collaborator

Hi @audacioustux
This is a great contribution, Thank you very much!
We will try our best to review this ASAP but the PR is huge so it will take time.

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.

3 participants