Commit Graph

478 Commits

Author SHA1 Message Date
Émile Ré
d955489d11 Bump @probo/cookie-banner version
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 19:28:48 +04:00
Émile Ré
56a32fe162 Fix intrusive auto-focus on cookie banner initial load
Only focus the banner on user-initiated re-opens (hidden -> banner),
not on the initial page load (loading -> banner). Also use
preventScroll to avoid jarring scroll jumps on programmatic focus.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:55:34 +04:00
Émile Ré
d7eec08cd2 Add banner ID to probo_consent cookie
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>
2026-04-24 18:48:40 +04:00
Émile Ré
a1d2eebf2b Hide cookie details toggle for categories with no cookies
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:39 +04:00
Émile Ré
598c6b112c Add cookie_policy_url field to cookie banners
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>
2026-04-24 18:48:39 +04:00
Émile Ré
4982cebb9c Gracefully handle config fetch failure in cookie banner SDK
When the banner config request fails, the SDK now silently returns
instead of throwing, so the rest of the page is unaffected.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:39 +04:00
Émile Ré
4ec075fa80 Limit detected cookies to 100 per request in cookie banner SDK
The server rejects requests with more than 100 cookies but the client
had no matching cap, causing the entire batch to be lost on cookie-heavy
pages. Flush now drains at most 100 entries and re-schedules for the
remainder.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:38 +04:00
Émile Ré
7a270f75ec Fix Google Consent Mode integration for GTM / dataLayer
Fall back to pushing consent commands onto window.dataLayer when
window.gtag is not available, enabling compatibility with Google Tag
Manager setups that don't define a global gtag function.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 17:13:13 +04:00
Émile Ré
7f1dffad80 Add PostHog consent integration and extract integration plugin system
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>
2026-04-24 17:13:12 +04:00
Émile Ré
c6f548b8ff Add Google Consent Mode v2 to cookie banner SDK
Set GCM defaults to denied on load, then push consent
updates via gtag('consent', 'update', ...) before
activating scripts. The mapping from category slugs to
GCM consent types comes from the banner config endpoint.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 17:13:12 +04:00
Émile Ré
c364c3eacb Update packages/cookie-banner/src/client.ts
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>
2026-04-24 13:11:22 +04:00
Émile Ré
f29a2f05cc Fix gpc applied from cookie / api consent
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 13:05:02 +04:00
Émile Ré
4f4bb72760 Clear GPC badge when user overrides consent via preference panel
When the user explicitly changes consent (accept all, reject all, or
customize), clear the gpcApplied flag and remove the gpc-label attribute
from the settings button so the "Opt-Out Preference Signal Honored"
badge no longer displays.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:44:44 +04:00
Émile Ré
98dc04b7de Add Global Privacy Control (GPC) support to cookie banner SDK
When navigator.globalPrivacyControl is true and no prior consent exists,
auto-reject all non-necessary cookies with action "GPC", skip showing
the banner, and display an "Opt-Out Preference Signal Honored" badge on
the settings button (CPRA compliance). GPC labels are hardcoded in the
SDK for en/fr/de/es. Users can still override via the preference panel.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:21:40 +04:00
Émile Ré
2b6f131f43 Fix PR review comments on cookie banner i18n
Address locale normalization for region-tagged values, guard
language detection for non-DOM runtimes, validate DefaultLanguage
on update, pass translated texts through the deactivation flow,
handle slug collisions in migration, add organizations FK, fix
consent migration from name-keyed to slug-keyed data, render all
template placeholders in previews, and wrap helper text for i18n.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:12 +04:00
Émile Ré
fa7a9c96c1 Move cookie detail labels from backend to JS SDK
label_description and label_duration are not user-customizable, so they
don't belong in the backend translation defaults. Hardcode them in the
cookie-banner SDK's i18n module instead.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:12 +04:00
Émile Ré
e24813202b Fix XSS in cookie banner translation rendering
Validate translation string values server-side with NoHTML() and
MaxLen(2000) to reject HTML in the translations JSON blob. On the
client side, escape user-provided template text before innerHTML
injection in banner_description and placeholder_text paths.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:11 +04:00
Émile Ré
165b9ad9d3 Add slug to cookie categories for stable consent identifiers
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>
2026-04-24 12:18:10 +04:00
Émile Ré
bb39daebc5 Move duration translations from server defaults to JS SDK
Duration strings are static and not customizable through the console,
so they belong in the frontend cookie-banner package instead.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:10 +04:00
Émile Ré
f9458fb18a Remove unused available_languages from cookie banner config
The field was populated by the backend and declared in the TS interface
but never consumed by any client-side code. The banner resolves its
language via detectLanguage() and passes it as a query param.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:09 +04:00
Émile Ré
ef60f080f1 Add i18n support to cookie banner JS client
Replace all hardcoded user-facing strings with server-driven
texts from config.texts. Add language detection chain
(data-lang > html lang > navigator.language), pass ?lang
query param on config fetch, and use {{variable}} interpolation
for dynamic content in templates.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:09 +04:00
Émile Ré
4678c58ab0 Add description to cookie banner preference panel
Adds a short description under the preference panel title:
"Choose which cookie categories to allow. Necessary cookies are
always active as they are needed for the site to work."

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:08 +04:00
Émile Ré
d5d77fee8a Stop existing detector before creating a new one in startDetector()
Prevents orphaned detector state when load() is called multiple times.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-23 10:50:57 +04:00
Émile Ré
137b63a47e Add cookie detector to JS SDK for automatic cookie discovery
Overrides document.cookie setter to intercept cookie writes and detect
unknown cookies not present in the banner config. Also scans pre-existing
cookies on startup. Detected cookies are debounced and reported in batches
to the detected-cookies endpoint with inferred durations from max-age/expires.

