Commit Graph

1898 Commits

Author SHA1 Message Date
Émile Ré
36dbe47192 cli: add tracker-resource commands
Add pkg/cmd/tracker-resource/ with list, view, create, update, delete,
and move subcommands mirroring the tracker-pattern CLI surface.
Register in pkg/cmd/root/root.go.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:54 +04:00
Émile Ré
5bf6a67c06 mcp: add TrackerResource tools and drop SCRIPT/IFRAME from tracker_type
- Remove SCRIPT/IFRAME from the tracker_type enum in the MCP spec.
- Add TrackerResource schema in components/schemas.
- Add 6 tools: listTrackerResources, getTrackerResource,
  addTrackerResource, updateTrackerResource, deleteTrackerResource,
  moveTrackerResourceToCategory with input/output schemas.
- Add types/tracker_resource.go helper and resolver implementations.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:54 +04:00
Émile Ré
663dc7478f graphql: add TrackerResource schema, resolvers, and IAM actions
Add the full GraphQL surface for the new tracker_resources table:

- TrackerResourceType enum (SCRIPT, IFRAME), TrackerResource node type
  with connection/edge/order/filter, fields on CookieBanner
  (uncategorisedTrackerResources) and CookieCategory (trackerResources).
- Mutations: createTrackerResource, updateTrackerResource,
  deleteTrackerResource, moveTrackerResourceToCategory with
  inputs and payloads.
- Resolvers for all mutations, connection fields, field resolvers
  (cookieCategory, permission), and totalCount.
- IAM actions: core:tracker-resource:{get,list,create,update,delete}.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:54 +04:00
Émile Ré
6e07da9ab2 cookiebanner: add TrackerResource service layer
Wire resource ingestion and add full CRUD + list/count service methods
for the new tracker_resources table.

- reportDetectedResource splits the URL into origin/path and upserts
  into tracker_resources with the uncategorised category.
- CreateTrackerResource, GetTrackerResource, UpdateTrackerResource,
  DeleteTrackerResource, MoveTrackerResourceToCategory mirror the
  tracker-pattern service surface.
- ListTrackerResourcesForCategory, CountTrackerResourcesForCategory,
  ListUncategorisedTrackerResources, CountUncategorisedTrackerResources
  provide paginated access.
- Request structs with Validate() and dedicated error sentinels.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:53 +04:00
Émile Ré
93407f6431 coredata: split SCRIPT/IFRAME into tracker_resources table
Move resource tracking (scripts, iframes) out of the pattern-based
tracker_patterns/detected_trackers machinery into its own
tracker_resources table keyed by (banner, type, origin, path).

- Add migration that creates the tracker_resource_type enum, the
  tracker_resources table with a unique index, drops existing
  SCRIPT/IFRAME rows (not yet in production), and recreates the
  tracker_type enum without those values.
- Add TrackerResource coredata model with full CRUD, Upsert (bumps
  last_detected_at on conflict), list/count/move operations, filter,
  and order field support.
- Register TrackerResourceEntityType (91) in the entity type registry.
- Drop TrackerTypeScript/TrackerTypeIframe from TrackerType enum.
- Update handler to use TrackerResourceType for resource detection.
- Temporarily stub out resource ingestion in ReportDetectedTrackers
  pending the service-layer wiring in the next commit.
