Skip to content

add platform api changes for event gateway hmac secret generation#2237

Merged
tharindu1st merged 7 commits into
wso2:mainfrom
tharindu1st:platform-api-hmac-secret-generation
Jun 24, 2026
Merged

add platform api changes for event gateway hmac secret generation#2237
tharindu1st merged 7 commits into
wso2:mainfrom
tharindu1st:platform-api-hmac-secret-generation

Conversation

@tharindu1st

Copy link
Copy Markdown
Contributor

add platform api changes for event gateway hmac secret generation

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@tharindu1st, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 45 minutes and 7 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb199eae-b137-4ed2-87cb-3eff2f8f0618

📥 Commits

Reviewing files that changed from the base of the PR and between 6202dcd and 3e3db16.

📒 Files selected for processing (6)
  • .github/workflows/platform-api-pr-check.yml
  • platform-api/src/config/config.go
  • platform-api/src/internal/repository/subscription_repository.go
  • platform-api/src/internal/server/server.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go
  • platform-api/src/internal/utils/subscription_token_crypto.go
📝 Walkthrough

Walkthrough

This PR introduces end-to-end WebSub API HMAC secret management for inbound webhook verification. The platform-API side adds a websub_api_hmac_secrets database table across PostgreSQL, SQLite, and SQL Server backends. A repository layer implements CRUD operations, while the WebSubAPIHmacSecretService handles AES-256-GCM encryption, cryptographically secure random value generation with the whsec_ prefix, display-name slugification, and gateway event broadcasting. Public REST handlers provide create, list, delete, and regenerate operations with validation and error mapping. An internal gateway endpoint decrypts and returns plaintext secrets on demand. The API contract layer updates the OpenAPI specification and generated types, with manually preserved types in manual_types.go. The gateway-controller side extends the control-plane WebSocket client to subscribe to websub.hmacsecret.* events, fetch plaintext secrets from the platform internal API, and synchronize them into an in-memory webhook secret store with xDS snapshot refresh for Envoy distribution. Comprehensive unit tests cover encryption, random generation, validation, and error cases. Integration tests verify cascade deletion and pagination. Feature scenarios validate plaintext non-leakage and cross-API isolation. New GitHub Actions workflows and Docker Compose configurations support PostgreSQL and SQL Server integration testing with health verification and artifact collection.

Suggested reviewers

  • pubudu538
  • malinthaprasan
  • RakhithaRR
  • Tharsanan1
  • renuka-fernando
  • VirajSalaka
  • AnuGayan
  • chamilaadhi
  • Arshardh
  • dushaniw
  • tgtshanika
  • tharikaGitHub
  • CrowleyRajapakse
  • hisanhunais
  • HiranyaKavishani
  • HeshanSudarshana
  • ashera96
  • PasanT9
  • Krishanx92
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal, containing only the title repeated. It lacks all required template sections: Purpose, Goals, Approach, User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, and Test environment. Expand the description to include all template sections: clearly articulate why HMAC secret generation is needed, what specific solutions are implemented, how the implementation works, user stories affected, documentation links, test coverage details, security validation, samples if applicable, related PRs, and test environments used.
Docstring Coverage ⚠️ Warning Docstring coverage is 72.55% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main objective—adding platform API changes for event gateway HMAC secret generation—and is directly supported by the extensive changes across multiple files.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
platform-api/src/internal/service/gateway_events.go (1)

192-203: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Reject unsupported actions instead of silently mapping to UPDATED.

Line 201 silently coerces invalid actions, which can produce incorrect event semantics. Prefer returning an error for unknown values.

Suggested change
 func (s *GatewayEventsService) BroadcastWebSubAPIHmacSecretEvent(gatewayID, action string, event *model.WebSubAPIHmacSecretEvent) error {
 	var eventType string
 	switch action {
 	case "CREATED":
 		eventType = EventTypeWebSubAPIHmacSecretCreated
 	case "UPDATED":
 		eventType = EventTypeWebSubAPIHmacSecretUpdated
 	case "DELETED":
 		eventType = EventTypeWebSubAPIHmacSecretDeleted
 	default:
-		eventType = EventTypeWebSubAPIHmacSecretUpdated
+		return fmt.Errorf("unsupported hmac secret action: %s", action)
 	}
 	return s.broadcastEvent(gatewayID, eventType, event)
 }