Extracts shared cookie helpers (parseCookieName, parseDuration, isDeletion,
removeCookies) into cookie-utils.ts, used by both activation.ts and
detector.ts. Unifies the duplicated "probo_consent" constant into a single
export from cookie.ts.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-23 10:50:57 +04:00
Sacha Al Himdani
7be92defcc Add missing resources to CLI, MCP, and n8n surfaces
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>
2026-04-22 19:02:22 +02:00
Émile Ré
72443ff32f Use full Probo logo (picto + wordmark) in cookie banner branding
Replace the picto-only SVG with the full Logo component in both the
console theme preview and the cookie-banner web component.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 15:49:56 +04:00
Émile Ré
d71915abd4 Fix PR review comments on cookie banner branding
- Add scope parameter to UpdateShowBranding to prevent cross-tenant updates
- Use cmd.Context() instead of context.Background() in proboctl CLI
- Drop SQL column default after backfill in migration
- Add bounds check for int-to-int32 conversion in PG_POOL_SIZE
- Update branding link to getprobo.com homepage

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 15:22:03 +04:00
Émile Ré
df698b5b21 Change branding copy from "Powered by" to "Privacy by"
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 15:22:02 +04:00
Émile Ré
7102c17b19 Add "Powered by Probo" branding to cookie banner SDK
Renders a subtle branded link at the bottom of both the banner
and preference panel, conditionally hidden via show_branding
config flag. Extracts all icon and HTML template constants into
a shared html.ts module.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 15:22:02 +04:00
Émile Ré
9510d91c61 Align theme preview with real themed banner styles
Fix button layout in ThemePreview to match the actual themed-banner:
wrap buttons in spans to mimic custom element wrappers (preventing
flex: 1 from stretching them), use correct padding/background/max-width,
and set explicit line-height: normal. Also clean up dead flex properties
in themed-banner styles.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:51 +04:00
Émile Ré
ae68196d76 Shrink cookie-banner README and link to docs site
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:51 +04:00
Émile Ré
4a88cc5902 Rework cookie-banner exports: themed banner as default, headless as subpath
Make the themed banner the default "." export and move the headless
components to "./headless". Remove unused VERSION export.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:50 +04:00
Émile Ré
0cb48a4f33 Add accept/reject all buttons to preference panel
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:50 +04:00
Émile Ré
c942aaa7bb Make consent API calls fire-and-forget to avoid blocking UI
The server-side consent record is for audit purposes and does not need
to complete before the UI responds. Local state (cookie, script
activation) is applied synchronously, and the API call runs in the
background with queue fallback on failure.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:50 +04:00
Émile Ré
140e614e4f Fix placeholder style copying and root domain calculation
Only copy dimensional style properties (width, height, margin, etc.)
to placeholders instead of blindly copying all cssText, which could
override layout properties like display: flex.

