Commit Graph

4708 Commits

Author SHA1 Message Date
Bryan Frimin
c8de75cc03 Remove deadcode
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:09 +02:00
Bryan Frimin
2dc34df4e1 Fix access review e2e tests after API refactor
The campaign source node test queried entries before starting the
campaign, and the lifecycle test used the old accessEntry field name
instead of accessReviewEntry.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:08 +02:00
Bryan Frimin
67a68da371 Fix missing organization id
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:08 +02:00
Bryan Frimin
35910d1e6e Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:08 +02:00
Bryan Frimin
eed6bf579d Refactor access review campaign source API
Expose campaign sources as first-class nodes, paginate fetch attempts
instead of denormalized status fields, and bind entries to their
campaign snapshot. Update GraphQL, MCP, CLI, console, and e2e coverage
to match.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:08 +02:00
Bryan Frimin
82c9800677 Drop unused access review source category
Source category was never surfaced in the product and added noise to
snapshots and APIs. Remove the enum, columns, and service fields so
campaign sources track only the identity fields reviewers need.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:07 +02:00
Bryan Frimin
7138ae2273 Backfill csv_data on campaign sources
Fresh installs already had csv_data on campaign sources from the
initial migration; add a follow-up migration to backfill existing
rows from the live source snapshot.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:07 +02:00
Bryan Frimin
8d8a5ebb26 Probe every access review connector on status check
Bad API keys and expired OAuth tokens showed Connected because
probes ran only for OAuth2 and many providers had no ProbeURL.
Add a registry ProbeConnection dispatcher with static, dynamic,
and custom probes so all 41 providers are checked on demand.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:07 +02:00
Bryan Frimin
26d64a970f Remove deleted source badge from campaign detail
The badge did not help reviewers act on campaign entries once a
source snapshot is attached.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:49:07 +02:00
Bryan Frimin
a30a714fcd Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:48:54 +02:00
Bryan Frimin
aba0ae71ba Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:47:54 +02:00
Bryan Frimin
f951695108 Use dockerhub for public compose
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 16:40:50 +02:00
Bryan Frimin
a542851e8c Fix Cursor driver inactive account detection
Cursor's Admin API exposes two independent removal signals: the isRemoved
boolean and a role value of "removed". They are not always consistent —
a member can carry role "removed" while isRemoved is still false, a known
gap documented on the Cursor community forum.

Previously Active was derived from isRemoved alone, so a member with
role "removed" but isRemoved=false was incorrectly reported as active.
Now either signal is sufficient to mark the account inactive.

Add a cassette entry and test case covering the inconsistent state
(role "removed", isRemoved false) to prevent regression.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 15:33:22 +02:00
Bryan Frimin
4b64e59da4 Introduce access-review source snapshot and normalize naming
Decouple each campaign from the live access-review sources it was started
with by introducing a per-campaign source snapshot table
(access_review_campaign_sources). The snapshot captures the source name,
category, and connector at start time, so a review remains coherent even
after the underlying source is edited or deleted. Fetch tracking becomes
an append-only log (access_review_campaign_source_fetch_attempts) that
preserves every attempt with its own status and error rather than
overwriting a single row.

Rename the shared access-review tables and enums to use a consistent
access_review_ prefix throughout:

  access_entries          → access_review_entries
  access_sources          → access_review_sources
  access_source_category  → access_review_source_category
  access_entry_*          → access_review_entry_*