🤖 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 `@platform-api/src/internal/service/gateway_events.go` around lines 192 - 203,
The default case in the switch statement within the
BroadcastWebSubAPIHmacSecretEvent method currently silently maps unknown actions
to EventTypeWebSubAPIHmacSecretUpdated, which masks unsupported input. Replace
the default case to return an error instead of coercing invalid actions,
ensuring that only the supported actions (CREATED, UPDATED, DELETED) are
processed and unknown actions are explicitly rejected with a descriptive error
message.
platform-api/src/api/generated.go (1)

473-478: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Inconsistent naming for WebSub visibility constants.

The generated constants Private and Public lack the type prefix, unlike the WebBroker equivalents (WebBrokerAPIDevPortalResponseVisibilityPrivate/Public). This may cause naming conflicts. If this is a code-generation artifact, consider updating the OpenAPI spec to ensure consistent naming.

🤖 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 `@platform-api/src/api/generated.go` around lines 473 - 478, The constants
Private and Public for WebSubAPIDevPortalResponseVisibility lack the type prefix
that exists in the equivalent WebBrokerAPIDevPortalResponseVisibility constants,
creating potential naming conflicts. Rename the Private constant to
WebSubAPIDevPortalResponseVisibilityPrivate and the Public constant to
WebSubAPIDevPortalResponseVisibilityPublic to maintain consistency with the
WebBroker equivalents. If this is generated code from an OpenAPI spec, ensure
the spec is updated to produce consistent naming patterns across all visibility
constant definitions.
🤖 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 `@gateway/gateway-controller/pkg/controlplane/client.go`:
- Around line 2499-2512: The RemoveAllByAPI error handler currently logs a
warning but continues to the Store loop, which can leave old and new secrets
mixed together and break the intended clear-and-replace behavior. Modify the
error handling for the RemoveAllByAPI call on the webhookSecretStore to abort
the entire operation by returning early or skipping the subsequent for loop that
stores new secrets, ensuring that the Store operations only execute if the
RemoveAllByAPI call succeeds.

In `@platform-api/src/config/config.go`:
- Around line 179-183: The HMACSecretEncryptionKey field is missing the koanf
tag and corresponding mapping in the envToKoanfKey function. Add a koanf tag to
the HMACSecretEncryptionKey struct field following the naming pattern of other
fields, and add a case statement in the envToKoanfKey function to map the
environment variable DATABASE_HMAC_SECRET_ENCRYPTION_KEY to the appropriate
koanf key path (following the same pattern as SubscriptionTokenEncryptionKey).
This ensures the environment variable will be properly loaded by the koanf
config mechanism.

In `@platform-api/src/internal/handler/websub_api_hmac_secret.go`:
- Around line 239-241: In the case statement handling errors.Is(err,
constants.ErrHmacSecretEncryptionKeyMissing), change the HTTP status code from
http.StatusInternalServerError (500) to http.StatusServiceUnavailable (503) to
align with the OpenAPI spec which defines 503 as the response for when HMAC
secret management is not configured. Keep the error message the same.

In `@platform-api/src/internal/service/websub_api_hmac_secret.go`:
- Around line 91-97: The current logic in the slugifyHmacSecret function
truncates the name to 63 characters, but when the name becomes empty and a
fallback value is assigned (name = "secret-" + apiHandle), there is no length
enforcement on this fallback. After the fallback name assignment, reapply the
max-length truncation logic to ensure the final name does not exceed 63
characters, matching the enforcement applied before the fallback assignment.

In `@platform-api/src/resources/gateway-internal-api.yaml`:
- Around line 819-825: The OpenAPI specification for this endpoint is missing
the 503 Service Unavailable response definition even though the handler returns
this status when HMAC secret management is not configured. Add a 503 response
entry after the existing 500 response in the responses section, with appropriate
description documenting the service-not-configured state, and include the same
ErrorResponse schema reference to align the spec with the actual runtime
behavior.

In `@platform-api/src/resources/openapi.yaml`:
- Around line 5804-5809: The regenerate endpoint's requestBody references the
WebSubAPIHmacSecretRequest schema which requires a displayName field, but this
conflicts with the regenerate contract where only a new secret value should be
needed (since the secret is already identified by secretName in the path).
Create a new schema specifically for the regenerate request (for example,
WebSubAPIHmacSecretRegenerateRequest) that only requires the secret field
without displayName, then update the requestBody reference in the regenerate
endpoint to use this new schema instead of WebSubAPIHmacSecretRequest.

---

