Skip to content

Cookie banner public REST API#1032

Merged
codenem merged 14 commits intomainfrom
cookie-banner-origin-management
Apr 15, 2026
Merged

Cookie banner public REST API#1032
codenem merged 14 commits intomainfrom
cookie-banner-origin-management

Conversation

@codenem
Copy link
Copy Markdown
Contributor

@codenem codenem commented Apr 13, 2026

Closes ENG-271


Summary by cubic

Validate and canonicalize cookie banner origins, enforce one active banner per origin per tenant, and add public JS SDK endpoints (config, consent read/write) with per-banner CORS, CSRF bypass, and anonymized IP logging. Add pkg/server/trustedproxy to strip untrusted forwarded headers and convert pkg/server/api/clientip into an RFC 7239 extractor; addresses ENG-271.

  • New Features

    • Origin handling: new validator Origin() and canonicalization to scheme://host[:port] (strip www., remove trailing slash, no path/query/userinfo, reject empty port). Applied on create/update so CORS checks match.
    • Uniqueness: partial unique index idx_cookie_banners_unique_active_origin; DB violations map to ErrOriginAlreadyInUse on create/update/activate.
    • Public REST API under /cookie-banner/v1: GET /{bannerID}/config, GET /{bannerID}/consents/{visitorID}, POST /{bannerID}/consents. Per-banner CORS validates the request Origin against the banner’s canonical origin; CSRF bypass added.
    • Consent flow: IPs anonymized (IPv4 last octet, IPv6 /48). Client IP from RFC 7239 Forwarded, then leftmost X-Forwarded-For; consent reads are deterministic (created_at then id desc). Adds pkg/server/jsonutil error helpers.
    • Trusted proxies: new pkg/server/trustedproxy middleware strips Forwarded and X-Forwarded-For from untrusted sources; wired into the API server using existing trusted-proxies config. pkg/server/api/clientip is now a pure extractor.
  • Migration

    • Run DB migrations to create the partial unique index.
    • Ensure no tenant has multiple ACTIVE banners with the same origin before migrating; docs updated with timestamp naming.

Written for commit e3ab373. Summary will update on new commits.

@codenem codenem force-pushed the cookie-banner-origin-management branch 3 times, most recently from 376ea5b to b6f151a Compare April 13, 2026 12:57
@codenem codenem marked this pull request as ready for review April 13, 2026 14:30
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 12 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="pkg/server/api/cookiebanner/v1/handler.go">

<violation number="1" location="pkg/server/api/cookiebanner/v1/handler.go:48">
P1: The new `/{bannerID}/config` handler returns a static `{\"status\":\"ok\"}` response instead of banner configuration, so SDK clients cannot fetch the data this endpoint is meant to provide.</violation>
</file>

<file name="pkg/server/api/api.go">

<violation number="1" location="pkg/server/api/api.go:186">
P1: `cfg.CookieBanner` is used without validation. A nil `CookieBanner` service will panic when the cookie-banner middleware handles requests.</violation>
</file>

<file name="contrib/claude/coredata.md">

<violation number="1" location="contrib/claude/coredata.md:177">
P3: The first migration bullet is incomplete and drops the explicit naming rule, making the guideline unclear.</violation>
</file>

<file name="pkg/coredata/cookie_banner.go">

<violation number="1" location="pkg/coredata/cookie_banner.go:188">
P1: `LoadActiveByOrigin` is not tenant-scoped, so origin lookups can return another tenant’s active banner when origins overlap.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
Comment thread pkg/server/api/api.go
Comment thread pkg/coredata/cookie_banner.go Outdated
Comment thread contrib/claude/coredata.md Outdated
Comment thread pkg/coredata/cookie_banner.go Outdated
Comment thread pkg/coredata/cookie_banner.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/cors_middleware.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/cors_middleware.go Outdated
Copy link
Copy Markdown
Contributor

@gearnode gearnode left a comment

Choose a reason for hiding this comment

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

LGTM

@codenem codenem marked this pull request as draft April 14, 2026 08:29
@codenem codenem force-pushed the cookie-banner-origin-management branch 3 times, most recently from 3b89d60 to b273894 Compare April 14, 2026 10:21
@codenem codenem marked this pull request as ready for review April 14, 2026 10:25
@codenem codenem changed the title Handle cookie banner origin validation + unicity Cookie banner public REST API Apr 14, 2026
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="pkg/server/api/cookiebanner/v1/handler.go">

