Commit Graph

4312 Commits

Author SHA1 Message Date
Aurélien Sibiril
a5259fc978 Refine connector provider registry per review
Three follow-ups from review of the registry consolidation:

- Build Vercel's authorization URL with net/url instead of a
  hand-rolled "{integration_slug}" placeholder resolved by
  strings.ReplaceAll. The slug is escaped via url.PathEscape in a
  per-provider Registration.BuildAuthURL closure, and the unused
  AuthURLParams plumbing on Registration and OAuth2Connector is
  removed (OAuth2Connector now carries a typed IntegrationSlug).

- Drop the SettingsInput union type and the per-provider
  MarshalSettings closures. The create resolvers now build the typed
  coredata.*ConnectorSettings directly from the gqlgen input, the
  same way the OAuth callback path already does, so there is no
  shared catch-all DTO and no stringly-typed boundary.

- Restore ConnectorProviders() to a plain ordered slice literal; the
  intermediate map + slices.Sort added nondeterminism and a sort for
  no benefit.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-27 00:34:39 +02:00
Aurélien Sibiril
e18ecdda8b Consolidate connector provider dispatch behind a typed *Registry
The console previously dispatched per-provider logic through a fan of
init()-side-effect maps (driver names, OAuth2 metadata, probe URLs,
display names, settings switches), spread across pkg/connector,
pkg/accessreview/drivers and the console v1 resolvers. Adding a new
provider required edits in every one of those places and a corresponding
switch arm in CreateConnectorRequest. The same per-provider knowledge
also leaked into Helm templates as hand-rolled environment-variable
blocks per connector.

This commit collapses the dispatch surface into a single typed
*provider.Registry. The registry is constructed once by
NewBuiltinRegistry at probod startup and threaded as an explicit
dependency into every consumer (accessreview service, console v1
resolver, OAuth2 wiring). There is no package-level state. Each
provider lives in one file under pkg/connector/provider/ that exposes
a private xxxRegistration() *Registration constructor; NewBuiltinRegistry
enumerates them.

CreateConnectorRequest loses its per-provider settings fields and
takes a single RawSettings json.RawMessage produced by the
per-provider MarshalSettings closure. The 1Password SCIM bridge URL
is validated at create time (http(s) scheme + non-empty host) so a
malformed value fails fast at the resolver boundary. The Helm chart
gains probo.connectorEnv and probo.connectorSecretEntries templates
so adding a connector requires zero Helm changes. Access-review name
resolution moves into the same Registration value to keep one
authoritative dispatch table.