Nitpick comments:
In `@platform-api/src/api/generated.go`:
- Around line 473-478: The constants Private and Public for
WebSubAPIDevPortalResponseVisibility lack the type prefix that exists in the
equivalent WebBrokerAPIDevPortalResponseVisibility constants, creating potential
naming conflicts. Rename the Private constant to
WebSubAPIDevPortalResponseVisibilityPrivate and the Public constant to
WebSubAPIDevPortalResponseVisibilityPublic to maintain consistency with the
WebBroker equivalents. If this is generated code from an OpenAPI spec, ensure
the spec is updated to produce consistent naming patterns across all visibility
constant definitions.

In `@platform-api/src/internal/service/gateway_events.go`:
- Around line 192-203: The default case in the switch statement within the
BroadcastWebSubAPIHmacSecretEvent method currently silently maps unknown actions
to EventTypeWebSubAPIHmacSecretUpdated, which masks unsupported input. Replace
the default case to return an error instead of coercing invalid actions,
ensuring that only the supported actions (CREATED, UPDATED, DELETED) are
processed and unknown actions are explicitly rejected with a descriptive error
message.
🪄 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: CHILL

Plan: Pro

Run ID: 98e4f5a1-baa3-470d-922b-20c9c4999616

📥 Commits

Reviewing files that changed from the base of the PR and between 01d2cb2 and e6a92a7.

📒 Files selected for processing (24)
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/controlplane/client.go
  • gateway/gateway-controller/pkg/controlplane/client_integration_test.go
  • gateway/gateway-controller/pkg/controlplane/controlplane_test.go
  • gateway/gateway-controller/pkg/utils/api_utils.go
  • platform-api/src/api/generated.go
  • platform-api/src/api/manual_types.go
  • platform-api/src/api/websub_hmac_secret.go
  • platform-api/src/config/config.go
  • platform-api/src/internal/constants/error.go
  • platform-api/src/internal/database/schema.postgres.sql
  • platform-api/src/internal/database/schema.sql
  • platform-api/src/internal/database/schema.sqlite.sql
  • platform-api/src/internal/dto/gateway_internal.go
  • platform-api/src/internal/handler/gateway_internal.go
  • platform-api/src/internal/handler/websub_api_hmac_secret.go
  • platform-api/src/internal/model/websub_api_hmac_secret.go
  • platform-api/src/internal/repository/interfaces.go
  • platform-api/src/internal/repository/websub_api_hmac_secret.go
  • platform-api/src/internal/server/server.go
  • platform-api/src/internal/service/gateway_events.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go
  • platform-api/src/resources/gateway-internal-api.yaml
  • platform-api/src/resources/openapi.yaml

Comment thread gateway/gateway-controller/pkg/controlplane/client.go
Comment thread platform-api/src/config/config.go Outdated
Comment thread platform-api/src/internal/handler/websub_api_hmac_secret.go Outdated
Comment thread platform-api/src/internal/service/websub_api_hmac_secret.go
Comment thread platform-api/src/resources/gateway-internal-api.yaml
Comment thread platform-api/src/resources/openapi.yaml Outdated
@tharindu1st tharindu1st force-pushed the platform-api-hmac-secret-generation branch from e6a92a7 to 29f3c04 Compare June 23, 2026 17:08
@tharindu1st tharindu1st force-pushed the platform-api-hmac-secret-generation branch from 29f3c04 to f51e579 Compare June 23, 2026 18:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 (1)
platform-api/src/internal/service/websub_api_hmac_secret_test.go (1)

128-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test coverage for name generation boundary conditions.

The Generate tests cover core functionality well but miss three boundary cases in the name generation logic (from service code):

  1. Display names that slugify to >63 characters (triggering truncation).
  2. Display names that slugify to empty strings (triggering fallback to "secret-" + apiHandle).
  3. Very long apiHandle values where the fallback name itself exceeds 63 characters (triggering second truncation).

These are distinct conditional branches in production code that should have explicit test coverage to prevent bugs in identifier generation.

Suggested test cases
// TestWebSubAPIHmacSecret_Generate_LongDisplayNameTruncated verifies that
// display names longer than 63 chars are truncated after slugification.
func TestWebSubAPIHmacSecret_Generate_LongDisplayNameTruncated(t *testing.T) {
	websubRepo := newMockWebSubAPIRepository()
	withWebSubAPI(websubRepo)
	hmacRepo := newMockHmacSecretRepo()
	svc := buildHmacSecretService(t, websubRepo, hmacRepo)

	longName := strings.Repeat("verylongname-", 10) // >63 chars after slug
	meta, _, err := svc.Generate("org-uuid", "test-api", longName, "")
	if err != nil {
		t.Fatalf("Generate with long name failed: %v", err)
	}
	if len(meta.Name) != 63 {
		t.Errorf("expected name truncated to 63 chars, got %d", len(meta.Name))
	}
}