Handle multi-part TLDs (e.g. .co.uk, .com.au) when computing the root
domain for cookie deletion.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:50 +04:00
Émile Ré
2b59a10c14 Add ARIA attributes and focus management to cookie banner
Improve accessibility across the cookie banner components: add
aria-modal/aria-labelledby/aria-describedby on dialog cards, aria-label
on icon-only buttons and category toggles, aria-expanded on cookie
detail disclosure, aria-hidden on decorative SVGs, and auto-focus into
dialogs when they become visible.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:50 +04:00
Émile Ré
9a92ebd51a Add placeholder overlays for blocked cookie-consent elements
When visual elements (iframe, img, video, etc.) are blocked due to
missing cookie consent, display a styled placeholder that explains
which cookie category must be accepted and offers a link to open
the preference panel.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:50 +04:00
Émile Ré
1fb0464948 Add element deactivation with cookie removal on consent withdrawal
When a user changes their consent preferences, elements for rejected
categories are now deactivated: data attributes are restored, resources
are unloaded, and associated cookies are removed.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:49 +04:00
Émile Ré
0e3c79b0e8 Forward data-reopen-widget from script tag in IIFE entry point
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:49 +04:00
Émile Ré
68fffbf2f3 Add reopen-widget attribute and probo-settings-link element
Allow customers to replace the built-in floating settings button with
their own re-open trigger (e.g. a footer link) by either placing a
<probo-settings-link> element anywhere on the page or manually setting
reopen-widget="custom" on the root. The settings-link auto-hides the
floating button on connect via a reopen-widget attribute change event.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:49 +04:00
Émile Ré
6391d276d9 Clean up settings button CSS custom properties
Remove undocumented --probo-settings-bottom variable and use the
shared --probo-z-index variable instead of --probo-settings-z-index.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:48 +04:00
Émile Ré
55d81e21eb Fix preference panel close returning to banner when consent exists
When a visitor with existing consent opened the preference panel via
the floating settings button and closed it, the state was always set
to "banner" instead of "hidden". Now check hasConsent so closing the
panel returns to the floating button when preferences are already saved.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:48 +04:00
Émile Ré
9ec6922fde Hide empty categories in cookie banner preference panel
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:48 +04:00
Émile Ré
fab95413df Improve UI of themed banner
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 11:34:48 +04:00
Sacha Al Himdani
b603d04d8d Assets as document: replace snapshot with publish workflow
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>
2026-04-21 19:36:17 +02:00
Sacha Al Himdani
e1148f812e Replace implemented column with CMMI maturity level
Drop the boolean implemented/not-implemented state in favor of a
mandatory CMMI maturity level enum (NONE, INITIAL, MANAGED, DEFINED,
QUANTITATIVELY_MANAGED, OPTIMIZING) stored as a Postgres enum type.

The migration backfills existing rows (NOT_IMPLEMENTED → NONE,
IMPLEMENTED → INITIAL), makes the column NOT NULL, and drops the old
implemented column and its enum type.

- maturityLevel is required on CreateControlInput and non-nullable (!)
  in the GraphQL schema
- CLI displays human-readable CMMI labels instead of raw enum tokens
- SOA table and published document use a single Maturity column in
  place of the old Implemented + Maturity columns
- Remove ControlImplementationState type and all implemented references
  across backend, frontend, CLI, MCP, n8n, and E2E tests

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-21 11:06:36 +02:00
Alejandro Juan
da91afc2a7 Add CMMI maturity level to compliance controls
Adds an optional CMMI 0-5 maturity level field to Control to support
ISO 27001 clause 9.1 effectiveness measurement and HITRUST CSF maturity
requirements. The field is nullable, framework-agnostic, and exposed
across all four API surfaces (GraphQL, MCP, CLI, n8n) plus the
generated SoA document.

Signed-off-by: Alejandro Juan <alejandrojuan@alejandrojuan.com>
2026-04-21 11:06:36 +02:00
Émile Ré
1574600c72 Use fragment and add confirm
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-21 11:40:22 +04:00
Émile Ré
7cd8c516b9 Add uncategorised cookie category
Replace the `required` boolean column on cookie_categories with a `kind`
enum (NORMAL, NECESSARY, UNCATEGORISED). The Necessary category remains
undeletable and always-on for consent; the new Uncategorised category is
also undeletable but users can opt out of it.

When a category is deleted, its cookies are merged into the Uncategorised
category (lazy-created for legacy banners that don't have one yet).

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-21 11:40:21 +04:00
Émile Ré
465f43d359 Refactor UX to remove useless page
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-20 17:47:06 +04:00