Tests cover every Registration (DisplayName, NewDriver wired),
Register error paths (nil, empty Provider, empty DisplayName,
duplicate), All / ProviderDisplayName / ProviderOAuth2Scopes /
ProbeURL hit and miss paths, the ApplyOAuth2Defaults templating and
PKCE branches, and ConnectorSettings[T] round-trip plus malformed-JSON
error path. The pre-refactor ApplyProviderDefaults test in
pkg/connector is replaced by the equivalent in
pkg/connector/provider.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-27 00:34:39 +02:00
Bryan Frimin
f016e2cf06 Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 15:26:22 -07:00
Bryan Frimin
e5041a882a Upgrade golang ci
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 15:22:27 -07:00
Aurélien Sibiril
0c34d4fa05 Fix SA5011 nil-deref lint in validator security test
CI's staticcheck does not treat t.Fatal as terminating, so every
`err.Message` deref sitting after a separate `if err == nil { t.Fatal(...) }`
block is flagged SA5011 (possible nil dereference). Collapse all 26
occurrences in TestNoHTML / TestPrintableText / etc. into a guarded
`} else if`, matching the fix already applied to validator_format_test.go
in bca36636f. The deref now lives in a branch where err is provably
non-nil, which satisfies SA5011 regardless of t.Fatal recognition.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-05-26 15:07:18 -07:00
Bryan Frimin
b8ea34cdea Fix test linux
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 15:02:50 -07:00
Bryan Frimin
bca36636f9 Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 14:29:48 -07:00
Bryan Frimin
100e5a154d Fix docker hub rate limit
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 14:20:28 -07:00
Bryan Frimin
4296d168aa Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 14:18:34 -07:00
Bryan Frimin
138f25a170 Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 14:11:59 -07:00
Bryan Frimin
51f37b9d85 Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 14:07:22 -07:00
Bryan Frimin
712f00f434 Update copyright year
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:59:17 -07:00
Bryan Frimin
c46eab5c6e Add i3, kde and more support
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:55:43 -07:00
Bryan Frimin
5c73886faf Fix label name
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:47:14 -07:00
Bryan Frimin
7a3d3db7ac Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:58 -07:00
Bryan Frimin
e040851a4c Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:57 -07:00
Bryan Frimin
e6f988834d Apply go fix stringsseq rewrites to platform-specific check files
Replace strings.Split + for-range-slice with strings.SplitSeq +
for-range-iterator in checks_linux.go and checks_windows.go. These
files are invisible to go fix on macOS (darwin build tag), so the CI
Linux runner caught them first.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:57 -07:00
Bryan Frimin
378a7d6d9c Restore GNUmakefile targets dropped with probo-agent
The probo-agent commit replaced proboctl build vars, Docker registry
settings, and fix targets instead of adding probo-agent alongside them.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:57 -07:00
Bryan Frimin
941ab121da Reject shell-unsafe paths in FreeBSD rc.d install
Validate executable and state directory paths before rendering the
rc.d script so crafted values cannot inject shell syntax.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:57 -07:00
Bryan Frimin
27cccd33aa Make Windows service uninstall idempotent
Treat sc.exe delete error 1060 as success when the service is
already absent so repeated uninstalls do not fail.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:56 -07:00
Bryan Frimin
d310886560 Use platform-specific atomic key replacement on Windows
Swap the keystore through a temp and .old file on Windows instead
of relying on os.Rename alone, which is not reliably atomic there.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:56 -07:00
Bryan Frimin
0a4922f571 Share copyFile helper across Unix and Windows updaters
Move the duplicated file-copy logic into update/copy.go so both
platform installers stay in sync during maintenance.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:56 -07:00
Bryan Frimin
842081b66e Parse macOS postinstall conf without eval
Read KEY=VALUE lines from /tmp/probo-agent.conf directly so a
user-controlled temp file cannot execute arbitrary shell as root.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:55 -07:00
Bryan Frimin
75051cc150 Handle FreeBSD check command failures before reading status
Return unknown when service status commands fail so remote login
and malware checks cannot pass on empty output after an error.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:55 -07:00
Bryan Frimin
0d3b526db0 Style
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:55 -07:00
Bryan Frimin
22e50b3f11 Add probo-agent CLI and deviceagent library
Introduce the standalone device agent binary and shared library
for enrollment, posture checks, self-update, and OS service
integration. Include build targets, module deps, and release
workflow so the agent can ship independently of server changes.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:54 -07:00
Bryan Frimin
e643a3259c Update README
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 13:45:33 -07:00
Bryan Frimin
4cdf7699e8 Release probod/v0.193.1 2026-05-26 11:48:15 -07:00
Bryan Frimin
59f8e943fb Fix open redirect bypass in saferedirect
Relative redirect URLs are now normalized before validation. Paths
containing backslashes (including percent-encoded %5c) are rejected,
closing a bypass where /../\evil.com passed checks but http.Redirect
normalized to /\evil.com.

Reported by Fushuling and RacerZ.

Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 11:41:33 -07:00
Bryan Frimin
bf6fc2d509 Update README
Signed-off-by: Bryan Frimin <bryan@probo.com>
2026-05-26 09:48:05 -07:00
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
Sacha Al Himdani
08d1256ac0 Release probod/v0.193.0
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-26 18:30:27 +02:00
Sacha Al Himdani
b25b1e6b59 Release prb/v0.189.0
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-05-26 18:26:07 +02:00
Émile Ré
dc92fd238f Fold PromoteSource into Update
Every PromoteSource caller already loaded the tracker pattern under
the same transaction, so a dedicated single-column UPDATE only
duplicated machinery and forced callers to learn a second mutation
verb. Add `source = @source` to Update's SET clause, mutate
Source/UpdatedAt on the receiver, and call Update at the three
promotion sites (worker merge loop, worker adoption loop, and
reportDetectedTracker). The shouldPromoteSource gate still ranks the
candidate against the loaded value; Update is now the single write
path that can advance source, with a doc comment spelling out the
load-first contract.