The same rename propagates to every coredata type, service, GraphQL
schema, MCP specification, CLI command, frontend component, and e2e test.
The accessreview package gains dedicated actions.go and policies.go files
for its own IAM policy set, mirroring the agentrun package pattern.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-06-15 15:33:22 +02:00
Émile Ré
8a53e0ec1c CI npm version should respect engines field
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-15 15:26:26 +02:00
Émile Ré
c79191f972 Update @vitest/* direct dependencies to exact 4.1.8 versions in sync with locked vitest version
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-15 15:13:05 +02:00
Émile Ré
ce0dfc1ee5 Release helm/v0.7.0
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 19:05:03 +02:00
Émile Ré
2fd3a4acfb Release @probo/cookie-banner/v0.9.3
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 19:04:35 +02:00
Émile Ré
0503e56f56 Release proboctl/v0.4.0
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 19:04:11 +02:00
Émile Ré
d9016b2d3a Release probod/v0.209.0
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 19:03:47 +02:00
Émile Ré
1fb92104bd Require original wording in tracker descriptions
The enrichment agent now reads full cookie-database and vendor pages
through the browser. The facts it gathers (setter, purpose, data
stored) are not protectable, but a source page's written description
is original expression. Instruct the agent to state the facts in its
own words and never copy a sentence verbatim, so stored descriptions
stay clear of third-party prose.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 18:34:20 +02:00
Émile Ré
b5bf63b436 Give tracker agents a browser to read setters
The tracker-mapping and common-pattern enrichment agents only had
web search, which returns title/url/snippet, so they could never
open a cookie-database or cookie-policy page to read which vendor
actually sets a tracker. This mis-attributed setters whose snippet
is misleading (e.g. _li_* read as LinkedIn rather than LiveIntent).

Wire the read-only headless-browser toolset into both agents, gated
on a configured Chrome endpoint, mirroring the common-third-party
enrichment worker: agent construction moves into the run path so each
run can carry a per-run browser that is closed when the run returns.
The prompts now direct the agent to open a promising result and read
the named setter from the full page text. Both agents stay unchanged
when no Chrome endpoint is configured.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 18:34:20 +02:00
Ludovic Vielle
dbfd191bc5 Add cache control to Files API static assets
Brand assets served at /api/files/v1/static had no cache headers.
Introduce brand.Assets to own the embedded filesystem, content-hash
ETags, and HTTP serving. Responses now carry Cache-Control and ETag
so clients can cache and revalidate; stable email URLs stay
revalidatable (max-age=3600, no immutable).

Replace hardcoded Default*Path constants with StaticPathPrefix,
logical filename constants, and StaticPath(). NewAssets validates
required assets at startup so a rename fails fast instead of 404ing
in sent emails. The files handler keeps routing and 404 rendering;
ServeAssets sets cache headers and serves the file.

Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-12 17:24:00 +02:00
Ludovic Vielle
c33540637d Remove useless migration tests
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-12 17:19:17 +02:00
Émile Ré
92e829cbe5 Hold cookie banner scroll lock against smooth scrollers
The preference panel scroll lock relied on `overflow: hidden` on the
document element, which JS-driven smooth-scroll libraries such as Lenis
(used by Framer, e.g. getbluejay.ai) bypass entirely since they
intercept wheel/touch events and scroll programmatically.

Pin the body with `position: fixed` (saving and restoring the scroll
position) so the viewport has no scroll distance left to animate, even
for libraries that capture wheel events on the window before us. Keep
capture-phase wheel/touch cancellation as defense-in-depth for sites
that scroll an inner container, while still letting the panel's own
list scroll.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 15:07:09 +02:00
Émile Ré
01cd58cba4 Rename strutil to stringsx, use wg.Go
Address PR review feedback: rename the shared string-helper package
from strutil to stringsx to avoid the discouraged util suffix and the
collision with the standard strings package, updating all import paths
and call sites.

Replace the manual wg.Add/wg.Done bookkeeping in the enrichment worker
with wg.Go, which is less error-prone.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:53 +02:00
Émile Ré
5e6478aa09 Fix lint issues
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:52 +02:00
Émile Ré
213ac6ddbe Extract shared NormalizeAlnum into pkg/strutil
The alphanumeric-normalisation helper was duplicated verbatim in the
common-third-party owned-domains resolver and the cookiebanner tracker
mapping worker. Hoist it into a new dependency-free strutil package so
both call sites share one implementation and one test.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:52 +02:00
Émile Ré
474074480a Seed enrichment_attempts in common third party e2e
The enrichment_attempts column dropped its default after backfill, so
inserts must supply it explicitly. The e2e seed helper omitted the
column and tripped the NOT NULL constraint, failing
TestThirdParty_ImportFromCommon and TestCommonThirdParties_QueryWithLogo.
Set it to 0 in the seed insert.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:52 +02:00
Émile Ré
3281f2b81c Parallelize enrichment agents, calibrate prompts
The common-third-party enrichment pipeline ran Agent B (compliance
docs), Agent C (owned domains), and the deterministic logo step
sequentially even though, once Agent A resolves the website, the
three depend only on that website and not on each other. Fan them
out across goroutines under a WaitGroup so wall time is the slowest
of the three rather than their sum. Each step builds its own per-run
browser and writes only into its own locals; the shared LLM, HTTP,
and FileManager clients are safe for concurrent use and the database
is untouched until persist. Results merge in a fixed order so
runErrors and log output stay deterministic.

Also replace the single-sentence confidence guidance in the three
agent prompts with an explicit, calibrated rubric tied to evidence
strength, and remind the model that a downstream threshold gates
persistence so it should neither inflate nor deflate its estimates.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:52 +02:00
Émile Ré
d226a8be9a Harden common third party enricher edge cases
Reject oversized logo responses instead of silently truncating them,
which could persist corrupt image bytes as a valid logo.

Tighten ownership substring matching with a length-ratio guard so a
short label root no longer attributes unrelated domains to a vendor.

Render the worker confidence threshold when set to zero by testing
against nil, so an explicit "accept all" value is not dropped by Helm's
falsy-numeric truthiness.

Sanitize and bound per-agent error text before persisting it to the
enrichment metadata column to avoid leaking unbounded internal detail.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:52 +02:00
Émile Ré
08ff9277e7 Drop enrichment_attempts default after backfill
Columns must not carry DEFAULT clauses. The DEFAULT only backfills
existing rows when the non-nullable column is added, so drop it in the
same migration; inserts already supply enrichment_attempts explicitly.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:52 +02:00
Émile Ré
b617741feb Add common third party reenrich and stats CLIs
Add `proboctl common-third-party reenrich` to re-arm the async
enrichment worker for selected catalog rows, and `stats` to summarize
the catalog by enrichment state and last run status. Rows are selected
verbatim via --id/--slug or across the catalog via
--category/--keyword/--state/--status, gated by --dry-run and --yes.

Extend `list` with --state/--status filters and STATE/STATUS columns,
and `show` with enrichment state, attempts, last run status, error,
per-field provenance, and discovered domains.

Back these with CommonThirdPartyFilter state/status/IDs filters plus
CommonThirdParties.LoadAllIDs and RequestEnrichmentByIDs. The latter
stamps enrichment_requested_at and resets the attempt counter while
preserving the existing payload, so the worker merge keeps curated and
human-edited provenance.

Also simplify exactLabelMatch to use slices.Contains.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:51 +02:00
Émile Ré
968895bdfd Remap unmapped trackers on new vendor domains
When the common-third-party enrichment worker discovers owned domains
for a vendor, tracker patterns that were detected and left unmatched
before those domains were known had nothing to re-trigger them.

Add RequestMappingForUnmappedByInitiatorDomains, a global re-arm that
stamps mapping_requested_at on still-unmapped patterns whose detected
trackers share one of the new domains, and call it from the enrichment
worker's persist step for newly-inserted domains only. The mapping
worker then re-resolves them through its existing domain-overlap path.

Targeting is limited to patterns with no resolved vendor (no org third
party and an absent or unlinked catalog row), so a pattern already
attributed to this or any other vendor is never disturbed.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:51 +02:00
Émile Ré
7649f19e33 Discover and persist common third-party domains
Add a domain-discovery step to the enrichment pipeline so the catalog's
domain set, previously written only by the curated seed, grows
automatically. A focused agent enumerates the registrable domains a
vendor owns and operates - marketing, product and sub-brand, app, API,
and CDN/asset domains - from links seen while browsing and from web
search, anchored on the website resolved earlier in the run.

A deterministic ownership gate reduces the candidates to eTLD+1 and
keeps only those that clear a strict confidence floor and match the
vendor by domain label. Shared tracker-delivery and CDN infrastructure
is dropped unless the vendor itself is that provider, in which case its
own brand-matching domain passes a stricter exact-label check. The
survivors are upserted into common_third_party_domains in the run's
final transaction and recorded in the enrichment payload, feeding the
tracker-mapping domain step and disambiguation.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:51 +02:00
Émile Ré
8182c61fa8 Improve common third-party enrichment quality
Give the company-profile agent (Agent A) the read-only browser toolset
and build it per-run, so it can read footer, imprint, about, and legal
pages and follow a product domain to the corporate one to resolve the
legal name and headquarters address rather than failing cold.

Make the website the hard precondition: when Agent A cannot resolve a
canonical website, skip the compliance-docs agent and logo step instead
of running them blind, which previously produced inconsistent
cross-domain document URLs. Fall back to the catalog display name for
the legal name when nothing better is found, recorded with a distinct
provenance status so a later real find overwrites it.

Rewrite both enrichment prompts in the project's role/task/instructions
XML style, add a domain-consistency rule for document URLs and a
tool-budget directive, and document the prompt style as a rule and guide.

Fix the find_links_matching browser tool, which double-encoded its
pattern and made JSON.parse fail on every keyword, starving any agent
that used it until it hit the turn cap. Salvage output when an agent
exhausts its turn budget while still exploring with a pending structured
output by forcing one final synthesis turn instead of failing outright.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:51 +02:00
Émile Ré
b36bbf9992 Fix lint issues
Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:51 +02:00
Émile Ré
229c6b99c6 Add common third party enricher worker
Introduce a poll-based worker that fills the global common_third_parties
catalog (URLs, headquarter address, legal name, certifications, logo)
so each tenant no longer starts from sparse, name-only rows. Enrichment
is requested at row creation by ResolveOrCreateCommonThirdParty; curated
seed rows are not enqueued, to avoid a re-seed storm.

The pipeline uses two specialized agents plus a deterministic logo step.
Agent A (company profile) resolves legal name, headquarter address, and
the canonical website over web search; its website and legal name feed
Agent B and the logo step. Agent B (compliance docs) resolves the legal
document URLs, trust/security/status pages, and certifications using the
browser read-only toolset (gated on ChromeDPAddr) plus web search. The
logo step restores pkg/webinspect as a pure deterministic package and
stores the discovered icon in S3, linked via logo_file_id.

Each agent returns per-field value/confidence/source_url. The worker
writes a column only when confidence clears a configurable threshold and
the field is not externally owned (seed or human), and always records
full per-field provenance in a new enrichment JSONB column so re-runs
fill only gaps and human edits are never clobbered. New bookkeeping
columns (enrichment_requested_at, enrichment, enrichment_attempts) back
the claim queue and stale recovery; agents run outside transactions and
results persist in one final transaction.

The worker is opt-in: it no-ops unless its agent provider is configured.

Signed-off-by: Émile Ré <emile@probo.com>
2026-06-12 14:39:51 +02:00
Sacha Al Himdani
99d568d07d Fix activate login path
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-06-12 12:53:15 +02:00
Ludovic Vielle
beeee81a20 Release probod/v0.208.1
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-12 10:05:48 +02:00
Ludovic Vielle
8dbfa35382 Fix trust center file creation in console
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-12 09:57:36 +02:00
Cursor Agent
6c74a9fe6e Update presigned URL test for split service
Use the rebased filemanager method name in the regression test after
main split the S3 URL helpers into their own file.

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

Co-authored-by: Ludovic <ludovic@probo.com>
2026-06-11 15:26:18 +00:00
Cursor Agent
6794f14a24 Fix S3 filename header escaping
Use an ASCII-only fallback for Content-Disposition filename and
RFC 5987 path escaping for filename* so presigned S3 responses keep
spaces and Unicode filenames interoperable.

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

Co-authored-by: Ludovic <ludovic@probo.com>
2026-06-11 15:25:15 +00:00
Ludovic Vielle
732493d054 Release probod-bootstrap/v0.1.2
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:30:11 +02:00
Ludovic Vielle
1014dd2513 Release probo-agent/v0.1.1
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:29:20 +02:00
Ludovic Vielle
c80b6ae62c Release proboctl/v0.3.1
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:28:16 +02:00
Ludovic Vielle
f713a8b1cb Release @probo/cookie-banner/v0.9.2
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:27:38 +02:00
Ludovic Vielle
bb253cda8e Release helm/v0.6.0
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:25:58 +02:00
Ludovic Vielle
452a7625a7 Release @probo/n8n-nodes-probo/v0.194.0
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:25:03 +02:00
Ludovic Vielle
e962d300cb Release prb/v0.194.0
Signed-off-by: Ludovic Vielle <ludovic@probo.com>
2026-06-11 16:23:30 +02:00