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>
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>
- 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>
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>
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>
- 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>
Replace the free-form duration TEXT column with a nullable
max_age_seconds INTEGER on both cookies and cookie_patterns
tables. The SDK detector now sends raw seconds instead of
humanized strings, eliminating locale-dependent comparisons
in the pattern merge worker. Humanization happens at display
time in the widget and console UI.
Signed-off-by: Émile Ré <emile@getprobo.com>
The console now manages CookiePattern entities instead of raw
Cookie rows. The frontend queries cookiePatterns on each category
and uses createCookiePattern, updateCookiePattern,
deleteCookiePattern, and moveCookiePatternToCategory mutations.
The entire Cookie GraphQL surface (type, connection, mutations,
inputs, payloads, resolvers, Go types) is removed since the
backing struct already lost description and cookieCategoryID.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add CookiePattern type, connection, and mutations to the GraphQL schema
with full resolver implementations. Add service methods for pattern
CRUD, category movement, listing, and counting. This enables the console
to manage cookie patterns instead of individual cookies.
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce a cookie_patterns table that groups cookies sharing a common
prefix (e.g. phc_*) into a single manageable row. Every cookie now
belongs to a pattern (EXACT or PREFIX match type). Category, description,
and display metadata move from cookies to patterns, making patterns the
unit of management and display in the console and published snapshots.
Signed-off-by: Émile Ré <emile@getprobo.com>
Cookies set by browser extensions are not the website operator's
compliance responsibility. This adds stack-trace inspection to
filter out extension-originated document.cookie writes, and
annotates pre-existing cookies with a source field so operators
can triage them separately.
Introduces a CookieSource enum (SCRIPT / PRE_EXISTING) across
the full stack: PostgreSQL, coredata, service, HTTP handler, and
GraphQL schema. On conflict, source is upgraded from PRE_EXISTING
to SCRIPT when a page script is later observed setting the cookie.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the old snapshot-based system for vendors with the publish
document system, mirroring the prior 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 vendor profile fields plus per-vendor
sections for services, contacts, risk assessments, compliance reports,
BAA and DPA agreements.
The vendor 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 Vendors page exposes a Publish button and a Document link
button when the document exists, and pre-fills the previous default
approvers.
Remove snapshot mode entirely from vendors and their sub-entities: drop
snapshotId/sourceId from GraphQL Vendor type and VendorFilter; remove
SnapshotsTypeVendors from the snapshot registry and delete
Vendors.Snapshot, VendorSnapshotter interface and all
*.InsertVendorSnapshots methods on contacts, services, risk
assessments, compliance reports, BAA and DPA. Drop the snapshot routes
and banner from the frontend. 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 Vendor backed by a
new vendors_document_id column on generated_documents, matching the
ProcessingActivity/Finding/Obligation pattern.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
RFC 7662 lets clients introspect any OAuth2 token, but the endpoint
only resolved access tokens. Look up refresh tokens too, honor the
optional token_type_hint to drive lookup order with a fallback to the
other table, and report revoked or expired refresh tokens as inactive.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Session validity was decoupled from credential rotation: changing
a password (logged-in flow) or completing a forgot-password reset
left every existing iam_sessions row valid until its idle TTL.
A user who saw their account compromised on another device had
no way to actually evict that device by rotating the password.
Inside the same DB transaction as the password update, expire the
identity's other active sessions:
- ChangePassword keeps the caller's current session and revokes
every other session for the identity, so the user is not
logged out of the browser they just used.
- ResetPassword has no caller session (the user is anonymous,
authenticated only by a stateless token) and revokes all of
the identity's active sessions.
The session middleware already rejects rows with expire_reason
set, so revoked sessions are kicked out on the next request
without any middleware change.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The SCIM event handler read the client address from
r.RemoteAddr only, so when probod runs behind an HTTP
load balancer every event row recorded the load
balancer's internal IP. Switch to clientip.Extract,
which honors the Forwarded / X-Forwarded-For headers
already validated by trustedproxy middleware.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
A client can prepend a spoofed entry to X-Forwarded-For before
the request reaches our load balancer. Taking the first value
would return the attacker's address. Since we sit behind a
single trusted LB that appends the real client IP as the last
entry, switch to rightmost extraction for both X-Forwarded-For
and RFC 7239 Forwarded headers.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the old PDF/snapshot-based exports for processing activities,
Data Protection Impact Assessments and Transfer Impact Assessments with
the publish document system. Includes GraphQL mutations, MCP tools, CLI
commands, n8n operations, frontend publish dialogs, e2e tests, and
prosemirror register templates that mirror the previous PDF layouts.
Each 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
ProcessingActivities page exposes a Publish dropdown per register and a
Document link button per active tab, pre-fills the previous default
approvers, and navigates to the published document on success.
Remove snapshot mode entirely from these three entities: drop snapshotId
and sourceId from GraphQL schemas, types, filters, resolvers, MCP spec,
frontend routes and pages; remove SnapshotsTypeProcessingActivities from
the snapshot registry and delete the ProcessingActivities.Snapshot,
ProcessingActivitySnapshotter interface and *.InsertProcessingActivitySnapshots
methods. 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 each entity type
(ProcessingActivity, DataProtectionImpactAssessment,
TransferImpactAssessment) backed by new columns in the generated_documents
table, matching the Finding/Obligation pattern.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Batch-load CookieBanner and CookieCategory entities via
dataloadgen instead of making individual service calls in
GraphQL resolvers, matching the existing dataloader pattern
used for organizations, frameworks, etc.
Signed-off-by: Émile Ré <emile@getprobo.com>
Resolvers for CookieCategory, Organization, CookieBanner,
Translations, and CookieBannerVersion.Categories were either
missing authorization checks or returning ID-only stubs
without querying the database. This fixes both issues by
adding proper authorize calls and fetching full entities.
Signed-off-by: Émile Ré <emile@getprobo.com>
Display record attributes and parsed consent data with
per-category consent state and cookies from the banner
version snapshot. The page lives outside the config layout
with its own breadcrumb navigation.
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the opaque cookieBannerVersionId filter with an
integer version filter. The SQL filter now resolves the
version number via a subquery against cookie_banner_versions.
Also fix the CookieBannerVersion resolver on consent records
to load the full version from the database instead of
returning a stub with only the ID set (which caused the
version to always display as 0).
Signed-off-by: Émile Ré <emile@getprobo.com>
Exposes the cookie consent record audit trail through a new
"Consent Records" tab on the cookie banner configuration page.
The full stack includes: extended coredata filter (visitor ID,
banner version), GraphQL schema/types/resolvers, and a React
page with SortableTable (size 50) and three compliance filters
(action, visitor ID, banner version).
Signed-off-by: Émile Ré <emile@getprobo.com>
Replace the old snapshot-based approach with the new publish document
system for findings and obligations. Includes GraphQL mutations, MCP
tools, CLI commands, e2e tests, frontend publish dialogs, and
snapshot-to-document migration tools.
Remove snapshot mode entirely from findings and obligations: drop
snapshotId from GraphQL schemas, filters, resolvers, MCP spec, frontend
routes, pages, and helpers. The snapshot_id column remains in the
database but is now filtered out with snapshot_id IS NULL.
Remove auditor's ability to publish SoA.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Replace the --active boolean flag with two explicit filters:
--state (ACTIVE/INACTIVE) and --contract-ended (true/false).
Also add state filter support to the console GraphQL API.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add the profile state attribute (ACTIVE/INACTIVE) to the MCP
Profile schema so listUsers and getUser tools expose it, and
add a state filter to listUsers.
Rename excludeContractEnded to contractEnded across the entire
stack (MCP, GraphQL, CLI, frontend). The new boolean is two-way:
true returns only users with ended contracts, false returns only
users with active or no contract, and null returns all.
Signed-off-by: Émile Ré <emile@getprobo.com>
Move the cookie-banner SDK version from the POST consents
request body to a custom X-SDK-Version header sent on every
API call. The server now reads it from the header and the
CORS middleware allows it through preflight.
Signed-off-by: Émile Ré <emile@getprobo.com>
Include a `bid` field in the consent cookie so it explicitly
identifies which cookie banner it belongs to, making validation
direct instead of relying on the visitor ID as an implicit
discriminator. Existing cookies without `bid` self-heal on the
next load by falling through to the API fetch.
Signed-off-by: Émile Ré <emile@getprobo.com>
Introduce a required cookie_policy_url alongside the existing
privacy_policy_url (now optional) so banners can link directly to a
dedicated cookie policy — a compliance best practice recommended by
CNIL, ICO, and the EDPB. Existing rows are seeded from their current
privacy_policy_url value.
Both {{cookie_policy_link}} and {{privacy_policy_link}} placeholders
are supported independently in banner description translations.
Signed-off-by: Émile Ré <emile@getprobo.com>
Origin is a fundamental identity property of a banner tied to consent
records for a specific site. Changing it would break the audit trail
and violate GDPR consent specificity requirements.
Signed-off-by: Émile Ré <emile@getprobo.com>
When DNS verification, CAA checks, or HTTP challenge completion
fail during certificate provisioning, the error is now stored on
the custom domain record and exposed via GraphQL. The console
displays it in both the domain card and domain detail dialog so
users can diagnose configuration issues without checking logs.
Previously these failures returned an error that was only logged
server-side, leaving users with no visibility into why their
domain was stuck in a pending state.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Add a partial unique index ensuring only one category per banner can
have posthog_consent enabled. Default it to the analytics category on
banner creation, clear the previous mapping before setting a new one,
and restrict the toggle to NORMAL categories in both the service layer
and the console UI.
Signed-off-by: Émile Ré <emile@getprobo.com>
Add PostHog opt-in/opt-out consent support mirroring the existing Google
Consent Mode integration: database column, GraphQL field, console UI
toggle, and client-side posthog-js calls.
Extract both GCM and PostHog logic from CookieBannerClient into a
ConsentIntegration plugin interface so future integrations can be added
without modifying the client core.
Signed-off-by: Émile Ré <emile@getprobo.com>
Expose gcmConsentTypes on CookieCategory type and accept
it in UpdateCookieCategoryInput so the console can read
and write GCM consent type mappings per category.
Signed-off-by: Émile Ré <emile@getprobo.com>
The mutation was wired up in the GraphQL schema and resolver
but never called from the console frontend. This removes the
service method, request type, resolver, schema entries, and
two unused error sentinels (ErrTranslationNotFound,
ErrTranslationAlreadyExists).
Signed-off-by: Émile Ré <emile@getprobo.com>
The category slug provides a stable, URL-safe key used as the
data-cookie-consent attribute value and consent data key, replacing
the fragile category name. This prevents breakage when categories
are renamed.
- Add slug column with unique-per-banner constraint and backfill migration
- Add Slug validator (lowercase alphanumeric + hyphens)
- Propagate slug through GraphQL schema, service layer, and snapshot
- Update console UI with slug field in create/edit forms
- Switch cookie-banner widget to use slug as consent data keys
Signed-off-by: Émile Ré <emile@getprobo.com>
Add CookieBannerTranslation type, defaultLanguage field on
CookieBanner, and upsert/delete mutations for managing
per-language translations from the console.
Signed-off-by: Émile Ré <emile@getprobo.com>
Pass the lang parameter from the request URL to the service
so the config response returns localized texts and category
names for the requested language.
Signed-off-by: Émile Ré <emile@getprobo.com>
The JS SDK will override document.cookie to detect unknown cookies set
by the website and report them to a new POST /{bannerID}/detected-cookies
endpoint. Reported cookies are inserted into the "Uncategorised" category
and a draft version is created so the admin can review them.
Signed-off-by: Émile Ré <emile@getprobo.com>
Audit all three API surfaces against the console GraphQL schema and add
missing resources: asset, audit, datum, dpia, evidence upload, measure,
obligation, processing activity, rights request, snapshot, task, tia,
trust center (with references/files), and vendor management CLI
commands; MCP tools for deletes, rights requests, trust center, vendor
contacts/services, and compliance external URLs; n8n nodes for
obligation, finding, task, evidence, processing activity, dpia, tia,
rights request, snapshot, audit log, access review, organization
context, trust center, and additional control/measure/vendor operations.
Include MCP e2e test infrastructure (testutil MCP client with API key
auth and JSON-RPC session management) and tests covering all new MCP
tools.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Uses the Loader + Page + Fragment pattern so the snippet page fetches
its own data instead of relying on Outlet context.
Signed-off-by: Émile Ré <emile@getprobo.com>
The cookie banner's cross-origin POST was blocked by two layered issues:
1. The global cors.Handler (with OptionsPassthrough: false) intercepted
OPTIONS preflights before the cookie banner's own CORS middleware
could run. Customer website origins aren't in AllowedOrigins, so the
preflight response had no CORS headers. Move the cookie banner mount
outside the global CORS group since it handles CORS per-banner.
2. The CSRF bypass patterns used literal "*" instead of ServeMux wildcard
syntax "{rest...}", so they never matched real request paths like
POST /cookie-banner/v1/{bannerID}/consents. Also remove redundant
GET/OPTIONS bypass patterns since safe methods are always allowed.
Signed-off-by: Émile Ré <emile@getprobo.com>
Remove assets from the snapshot system and replace with a publish-based
document workflow that generates versioned ProseMirror documents.
- Remove snapshot_id/source_id from asset and asset_vendor models
- Delete AssetFilter (no longer needed without snapshot filtering)
- Add PublishAssetList service, GraphQL mutation, MCP tool, CLI command,
and n8n operation
- Add asset_list_document_id column to generated_documents table
- Generate ProseMirror documents with asset inventory tables
(name, type, amount, data types stored, owner, vendors)
- Add AssetListDocument resolver on Organization type
- Update frontend to remove snapshot routes/params and add publish dialog
- Add e2e tests for asset publish (immediate, with approvers, reuse, RBAC)
- Add migration script for converting legacy asset snapshots to documents
- Exclude ASSETS from snapshot type lists and e2e snapshot tests
- Move generated_documents SQL to coredata methods on Datum and Asset
- Clear generated document and SOA references on soft delete and archive
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>