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>
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>
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>
Introduces a BRANDING boolean config (default true) propagated through
the standard config pipeline. Cookie banners now initialize their
show_branding column from this config instead of hardcoding true.
Signed-off-by: Émile Ré <emile@getprobo.com>
- 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>
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>
New staff-only CLI that connects directly to PostgreSQL (via PG_*
env vars) to manage parameters not exposed through the public API.
First command: proboctl cookie-banner set-branding <id> <true|false>
Signed-off-by: Émile Ré <emile@getprobo.com>
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>
Internal-only flag (defaults to true) that controls whether
"Powered by Probo" branding appears on the cookie banner.
Read directly from the live row (not the version snapshot)
since branding is cosmetic, not consent-relevant.
Signed-off-by: Émile Ré <emile@getprobo.com>
Provider token_type values are not reliably valid HTTP auth schemes
(Slack returns "bot" / "user", some providers send an empty string),
which produces a malformed Authorization header on subsequent
requests. Every OAuth2 connector in this codebase actually uses a
bearer token, so we always send "Bearer".
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Subsequent fetches of the same source re-upsert every access entry.
Without a guard, a reviewer's (or an agent's) decision, its note, and
the flags that drove it would be silently replaced on every poll.
The ON CONFLICT DO UPDATE SET clause now refreshes only the columns
that track live source state (email, full_name, role, is_admin, MFA,
etc.) and deliberately omits the verdict bundle -- flags, flag_reasons,
decision, decision_note, decided_by, decided_at -- so the audit trail
stays consistent with whatever the reviewer or agent last recorded.
Tighten the signature from pg.Querier to pg.Tx to match the rest of
the coredata package -- every caller already runs inside WithTx.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
Drivers whose source API surfaces an account-status signal return every
account (including inactive / suspended / deleted) and populate Active
accordingly; drivers without such a signal no longer fabricate it.
The fetch pipeline records every account with decision PENDING and no
flags. Producing flags or a non-PENDING decision is reserved to human
reviewers or a purpose-built agent run against the campaign -- the engine
must not form an implicit verdict.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
The dev-config Make target was missing the ACME_ROOT_CA env var,
causing probod to fail with an untrusted certificate error when
connecting to the local Pebble ACME server.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Use the conventional .env / .env.example location at the repo
root instead of cfg/dev.env / cfg/dev.env.example. .env is what
contributors expect, keeps cfg/ a pure generated-config directory,
and shares the same file if we ever add another dev target that
needs the same overrides.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Apply the review feedback on the dev-config target:
- Treat cfg/dev.env as a prerequisite via $(wildcard ...) so edits
to it re-trigger cfg/dev.yaml without the dev having to delete
the output first; update the help string accordingly.
- Drop the @ silence prefix on the recipe body so failures are
debuggable; the values are all known dev placeholders, no leak.
- Call out in cfg/dev.env.example that the file is sourced as a
POSIX shell snippet (not Docker-compose .env semantics), and
list the previously-missing overrides: observability addrs,
PG_DEBUG, SMTP auth/TLS, AUTH_COOKIE_DURATION, and the
per-worker LLM knobs (probo-agent, evidence-describer).
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Committing a fully-materialised cfg/dev.yaml hid the dev
configuration surface and blocked the OAuth2 signing-key inlining
change: the new config requires a per-dev private key that must
not be committed. Replace the checked-in file with a dev-config
Make target that shells out to probod-bootstrap with dev-safe
defaults and a stable RSA signing key stashed under
cfg/.dev-oauth2-signing-key.pem on first run.
The recipe sources cfg/dev.env when present so devs can override
any setting without editing the Makefile; cfg/dev.env.example
ships the full list of overridable knobs. cfg/dev.yaml,
cfg/dev.env, and the signing key are all gitignored.
Update README, CONTRIBUTING, and contrib/claude/config.md to
describe the new workflow.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
The OAuth2/OIDC server accepted its signing key via a file path
(key-file), while every other PEM key in the probod config (SAML
private key, ACME account key) is embedded inline. Switch the
field to a private-key string so the convention is uniform.
The signing key is operator-supplied material that must outlive
any process restart, so the bootstrap builder now treats
OAUTH2_SERVER_SIGNING_KEY as required and refuses to start
without one; silently minting a fresh key per boot would break
token validation across rollouts. The OAUTH2_SERVER_* env vars
otherwise flow through builder.Build like the existing SAML
block so the new OAuth2Server section is populated end-to-end.
Rework the e2e harness to render its config via bootstrap at
test setup, which removes the static
e2e/console/testdata/config.yaml and the previously generated
test-only PEM file. A per-run RSA key is minted via
bootstrap.GenerateOAuth2SigningKey (kept public for test
tooling) and injected through the builder env map. CI now
passes ACME_ROOT_CA inline instead of mutating a YAML on disk.
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Two bugs caused SCIM sync failures:
1. buildUserPayload conditionally omitted empty fields. When a field was
cleared in the identity provider, the PUT payload didn't include it,
so the SCIM handler never cleared the stored value. The bridge kept
detecting a mismatch every sync cycle, causing a perpetual PUT loop.
Fix: always include all fields unconditionally.
2. ListUsers ignored the startIndex parameter — the cursor always started
from nil, so every page returned the same first N users. Organizations
with more than 100 SCIM-managed users never got a full listing; users
beyond the first page appeared missing, causing CreateUser calls that
failed with 409 (uniqueness conflict) and eventually disabled the
bridge. Fix: replace cursor-based pagination with OFFSET/LIMIT to
honor SCIM's 1-based startIndex.
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
Address PR review comments: capture Exec result and check
RowsAffected() == 0 to return ErrResourceNotFound in
Cookie.Update, CookieCategory.Update, and CookieCategory.UpdateRank.
Also update coredata and relay contributor docs accordingly.
Signed-off-by: Émile Ré <emile@getprobo.com>
ConnectionHandler.getConnection could not find the cookies
connection because orderBy was implicitly treated as a filter.
Add filters: [] to the @connection directive and document the
filters rule in contrib/claude/relay.md.
Signed-off-by: Émile Ré <emile@getprobo.com>