Commit Graph

1402 Commits

Author SHA1 Message Date
Sacha Al Himdani
a052d6f481 Allow editing SOA generated document approvers inline
Adds an "Approvers" row to the SOA details card with the same inline
edit pattern as DocumentDetailsCard, calling updateDocument with
defaultApproverIds. Visible only after first publish, when the SOA
has an associated document.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-07 14:59:14 +02:00
Émile Ré
3c01c87572 Improve detected pattern row
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:48 +04:00
Émile Ré
5f4fd3c427 Surface regulation and user agent across consent record API layers
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:48 +04:00
Émile Ré
806bd672ed Surface regulation and country code enums across API layers
Add proper enum types for Regulation and CountryCode in GraphQL
(with @goModel/@goEnum directives) and MCP (as standalone reusable
schemas with $ref). Update CLI, console UI, and n8n to include
the new fields.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:48 +04:00
Émile Ré
4598506076 Extract shared types and add Regulation type with parsing methods
Move cookie banner types (CookieItem, Category, Regulation, BannerConfig,
etc.) into a dedicated types.ts file. Add a coredata.Regulation type with
parsing, JSON marshaling, and database scanning methods. Hardcode the
geoloc-import data directory since the submodule path is fixed.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +04:00
Bryan Frimin
0ea991b628 Drop redundant @deleteRecord on campaigns list mutation
@deleteEdge already removes the deleted campaign from the
connection on the list view, so the extra @deleteRecord was
redundant. The detail page mutation still uses @deleteRecord
since it has no connection to delete from.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 13:02:02 +02:00
Bryan Frimin
f26bd8b0bf Add @deleteRecord directive to campaign delete mutations
Without @deleteRecord the deleted campaign stays in the Relay
store, so any other component that still holds a reference to it
keeps reading stale data. Pair it with @deleteEdge on the list
mutation so the connection and the record are both cleaned up.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 13:02:02 +02:00
Bryan Frimin
fd514d480b Mark navigate() call as void to satisfy lint
The post-delete redirect on the campaign detail page returned a
floating Promise from react-router's navigate(), tripping the
@typescript-eslint/no-floating-promises rule. Prefix the call
with `void` to match the convention used elsewhere in the app.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 13:02:02 +02:00
Bryan Frimin
177bc5e825 Allow deleting access review campaigns from the UI
Add a delete action to the access review campaigns list and the
campaign detail page. The action is gated on the
core:access-review-campaign:delete permission and only exposed
for campaigns whose status is DRAFT or CANCELLED, matching the
backend constraint enforced by CampaignService.Delete.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 13:02:02 +02:00
Bryan Frimin
6708cc901c Drop FAILED status from access review campaigns
Source fetch failures stay surfaced on the source fetch (status and
last error) instead of failing the entire campaign, so reviewers can
proceed on the sources that succeeded.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 12:59:55 +02:00
Sacha Al Himdani
8bdab65963 Allow editing metadata of generated document versions
Title, document type, and classification on generated documents could
not be changed: any version-tracked field on a GENERATED write-mode
document was rejected with ErrDocumentVersionGenerated. The error now
fires only when content is being changed, so manual metadata edits
flow through the same draft-on-edit path as authored documents and
produce a draft version that the user can review and publish.

The CLI document update --document-type enum gains
STATEMENT_OF_APPLICABILITY (which generated SoA documents already
use), and the GraphQL resolver maps the content-edit rejection to a
Conflict instead of falling through to a generic Internal error.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-07 11:54:49 +02:00
Bryan Frimin
c00711360c Support Google Cloud Identity in SCIM bridge
The SCIM bridge requested admin.directory.userschema.readonly during
OAuth consent, which is a Google Workspace-only entitlement. Cloud
Identity-only admins could not grant it, so the connect flow failed
before any sync ran. The scope was also unused: the provider only
calls Users.List, never the schemas, groups, or customers endpoints.

