Commit Graph

597 Commits

Author SHA1 Message Date
Sacha Al Himdani
82b531718e Release @probo/cookie-banner/v0.6.0
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-26 18:38:07 +02:00
Sacha Al Himdani
d8f0cf558b Release @probo/n8n-nodes-probo/v0.188.0
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-26 18:34:07 +02:00
Émile Ré
bd5c527548 fix(cookie-banner): disconnect observer in load() error path
When fetchJSON failed, load() unconditionally overwrote this.observer
with a fresh MutationObserver, leaving any previous one observing the
DOM. Repeated load() calls that hit the catch branch leaked one
observer per call. Mirror the disconnect-first pattern already used in
activate().

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:54 +02:00
Émile Ré
d181fbe386 Add eager GCM bootstrap and remove PostHog integration
GCM now creates dataLayer and denies all consent types before the
config fetch, closing the gap where gtag could track freely during
the async config load. PostHog integration is removed because
script blocking via data-cookie-consent is the correct mechanism
for vendors that don't support a queue/replay pattern.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:53 +02:00
Cursor Agent
b721179974 Allow initial document minor publishing
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-05-26 08:26:47 -07:00
Sacha Al Himdani
6dfdd7ca49 Link measures to third parties
Add a many-to-many relationship between measures and third parties,
surfaced as a measures tab on the third party detail page and a third
parties tab on the measure detail page. Each side gets a paginated
list with a link/unlink dialog.

Also remove the right-hand drawer on the measure detail page and
expose the state as a badge in the page header, mirroring how the
compliance page surfaces its active flag.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-26 15:49:17 +02:00
Sacha Al Himdani
b6b1e801b1 Add third-party self-referential relations
Introduce a self-referential many-to-many relation table so a
third party can have child third parties. Each relation is
directional (parent to child); both directions can coexist as
independent rows.

Add a first_level boolean on third_parties (default true) with
a filter on the list page that defaults to showing only
first-level third parties.

Frontend adds a "Third Parties" tab on the detail page where
users can link existing third parties or create new ones from
the common third party catalog (created as non-first-level).
The list page gets a First Level/All toggle filter.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-26 15:21:38 +02:00
Émile Ré
8ff68798fb fix(cookie-banner): detect browser-extension stack frames
STACK_URL_RE only matched `https?://` URLs, so extension URLs
(`chrome-extension://`, `moz-extension://`, `safari-web-extension://`)
never reached the EXTENSION_URL_RE classifier. The `fromExtension`
flag was effectively always false and extension-originated cookie
and storage activity was misattributed as `script`.

Broaden STACK_URL_RE to also match the three extension schemes so
the existing classifier branch fires and sets `fromExtension = true`
before the URL parsing path runs.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:48 +02:00
Émile Ré
1c3ce56b48 Mark page-world extension writes with EXTENSION source
The previous cleanup deleted every isExtensionCaller() site, including
the one in cookie/storage detectors that did fire reliably for the
residual case: page-world extensions (MV3 main world, userscripts with
@grant none) whose stack contains a chrome-/moz-/safari-web-extension
frame at the synchronous write. Recover that signal for free by
returning fromExtension from getInitiatorURL (it already walks the
stack and discards extension frames via continue), and have the cookie
and storage detectors report source: "extension" instead of "script"
when the flag is set.

End-to-end plumbing reuses the existing source column: extend the
cookie_source Postgres enum with EXTENSION, add the CookieSourceExtension
constant with a doc block describing each bucket's actual semantics,
add the handler.go switch cases, expose EXTENSION on the GraphQL and
MCP CookieSource enums, and add the Extension option to the console
source filter.

Update bestSource in the pattern analysis worker so a glob merging
only extension-attributed exact patterns is no longer silently rolled
up to PRE_EXISTING. New precedence is SCRIPT > EXTENSION > PRE_EXISTING,
matching the upsert SQL's "page-script wins" rule and the asymmetric
signal strength of each bucket.

Out of scope: any behavioural use of EXTENSION (auto-exclusion,
denylist classification, dashboard surfacing) -- that belongs in the
follow-up backend denylist plan.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:48 +02:00
Émile Ré
b04454eaee Drop ineffective extension-caller wraps from cookie banner
The synchronous Storage/Document/Element/fetch/XHR/sendBeacon wraps
and the resource-detector attribution machinery relied on
isExtensionCaller() finding a chrome-extension:// frame in the JS
stack. For Chromium/Edge/Safari MV3 isolated-world content scripts
-- the dominant case -- those wraps live in the page realm while
the extension uses its own copy of every prototype we hook, so the
check never fires and the marking never runs.

