Skipping unconnectable providers hides OAuth-only providers when the
deployment has no OAuth credentials configured, which is the case in
e2e. The test asserted SLACK (OAuth-only) is present, so it now fails.
Assert on API-key providers instead, which are connectable regardless
of OAuth configuration.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
ConnectorProviderInfos listed every known provider, including ones
this deployment cannot connect: no OAuth client credentials configured
and no API-key or client-credentials fallback supported. Those
rendered as dead entries the operator has no way to use, so skip them.
Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>