// TestWebSubAPIHmacSecret_Generate_EmptySlugFallback verifies that
// display names that slugify to empty trigger fallback name generation.
func TestWebSubAPIHmacSecret_Generate_EmptySlugFallback(t *testing.T) {
	websubRepo := newMockWebSubAPIRepository()
	withWebSubAPI(websubRepo)
	hmacRepo := newMockHmacSecretRepo()
	svc := buildHmacSecretService(t, websubRepo, hmacRepo)

	meta, _, err := svc.Generate("org-uuid", "test-api", "!@#$%^", "")
	if err != nil {
		t.Fatalf("Generate with special-char-only name failed: %v", err)
	}
	if meta.Name != "secret-test-api" {
		t.Errorf("expected fallback name 'secret-test-api', got %q", meta.Name)
	}
}
🤖 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 `@platform-api/src/internal/service/websub_api_hmac_secret_test.go` around
lines 128 - 234, The test file is missing coverage for three boundary conditions
in the name generation logic of the Generate method. Add three new test
functions to cover: (1) display names longer than 63 characters that get
truncated after slugification by verifying the returned meta.Name length equals
63, (2) display names containing only special characters that slugify to empty
strings by verifying the fallback generates a name in the format
"secret-{apiHandle}", and (3) very long apiHandle values where the fallback name
itself exceeds 63 characters by verifying the truncation occurs on the fallback.
Follow the existing test structure in the file (using
newMockWebSubAPIRepository, newMockHmacSecretRepo, buildHmacSecretService, and
calling svc.Generate) to ensure consistency with the current test patterns.
🤖 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 `@platform-api/src/internal/database/schema.sqlserver.sql`:
- Around line 395-405: The FOREIGN KEY constraint on project_uuid in the
mcp_proxies table uses ON DELETE CASCADE, which causes automatic deletion of
mcp_proxy records when a project is deleted. However, the application manages
mcp_proxies cleanup at the application layer and expects the database to prevent
project deletion until dependent records are handled. Change the ON DELETE
CASCADE clause on line 404 (the FOREIGN KEY constraint referencing
projects(uuid)) to ON DELETE NO ACTION to enforce this contract and allow the
application to control the cleanup order.

In `@platform-api/src/internal/server/server.go`:
- Around line 237-241: The current logic derives an HMAC encryption key from
cfg.Auth.JWT.SecretKey even when that secret is empty, which produces a valid
hash and masks the missing-key error. Fix this by adding an additional check to
ensure cfg.Auth.JWT.SecretKey is not empty before performing the sha256 hashing
and hex encoding. Only apply the fallback key derivation when both
hmacEncryptionKey is empty AND cfg.Auth.JWT.SecretKey is non-empty, otherwise
leave hmacEncryptionKey empty so the missing-key validation path is properly
triggered.

---

Nitpick comments:
In `@platform-api/src/internal/service/websub_api_hmac_secret_test.go`:
- Around line 128-234: The test file is missing coverage for three boundary
conditions in the name generation logic of the Generate method. Add three new
test functions to cover: (1) display names longer than 63 characters that get
truncated after slugification by verifying the returned meta.Name length equals
63, (2) display names containing only special characters that slugify to empty
strings by verifying the fallback generates a name in the format
"secret-{apiHandle}", and (3) very long apiHandle values where the fallback name
itself exceeds 63 characters by verifying the truncation occurs on the fallback.
Follow the existing test structure in the file (using
newMockWebSubAPIRepository, newMockHmacSecretRepo, buildHmacSecretService, and
calling svc.Generate) to ensure consistency with the current test patterns.
🪄 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: CHILL

Plan: Pro

Run ID: e215386e-064e-4cd4-9038-4157d14f9c9a

📥 Commits

Reviewing files that changed from the base of the PR and between 29f3c04 and 35a4b57.

