Non-not-found errors from LoadByBannerIDTypeAndPattern were
silently swallowed, risking partial state on DB failures.
Signed-off-by: Émile Ré <emile@getprobo.com>
CookiePattern Create/Update/Delete/Move now also write the
corresponding TrackerPattern (TrackerType=COOKIE) so the
tracker-pattern-based snapshot picks up the changes and
draft versions are created correctly.
Signed-off-by: Émile Ré <emile@getprobo.com>
The worker now operates on TrackerPattern/DetectedTrackers
instead of CookiePattern/Cookies, with TrackerType included
in merge group keys to prevent cross-type merging.
Signed-off-by: Émile Ré <emile@getprobo.com>
The compat endpoint now writes to the unified tracker tables
instead of the legacy cookies + cookie_patterns tables.
Signed-off-by: Émile Ré <emile@getprobo.com>
- TrackerType enum (cookie, local_storage, session_storage, indexed_db, script, iframe)
- TrackerPattern model with EXACT + PREFIX matching for all types
- DetectedTracker model with upsert on conflict
- ReportDetectedTrackers service method handling cookies, storage, and resources
- POST /detected-trackers endpoint on cookie-banner v1 API
- buildSnapshot() now reads from tracker_patterns (cookie type only)
- Entity types registered (89, 90)
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce a unified detection schema that supports cookies,
localStorage, sessionStorage, IndexedDB, scripts, and iframes
as tracker types. Existing cookie_patterns and cookies data is
backfilled into the new tables to prepare for the organic
privacy scanner feature.
Signed-off-by: Émile Ré <emile@getprobo.com>
The Google Workspace driver test broke after the dependabot
google.golang.org/api bump (v0.269.0 → v0.277.0): the recorded cassette
embeds X-Goog-Api-Client: gdcl/0.269.0, which the upgraded SDK no
longer sends, so the default matcher rejects every interaction.
Configure the matcher to ignore User-Agent and X-Goog-Api-Client so
cassettes survive client-library version bumps.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The go-sdk v1.6.0 release no longer applies a default
CrossOriginProtection when the field is nil in StreamableHTTPOptions,
silently removing Origin header verification. Wrap the streamable
handler with http.NewCrossOriginProtection().Handler(...) (the
recommended replacement, since the SDK field is deprecated).
Also regenerate gqlgen resolvers to track v0.17.90.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
The HTTP middleware and proxy-protocol listeners both pinned trust
to exact IPs, which forced re-applying terraform every time AWS
rotated an ALB or NLB ENI. Trusted-proxies entries now accept CIDR
ranges in addition to plain IPs, so callers can trust whole subnets
(where the load balancer ENIs always live) and stop chasing
rotating IPs.
The HTTP middleware splits parsed entries into IPs and IPNets and
checks both. The proxy-protocol listeners switch from
TrustProxyHeaderFrom (IP-only, REJECT) to ConnStrictWhiteListPolicy
(IP or CIDR, REJECT) which preserves the existing reject-on-unknown
semantics.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Exclude the UNCATEGORISED category at the SQL level so
the admin cookie/display/translations pages only see
consent-relevant categories. Removes dead client-side
UNCATEGORISED filters that are no longer needed.
Signed-off-by: Émile Ré <emile@getprobo.com>
Backend for the cookie banner detection page: a new
uncategorisedPatterns connection on CookieBanner with
sortable (NAME, LAST_MATCHED_AT, UPDATED_AT, SOURCE)
and filterable (text ILIKE on name/description, source
enum) paginated results. COALESCE handles NULL-first
ordering for last_matched_at.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add lastMatchedAt to the CookiePattern GraphQL type, MCP schema,
CLI view/list commands, and n8n get/getAll operations. Run
codegen to regenerate gqlgen models and MCP types.
Signed-off-by: Émile Ré <emile@getprobo.com>
Track when cookies are last detected (last_detected_at on cookies)
and when patterns last matched a detected cookie
(last_matched_at on cookie_patterns). The cookie timestamp is
refreshed on every detection report; the pattern timestamp is
computed as MAX(last_detected_at) during pattern analysis.
Signed-off-by: Émile Ré <emile@getprobo.com>
The test was passing UNCATEGORISED categories to buildSnapshot
and expecting them to be filtered out. Now that filtering
happens at the query level, the test input should only contain
consent categories.
Signed-off-by: Émile Ré <emile@getprobo.com>
Instead of loading all categories and filtering out
UNCATEGORISED in Go, add LoadConsentCategoriesByCookieBannerID
which excludes it in the query. This avoids fetching data we
immediately discard and makes the intent explicit at each call
site.
Signed-off-by: Émile Ré <emile@getprobo.com>
- Return valid JSON (null) in `prb scim view --json` when no
SCIM configuration exists
- Remove organization ID from MCP not-found error to prevent
identifier disclosure in multi-tenant paths
- Use nullable GID refs for bridge_id and connector_id in
MCP specification for correct generated ID typing
Signed-off-by: Émile Ré <emile@getprobo.com>
Adds `prb scim` command group with view, create, delete,
regenerate-token, bridge (view/update), and event (list)
subcommands, using the connect GraphQL API.
Signed-off-by: Émile Ré <emile@getprobo.com>
The SCIM operations (configuration, bridge, events) were only
available through the GraphQL Connect API. This adds the
equivalent MCP tools so MCP clients can manage SCIM
provisioning: get/create/delete configuration, regenerate
token, get/update bridge, and list events.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the old snapshot-based system for risks with the publish
document system, mirroring the prior vendor / processing activity / DPIA
/ TIA migration. Includes the GraphQL mutation, MCP tool, CLI command,
n8n operation, frontend publish dialog, e2e tests, and a prosemirror
register template covering name, description, category, treatment,
owner, inherent and residual scoring, and notes.
The risk register lives as a generated DocumentTypeRegister document on
the organization, reused across publishes (the major version bumps on
every republish). Approvers can be passed in to create a draft pending
approval; otherwise the version is published immediately. The frontend
Risks page exposes a Publish button and a Document link button when the
document exists, and pre-fills the previous default approvers.
Risks was the last remaining snapshot type, so this commit also removes
the entire snapshot system: drop snapshotId from the Risk GraphQL type
and RiskFilter; remove RiskSnapshotter, Risks.Snapshot,
InsertRiskSnapshots, and the SnapshotID/SourceID fields on Risk; delete
Snapshot, ControlSnapshot, SnapshotsType, SnapshotOrderField,
Snapshottable, the SnapshotService, the Snapshot console resolvers and
GraphQL schema, the Snapshot MCP types and operations
(list/get/take/listControlSnapshots), the snapshot CLI (prb snapshot),
the snapshot frontend pages, routes, banner, LinkedSnapshotsCard,
SnapshotGraph, snapshot helpers, and the snapshot n8n resource and
control link/unlink snapshot operations. The snapshot_id columns remain
in the database but are now filtered out with snapshot_id IS NULL.
Add Get/Upsert/Clear GeneratedDocumentID methods on Risk backed by a new
risks_document_id column on generated_documents, matching the
ProcessingActivity/Finding/Vendor pattern. The migration command
migrate-risk-snapshots-to-documents uses raw SQL queries instead of the
Go snapshot types, since those are gone.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Pattern merges and adoptions that stay entirely within the
uncategorised category never change the visitor-facing snapshot.
Only trigger ensureDraftVersionForBanner when a merge group
belongs to a consent category.
Signed-off-by: Émile Ré <emile@getprobo.com>
Translations are no longer part of the version snapshot, so the
banner_description text must be validated at write time to ensure
the {{cookie_policy_link}} placeholder is present. Without it
the cookie policy URL silently disappears from the rendered banner.
Signed-off-by: Émile Ré <emile@getprobo.com>
Decouple detection from the banner's active state so admins
can discover cookies before making the banner visible. The
client now starts the detector even when the config endpoint
returns 404, and the detector stops itself if the report
endpoint returns 404 (wrong or deleted banner ID).
Signed-off-by: Émile Ré <emile@getprobo.com>
The uncategorised category is an admin-side inbox for detected
cookies and should not be part of the visitor-facing consent
contract. Filter it out of snapshots so changes to uncategorised
patterns no longer trigger version bumps.
Signed-off-by: Émile Ré <emile@getprobo.com>
Translation changes are cosmetic, not consent-contract changes,
so they should not trigger a version bump. Translations are now
loaded live from the database at serve time instead of being
frozen in the snapshot.
Signed-off-by: Émile Ré <emile@getprobo.com>
A central snapshot-equality guard in ensureDraftVersion now returns
the latest version unchanged when the candidate snapshot matches it,
so no-op admin saves no longer force visitors to re-consent. Per-
operation short-circuits in UpdateCookieBanner, UpdateCookieCategory,
UpdateCookiePattern, DeleteCookiePattern, MoveCookiePatternToCategory,
ReorderCookieCategory, and UpsertCookieBannerTranslation skip the row
update and version bump when nothing visitor-facing changes (excluded
patterns, identical values, identical translation JSON).
Rank is now treated as admin-only metadata: buildSnapshot sorts
categories by (Kind weight, ID byte order) instead of relying on the
implicit rank-driven slice order, and ReorderCookieCategory no longer
calls ensureDraftVersionForBanner. Default banners keep their
visitor-facing order (insertion order matches Kind+ID); banners with
admin-customised ranks see a one-time order shift to insertion order
on the next snapshot rebuild.
Reusable equality helpers (Ptr generic + JSON canonicalisation) move
to a new pkg/equal package; snapshotsEqual stays in service.go as the
documented chokepoint for visitor-identical snapshot comparison.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add excluded boolean to the MCP CookiePattern schema and
UpdateCookiePatternInput. Expose it in CLI cookie-pattern
view, list, and update commands.
Signed-off-by: Émile Ré <emile@getprobo.com>
Expose excluded on the CookiePattern type and accept it as
an optional input on UpdateCookiePatternInput so the console
can toggle pattern exclusion inline.
Signed-off-by: Émile Ré <emile@getprobo.com>
Filter excluded and non-exact patterns in SQL when loading
patterns for the pattern analysis worker. Both merge group
building and uncategorised adoption only see non-excluded
exact patterns, so excluded patterns are preserved as
punch-out overrides.
Signed-off-by: Émile Ré <emile@getprobo.com>
In ReportDetectedCookies, silently skip cookies that match an
excluded pattern instead of recording them. Filter excluded
patterns in SQL when building version snapshots so they never
appear in the published banner config. Add Excluded field to
UpdateCookiePatternRequest.
Signed-off-by: Émile Ré <emile@getprobo.com>
Adds an `excluded` boolean column to the cookie_patterns table
so operators can mark patterns to be omitted from the consent
banner without deleting them. Includes the migration, struct
field, updated SQL queries, and filter support.
Signed-off-by: Émile Ré <emile@getprobo.com>
- Validate __typename in all list command pagination callbacks
- Serialize PostHogConsent=false as explicit false (not nil)
- Fix maxAgeSeconds factory to only include when value is an int
- Rename cookie-banner versions to latest-version subcommand
Signed-off-by: Émile Ré <emile@getprobo.com>
Wire cookiebanner.Service into the MCP resolver and expose 24 tools
covering full CRUD, activation, versioning, translations, and consent
record queries with pagination and filtering support.
Signed-off-by: Émile Ré <emile@getprobo.com>
Users authenticated via Google/Microsoft OIDC or magic link previously
relied on a fall-through in the password-only org check. Make the rule
explicit so SSO-only users can access password-only organizations
without being bounced to the password login form they cannot satisfy.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The adoptUncategorisedPatterns method loaded all patterns for a
banner then filtered in Go. This adds a CookiePatternFilter
(match_type + cookie_category_id) and wires it into
LoadAllByCookieBannerID so the two targeted loads only fetch
the rows they need.
Signed-off-by: Émile Ré <emile@getprobo.com>
When a cookie pattern already exists for a banner, the insert
conflict caused a continue that silently dropped the detected
cookie instead of linking it to the existing pattern. Load the
existing pattern to obtain its ID and proceed with cookie
insertion.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the ad-hoc patternAnalysisTask struct with coredata.CookieBanner
as the worker type parameter, matching the pattern used by other workers
(esign, accessreview). SQL methods move back to coredata on *CookieBanner.
Signed-off-by: Émile Ré <emile@getprobo.com>
Use InsertIfNotExists instead of Insert with error check, since
a unique-violation aborts the PostgreSQL transaction even when caught.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move the DurationInput component from the console app into @probo/ui
for reuse, add duration formatting helpers to @probo/helpers, and
update pattern merge to group by both category ID and prefix.
Signed-off-by: Émile Ré <emile@getprobo.com>