Backend denylisting (planned) covers the same cases more cheaply,
retroactively, and across all tenants, so the elaborate frontend
plumbing no longer earns its complexity. Keep only the parts that
backend classification cannot replace: isExtensionContext() (SDK
loaded inside an extension page) and the http/https-only filter
in processResource (drops chrome-extension:// URLs surfaced via
PerformanceObserver). resource-detector.ts shrinks from ~920 to
~225 lines.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:48 +02:00
Émile Ré
5b9698494d Mark extension <link> writes before rel-based classification
handleAttributeMutation returned early when resourceTypeForElement
yielded null, which happens for `<link href>` whenever `rel` is not yet
"stylesheet". An extension that called `link.setAttribute("href", ...)`
before setting `rel` therefore skipped extensionElements and
extensionUrls marking; when the rel was filled in later and the browser
fetched the stylesheet, PerformanceObserver -- whose stack carries no
extension frame -- reported it as a page tracker.

Check isExtensionCaller() before classification and tag the element and
URL whenever the element type can ever initiate a load via the given
attribute, using a new couldLoadResource helper. The page-caller path
still uses the strict resourceTypeForElement so non-stylesheet `<link>`
writes do not generate spurious reports.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:48 +02:00
Émile Ré
2261ed0c8f Add synchronous DOM hooks to cookie banner resource detector
Wrap script/iframe/img/link/source IDL setters, setAttribute, the four
HTML-parsing entry points, fetch, XHR, and sendBeacon so extension stacks
are visible at the synchronous call site. Coordinate via an
extensionElements WeakSet and an extensionUrls Set so the existing
MutationObserver and PerformanceObserver paths drop the same resources.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:47 +02:00
Émile Ré
f1d320361e Filter extension activity across all cookie banner detectors
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:47 +02:00
Émile Ré
e2b8ee15e7 Track source on detected storage trackers
The cookie detector tags every detection with a source (script,
pre-existing, http) but the storage detector did not, so storage
rows always landed in detected_trackers with source NULL even though
the SDK already distinguishes wrapper writes from pre-load scans.

Plumb a "script"/"pre-existing" source from the storage detector
through the report endpoint into DetectedStorageItem, so the column
gets populated for localStorage, sessionStorage, indexedDB and
cacheStorage entries. No schema change is needed: detected_trackers
already accepts CookieSource values regardless of tracker_type, and
the existing row renders the badge as soon as it is non-null.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:47 +02:00
Bryan Frimin
46cc1e11ea Release @probo/n8n-nodes-probo/v0.187.1
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-25 17:07:13 -07:00
Sacha Al Himdani
83445c6e34 Fix signature count mismatch with signatures tab on documents
The badge on a document version showed signatures filtered by
activeContract: true, while the signatures tab fetched signatures with
no filter and listed people filtered by contractEnded: false and
state: ACTIVE. The two views disagreed both when a signer's contract had
ended and when a signer was deactivated while still under contract.

Add a state: ProfileState field to DocumentVersionSignatureFilter
alongside the existing activeContract filter, so the signature query
can mirror the same predicates as the people query. Pass
{ activeContract: true, state: ACTIVE } from the badge, the document
list item, and the signatures tab fragment. The same filter is now
evaluated on both the count and the list.