- Drop SCRIPT/IFRAME from the GraphQL TrackerType enum.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:53 +04:00
Émile Ré
923488b2b0 Rename migrations
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:53 +04:00
Émile Ré
8393e4bfb1 Fix unknown function ends_with in pg
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:53 +04:00
Émile Ré
3e4b4eec78 Go fix
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:53 +04:00
Émile Ré
855de92e1f Update tests for GLOB match type
Rewrite worker unit tests: TestTemplateCandidates, TestGlobMatch,
TestSplitTokens, and updated TestFindMergeGroups with sandwich pattern
cases. Update e2e test to use GLOB instead of PREFIX.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:52 +04:00
Émile Ré
0358aafb62 Update API surface to use GLOB match type instead of PREFIX
Replace PREFIX with GLOB in GraphQL enum, MCP specification, CLI
interactive prompts, and n8n node options.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:52 +04:00
Émile Ré
d006d4e462 Implement glob-based pattern discovery and matching in worker
Replace prefix-only merge logic with token-template analysis that
discovers sandwich patterns (e.g. ph_phc_*_posthog). The worker now
emits GLOB patterns, adoption uses globMatch, and validation enforces
exactly one wildcard for GLOB patterns.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:52 +04:00
Émile Ré
70d86d68af Add GLOB match type to replace PREFIX for tracker patterns
Introduces a wildcard-based match type that supports prefix, suffix,
and sandwich patterns (e.g. ph_phc_*_posthog). The SQL matching uses
starts_with/ends_with on the parts split at '*', avoiding LIKE and
its underscore escaping issues. Existing PREFIX rows are migrated to
GLOB with a trailing '*'.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:52 +04:00
Émile Ré
31bfbefc45 Make tracker pattern displayName read-only
The displayName field was always predictable from pattern + matchType
and allowing edits added unnecessary complexity. Remove displayName
from UpdateTrackerPatternInput across all surfaces (GraphQL, MCP, CLI,
n8n) and make the frontend show it as non-editable text.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:52 +04:00
Émile Ré
c0d0221be1 Detect HTTP-header cookies via CookieStore change event
Progressive enhancement for Chromium browsers: listen on the
CookieStore change event to catch cookies set by Set-Cookie HTTP
response headers, which the document.cookie setter hook cannot see.
Adds a new "http" cookie source through the full stack.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:51 +04:00
Émile Ré
a4cb61366f Report full URL instead of bare origin for detected scripts and iframes
Strip query params and send origin+pathname so the backend can
distinguish resources served from the same domain but different paths
(e.g. gtm.js vs recaptcha/api.js on googletagmanager.com).

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:48 +04:00
Émile Ré
93cf5a5986 Add duration-aware tracker pattern merging
Trackers sharing a prefix but with materially different lifetimes
(e.g. session vs 1-year) were incorrectly merged into a single
prefix pattern. Port the snap table from cookie-utils.ts into Go
and use it to bucket durations so only trackers that display the
same human-readable lifetime can merge. Update the unique index
to include COALESCE(max_age_seconds, -1) so prefix patterns with
different durations can coexist.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-11 14:52:48 +04:00
Aurélien Sibiril
0f1d893668 Tighten ctx-suspend plumbing and trim docs
Address review feedback:

- Move ErrSuspendForCheckpoint from checkpoint.go to errors.go
  next to the rest of the agent error declarations; drop the
  colon in the error string so it matches the existing
  `agent run <event>` style used by the supervisor sentinels.
- Replace the inline `outerCtx := ctx; ctx = context.WithoutCancel(ctx)`
  pattern with a small `suspendShield` helper in context.go used
  by coreLoop, resumeWithOpts, and resumeNested. Reads more
  cleanly and stops surfacing the WithoutCancel mechanism at
  every call site.
- Trim the doc comments on Run, RunStreamed, Resume, Restore, the
  ErrSuspendForCheckpoint declaration, and the saveCtx comment in
  restoreNestedSuspended down to the contract bullet.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-08 13:02:52 +02:00
Aurélien Sibiril
f715a806ee Strengthen ctx-cancel test coverage
Two additions:
- agent_test.go's "context cancellation triggers graceful suspend"
  now also asserts the input messages land in the suspension
  checkpoint — verifies the embedded-Checkpoint path that fires
  when no Checkpointer is configured.
- cancel_test.go gets a third subtest that parks the LLM provider
  inside ChatCompletion via a release channel, cancels ctx while
  the call is in flight, then confirms the LLM call still saw a
  non-cancelled ctx and the just-completed turn lands in the
  persisted checkpoint. Proves the framework's WithoutCancel
  shielding works end-to-end at the unit level.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-08 12:49:03 +02:00