📒 Files selected for processing (37)
  • .github/workflows/event-gateway-integration-test-postgres.yml
  • .github/workflows/event-gateway-integration-test-sqlserver.yml
  • event-gateway/docker-compose.test.postgres.yaml
  • event-gateway/docker-compose.test.sqlserver.yaml
  • event-gateway/it/Makefile
  • event-gateway/it/features/websub-webhook-secrets.feature
  • event-gateway/it/suite_test.go
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/controlplane/client.go
  • gateway/gateway-controller/pkg/controlplane/client_integration_test.go
  • gateway/gateway-controller/pkg/controlplane/controlplane_test.go
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.sqlserver.sql
  • gateway/gateway-controller/pkg/utils/api_utils.go
  • platform-api/src/api/generated.go
  • platform-api/src/api/manual_types.go
  • platform-api/src/api/websub_hmac_secret.go
  • platform-api/src/config/config.go
  • platform-api/src/internal/constants/error.go
  • platform-api/src/internal/database/schema.postgres.sql
  • platform-api/src/internal/database/schema.sql
  • platform-api/src/internal/database/schema.sqlite.sql
  • platform-api/src/internal/database/schema.sqlserver.sql
  • platform-api/src/internal/dto/gateway_internal.go
  • platform-api/src/internal/handler/gateway_internal.go
  • platform-api/src/internal/handler/websub_api_hmac_secret.go
  • platform-api/src/internal/integration/cascade_test.go
  • platform-api/src/internal/integration/lifecycle_test.go
  • platform-api/src/internal/model/websub_api_hmac_secret.go
  • platform-api/src/internal/repository/interfaces.go
  • platform-api/src/internal/repository/websub_api_hmac_secret.go
  • platform-api/src/internal/server/server.go
  • platform-api/src/internal/service/gateway_events.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go
  • platform-api/src/internal/service/websub_api_hmac_secret_test.go
  • platform-api/src/resources/gateway-internal-api.yaml
  • platform-api/src/resources/openapi.yaml
✅ Files skipped from review due to trivial changes (3)
  • platform-api/src/api/websub_hmac_secret.go
  • platform-api/src/internal/constants/error.go
  • platform-api/src/api/generated.go
🚧 Files skipped from review as they are similar to previous changes (20)
  • gateway/gateway-controller/cmd/controller/main.go
  • platform-api/src/internal/dto/gateway_internal.go
  • event-gateway/it/features/websub-webhook-secrets.feature
  • platform-api/src/resources/gateway-internal-api.yaml
  • platform-api/src/internal/repository/interfaces.go
  • gateway/gateway-controller/pkg/controlplane/controlplane_test.go
  • platform-api/src/internal/service/gateway_events.go
  • platform-api/src/internal/database/schema.postgres.sql
  • platform-api/src/internal/model/websub_api_hmac_secret.go
  • platform-api/src/internal/repository/websub_api_hmac_secret.go
  • platform-api/src/api/manual_types.go
  • gateway/gateway-controller/pkg/controlplane/client_integration_test.go
  • platform-api/src/internal/handler/gateway_internal.go
  • platform-api/src/internal/database/schema.sql
  • platform-api/src/resources/openapi.yaml
  • platform-api/src/internal/database/schema.sqlite.sql
  • gateway/gateway-controller/pkg/utils/api_utils.go
  • gateway/gateway-controller/pkg/controlplane/client.go
  • platform-api/src/internal/handler/websub_api_hmac_secret.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🧹 Nitpick comments (1)
platform-api/src/internal/service/websub_api_hmac_secret_test.go (1)

128-234: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test coverage for name generation boundary conditions.

The Generate tests cover core functionality well but miss three boundary cases in the name generation logic (from service code):

  1. Display names that slugify to >63 characters (triggering truncation).
  2. Display names that slugify to empty strings (triggering fallback to "secret-" + apiHandle).
  3. Very long apiHandle values where the fallback name itself exceeds 63 characters (triggering second truncation).

These are distinct conditional branches in production code that should have explicit test coverage to prevent bugs in identifier generation.

Suggested test cases
// TestWebSubAPIHmacSecret_Generate_LongDisplayNameTruncated verifies that
// display names longer than 63 chars are truncated after slugification.
func TestWebSubAPIHmacSecret_Generate_LongDisplayNameTruncated(t *testing.T) {
	websubRepo := newMockWebSubAPIRepository()
	withWebSubAPI(websubRepo)
	hmacRepo := newMockHmacSecretRepo()
	svc := buildHmacSecretService(t, websubRepo, hmacRepo)

	longName := strings.Repeat("verylongname-", 10) // >63 chars after slug
	meta, _, err := svc.Generate("org-uuid", "test-api", longName, "")
	if err != nil {
		t.Fatalf("Generate with long name failed: %v", err)
	}
	if len(meta.Name) != 63 {
		t.Errorf("expected name truncated to 63 chars, got %d", len(meta.Name))
	}
}