Threaded through the console and MCP resolvers, the MCP spec, and the
n8n getAllSignatures operation.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-22 14:34:35 +02:00
Émile Ré
63e8c85f66 Release @probo/n8n-nodes-probo/v0.187.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-22 13:58:43 +02:00
Sacha Al Himdani
883031830f Add risk assessment system to CLI, MCP, and N8N
Expose the full risk assessment hierarchy (assessments, scopes, nodes,
processes, threats, scenarios) with CRUD operations and scenario
linking across all three interfaces.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-21 17:50:22 +02:00
Sacha Al Himdani
6948a6b9ab Tailor signature certificate email copy for approvals
Store the per-signature email subject as text on the electronic_signatures
row at creation time, mirroring the consent_text pattern. The document
approval service sets "Your approved <Title> - Certificate of Completion";
other callers default to "Your signed <Name> - Certificate of Completion".
The certificate worker uses signature.email_subject as the email subject,
falling back to the default format when the column is empty.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-21 14:49:19 +02:00
Émile Ré
f41dd9566b Release @probo/cookie-banner/v0.5.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-20 13:55:22 +04:00
Émile Ré
9156d6a16a Add wsl linter and fix
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-20 09:27:28 +04:00
Sacha Al Himdani
eedfdcecc8 Render mermaid diagram per risk assessment scope
Each scope card now shows a flowchart of its nodes, processes, and
threats, with a distinct shape per type: stadium for entities,
hexagon for boundaries, rectangle for assets, cylinder for data, and
a red hexagon for threats attached via dashed edges to their process
target. The Mermaid source is built on the backend and exposed as a
new `mermaid` field on RiskAssessmentScope; the frontend just renders
it via @probo/ui's MermaidDiagram and shows a copy button + legend.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-19 21:44:28 +02:00
Émile Ré
10e35c6afc Share consent
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-19 12:52:11 +04:00
Émile Ré
0f32037e70 Fix duplicate callback dispatch in ConsentManager.subscribe
When `_ready` was already true, `subscribe` called `cb` directly and
then `onReady` called it again, producing duplicate side effects.
Remove the redundant direct call since `onReady` already handles the
immediate-dispatch case.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-19 12:52:10 +04:00
Émile Ré
c17757565f Make ConsentManager referentially stable and add subscribe method
getAll() now returns a cached snapshot that is only replaced on state
changes, making it safe for useSyncExternalStore. The new subscribe()
method unifies onReady + onChange with immediate replay so React
consumers get a single subscription point.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-19 12:52:10 +04:00
Émile Ré
4078567c04 Add programmatic consent API to @probo/cookie-banner
Expose a ConsentManager singleton via `@probo/cookie-banner/consent`
(ESM) and `window.Probo.consent` (IIFE) so customers can check and
react to consent state in their bundled JavaScript code, solving the
problem of third-party SDKs initialized programmatically that cannot
be blocked via data-cookie-consent attributes.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-19 12:52:10 +04:00
Émile Ré
b3c67fb0e2 Move third parties data.json and remove @probo/third-parties
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-18 19:05:10 +04:00
Émile Ré
59f3b06824 Add third parties to seed dataset
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-18 19:05:10 +04:00
Émile Ré
0866276074 Remove json file
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-18 19:05:08 +04:00
Émile Ré
a16821802e Fix PR review findings
- Replace copy-pasted New Relic URLs in Resend third-party entry
  with correct resend.com URLs and drop inapplicable fields
- Escape single '%' instead of '%%' in LIKE pattern conversion so
  literal percent signs are not treated as wildcards
- Return actual row ID from CommonTrackerPattern.Upsert via
  RETURNING id so conflict-path callers get the existing ID
- Add ORDER BY id ASC to vendor-by-common-third-party query for
  deterministic LIMIT 1 selection

Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-18 19:05:08 +04:00
Émile Ré
f0483d5280 Add tracker pattern seed data and domain aliases
Seed common_tracker_patterns from Open Cookie Database (2300
patterns with max_age_seconds where available). Add domain
aliases to all 92 vendors in data.json. Extend third-parties
import to upsert domain aliases. Add dedicated import command
for tracker patterns with third-party name cache.

Signed-off-by: Émile Ré <emile@getprobo.com>
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-18 19:05:06 +04:00
Aurélien Sibiril
c2db47e698 Require CLIENT_SECRET for new access-review connectors → Drop Snyk, Ramp, Lever, Deel access-review providers
- Require CLIENT_SECRET for new access-review connectors
- Use Heroku account UUID as ExternalID
- Bump GitHub orgs picker to per_page=100
- Drop Snyk, Ramp, Lever, Deel access-review providers

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-17 17:22:50 +02:00
Aurélien Sibiril
8f6ecd9f81 Wire post-OAuth pickers and 2-auto callback handling → Track e2e gap for new access-review connectors
- Wire post-OAuth pickers and 2-auto callback handling
- Add 13 vendor logo components for new connectors
- Wire access-review connectors into bootstrap config
- Track e2e gap for new access-review connectors

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-17 17:22:48 +02:00
Sacha Al Himdani
6a20515a68 Release @probo/n8n-nodes-probo/v0.186.0
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-15 09:31:19 +02:00
Sacha Al Himdani
eecbe4c46c Rename vendors to third parties
Renames the user-facing 'vendor' concept to 'third party' across the
entire codebase. The shared common_third_parties reference table is
unchanged.

Migration. Renames the vendor_category enum, the vendors and
vendor_<entity> tables (contacts, services, compliance_reports,
business_associate_agreements, data_privacy_agreements,
risk_assessments) and their vendor_id columns, the asset_vendors /
data_vendors / processing_activity_vendors junction tables,
generated_documents.vendors_document_id, the webhook_event_type
'vendor:<verb>' values, and the snapshots_type 'VENDORS' value.