Aurélien Sibiril
bdd207a100 Move ErrSuspendForCheckpoint to pkg/agent
The sentinel is part of the agent cancellation contract — the only
caller that needs it (the supervisor) imports pkg/agent already, so
keeping it next to SuspendedError prevents the upward dependency
that would arise if any future agent.Run caller wanted to trigger
graceful suspend. Update pkg/probo/agent_run_handler.go to
reference agent.ErrSuspendForCheckpoint.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-08 12:49:03 +02:00
Aurélien Sibiril
1b62d39a2e Update agent tests for the ctx-cancel suspend contract
Rewrite the WithStopSignal-driven test in restore_test.go to use a
cancellable ctx. Update agent_test.go's "context cancellation"
case from asserting "cannot complete" failure to asserting a
SuspendedError. Add cancel_test.go covering both pre-first-turn
cancel (no LLM call, empty checkpoint persisted) and mid-run
cancel from inside a tool (just-completed turn preserved in the
checkpoint, second LLM call suppressed).

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-08 12:49:03 +02:00
Aurélien Sibiril
53747733b3 Map supervisor shutdown onto run ctx cancellation
Process now has the per-run forwarder goroutine call
cancelRun(ErrSuspendForCheckpoint) when h.shutdownCh closes,
rather than closing a separate stopCh and embedding it via
agent.WithStopSignal. The agent loop's new ctx-cancel = graceful
suspend contract covers the rest. h.shutdownCh and signalShutdown
stay as the supervisor-level broadcast (still observable through
ShutdownBroadcastForTests).

The lease-loss path keeps its existing cancelRun call; under the
new contract that triggers a best-effort save before executeRun
detects ErrAgentRunLeaseLost and skips the row commit, which is
race-safe because Worker B can only claim the row after stale
recovery — by then our save has long landed.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-08 12:49:03 +02:00
Aurélien Sibiril
c4228e8e7c Drive graceful agent suspend from ctx cancellation
Collapse the dual-mechanism (ctx.Done() = abort + WithStopSignal =
graceful suspend) into a single signal: ctx.Done() now means
graceful suspend. coreLoop shadows the incoming ctx with
context.WithoutCancel(ctx) on entry and uses the shadow for every
downstream call (LLM, tools, hooks, guardrails, save), keeping the
original ctx only for the at-boundary cancellation check.
restoreNestedSuspended applies the same shadow to its
saveProgress closure so partial nested-restore writes survive a
graceful cancel. Resume and resumeNested mirror the pattern so
their pre-loop tool dispatch is non-cancellable while coreLoop
still detects the cancel at its first turn boundary. The dedicated
stop signal API (WithStopSignal / stopSignalFrom) is removed.

There is no longer an in-process hard-abort path; tool authors
who need a deadline must derive it themselves. Document the new
contract on Run, RunStreamed, Resume, and Restore.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-08 12:49:03 +02:00
Émile Ré
3acc3191ea Fix builder test
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-08 14:35:52 +04:00
Émile Ré
b199ceaa49 Fix probod-bootstrap builder tracing addr default port
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-08 14:10:28 +04:00
Émile Ré
16504814a8 Fix opt-out button opening panel instead of rejecting
In OPT_OUT mode the button_opt_out text was mapped to
button_customize, which opens the preference panel. Map it
to button_reject_all instead so the button performs a
one-click reject for all OPT_OUT regulations.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-08 11:28:58 +04:00
Bryan Frimin
4336b8eb48 Remove MS365 external user from access review
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 17:18:17 +02:00
Bryan Frimin
cf53ca3a0d Fix Microsoft 365 SCIM bridge connection and scope
Add the missing MICROSOFT_365 value to the connector_provider enum
so the connector loader stops failing with SQLSTATE 22P02.