Re-cast the coredata tests around Update: WritesSource pins the
round-trip from receiver to DB, NotFoundForMissingRow preserves the
ErrResourceNotFound contract callers rely on. The old
OnlyTouchesSourceAndUpdatedAt test was a property of the narrow
PromoteSource UPDATE and no longer applies — Update intentionally
rewrites the full editable column set from the receiver.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:55 +02:00
Émile Ré
27a9f83825 Promote on detection
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:54 +02:00
Émile Ré
ceb98740a5 Fix lint
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:54 +02:00
Émile Ré
cef0faf2d3 Forbid Conventional Commits in commit conventions
The repo guide already prescribed the seven-rules format but did
not rule out the Conventional-Commits alternative, and recent
history has drifted into mixing both styles. Several existing
commits use a "fix(scope): ..." prefix that the project does not
consume for any tooling (no changelog generator, no semantic
release, no commit-lint), so the prefix only adds noise and hurts
log readability.

Add an alwaysApply Cursor rule that explicitly bans Conventional
Commits and restates the seven-rules format with concrete good and
bad examples. Update contrib/claude/commit.md with the same
prohibition so the documentation and the rule agree, and
cross-link both from the existing signing rule so an agent reading
git-commit-signing.mdc lands on the style rule too.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:54 +02:00
Émile Ré
05cbab7258 Promote glob source and trigger draft on adoption
The pattern-analysis worker dropped two signals on every run. When
InsertIfNotExists hit a pre-existing glob, the computed bestSource
was discarded by the LoadByBannerIDTypeAndPattern fallback, so the
SCRIPT > EXTENSION > PRE_EXISTING precedence advertised on
bestSource was only ever enforced at first insert. Subsequent
batches with stronger sources could not promote the glob, even
though the page-script-wins rule already lives in detected_trackers
at the row level.

Separately, adoptUncategorisedPatterns returned an adopted bool
that the worker discarded; the function moves detected trackers
from uncategorised exact patterns into categorised globs, which is
a real consent transition, but no draft banner version was created
on adoption-only runs.

Add a focused TrackerPattern.PromoteSource that only updates the
source and updated_at columns. Express the precedence as a pure-Go
shouldPromoteSource helper alongside bestSource so the rule is
unit-testable without a database. The worker now calls
InsertIfNotExists, then on conflict loads, skips when the slot is
held by an exact pattern or a user-recategorised glob, and only
calls PromoteSource when the candidate source ranks above the
existing one.

The skip branch is now documented: adoptUncategorisedPatterns is
the safety net that re-homes uncategorised exacts into the existing
glob via globMatch. Capture its adopted return value and use it
(instead of the previous over-eager consentChanged flag) to gate
ensureDraftVersionForBanner. Merging exacts into a glob in their
own category never changes visitor consent, so the prior flag
produced redundant draft versions on every non-uncategorised merge.

Cover the new pieces with three test layers: pure-unit cases for
shouldPromoteSource (precedence matrix including HTTP/nil collapse
and equal-rank no-write), DB-backed tests for PromoteSource (touch
only source + updated_at, ErrResourceNotFound for missing rows),
and end-to-end worker tests for source promotion on an existing
glob, draft-on-adoption, and the merge-only no-draft case.

Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:54 +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
Émile Ré
381b43f891 compose.yaml images are public
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 18:06:41 +02:00
Cursor Agent
600aa6b8a8 Remove obsolete minor publish error
Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
2026-05-26 08:26:49 -07: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é
67122891b8 Fix lint
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-26 11:14:48 +02:00