Trim the requested scopes down to admin.directory.user.readonly so
the integration works for Workspace and Cloud Identity (Free and
Premium) tenants. Switch Users.List to projection=full so standard
extended fields (Organizations, ExternalIds, Relations, Languages)
are populated on synced users; full projection does not require any
extra OAuth scope. Relabel the connector UI to "Google Workspace /
Cloud Identity" to reflect the broader support.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 09:41:46 +02:00
Bryan Frimin
25f8ade712 Fix cookie banner docs link to www.getprobo.com/docs
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-06 20:55:29 +02:00
Bryan Frimin
5e55c888c4 Add Microsoft 365 SCIM bridge and access review driver
Microsoft 365's native SCIM endpoint is unreliable, so mirror the
Google Workspace bridge over Microsoft Graph: a new MICROSOFT_365
OAuth2 connector, a SCIM bridge provider listing /v1.0/users with
$select pagination, and an access review driver that derives admin
status from /directoryRoles members. Refactor the bridge runner to
share OAuth2 plumbing across providers and surface the new bridge
type, scopes, UI card, and bootstrap env wiring.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-06 15:41:30 +02:00
Sacha Al Himdani
1434d8336c Allow publishing generated documents as minor versions
Generated documents (asset list, risk register, SoA, ...) previously
only ever produced a new major version. Every regeneration of an
auto-built register consumed a major number, even when the change was
trivial. They now accept a minor flag and publish as
currentMajor.currentMinor+1 when set, bypassing the approval flow.

To carry the flag through cleanly, the document publish API was
refactored. The three split mutations (publishMajor, publishMinor,
requestDocumentVersionApproval) and the two bulk variants collapse
into a single publishDocument / bulkPublishDocuments, both taking the
new minor: Boolean! and a now-required changelog: String!. The same
shape flows through the CLI ("prb document publish --minor"), the MCP
tool, the n8n operations, and the Relay dialogs, where each
generated-doc dialog gains a "Publish as minor" button. Publishing
minor without an existing major is rejected with
ErrCannotPublishMinorWithoutMajor.

This is a deliberate breaking change for callers of the prior
mutations.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-06 14:02:59 +02:00
Émile Ré
fe2871dda5 Fix lint
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-06 12:26:12 +04:00
Émile Ré
5302a9da7e Migrate display page to TrackerPattern GraphQL types
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-06 12:26:12 +04:00
Émile Ré
21f92352d5 Migrate detection page to TrackerPattern GraphQL types
- Add TrackerPattern type, connection, and filter to GraphQL schema
- Add uncategorisedTrackerPatterns field on CookieBanner
- Add updateTrackerPattern, deleteTrackerPattern, moveTrackerPatternToCategory mutations
- Implement all resolvers backed by tracker_patterns table
- Add TrackerPattern service methods (CRUD + list/count)
- Add LoadUncategorisedByCookieBannerID on TrackerPatterns coredata
- Update detection page to use TrackerPattern fragment, queries, and mutations
- CookieCategory resolver uses dataloader (not just struct ID)

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-06 12:26:11 +04:00
Émile Ré
c853d59fe5 Add trackerType to GraphQL schema and detection UI
- Add TrackerType enum to GraphQL schema with all 6 values
- Add trackerType field to CookiePattern type (nullable source)
- Add optional trackerType to CreateCookiePatternInput
- Add Type column to detection page table
- Add NewTrackerPattern helper in types package
- Regenerate gqlgen + Relay artifacts

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-06 12:26:11 +04:00
Émile Ré
39db90168c Guard store updater and handle reorder errors
Check that the move mutation payload contains the pattern
before removing it from the uncategorised connection. Add
onCompleted error handling to reorder mutations so GraphQL
errors are surfaced to the user.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-05 11:08:55 +04:00
Émile Ré
3b4aa7f971 Move cookie management to display page
Consolidate the separate Cookies tab into the Display page so
categories with full cookie CRUD, reordering, and theme preview
live together. Delete the now-redundant CookieBannerCookiesPage,
its loader, route, and nav tab.

Add row actions (edit, move-to-category, exclude, delete) to the
detection page. The move-to-category dropdown uses an
interaction-triggered preloaded query following the
useQueryLoader pattern. Document this pattern in the
react-components guide.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-05 11:06:55 +04:00
Émile Ré
c26b9c0abe Rename categories to consentCategories
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>
2026-05-05 11:06:54 +04:00
Émile Ré
3254c6ddec Add cookie banner detection page
Displays uncategorised cookie patterns in a sortable,
filterable table under a new Detection tab on the banner
configuration layout.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-05 11:06:54 +04:00
Sacha Al Himdani
553901e4ad Add risk publish to document system
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>
2026-05-04 14:13:42 +02:00
Émile Ré
1d08760d3b Display cookie pattern source badge in category table
Show the origin of each cookie pattern (Script vs Pre-existing)
as a badge with a tooltip in the cookies configuration table.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-01 17:41:50 +04:00
Émile Ré
7bc8343c18 Add MCP tools for cookie banner, category, pattern, version, and consent
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>
2026-05-01 15:19:21 +04:00
Cursor Agent
17b75f2017 Fix contract end date calendar cut off in dialog
The Add Person dialog wrapped its form content in a plain <div>
instead of DialogContent. When the form is tall enough, the
dialog extends beyond the viewport with no way to scroll,
cutting off the contract end date field.

