From 60d373e5c4b9beb006f8b65e42b5d0fb12ddef65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:22:13 +0200 Subject: [PATCH] Disable Clerk access-review connector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clerk's Backend API secret key only exposes the customer's application end-users (GET /v1/users) -- the people who sign into the customer's product -- not the Clerk workspace/dashboard team who administer the platform. Those admins have no Backend API and are managed only in the Clerk Dashboard UI. So an access-review campaign against Clerk reviews the wrong population entirely (verified in prod: one source paginating 16K+ end-users), which is a compliance defect, not just the pagination timeouts it also caused. Unregister Clerk from the builtin provider registry and drop its driver, provider registration, tests, and cassette so it can no longer be added or fetched. Keep the ConnectorProviderClerk enum value, its IsValid membership, the GraphQL enum value, and the frontend logo so existing stored CLERK connectors still validate, serialize, and display (users can still see and remove them). Drop it from ConnectorProviders so the registry stays in sync with the registered set. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/accessreview/drivers/clerk.go | 236 ------------------- pkg/accessreview/drivers/clerk_test.go | 87 ------- pkg/accessreview/drivers/testdata/clerk.yaml | 43 ---- pkg/connector/provider/builtin.go | 1 - pkg/connector/provider/clerk.go | 53 ----- pkg/connector/provider/clerk_test.go | 56 ----- pkg/coredata/connector_provider.go | 61 ++--- 7 files changed, 34 insertions(+), 503 deletions(-) delete mode 100644 pkg/accessreview/drivers/clerk.go delete mode 100644 pkg/accessreview/drivers/clerk_test.go delete mode 100644 pkg/accessreview/drivers/testdata/clerk.yaml delete mode 100644 pkg/connector/provider/clerk.go delete mode 100644 pkg/connector/provider/clerk_test.go diff --git a/pkg/accessreview/drivers/clerk.go b/pkg/accessreview/drivers/clerk.go deleted file mode 100644 index cee71a58b..000000000 --- a/pkg/accessreview/drivers/clerk.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package drivers - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "strconv" - "strings" - "time" - - "go.probo.inc/probo/pkg/coredata" -) - -const ( - clerkUsersEndpoint = "https://api.clerk.com/v1/users" - clerkUsersPageSize = 100 -) - -type ClerkDriver struct { - httpClient *http.Client -} - -var _ Driver = (*ClerkDriver)(nil) - -type clerkUser struct { - ID string `json:"id"` - PrimaryEmailAddressID *string `json:"primary_email_address_id"` - Username *string `json:"username"` - FirstName *string `json:"first_name"` - LastName *string `json:"last_name"` - PasswordEnabled bool `json:"password_enabled"` - TwoFactorEnabled bool `json:"two_factor_enabled"` - TOTPEnabled bool `json:"totp_enabled"` - BackupCodeEnabled bool `json:"backup_code_enabled"` - Banned bool `json:"banned"` - Locked bool `json:"locked"` - Deprovisioned bool `json:"deprovisioned"` - LastSignInAt *int64 `json:"last_sign_in_at"` - CreatedAt int64 `json:"created_at"` - EmailAddresses []struct { - ID string `json:"id"` - EmailAddress string `json:"email_address"` - } `json:"email_addresses"` -} - -func NewClerkDriver(httpClient *http.Client) *ClerkDriver { - return &ClerkDriver{ - httpClient: &http.Client{ - Transport: &retryRoundTripper{ - next: httpClient.Transport, - maxRetries: 3, - }, - }, - } -} - -func (d *ClerkDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) { - var ( - records []AccountRecord - offset = 0 - ) - - for range maxPaginationPages { - users, err := d.fetchUsersPage(ctx, offset) - if err != nil { - return nil, err - } - - for _, u := range users { - email := clerkPrimaryEmail(u) - if email == "" { - continue - } - - record := AccountRecord{ - Email: email, - FullName: clerkFullName(u, email), - Active: new(!u.Banned && !u.Locked && !u.Deprovisioned), - IsAdmin: false, - MFAStatus: clerkMFAStatus(u), - AuthMethod: clerkAuthMethod(u), - AccountType: coredata.AccessReviewEntryAccountTypeUser, - ExternalID: u.ID, - } - - if createdAt := clerkUnixMillisToTime(u.CreatedAt); createdAt != nil { - record.CreatedAt = createdAt - } - - if u.LastSignInAt != nil { - record.LastLogin = clerkUnixMillisToTime(*u.LastSignInAt) - } - - records = append(records, record) - } - - offset += len(users) - - if len(users) < clerkUsersPageSize { - return records, nil - } - } - - return nil, fmt.Errorf("cannot list all clerk users: %w", ErrPaginationLimitReached) -} - -func (d *ClerkDriver) fetchUsersPage(ctx context.Context, offset int) ([]clerkUser, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, clerkUsersEndpoint, nil) - if err != nil { - return nil, fmt.Errorf("cannot create clerk users request: %w", err) - } - - q := req.URL.Query() - q.Set("limit", strconv.Itoa(clerkUsersPageSize)) - q.Set("offset", strconv.Itoa(offset)) - req.URL.RawQuery = q.Encode() - - req.Header.Set("Accept", "application/json") - - httpResp, err := d.httpClient.Do(req) - if err != nil { - return nil, fmt.Errorf("cannot execute clerk users request: %w", err) - } - - defer func() { - _ = httpResp.Body.Close() - }() - - if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 { - return nil, fmt.Errorf("cannot fetch clerk users: unexpected status %d", httpResp.StatusCode) - } - - // GET /v1/users returns a bare JSON array of user objects; the total - // count is exposed separately via /v1/users/count. Decode directly - // into a slice. - var users []clerkUser - if err := json.NewDecoder(httpResp.Body).Decode(&users); err != nil { - return nil, fmt.Errorf("cannot decode clerk users response: %w", err) - } - - return users, nil -} - -func clerkPrimaryEmail(u clerkUser) string { - if u.PrimaryEmailAddressID != nil && *u.PrimaryEmailAddressID != "" { - for _, email := range u.EmailAddresses { - if email.ID == *u.PrimaryEmailAddressID && email.EmailAddress != "" { - return email.EmailAddress - } - } - } - - for _, email := range u.EmailAddresses { - if email.EmailAddress != "" { - return email.EmailAddress - } - } - - return "" -} - -func clerkFullName(u clerkUser, fallback string) string { - firstName := "" - lastName := "" - username := "" - - if u.FirstName != nil { - firstName = *u.FirstName - } - - if u.LastName != nil { - lastName = *u.LastName - } - - if u.Username != nil { - username = *u.Username - } - - fullName := strings.TrimSpace(firstName + " " + lastName) - if fullName != "" { - return fullName - } - - if username != "" { - return username - } - - return fallback -} - -func clerkMFAStatus(u clerkUser) coredata.MFAStatus { - if u.TwoFactorEnabled || u.TOTPEnabled || u.BackupCodeEnabled { - return coredata.MFAStatusEnabled - } - - return coredata.MFAStatusDisabled -} - -func clerkAuthMethod(u clerkUser) coredata.AccessReviewEntryAuthMethod { - if u.PasswordEnabled { - return coredata.AccessReviewEntryAuthMethodPassword - } - - return coredata.AccessReviewEntryAuthMethodUnknown -} - -func clerkUnixMillisToTime(unixMillis int64) *time.Time { - if unixMillis <= 0 { - return nil - } - - t := time.UnixMilli(unixMillis).UTC() - - return &t -} diff --git a/pkg/accessreview/drivers/clerk_test.go b/pkg/accessreview/drivers/clerk_test.go deleted file mode 100644 index ad13ff4a5..000000000 --- a/pkg/accessreview/drivers/clerk_test.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package drivers - -import ( - "context" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.probo.inc/probo/pkg/coredata" -) - -func TestClerkDriver(t *testing.T) { - t.Parallel() - - rec := newRecorder(t, "testdata/clerk", "CLERK_SECRET_KEY") - client := newVCRClient(rec, bearerAuth(os.Getenv("CLERK_SECRET_KEY"))) - - driver := NewClerkDriver(client) - records, err := driver.ListAccounts(context.Background()) - require.NoError(t, err) - require.Len(t, records, 3) - - // Clerk returns users newest-first (default order_by=-created_at). - first := records[0] - assert.Equal(t, "user_3EfkCEWmtIsoMD3rRxIpDsBOPzv", first.ExternalID) - assert.Equal(t, "c@example.com", first.Email) - assert.Equal(t, "c c", first.FullName) - assert.Equal(t, coredata.AccessReviewEntryAccountTypeUser, first.AccountType) - require.NotNil(t, first.Active) - assert.True(t, *first.Active) - assert.Equal(t, coredata.MFAStatusDisabled, first.MFAStatus) - assert.Equal(t, coredata.AccessReviewEntryAuthMethodPassword, first.AuthMethod) - assert.NotNil(t, first.CreatedAt) - assert.Nil(t, first.LastLogin) - - second := records[1] - assert.Equal(t, "b@example.com", second.Email) - assert.Equal(t, "b b", second.FullName) - require.NotNil(t, second.Active) - assert.True(t, *second.Active) - - // a@example.com is locked, so it must be reported inactive. - third := records[2] - assert.Equal(t, "a@example.com", third.Email) - assert.Equal(t, "a a", third.FullName) - require.NotNil(t, third.Active) - assert.False(t, *third.Active) - assert.Equal(t, coredata.AccessReviewEntryAuthMethodPassword, third.AuthMethod) -} - -func TestClerkPrimaryEmail(t *testing.T) { - t.Parallel() - - user := clerkUser{ - PrimaryEmailAddressID: new("eml_primary"), - EmailAddresses: []struct { - ID string `json:"id"` - EmailAddress string `json:"email_address"` - }{ - {ID: "eml_secondary", EmailAddress: "secondary@example.com"}, - {ID: "eml_primary", EmailAddress: "primary@example.com"}, - }, - } - - assert.Equal(t, "primary@example.com", clerkPrimaryEmail(user)) -} diff --git a/pkg/accessreview/drivers/testdata/clerk.yaml b/pkg/accessreview/drivers/testdata/clerk.yaml deleted file mode 100644 index 1fdf431aa..000000000 --- a/pkg/accessreview/drivers/testdata/clerk.yaml +++ /dev/null @@ -1,43 +0,0 @@ ---- -version: 2 -interactions: - - id: 0 - request: - proto: HTTP/1.1 - proto_major: 1 - proto_minor: 1 - content_length: 0 - host: api.clerk.com - form: - limit: - - "100" - offset: - - "0" - headers: - Accept: - - application/json - url: https://api.clerk.com/v1/users?limit=100&offset=0 - method: GET - response: - proto: HTTP/2.0 - proto_major: 2 - proto_minor: 0 - content_length: -1 - uncompressed: true - body: '[{"id":"user_3EfkCEWmtIsoMD3rRxIpDsBOPzv","object":"user","username":null,"first_name":"c","last_name":"c","locale":null,"image_url":"https://img.clerk.com/anonymized","has_image":false,"primary_email_address_id":"idn_3EfkCCq17KQsZzWLhtKnhAlguva","primary_phone_number_id":null,"primary_web3_wallet_id":null,"password_enabled":true,"two_factor_enabled":false,"totp_enabled":false,"backup_code_enabled":false,"email_addresses":[{"id":"idn_3EfkCCq17KQsZzWLhtKnhAlguva","object":"email_address","email_address":"c@example.com","reserved":false,"verification":{"object":"verification_admin","status":"verified","strategy":"admin","attempts":null,"expire_at":null},"linked_to":[],"matches_sso_connection":false,"created_at":1780576881106,"updated_at":1780576881106}],"phone_numbers":[],"web3_wallets":[],"passkeys":[],"external_accounts":[],"saml_accounts":[],"enterprise_accounts":[],"password_last_updated_at":1780576881104,"public_metadata":{},"private_metadata":{},"unsafe_metadata":{},"external_id":null,"last_sign_in_at":null,"banned":false,"locked":false,"lockout_expires_in_seconds":null,"verification_attempts_remaining":100,"created_at":1780576881104,"updated_at":1780576881109,"delete_self_enabled":true,"bypass_client_trust":false,"create_organization_enabled":true,"last_active_at":null,"mfa_enabled_at":null,"mfa_disabled_at":null,"legal_accepted_at":null,"requires_password_reset":false,"deprovisioned":false,"profile_image_url":"https://www.gravatar.com/avatar?d=mp"},{"id":"user_3EfkAUnJSm40lEsbkAuF9xUs5p2","object":"user","username":null,"first_name":"b","last_name":"b","locale":null,"image_url":"https://img.clerk.com/anonymized","has_image":false,"primary_email_address_id":"idn_3EfkAT1dfG5X3qrNnhntuaNC85w","primary_phone_number_id":null,"primary_web3_wallet_id":null,"password_enabled":true,"two_factor_enabled":false,"totp_enabled":false,"backup_code_enabled":false,"email_addresses":[{"id":"idn_3EfkAT1dfG5X3qrNnhntuaNC85w","object":"email_address","email_address":"b@example.com","reserved":false,"verification":{"object":"verification_admin","status":"verified","strategy":"admin","attempts":null,"expire_at":null},"linked_to":[],"matches_sso_connection":false,"created_at":1780576867751,"updated_at":1780576867751}],"phone_numbers":[],"web3_wallets":[],"passkeys":[],"external_accounts":[],"saml_accounts":[],"enterprise_accounts":[],"password_last_updated_at":1780576867746,"public_metadata":{},"private_metadata":{},"unsafe_metadata":{},"external_id":null,"last_sign_in_at":null,"banned":false,"locked":false,"lockout_expires_in_seconds":null,"verification_attempts_remaining":100,"created_at":1780576867746,"updated_at":1780576960608,"delete_self_enabled":true,"bypass_client_trust":false,"create_organization_enabled":true,"last_active_at":null,"mfa_enabled_at":null,"mfa_disabled_at":null,"legal_accepted_at":null,"requires_password_reset":true,"deprovisioned":false,"profile_image_url":"https://www.gravatar.com/avatar?d=mp"},{"id":"user_3Efk88E4dBmhk95VtBSXcOJ9vCx","object":"user","username":null,"first_name":"a","last_name":"a","locale":null,"image_url":"https://img.clerk.com/anonymized","has_image":false,"primary_email_address_id":"idn_3Efk87Z8o0h9gP86FnA8lUC2Alz","primary_phone_number_id":null,"primary_web3_wallet_id":null,"password_enabled":true,"two_factor_enabled":false,"totp_enabled":false,"backup_code_enabled":false,"email_addresses":[{"id":"idn_3Efk87Z8o0h9gP86FnA8lUC2Alz","object":"email_address","email_address":"a@example.com","reserved":false,"verification":{"object":"verification_admin","status":"verified","strategy":"admin","attempts":null,"expire_at":null},"linked_to":[],"matches_sso_connection":false,"created_at":1780576848230,"updated_at":1780576848230}],"phone_numbers":[],"web3_wallets":[],"passkeys":[],"external_accounts":[],"saml_accounts":[],"enterprise_accounts":[],"password_last_updated_at":1780576848227,"public_metadata":{},"private_metadata":{},"unsafe_metadata":{},"external_id":null,"last_sign_in_at":null,"banned":false,"locked":true,"lockout_expires_in_seconds":3554,"verification_attempts_remaining":100,"created_at":1780576848227,"updated_at":1780576947260,"delete_self_enabled":true,"bypass_client_trust":false,"create_organization_enabled":true,"last_active_at":null,"mfa_enabled_at":null,"mfa_disabled_at":null,"legal_accepted_at":null,"requires_password_reset":false,"deprovisioned":false,"profile_image_url":"https://www.gravatar.com/avatar?d=mp"}]' - headers: - Cf-Cache-Status: - - DYNAMIC - Clerk-Api-Version: - - "2025-11-10" - Content-Type: - - application/json - Date: - - Thu, 04 Jun 2026 12:43:12 GMT - Server: - - cloudflare - X-Cfworker: - - "1" - status: 200 OK - code: 200 - duration: 221.280708ms diff --git a/pkg/connector/provider/builtin.go b/pkg/connector/provider/builtin.go index 204de7eb0..edaf267fb 100644 --- a/pkg/connector/provider/builtin.go +++ b/pkg/connector/provider/builtin.go @@ -35,7 +35,6 @@ func NewBuiltinRegistry() *Registry { bitbucketRegistration(), brevoRegistration(), brexRegistration(), - clerkRegistration(), clickhouseRegistration(), clickupRegistration(), cloudflareRegistration(), diff --git a/pkg/connector/provider/clerk.go b/pkg/connector/provider/clerk.go deleted file mode 100644 index 4e0ccf1c3..000000000 --- a/pkg/connector/provider/clerk.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package provider - -import ( - "context" - "net/http" - - "go.gearno.de/kit/log" - "go.probo.inc/probo/pkg/accessreview/drivers" - "go.probo.inc/probo/pkg/coredata" -) - -func clerkRegistration() *Registration { - return &Registration{ - Provider: coredata.ConnectorProviderClerk, - DisplayName: "Clerk", - SupportsAPIKey: true, - // Clerk's Backend API authenticates with a server-side secret key - // (sk_...) presented as Authorization: Bearer, the default - // APIKeyConnection scheme. There is no third-party OAuth2 flow for - // account-listing: Clerk's OAuth is an end-user IdP (scoped consent - // to a single user's profile), not a partner grant over the Backend - // API. The secret key is bound to one Clerk instance, so there is - // nothing to pick (Pattern 3): no settings struct, no picker, no - // SetOrganizationSettings. - ProbeURL: "https://api.clerk.com/v1/users?limit=1", - // No NewNameResolver: the Backend API exposes no instance/application - // name endpoint reachable with a secret key, so the source keeps its - // generic name (the source-name worker degrades gracefully). - NewDriver: func(_ context.Context, c *http.Client, _ *coredata.Connector, _ *log.Logger) (drivers.Driver, error) { - return drivers.NewClerkDriver(c), nil - }, - } -} diff --git a/pkg/connector/provider/clerk_test.go b/pkg/connector/provider/clerk_test.go deleted file mode 100644 index 42696d062..000000000 --- a/pkg/connector/provider/clerk_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2026 Probo Inc . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package provider_test - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.gearno.de/kit/httpclient" - "go.probo.inc/probo/pkg/accessreview/drivers" - "go.probo.inc/probo/pkg/connector/provider" - "go.probo.inc/probo/pkg/coredata" -) - -func TestClerkRegistration(t *testing.T) { - t.Parallel() - - r := provider.NewBuiltinRegistry() - reg, ok := r.Get(coredata.ConnectorProviderClerk) - require.True(t, ok, "clerk provider must be registered") - - assert.Equal(t, "Clerk", reg.DisplayName) - assert.True(t, reg.SupportsAPIKey) - assert.Equal(t, "", reg.APIKeyHeader) - assert.False(t, reg.APIKeyBasicAuth) - require.NotNil(t, reg.NewDriver, "clerk NewDriver closure must be wired") - - drv, err := reg.NewDriver( - context.Background(), - httpclient.DefaultClient(httpclient.WithSSRFProtection()), - &coredata.Connector{Provider: coredata.ConnectorProviderClerk}, - nil, - ) - require.NoError(t, err) - assert.IsType(t, &drivers.ClerkDriver{}, drv) -} diff --git a/pkg/coredata/connector_provider.go b/pkg/coredata/connector_provider.go index 9e6222b11..25f7e0d0c 100644 --- a/pkg/coredata/connector_provider.go +++ b/pkg/coredata/connector_provider.go @@ -58,32 +58,40 @@ const ( ConnectorProviderAsana ConnectorProvider = "ASANA" ConnectorProviderNetlify ConnectorProvider = "NETLIFY" ConnectorProviderClickUp ConnectorProvider = "CLICKUP" - ConnectorProviderClerk ConnectorProvider = "CLERK" - ConnectorProviderVercel ConnectorProvider = "VERCEL" - ConnectorProviderMonday ConnectorProvider = "MONDAY" - ConnectorProviderMetabase ConnectorProvider = "METABASE" - ConnectorProviderTailscale ConnectorProvider = "TAILSCALE" - ConnectorProviderAnthropic ConnectorProvider = "ANTHROPIC" - ConnectorProviderCursor ConnectorProvider = "CURSOR" - ConnectorProviderDatadog ConnectorProvider = "DATADOG" - ConnectorProviderOkta ConnectorProvider = "OKTA" - ConnectorProviderZendesk ConnectorProvider = "ZENDESK" - ConnectorProviderQovery ConnectorProvider = "QOVERY" - ConnectorProviderRender ConnectorProvider = "RENDER" - ConnectorProviderNeon ConnectorProvider = "NEON" - ConnectorProviderMercury ConnectorProvider = "MERCURY" - ConnectorProviderApollo ConnectorProvider = "APOLLO" - ConnectorProviderDeepgram ConnectorProvider = "DEEPGRAM" - ConnectorProviderClickHouse ConnectorProvider = "CLICKHOUSE" - ConnectorProviderLangfuse ConnectorProvider = "LANGFUSE" - ConnectorProviderPylon ConnectorProvider = "PYLON" - ConnectorProviderOpenRouter ConnectorProvider = "OPENROUTER" - ConnectorProviderIncidentIO ConnectorProvider = "INCIDENT_IO" - ConnectorProviderBrevo ConnectorProvider = "BREVO" - ConnectorProviderScaleway ConnectorProvider = "SCALEWAY" - ConnectorProviderYousign ConnectorProvider = "YOUSIGN" - ConnectorProviderRailway ConnectorProvider = "RAILWAY" - ConnectorProviderCrisp ConnectorProvider = "CRISP" + // ConnectorProviderClerk is retained for existing connectors but is + // no longer a registerable access-review provider: Clerk's Backend API + // (secret key) only exposes the customer's application end-users, not + // the Clerk workspace/dashboard team who administer the platform, so a + // campaign reviews the wrong population. Kept in IsValid and the + // GraphQL enum so stored CLERK rows still validate and serialize; + // dropped from ConnectorProviders and unregistered from the builtin + // registry so it cannot be added or fetched. + ConnectorProviderClerk ConnectorProvider = "CLERK" + ConnectorProviderVercel ConnectorProvider = "VERCEL" + ConnectorProviderMonday ConnectorProvider = "MONDAY" + ConnectorProviderMetabase ConnectorProvider = "METABASE" + ConnectorProviderTailscale ConnectorProvider = "TAILSCALE" + ConnectorProviderAnthropic ConnectorProvider = "ANTHROPIC" + ConnectorProviderCursor ConnectorProvider = "CURSOR" + ConnectorProviderDatadog ConnectorProvider = "DATADOG" + ConnectorProviderOkta ConnectorProvider = "OKTA" + ConnectorProviderZendesk ConnectorProvider = "ZENDESK" + ConnectorProviderQovery ConnectorProvider = "QOVERY" + ConnectorProviderRender ConnectorProvider = "RENDER" + ConnectorProviderNeon ConnectorProvider = "NEON" + ConnectorProviderMercury ConnectorProvider = "MERCURY" + ConnectorProviderApollo ConnectorProvider = "APOLLO" + ConnectorProviderDeepgram ConnectorProvider = "DEEPGRAM" + ConnectorProviderClickHouse ConnectorProvider = "CLICKHOUSE" + ConnectorProviderLangfuse ConnectorProvider = "LANGFUSE" + ConnectorProviderPylon ConnectorProvider = "PYLON" + ConnectorProviderOpenRouter ConnectorProvider = "OPENROUTER" + ConnectorProviderIncidentIO ConnectorProvider = "INCIDENT_IO" + ConnectorProviderBrevo ConnectorProvider = "BREVO" + ConnectorProviderScaleway ConnectorProvider = "SCALEWAY" + ConnectorProviderYousign ConnectorProvider = "YOUSIGN" + ConnectorProviderRailway ConnectorProvider = "RAILWAY" + ConnectorProviderCrisp ConnectorProvider = "CRISP" ) var ( @@ -123,7 +131,6 @@ func ConnectorProviders() []ConnectorProvider { ConnectorProviderAsana, ConnectorProviderNetlify, ConnectorProviderClickUp, - ConnectorProviderClerk, ConnectorProviderVercel, ConnectorProviderMonday, ConnectorProviderMetabase,