// TestWebSubAPIHmacSecret_Generate_EmptySlugFallback verifies that
// display names that slugify to empty trigger fallback name generation.
func TestWebSubAPIHmacSecret_Generate_EmptySlugFallback(t *testing.T) {
	websubRepo := newMockWebSubAPIRepository()
	withWebSubAPI(websubRepo)
	hmacRepo := newMockHmacSecretRepo()
	svc := buildHmacSecretService(t, websubRepo, hmacRepo)

	meta, _, err := svc.Generate("org-uuid", "test-api", "!@#$%^", "")
	if err != nil {
		t.Fatalf("Generate with special-char-only name failed: %v", err)
	}
	if meta.Name != "secret-test-api" {
		t.Errorf("expected fallback name 'secret-test-api', got %q", meta.Name)
	}
}
🤖 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 `@platform-api/src/internal/service/websub_api_hmac_secret_test.go` around
lines 128 - 234, The test file is missing coverage for three boundary conditions
in the name generation logic of the Generate method. Add three new test
functions to cover: (1) display names longer than 63 characters that get
truncated after slugification by verifying the returned meta.Name length equals
63, (2) display names containing only special characters that slugify to empty
strings by verifying the fallback generates a name in the format
"secret-{apiHandle}", and (3) very long apiHandle values where the fallback name
itself exceeds 63 characters by verifying the truncation occurs on the fallback.
Follow the existing test structure in the file (using
newMockWebSubAPIRepository, newMockHmacSecretRepo, buildHmacSecretService, and
calling svc.Generate) to ensure consistency with the current test patterns.
🤖 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 `@platform-api/src/internal/database/schema.sqlserver.sql`:
- Around line 395-405: The FOREIGN KEY constraint on project_uuid in the
mcp_proxies table uses ON DELETE CASCADE, which causes automatic deletion of
mcp_proxy records when a project is deleted. However, the application manages
mcp_proxies cleanup at the application layer and expects the database to prevent
project deletion until dependent records are handled. Change the ON DELETE
CASCADE clause on line 404 (the FOREIGN KEY constraint referencing
projects(uuid)) to ON DELETE NO ACTION to enforce this contract and allow the
application to control the cleanup order.

In `@platform-api/src/internal/server/server.go`:
- Around line 237-241: The current logic derives an HMAC encryption key from
cfg.Auth.JWT.SecretKey even when that secret is empty, which produces a valid
hash and masks the missing-key error. Fix this by adding an additional check to
ensure cfg.Auth.JWT.SecretKey is not empty before performing the sha256 hashing
and hex encoding. Only apply the fallback key derivation when both
hmacEncryptionKey is empty AND cfg.Auth.JWT.SecretKey is non-empty, otherwise
leave hmacEncryptionKey empty so the missing-key validation path is properly
triggered.

---

Nitpick comments:
In `@platform-api/src/internal/service/websub_api_hmac_secret_test.go`:
- Around line 128-234: The test file is missing coverage for three boundary
conditions in the name generation logic of the Generate method. Add three new
test functions to cover: (1) display names longer than 63 characters that get
truncated after slugification by verifying the returned meta.Name length equals
63, (2) display names containing only special characters that slugify to empty
strings by verifying the fallback generates a name in the format
"secret-{apiHandle}", and (3) very long apiHandle values where the fallback name
itself exceeds 63 characters by verifying the truncation occurs on the fallback.
Follow the existing test structure in the file (using
newMockWebSubAPIRepository, newMockHmacSecretRepo, buildHmacSecretService, and
calling svc.Generate) to ensure consistency with the current test patterns.
🪄 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: CHILL

Plan: Pro

Run ID: e215386e-064e-4cd4-9038-4157d14f9c9a

📥 Commits

Reviewing files that changed from the base of the PR and between 29f3c04 and 35a4b57.

📒 Files selected for processing (37)
  • .github/workflows/event-gateway-integration-test-postgres.yml
  • .github/workflows/event-gateway-integration-test-sqlserver.yml
  • event-gateway/docker-compose.test.postgres.yaml
  • event-gateway/docker-compose.test.sqlserver.yaml
  • event-gateway/it/Makefile
  • event-gateway/it/features/websub-webhook-secrets.feature
  • event-gateway/it/suite_test.go
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/controlplane/client.go
  • gateway/gateway-controller/pkg/controlplane/client_integration_test.go
  • gateway/gateway-controller/pkg/controlplane/controlplane_test.go
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.sqlserver.sql
  • gateway/gateway-controller/pkg/utils/api_utils.go
  • platform-api/src/api/generated.go
  • platform-api/src/api/manual_types.go
  • platform-api/src/api/websub_hmac_secret.go
  • platform-api/src/config/config.go
  • platform-api/src/internal/constants/error.go
  • platform-api/src/internal/database/schema.postgres.sql
  • platform-api/src/internal/database/schema.sql
  • platform-api/src/internal/database/schema.sqlite.sql
  • platform-api/src/internal/database/schema.sqlserver.sql
  • platform-api/src/internal/dto/gateway_internal.go
  • platform-api/src/internal/handler/gateway_internal.go
  • platform-api/src/internal/handler/websub_api_hmac_secret.go
  • platform-api/src/internal/integration/cascade_test.go
  • platform-api/src/internal/integration/lifecycle_test.go
  • platform-api/src/internal/model/websub_api_hmac_secret.go
  • platform-api/src/internal/repository/interfaces.go
  • platform-api/src/internal/repository/websub_api_hmac_secret.go
  • platform-api/src/internal/server/server.go
  • platform-api/src/internal/service/gateway_events.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go
  • platform-api/src/internal/service/websub_api_hmac_secret_test.go
  • platform-api/src/resources/gateway-internal-api.yaml
  • platform-api/src/resources/openapi.yaml