<violation number="1" location="pkg/server/api/cookiebanner/v1/handler.go:170">
P1: `clientIP` can return the full `X-Forwarded-For` chain, which is not a single IP and bypasses anonymization (it gets stored raw when `ParseIP` fails). Parse and use only the first forwarded client IP before passing it to `RecordConsent`.</violation>
</file>

<file name="pkg/coredata/cookie_consent_record.go">

<violation number="1" location="pkg/coredata/cookie_consent_record.go:226">
P2: Make latest-consent selection deterministic by adding a secondary sort key.</violation>
</file>

<file name="pkg/validator/validator_format.go">

<violation number="1" location="pkg/validator/validator_format.go:163">
P2: Origin validation accepts malformed host values with an empty port suffix (e.g. `https://example.com:/`) because only `parsedURL.Host == ""` is checked.</violation>
</file>

<file name="pkg/coredata/migrations/20260413T130000Z.sql">

<violation number="1" location="pkg/coredata/migrations/20260413T130000Z.sql:15">
P1: Backfill/canonicalize existing `cookie_banners.origin` values before creating this unique index; otherwise canonical-equivalent legacy origins remain unenforced and can break origin matching.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
Comment thread pkg/coredata/migrations/20260413T130000Z.sql
Comment thread pkg/coredata/cookie_consent_record.go Outdated
Comment thread pkg/validator/validator_format.go Outdated
@codenem codenem force-pushed the cookie-banner-origin-management branch 2 times, most recently from 51397de to a5da664 Compare April 14, 2026 12:17
@codenem codenem requested review from a team and gearnode April 14, 2026 12:28
Comment thread pkg/coredata/cookie_banner.go Outdated
Comment thread pkg/coredata/cookie_banner_version.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/cors_middleware.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/cors_middleware.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
Comment thread pkg/server/api/cookiebanner/v1/handler.go Outdated
@codenem codenem force-pushed the cookie-banner-origin-management branch from 09aee7b to 6a1931f Compare April 14, 2026 13:30
@codenem codenem requested a review from gearnode April 14, 2026 13:38
@codenem codenem force-pushed the cookie-banner-origin-management branch from 5289e9f to 80d45c8 Compare April 15, 2026 05:20
Copy link
Copy Markdown
Contributor

@gearnode gearnode left a comment

Choose a reason for hiding this comment

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

LGTM

codenem and others added 14 commits April 15, 2026 10:20
Signed-off-by: Émile Ré <emile@getprobo.com>
Strip www. prefix and trailing slash from origin when creating or
updating a cookie banner so CORS lookups match regardless of whether
the customer's site redirects www to the apex domain.

Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce /cookie-banner/v1/{bannerID}/config endpoint for the JS SDK.
The custom CORS middleware validates each request origin against the
specific banner being requested, preventing cross-customer leakage.

Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@getprobo.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Signed-off-by: Émile Ré <nemile.re@gmail.com>
Signed-off-by: Émile Ré <emile@getprobo.com>
Implement config, consent retrieval, and consent recording
endpoints for the JS SDK. IP addresses are anonymized (last
octet zeroed for IPv4, /48 mask for IPv6) before storage.

Signed-off-by: Émile Ré <emile@getprobo.com>
Parse only the first IP from X-Forwarded-For to prevent
the full chain from bypassing anonymization. Add secondary
sort key for deterministic consent selection. Reject origins
with empty port suffix in the validator.

Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@getprobo.com>
Move clientIP extraction into a reusable pkg/server/api/clientip
package with RFC 7239 Forwarded header support. Add
pkg/server/jsonutil with helpers for common HTTP error responses
(RenderForbidden, RenderInternalServerError, RenderNotFound,
RenderBadRequest) and use them in the cookie banner handlers.

Signed-off-by: Émile Ré <emile@getprobo.com>
Add coredata guide section on using Go enum constants as named
SQL parameters instead of hardcoded string literals. Fix mixed
inline/multiline RenderJSON call in cookie banner handler.

Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@getprobo.com>
Strip forwarded headers (Forwarded, X-Forwarded-For, X-Real-Ip)
from requests originating from untrusted proxies at the HTTP
server level, reusing the existing proxy-protocol trusted-proxies
config. The clientip package is now a pure extraction helper;
context plumbing and middleware wrappers are removed.

Signed-off-by: Émile Ré <emile@getprobo.com>
@codenem codenem force-pushed the cookie-banner-origin-management branch from 80d45c8 to e3ab373 Compare April 15, 2026 06:21
@codenem codenem merged commit e3ab373 into main Apr 15, 2026
17 checks passed
@codenem codenem deleted the cookie-banner-origin-management branch April 15, 2026 08:24
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