Scope each Identity Provider card to its own SCIMBridge type so
connecting Microsoft 365 no longer marks Google Workspace as
connected (and vice versa).

Filter Microsoft Graph /users to userType eq 'Member' so the bridge
only syncs home-tenant members and skips B2B guest accounts that
were polluting the synced People list.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 18:01:24 +04:00
Émile Ré
d1f34add6f Fix cookiebanner rest API for sdk version <=0.2.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 17:57:00 +04:00
Émile Ré
d0060f6d2c Fix geoloc ip country block imports
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 17:30:09 +04:00
Émile Ré
245b9ef7c1 Fix lint
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:57:46 +04:00
Émile Ré
ac458e526f Format multiline calls in geoloc service
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é
b4d8bd8de2 Add country code to consent records
Store the visitor's resolved country code alongside regulation in
cookie_consent_records so downstream surfaces can expose it.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:48 +04:00
Émile Ré
dd6db7ca21 Some countries folders do not have ipv6 file
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:47 +04:00
Émile Ré
597d00d535 Remove dead code
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:47 +04:00
Émile Ré
b9c2ccd688 Add translation migrations
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:43 +04:00
Émile Ré
72fa79629f Review fixes
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:43 +04:00
Émile Ré
9e21f8fb08 Use shadow-table swap for IP country blocks import
Replace TRUNCATE+COPY inside a single transaction with a staging table
approach so that SELECT queries from the cookie banner handler are never
blocked during import. The ACCESS EXCLUSIVE lock is now only held for
the sub-millisecond DROP+RENAME swap.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:43 +04:00
Émile Ré
b4bb8715ee Adapt cookie banner UI and texts per regulation
The server now resolves regulation-specific translations
(opt-out notice for CCPA, simple notice when no regulation
applies) and remaps text keys before returning the config.
The client hides buttons whose text is empty, so the banner
layout adapts without client-side consent-mode logic.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +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
Émile Ré
f695b90b19 Store detected regulation in consent records and expose it in SDK
Add a regulation column to cookie_consent_records so each consent
captures which privacy law was in effect. Thread the value from the
handler's geoloc resolution through the service into the DB insert.

On the SDK side, add a Regulation union type to BannerConfig and expose
it via a getter on CookieBannerClient and in the probo-ready event
detail so themed-banner consumers can adapt their UI per regulation.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +04:00
Émile Ré
d5394317bb Add geolocation-based privacy regulation detection to cookie banner
Resolve the visitor's IP to a country code via the geoloc service and
map it to the applicable privacy regulation (GDPR, UK GDPR, FADP, CCPA,
PIPEDA, LGPD, LFPDPPP, POPIA, PDPA, PIPL, PIPA, APPI, DPDP, PDPL).
The regulation and its implied consent mode (OPT_IN / OPT_OUT) are
injected into the GET /config response so the SDK can adapt its behavior.

Also makes geoloc.Service self-contained: LookupCountry and IsPopulated
now manage their own DB connections instead of requiring callers to pass
a pg.Querier.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +04:00
Émile Ré
11d70c0d0d Move IP country block SQL to coredata
Extract struct and query functions into
pkg/coredata/ip_country_block.go following the convention that
all raw SQL lives in coredata. The geoloc service now delegates
to these functions.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:42 +04:00
Émile Ré
ad22fec81d Add IP-to-country geolocation service
Introduce a geoloc package that stores CIDR-to-country mappings in
PostgreSQL using the native cidr type with a GiST index for fast
containment lookups. Data comes from the ipverse/country-ip-blocks
dataset added as a git submodule.

A standalone geoloc-import command reads the TXT files from disk
and bulk-loads them via COPY. probod wires the service and logs a
warning when the table is empty.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-07 15:13:41 +04:00
Bryan Frimin
189c9eef1c Drop status default on access_review_campaigns
The application always sets status explicitly when creating a campaign,
so the database does not need to provide a default.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-05-07 12:59:55 +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
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