✅ Files skipped from review due to trivial changes (3)
  • platform-api/src/api/websub_hmac_secret.go
  • platform-api/src/internal/constants/error.go
  • platform-api/src/api/generated.go
🚧 Files skipped from review as they are similar to previous changes (20)
  • gateway/gateway-controller/cmd/controller/main.go
  • platform-api/src/internal/dto/gateway_internal.go
  • event-gateway/it/features/websub-webhook-secrets.feature
  • platform-api/src/resources/gateway-internal-api.yaml
  • platform-api/src/internal/repository/interfaces.go
  • gateway/gateway-controller/pkg/controlplane/controlplane_test.go
  • platform-api/src/internal/service/gateway_events.go
  • platform-api/src/internal/database/schema.postgres.sql
  • platform-api/src/internal/model/websub_api_hmac_secret.go
  • platform-api/src/internal/repository/websub_api_hmac_secret.go
  • platform-api/src/api/manual_types.go
  • gateway/gateway-controller/pkg/controlplane/client_integration_test.go
  • platform-api/src/internal/handler/gateway_internal.go
  • platform-api/src/internal/database/schema.sql
  • platform-api/src/resources/openapi.yaml
  • platform-api/src/internal/database/schema.sqlite.sql
  • gateway/gateway-controller/pkg/utils/api_utils.go
  • gateway/gateway-controller/pkg/controlplane/client.go
  • platform-api/src/internal/handler/websub_api_hmac_secret.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go
🛑 Comments failed to post (2)
platform-api/src/internal/database/schema.sqlserver.sql (1)

395-405: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align MCP proxy project cleanup with the application-managed contract.

Line 404 uses ON DELETE CASCADE, but this relationship is expected to block project deletion until the application nullifies or removes dependent MCP proxy rows. Use ON DELETE NO ACTION for SQL Server to match that contract. Based on learnings, mcp_proxies.project_uuid cleanup is handled at the application layer for this relationship.

Proposed fix
-    FOREIGN KEY (project_uuid) REFERENCES projects(uuid) ON DELETE CASCADE
+    FOREIGN KEY (project_uuid) REFERENCES projects(uuid) ON DELETE NO ACTION
🤖 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 `@platform-api/src/internal/database/schema.sqlserver.sql` around lines 395 -
405, The FOREIGN KEY constraint on project_uuid in the mcp_proxies table uses ON
DELETE CASCADE, which causes automatic deletion of mcp_proxy records when a
project is deleted. However, the application manages mcp_proxies cleanup at the
application layer and expects the database to prevent project deletion until
dependent records are handled. Change the ON DELETE CASCADE clause on line 404
(the FOREIGN KEY constraint referencing projects(uuid)) to ON DELETE NO ACTION
to enforce this contract and allow the application to control the cleanup order.

Source: Learnings

platform-api/src/internal/server/server.go (1)

237-241: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not derive an active HMAC key from an empty JWT secret.

Line 239 hashes cfg.Auth.JWT.SecretKey whenever the subscription-token key is empty. If the JWT secret is also unset, this still produces a non-empty key and enables the service instead of following the constructor’s missing-key path. Keep the fallback only for a non-empty JWT secret.