Backend. Renames coredata models and SQL queries, probo services,
GraphQL / MCP API surface, console / trust / webhook resolvers and
types, the CLI (prb vendor* -> prb third-party*; pkg/cmd/vendormgmt
-> pkg/cmd/thirdpartymgmt), the document generator, vetting agent
prompts, and the common-third-parties-import command.

Frontend, packages, n8n, e2e. Renames apps/console pages, components,
hooks, routes, dialogs, and tabs; the shared @probo/vendors package
(now @probo/third-parties); the @probo/ui Vendors atoms (now
ThirdParties, VendorLogo -> ThirdPartyLogo); the n8n community node
actions/vendor folder (now actions/thirdParty); and the e2e Go test
suite (console and MCP). Filesystem and URL paths use kebab-case
(third-parties), GraphQL fields and TypeScript identifiers use
camelCase (thirdParty / thirdParties), Go types use PascalCase
(ThirdParty), and human-facing text uses 'third party' with a space.

Co-authored-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-13 21:21:39 +02:00
Émile Ré
7b9090a9f1 Release @probo/cookie-banner/v0.4.1
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-13 15:25:57 +04:00
Émile Ré
0d37f7d315 Release @probo/n8n-nodes-probo/v0.185.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-13 15:24:34 +04:00
Émile Ré
e739473bcd Derive consent mode from geolocation, not banner config
The consent mode is now determined dynamically by the visitor's
country and its applicable regulation. The configured consent_mode
column is dropped from cookie_banners and added to
cookie_consent_records to persist the geo-derived mode at
consent-recording time. When no regulation matches, the default
is OPT_OUT.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-13 12:49:13 +04:00
Émile Ré
e9d786c5d6 Skip consent API fetch for new visitors
When visitorId is null (no localStorage entry), the client now
skips both the cookie check and the /consents/:id fetch that
was always returning 404 for first-time visitors. The visitor ID
is created lazily on first consent action instead.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-13 12:49:13 +04:00
Sacha Al Himdani
a67f2433b1 Address code review findings in relay fetch and iam
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-12 18:42:54 +02:00
Sacha Al Himdani
fc24306cd8 Address code review findings across relay, UI, and Go backend
- relay: key uploadables map by actual variable name instead of iteration
  index so order-mismatch between Object.keys passes can't desync the
  multipart map from form field names
- mcp/v1: drop dead commented middleware line
- DurationPicker: tighten parse regex to require PT prefix for M/H and P
  for D/W, and reject NaN in stringify so cleared inputs don't produce
  invalid duration strings

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-12 18:07:34 +02:00
Émile Ré
017b829114 Fix lint
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 16:43:38 +04:00
Émile Ré
f7dd08d432 Optimize website logo get
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 16:43:37 +04:00
Émile Ré
684fadea3e Remove unused description column from common_third_parties
The description field was never surfaced in the UI and added no value.
Drop it from the database, Go structs, GraphQL schema, import tool,
frontend fragment, and vendor seed data.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 16:43:36 +04:00
Émile Ré
caba84fd74 Fix missing newline at EOF
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 16:43:36 +04:00
Émile Ré
361fe1d713 Add common_third_parties shared reference table
Introduce a globally-shared, non-tenant-scoped common_third_parties
table that mirrors the public subset of vendor metadata, plus a
one-shot cmd/common-third-parties-import CLI that seeds it from
packages/vendors/data.json. The catalog will back future flows (e.g.
vendor autocomplete) so each tenant no longer needs to duplicate the
same baseline data.

The importer is idempotent via ON CONFLICT (lower(name)) DO UPDATE
and prints inserted/updated counts. GIDs use gid.NilTenant since the
table is not tenant-scoped; uniqueness still comes from the entity
type plus 14 bytes of timestamp/random suffix.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 16:43:35 +04:00
Sacha Al Himdani
ee22e9f964 Address CodeQL findings: unused imports, missing semicolons, dead conditionals
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-12 14:09:35 +02:00
Sacha Al Himdani
4f88b15f6d Bump mermaid to 11.15.0 to fix moderate security advisories
Fixes GHSA-6m6c-36f7-fhxh (Gantt infinite loop DoS), GHSA-xcj9-5m2h-648r
and GHSA-87f9-hvmw-gh4p (CSS injection via classDefs/configuration), and
GHSA-ghcm-xqfw-q4vr (HTML injection via classDef in state diagrams).

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-12 13:07:28 +02:00
Émile Ré
a07ced47ae Release @probo/cookie-banner/v0.4.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 10:51:26 +04:00
Émile Ré
d1a2a7cf32 Release @probo/n8n-nodes-probo/v0.184.0
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-05-12 10:50:55 +04:00