Replace the raw <div> with DialogContent which provides
overflow-y-auto and a maxHeight constraint, matching the
pattern used by all other dialogs in the codebase.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Émile Ré <nemile.re@gmail.com>
2026-05-01 10:50:55 +00:00
Émile Ré
5f087b47b8 Reveal cookie banner in sidebar
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-30 17:21:37 +04:00
Émile Ré
22cf3a7643 Fix create dialog mutation relay store update
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-30 15:03:47 +04:00
Émile Ré
0d257977a1 Refactor cookie banner forms to react-hook-form
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-30 14:05:45 +04:00
Émile Ré
427bbbaf5c Move DurationInput to @probo/ui and scope pattern merge by category
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>
2026-04-30 11:46:12 +04:00
Émile Ré
2fe77d9ddc Fix review issues in cookie pattern handling
- Fix DurationInput fallback unit from "minutes" to "seconds" and add
  seconds as a selectable unit to prevent silent duration inflation
- Use parseFloat instead of parseInt for duration input to preserve
  fractional values
- Scope prefix merge groups by category ID to prevent cross-category
  merging
- Relink cookies and delete exact patterns even when prefix pattern
  already exists
- Prefer exact matches and longest prefix in pattern selection query
- Fix wrong error type in GetCookiePattern (ErrCookiePatternNotFound)
- Handle singular/plural in humanizeSeconds fallback branch

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-30 11:46:11 +04:00
Émile Ré
5cdaddf8b1 Store cookie durations as max_age_seconds
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>
2026-04-30 11:46:11 +04:00
Émile Ré
94ccc71357 Switch console cookies page to cookie patterns
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>
2026-04-30 11:46:11 +04:00
Sacha Al Himdani
c026f67bd9 Add vendor publish to document system
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>
2026-04-29 16:24:29 +02:00
Sacha Al Himdani
ba8bce2ad3 Add processing activity, DPIA and TIA publish to document system
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>
2026-04-28 18:07:46 +02:00
Émile Ré
e850069b07 Fix consent records version coercion and cache key
Add strict numeric validation to all version filter refetch
paths and include orderBy in @connection filters to prevent
cache collisions across sort orders.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-28 15:28:18 +04:00
Émile Ré
eebec9deb3 Fix js lint
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-28 15:28:18 +04:00
Émile Ré
9b83e319a0 Add consent record detail page
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>
2026-04-28 15:28:17 +04:00
Émile Ré
84632fe795 Filter consent records by version number
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>
2026-04-28 15:28:17 +04:00
Émile Ré
4147239fbc Add consent records tab to cookie banner config
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>
2026-04-28 15:28:17 +04:00
Émile Ré
86ffb8fbd6 Reorganize tabs and add description on cookies tab
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-28 15:28:16 +04:00
Émile Ré
4b08a465d4 Reorganize cookie banner configuration pages
Move JS snippet (CodeSnippets) into Settings, move category
sorting (CategoryList) into the renamed Display page, remove
the blocking-scripts step, and rename the Snippet tab to Display.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-28 15:28:16 +04:00
Sacha Al Himdani
bdb16d4abe Add finding and obligation publish to document system
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>
2026-04-28 13:12:58 +02:00
Émile Ré
272f63828f Add state to MCP profile and refactor contract filter
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>
2026-04-27 16:06:44 +04:00
Émile Ré
d9db93911b Add cookie banner deletion from overview page
Wire the existing deleteCookieBanner GraphQL mutation to the
cookie banners overview list. Inactive banners with delete
permission show an action dropdown with a confirmed delete flow.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:40 +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é
11f856740a Make cookie banner origin immutable after creation
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>
2026-04-24 18:48:39 +04:00
Bryan Frimin
de1a33dc36 Fix ts type error
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-04-24 16:33:47 +02:00
Bryan Frimin
1cc9f011f4 Fix import order and floating promise lint errors
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-04-24 16:10:47 +02:00