Proposed fix
-	hmacEncryptionKey := cfg.Database.SubscriptionTokenEncryptionKey
+	hmacEncryptionKey := strings.TrimSpace(cfg.Database.SubscriptionTokenEncryptionKey)
 	if hmacEncryptionKey == "" {
-		h := sha256.Sum256([]byte(cfg.Auth.JWT.SecretKey))
-		hmacEncryptionKey = hex.EncodeToString(h[:])
+		jwtSecret := strings.TrimSpace(cfg.Auth.JWT.SecretKey)
+		if jwtSecret != "" {
+			h := sha256.Sum256([]byte(jwtSecret))
+			hmacEncryptionKey = hex.EncodeToString(h[:])
+		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

	hmacEncryptionKey := strings.TrimSpace(cfg.Database.SubscriptionTokenEncryptionKey)
	if hmacEncryptionKey == "" {
		jwtSecret := strings.TrimSpace(cfg.Auth.JWT.SecretKey)
		if jwtSecret != "" {
			h := sha256.Sum256([]byte(jwtSecret))
			hmacEncryptionKey = hex.EncodeToString(h[:])
		}
	}
🤖 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 `@platform-api/src/internal/server/server.go` around lines 237 - 241, The
current logic derives an HMAC encryption key from cfg.Auth.JWT.SecretKey even
when that secret is empty, which produces a valid hash and masks the missing-key
error. Fix this by adding an additional check to ensure cfg.Auth.JWT.SecretKey
is not empty before performing the sha256 hashing and hex encoding. Only apply
the fallback key derivation when both hmacEncryptionKey is empty AND
cfg.Auth.JWT.SecretKey is non-empty, otherwise leave hmacEncryptionKey empty so
the missing-key validation path is properly triggered.

@tharindu1st tharindu1st force-pushed the platform-api-hmac-secret-generation branch from 35a4b57 to 9c0812c Compare June 23, 2026 18:49
@tharindu1st

Copy link
Copy Markdown
Contributor Author

@coderabbitai review and approve

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

@tharindu1st I'll kick off a full review of the PR now and approve it.

✅ Action performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.


Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 @.github/workflows/platform-api-pr-check.yml:
- Around line 126-144: The timing calculation in the SQL Server readiness check
is off by one interval. In the success message at line 134 where it echoes "SQL
Server ready after $((i * 5))s", change the calculation from `i * 5` to `(i-1) *
5` because when the check succeeds on iteration i, only (i-1) sleep intervals of
5 seconds each have actually elapsed. The first iteration performs the check
immediately without any prior sleep.

In `@platform-api/src/internal/repository/subscription_repository.go`:
- Around line 55-60: The encryption key precedence change in the keyStr
assignment breaks backward compatibility with existing subscription tokens
encrypted using the legacy AUTH_JWT_SECRET_KEY fallback. Preserve the original
fallback order by keeping cfg.Auth.JWT.SecretKey as the final fallback instead
of inserting cfg.Database.EncryptionKey ahead of it, or implement a decryption
fallback mechanism in the decryptSubscriptionToken function that attempts
decryption with multiple keys in sequence (SubscriptionTokenEncryptionKey, then
EncryptionKey, then SecretKey) to ensure tokens encrypted with any of the
previous key sources remain readable during and after the upgrade.

In `@platform-api/src/internal/server/server.go`:
- Around line 239-241: The code at the empty dbEncryptionKey check
unconditionally derives a fallback encryption key by hashing
cfg.Auth.JWT.SecretKey, which bypasses security validation when the JWT secret
is unset. Add a guard condition to verify that cfg.Auth.JWT.SecretKey is
non-empty before performing the sha256 hash derivation in the fallback block; if
the JWT secret is not present, allow the service initialization to fail closed
rather than creating a key from an unset source.
🪄 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: CHILL

Plan: Pro

Run ID: 8d19a49e-d39b-4642-b923-5a3df91cad6d

📥 Commits

Reviewing files that changed from the base of the PR and between 35a4b57 and 6202dcd.

📒 Files selected for processing (8)
  • .github/workflows/platform-api-pr-check.yml
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.sqlserver.sql
  • platform-api/src/config/config.go
  • platform-api/src/internal/repository/subscription_repository.go
  • platform-api/src/internal/server/server.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go
  • platform-api/src/internal/utils/subscription_token_crypto.go
✅ Files skipped from review due to trivial changes (1)
  • platform-api/src/internal/utils/subscription_token_crypto.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.sqlserver.sql
  • platform-api/src/config/config.go
  • platform-api/src/internal/service/websub_api_hmac_secret.go

Comment thread .github/workflows/platform-api-pr-check.yml
Comment thread platform-api/src/internal/repository/subscription_repository.go
Comment thread platform-api/src/internal/server/server.go Outdated
@tharindu1st tharindu1st force-pushed the platform-api-hmac-secret-generation branch from 6202dcd to 3e3db16 Compare June 23, 2026 19:44
@tharindu1st tharindu1st merged commit 10f1b5e into wso2:main Jun 24, 2026
12 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.

2 participants