diff --git a/.env.example b/.env.example
index 9aab20142..f2235f2f6 100644
--- a/.env.example
+++ b/.env.example
@@ -108,12 +108,8 @@
# CONNECTOR_PAGERDUTY_CLIENT_SECRET=
# CONNECTOR_ASANA_CLIENT_ID=
# CONNECTOR_ASANA_CLIENT_SECRET=
-# CONNECTOR_SNYK_CLIENT_ID=
-# CONNECTOR_SNYK_CLIENT_SECRET=
# CONNECTOR_NETLIFY_CLIENT_ID=
# CONNECTOR_NETLIFY_CLIENT_SECRET=
-# CONNECTOR_RAMP_CLIENT_ID=
-# CONNECTOR_RAMP_CLIENT_SECRET=
# CONNECTOR_CLICKUP_CLIENT_ID=
# CONNECTOR_CLICKUP_CLIENT_SECRET=
# CONNECTOR_VERCEL_CLIENT_ID=
@@ -121,10 +117,6 @@
# CONNECTOR_VERCEL_INTEGRATION_SLUG=
# CONNECTOR_MONDAY_CLIENT_ID=
# CONNECTOR_MONDAY_CLIENT_SECRET=
-# CONNECTOR_LEVER_CLIENT_ID=
-# CONNECTOR_LEVER_CLIENT_SECRET=
-# CONNECTOR_DEEL_CLIENT_ID=
-# CONNECTOR_DEEL_CLIENT_SECRET=
# ── Custom domains (Pebble ACME via compose) ──────────────────────────
# CUSTOM_DOMAINS_CNAME_TARGET=custom.getprobo.com
diff --git a/contrib/helm/charts/probo/templates/deployment.yaml b/contrib/helm/charts/probo/templates/deployment.yaml
index e3ff407ce..ba07344f2 100644
--- a/contrib/helm/charts/probo/templates/deployment.yaml
+++ b/contrib/helm/charts/probo/templates/deployment.yaml
@@ -283,7 +283,7 @@ spec:
{{- end }}
# Connectors (access-review OAuth2 providers added 2026)
{{- range .Values.probo.connectors }}
- {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "snyk" "netlify" "ramp" "clickup" "vercel" "monday" "lever" "deel" }}
+ {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "netlify" "clickup" "vercel" "monday" }}
{{- if has .name $accessReviewProviders }}
{{- $envPrefix := printf "CONNECTOR_%s" (.name | upper) }}
{{- $secretPrefix := printf "connector-%s" .name }}
diff --git a/contrib/helm/charts/probo/templates/secret.yaml b/contrib/helm/charts/probo/templates/secret.yaml
index 20d227761..0c8cc4f3d 100644
--- a/contrib/helm/charts/probo/templates/secret.yaml
+++ b/contrib/helm/charts/probo/templates/secret.yaml
@@ -56,7 +56,7 @@ stringData:
connector-slack-signing-secret: {{ .config.signingSecret | quote }}
{{- end }}
{{- end }}
- {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "snyk" "netlify" "ramp" "clickup" "vercel" "monday" "lever" "deel" }}
+ {{- $accessReviewProviders := list "gitlab" "bitbucket" "heroku" "pagerduty" "asana" "netlify" "clickup" "vercel" "monday" }}
{{- if has .name $accessReviewProviders }}
{{- if .config.clientId }}
connector-{{ .name }}-client-id: {{ .config.clientId | quote }}
diff --git a/contrib/helm/charts/probo/values-production.yaml.example b/contrib/helm/charts/probo/values-production.yaml.example
index 45cbabd2d..61ce63527 100644
--- a/contrib/helm/charts/probo/values-production.yaml.example
+++ b/contrib/helm/charts/probo/values-production.yaml.example
@@ -185,7 +185,7 @@ probo:
# Supported providers (set name to one of):
# slack, hubspot, docusign, notion, github, sentry, intercom, brex,
# google_workspace, gitlab, bitbucket, heroku, pagerduty, asana,
- # snyk, netlify, ramp, clickup, vercel, monday, lever, deel
+ # netlify, clickup, vercel, monday
#
# Vercel additionally requires "integration-slug" so the templated
# AuthURL ("https://vercel.com/integrations/{integration_slug}/new")
diff --git a/e2e/console/access_review_test.go b/e2e/console/access_review_test.go
index 54365e51f..f66bf72df 100644
--- a/e2e/console/access_review_test.go
+++ b/e2e/console/access_review_test.go
@@ -27,13 +27,12 @@ import (
const testCsvData = "email,full_name,role,job_title,is_admin,mfa_status,auth_method,last_login,account_created_at,external_id\njane@example.com,Jane Smith,admin,CTO,true,ENABLED,SSO,2026-01-15T00:00:00Z,2024-06-01T00:00:00Z,ext-jane"
// TODO(access-review-connectors): add e2e tests for the new GitLab,
-// Bitbucket, Heroku, PagerDuty, Asana, Snyk, Netlify, Ramp, ClickUp,
-// Vercel, Monday.com, Lever, and Deel access-source connectors once the
-// e2e factory grows OAuth2-connector seed support for arbitrary
-// providers. For now the per-driver VCR tests under
-// pkg/accessreview/drivers cover the fetch path; this e2e suite still
-// exercises CSV + Slack which were the providers wired before these
-// sprints.
+// Bitbucket, Heroku, PagerDuty, Asana, Netlify, ClickUp, Vercel, and
+// Monday.com access-source connectors once the e2e factory grows
+// OAuth2-connector seed support for arbitrary providers. For now the
+// per-driver VCR tests under pkg/accessreview/drivers cover the fetch
+// path; this e2e suite still exercises CSV + Slack which were the
+// providers wired before these sprints.
func TestAccessSource_Create(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
diff --git a/packages/ui/src/Atoms/ThirdParties/Deel.tsx b/packages/ui/src/Atoms/ThirdParties/Deel.tsx
deleted file mode 100644
index 2dc9b2e42..000000000
--- a/packages/ui/src/Atoms/ThirdParties/Deel.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ComponentProps } from "react";
-
-export function Deel(props: ComponentProps<"svg">) {
- return (
-
- );
-}
diff --git a/packages/ui/src/Atoms/ThirdParties/Lever.tsx b/packages/ui/src/Atoms/ThirdParties/Lever.tsx
deleted file mode 100644
index 9cfb6f929..000000000
--- a/packages/ui/src/Atoms/ThirdParties/Lever.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ComponentProps } from "react";
-
-export function Lever(props: ComponentProps<"svg">) {
- return (
-
- );
-}
diff --git a/packages/ui/src/Atoms/ThirdParties/Ramp.tsx b/packages/ui/src/Atoms/ThirdParties/Ramp.tsx
deleted file mode 100644
index deede6ff9..000000000
--- a/packages/ui/src/Atoms/ThirdParties/Ramp.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ComponentProps } from "react";
-
-export function Ramp(props: ComponentProps<"svg">) {
- return (
-
- );
-}
diff --git a/packages/ui/src/Atoms/ThirdParties/Snyk.tsx b/packages/ui/src/Atoms/ThirdParties/Snyk.tsx
deleted file mode 100644
index 83b422a49..000000000
--- a/packages/ui/src/Atoms/ThirdParties/Snyk.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { ComponentProps } from "react";
-
-export function Snyk(props: ComponentProps<"svg">) {
- return (
-
- );
-}
diff --git a/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx
index 8f2550d93..0e6f2bf1a 100644
--- a/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx
+++ b/packages/ui/src/Atoms/ThirdParties/ThirdPartyLogo.tsx
@@ -19,7 +19,6 @@ import { Bitbucket } from "./Bitbucket";
import { Brex } from "./Brex";
import { ClickUp } from "./ClickUp";
import { Cloudflare } from "./Cloudflare";
-import { Deel } from "./Deel";
import { DocuSign } from "./DocuSign";
import { Figma } from "./Figma";
import { GitHub } from "./GitHub";
@@ -28,7 +27,6 @@ import { Google } from "./Google";
import { Heroku } from "./Heroku";
import { HubSpot } from "./HubSpot";
import { Intercom } from "./Intercom";
-import { Lever } from "./Lever";
import { Linear } from "./Linear";
import { Microsoft } from "./Microsoft";
import { Monday } from "./Monday";
@@ -37,11 +35,9 @@ import { Notion } from "./Notion";
import { OnePassword } from "./OnePassword";
import { OpenAI } from "./OpenAI";
import { PagerDuty } from "./PagerDuty";
-import { Ramp } from "./Ramp";
import { Resend } from "./Resend";
import { Sentry } from "./Sentry";
import { Slack } from "./Slack";
-import { Snyk } from "./Snyk";
import { Supabase } from "./Supabase";
import { Tally } from "./Tally";
import { Vercel } from "./Vercel";
@@ -52,7 +48,6 @@ const thirdParties: Record>> = {
BREX: Brex,
CLICKUP: ClickUp,
CLOUDFLARE: Cloudflare,
- DEEL: Deel,
DOCUSIGN: DocuSign,
FIGMA: Figma,
GITHUB: GitHub,
@@ -62,7 +57,6 @@ const thirdParties: Record>> = {
HEROKU: Heroku,
HUBSPOT: HubSpot,
INTERCOM: Intercom,
- LEVER: Lever,
LINEAR: Linear,
MICROSOFT: Microsoft,
MICROSOFT_365: Microsoft,
@@ -73,11 +67,9 @@ const thirdParties: Record>> = {
ONEPASSWORD: OnePassword,
OPENAI: OpenAI,
PAGERDUTY: PagerDuty,
- RAMP: Ramp,
RESEND: Resend,
SENTRY: Sentry,
SLACK: Slack,
- SNYK: Snyk,
SUPABASE: Supabase,
TALLY: Tally,
VERCEL: Vercel,
diff --git a/packages/ui/src/Atoms/ThirdParties/index.ts b/packages/ui/src/Atoms/ThirdParties/index.ts
index 61c9ede04..b10f4f840 100644
--- a/packages/ui/src/Atoms/ThirdParties/index.ts
+++ b/packages/ui/src/Atoms/ThirdParties/index.ts
@@ -3,7 +3,6 @@ export { Bitbucket } from "./Bitbucket";
export { Brex } from "./Brex";
export { ClickUp } from "./ClickUp";
export { Cloudflare } from "./Cloudflare";
-export { Deel } from "./Deel";
export { DocuSign } from "./DocuSign";
export { Figma } from "./Figma";
export { GitHub } from "./GitHub";
@@ -12,7 +11,6 @@ export { Google } from "./Google";
export { Heroku } from "./Heroku";
export { HubSpot } from "./HubSpot";
export { Intercom } from "./Intercom";
-export { Lever } from "./Lever";
export { Linear } from "./Linear";
export { Microsoft } from "./Microsoft";
export { Monday } from "./Monday";
@@ -21,11 +19,9 @@ export { Notion } from "./Notion";
export { OnePassword } from "./OnePassword";
export { OpenAI } from "./OpenAI";
export { PagerDuty } from "./PagerDuty";
-export { Ramp } from "./Ramp";
export { Resend } from "./Resend";
export { Sentry } from "./Sentry";
export { Slack } from "./Slack";
-export { Snyk } from "./Snyk";
export { Supabase } from "./Supabase";
export { Tally } from "./Tally";
export { ThirdPartyLogo } from "./ThirdPartyLogo";
diff --git a/pkg/accessreview/access_source_service.go b/pkg/accessreview/access_source_service.go
index b81c22471..75d0656d3 100644
--- a/pkg/accessreview/access_source_service.go
+++ b/pkg/accessreview/access_source_service.go
@@ -412,12 +412,6 @@ func (s AccessSourceService) ConfigureAccessSource(
}); err != nil {
return fmt.Errorf("cannot set asana settings: %w", err)
}
- case coredata.ConnectorProviderSnyk:
- if err := dbConnector.SetSettings(&coredata.SnykConnectorSettings{
- OrgID: req.OrganizationSlug,
- }); err != nil {
- return fmt.Errorf("cannot set snyk settings: %w", err)
- }
case coredata.ConnectorProviderNetlify:
if err := dbConnector.SetSettings(&coredata.NetlifyConnectorSettings{
AccountSlug: req.OrganizationSlug,
diff --git a/pkg/accessreview/drivers/deel.go b/pkg/accessreview/drivers/deel.go
deleted file mode 100644
index ab63943eb..000000000
--- a/pkg/accessreview/drivers/deel.go
+++ /dev/null
@@ -1,136 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "strings"
-
- "go.probo.inc/probo/pkg/coredata"
-)
-
-// DeelDriver fetches people from the Deel REST API using a
-// pre-authenticated HTTP client (Bearer token). Pagination is
-// offset-based: increment `offset` by `limit` until the response
-// `data` array is empty.
-//
-// Notes on data quality:
-// - Active is derived from `hiring_status == "active"`. When `end_date`
-// is set the worker is considered inactive regardless of hiring_status.
-// - MFA and last-login are not exposed by the people endpoint.
-type DeelDriver struct {
- httpClient *http.Client
-}
-
-var _ Driver = (*DeelDriver)(nil)
-
-func NewDeelDriver(httpClient *http.Client) *DeelDriver {
- return &DeelDriver{httpClient: httpClient}
-}
-
-type deelPerson struct {
- ID string `json:"id"`
- Email string `json:"email"`
- FirstName string `json:"first_name"`
- LastName string `json:"last_name"`
- JobTitle string `json:"job_title"`
- HiringStatus string `json:"hiring_status"`
- StartDate string `json:"start_date"`
- EndDate string `json:"end_date"`
-}
-
-type deelPeoplePage struct {
- Data []deelPerson `json:"data"`
-}
-
-func (d *DeelDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) {
- var records []AccountRecord
-
- const limit = 100
- offset := 0
-
- for range maxPaginationPages {
- people, err := d.queryPeople(ctx, offset, limit)
- if err != nil {
- return nil, err
- }
-
- if len(people) == 0 {
- return records, nil
- }
-
- for _, p := range people {
- fullName := strings.TrimSpace(p.FirstName + " " + p.LastName)
-
- active := p.HiringStatus == "active"
- if p.EndDate != "" {
- active = false
- }
-
- record := AccountRecord{
- Email: p.Email,
- FullName: fullName,
- JobTitle: p.JobTitle,
- Active: &active,
- MFAStatus: coredata.MFAStatusUnknown,
- AuthMethod: coredata.AccessEntryAuthMethodUnknown,
- AccountType: coredata.AccessEntryAccountTypeUser,
- ExternalID: p.ID,
- }
-
- records = append(records, record)
- }
-
- offset += limit
- }
-
- return nil, fmt.Errorf("cannot list all deel accounts: %w", ErrPaginationLimitReached)
-}
-
-func (d *DeelDriver) queryPeople(ctx context.Context, offset, limit int) ([]deelPerson, error) {
- q := url.Values{}
- q.Set("limit", strconv.Itoa(limit))
- q.Set("offset", strconv.Itoa(offset))
- u := url.URL{Scheme: "https", Host: "api.letsdeel.com", Path: "/rest/v2/people", RawQuery: q.Encode()}
- endpoint := u.String()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, fmt.Errorf("cannot create deel people request: %w", err)
- }
- req.Header.Set("Accept", "application/json")
-
- httpResp, err := d.httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("cannot execute deel people request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return nil, fmt.Errorf("cannot fetch deel people: unexpected status %d", httpResp.StatusCode)
- }
-
- var page deelPeoplePage
- if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil {
- return nil, fmt.Errorf("cannot decode deel people response: %w", err)
- }
-
- return page.Data, nil
-}
diff --git a/pkg/accessreview/drivers/deel_test.go b/pkg/accessreview/drivers/deel_test.go
deleted file mode 100644
index 6a93f9155..000000000
--- a/pkg/accessreview/drivers/deel_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestDeelDriver(t *testing.T) {
- t.Parallel()
-
- rec := newRecorder(t, "testdata/deel", "DEEL_TOKEN")
- client := newVCRClient(rec, bearerAuth(os.Getenv("DEEL_TOKEN")))
-
- driver := NewDeelDriver(client)
- records, err := driver.ListAccounts(context.Background())
- require.NoError(t, err)
- require.NotEmpty(t, records)
-
- r := records[0]
- assert.NotEmpty(t, r.Email)
- assert.NotEmpty(t, r.ExternalID)
- assert.NotEmpty(t, r.FullName)
- assert.NotEmpty(t, r.JobTitle)
- require.NotNil(t, r.Active)
- assert.True(t, *r.Active)
-
- // Inactive (or end_date set) people surface as Active=false.
- require.Len(t, records, 2)
- require.NotNil(t, records[1].Active)
- assert.False(t, *records[1].Active)
-}
diff --git a/pkg/accessreview/drivers/heroku.go b/pkg/accessreview/drivers/heroku.go
index 44137adae..8c66a92aa 100644
--- a/pkg/accessreview/drivers/heroku.go
+++ b/pkg/accessreview/drivers/heroku.go
@@ -96,6 +96,11 @@ func (d *HerokuDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
isAdmin := m.Role == "admin" || m.Role == "owner"
+ externalID := m.User.ID
+ if externalID == "" {
+ externalID = m.ID
+ }
+
record := AccountRecord{
Email: email,
FullName: fullName,
@@ -104,7 +109,7 @@ func (d *HerokuDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error
MFAStatus: mfaStatus,
AuthMethod: coredata.AccessEntryAuthMethodUnknown,
AccountType: coredata.AccessEntryAccountTypeUser,
- ExternalID: email,
+ ExternalID: externalID,
}
if m.CreatedAt != "" {
diff --git a/pkg/accessreview/drivers/lever.go b/pkg/accessreview/drivers/lever.go
deleted file mode 100644
index 8445857ce..000000000
--- a/pkg/accessreview/drivers/lever.go
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/url"
- "time"
-
- "go.probo.inc/probo/pkg/coredata"
-)
-
-// LeverDriver fetches users from the Lever REST API using a
-// pre-authenticated HTTP client (Bearer token from the Auth0-backed
-// flow). Pagination is body-cursor based: response carries `data[]`,
-// `hasNext` (bool), and `next` (cursor). The next request appends
-// `?offset=`.
-//
-// Notes on data quality:
-// - `lastLoggedInAt` and `createdAt` are epoch milliseconds —
-// defensive parse, may be null/undocumented.
-// - MFA is exposed only via SCIM/SSO, not the REST API.
-// - Active is derived from `deactivatedAt`: nil/missing = active.
-type LeverDriver struct {
- httpClient *http.Client
-}
-
-var _ Driver = (*LeverDriver)(nil)
-
-func NewLeverDriver(httpClient *http.Client) *LeverDriver {
- return &LeverDriver{
- httpClient: &http.Client{
- Transport: &retryRoundTripper{
- next: httpClient.Transport,
- maxRetries: 3,
- },
- },
- }
-}
-
-type leverUser struct {
- ID string `json:"id"`
- Email string `json:"email"`
- Name string `json:"name"`
- AccessRole string `json:"accessRole"`
- DeactivatedAt *int64 `json:"deactivatedAt"`
- LastLoggedInAt *int64 `json:"lastLoggedInAt"`
- CreatedAt *int64 `json:"createdAt"`
-}
-
-type leverUsersPage struct {
- Data []leverUser `json:"data"`
- HasNext bool `json:"hasNext"`
- Next string `json:"next"`
-}
-
-func (d *LeverDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) {
- var records []AccountRecord
-
- cursor := ""
- for range maxPaginationPages {
- page, err := d.queryUsers(ctx, cursor)
- if err != nil {
- return nil, err
- }
-
- for _, u := range page.Data {
- active := u.DeactivatedAt == nil
-
- record := AccountRecord{
- Email: u.Email,
- FullName: u.Name,
- Role: u.AccessRole,
- Active: &active,
- MFAStatus: coredata.MFAStatusUnknown,
- AuthMethod: coredata.AccessEntryAuthMethodUnknown,
- AccountType: coredata.AccessEntryAccountTypeUser,
- ExternalID: u.ID,
- }
-
- if u.LastLoggedInAt != nil {
- t := time.UnixMilli(*u.LastLoggedInAt)
- record.LastLogin = &t
- }
-
- if u.CreatedAt != nil {
- t := time.UnixMilli(*u.CreatedAt)
- record.CreatedAt = &t
- }
-
- records = append(records, record)
- }
-
- if !page.HasNext || page.Next == "" {
- return records, nil
- }
- cursor = page.Next
- }
-
- return nil, fmt.Errorf("cannot list all lever accounts: %w", ErrPaginationLimitReached)
-}
-
-func (d *LeverDriver) queryUsers(ctx context.Context, cursor string) (*leverUsersPage, error) {
- q := url.Values{}
- q.Set("limit", "100")
- if cursor != "" {
- q.Set("offset", cursor)
- }
- u := url.URL{Scheme: "https", Host: "api.lever.co", Path: "/v1/users", RawQuery: q.Encode()}
- endpoint := u.String()
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, fmt.Errorf("cannot create lever users request: %w", err)
- }
- req.Header.Set("Accept", "application/json")
-
- httpResp, err := d.httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("cannot execute lever users request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return nil, fmt.Errorf("cannot fetch lever users: unexpected status %d", httpResp.StatusCode)
- }
-
- var page leverUsersPage
- if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil {
- return nil, fmt.Errorf("cannot decode lever users response: %w", err)
- }
-
- return &page, nil
-}
diff --git a/pkg/accessreview/drivers/lever_test.go b/pkg/accessreview/drivers/lever_test.go
deleted file mode 100644
index c6e8be45c..000000000
--- a/pkg/accessreview/drivers/lever_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestLeverDriver(t *testing.T) {
- t.Parallel()
-
- rec := newRecorder(t, "testdata/lever", "LEVER_TOKEN")
- client := newVCRClient(rec, bearerAuth(os.Getenv("LEVER_TOKEN")))
-
- driver := NewLeverDriver(client)
- records, err := driver.ListAccounts(context.Background())
- require.NoError(t, err)
- require.NotEmpty(t, records)
-
- r := records[0]
- assert.NotEmpty(t, r.Email)
- assert.NotEmpty(t, r.ExternalID)
- assert.NotEmpty(t, r.FullName)
- assert.NotEmpty(t, r.Role)
- require.NotNil(t, r.Active)
- assert.True(t, *r.Active)
- require.NotNil(t, r.LastLogin)
-
- // Deactivated users (deactivatedAt non-null) should surface as Active=false.
- require.Len(t, records, 2)
- require.NotNil(t, records[1].Active)
- assert.False(t, *records[1].Active)
-}
diff --git a/pkg/accessreview/drivers/name_resolver.go b/pkg/accessreview/drivers/name_resolver.go
index 9d7577003..0ee051284 100644
--- a/pkg/accessreview/drivers/name_resolver.go
+++ b/pkg/accessreview/drivers/name_resolver.go
@@ -58,14 +58,10 @@ var providerDisplayNames = map[coredata.ConnectorProvider]string{
coredata.ConnectorProviderHeroku: "Heroku",
coredata.ConnectorProviderPagerDuty: "PagerDuty",
coredata.ConnectorProviderAsana: "Asana",
- coredata.ConnectorProviderSnyk: "Snyk",
coredata.ConnectorProviderNetlify: "Netlify",
- coredata.ConnectorProviderRamp: "Ramp",
coredata.ConnectorProviderClickUp: "ClickUp",
coredata.ConnectorProviderVercel: "Vercel",
coredata.ConnectorProviderMonday: "Monday.com",
- coredata.ConnectorProviderLever: "Lever",
- coredata.ConnectorProviderDeel: "Deel",
}
// ProviderDisplayName returns the human-readable label for a connector provider.
@@ -802,53 +798,6 @@ func (r *asanaNameResolver) ResolveInstanceName(ctx context.Context) (string, er
return resp.Data.Name, nil
}
-// snykNameResolver resolves the Snyk organization name.
-type snykNameResolver struct {
- httpClient *http.Client
- orgID string
-}
-
-func NewSnykNameResolver(httpClient *http.Client, orgID string) NameResolver {
- return &snykNameResolver{httpClient: httpClient, orgID: orgID}
-}
-
-func (r *snykNameResolver) ResolveInstanceName(ctx context.Context) (string, error) {
- if r.orgID == "" {
- return "", nil
- }
-
- endpoint := fmt.Sprintf("https://api.snyk.io/rest/orgs/%s?version=2024-10-15", url.PathEscape(r.orgID))
-
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return "", fmt.Errorf("cannot create snyk org request: %w", err)
- }
- req.Header.Set("Accept", "application/vnd.api+json")
-
- httpResp, err := r.httpClient.Do(req)
- if err != nil {
- return "", fmt.Errorf("cannot execute snyk org request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return "", fmt.Errorf("cannot fetch snyk org: unexpected status %d", httpResp.StatusCode)
- }
-
- var resp struct {
- Data struct {
- Attributes struct {
- Name string `json:"name"`
- } `json:"attributes"`
- } `json:"data"`
- }
- if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
- return "", fmt.Errorf("cannot decode snyk org response: %w", err)
- }
-
- return resp.Data.Attributes.Name, nil
-}
-
// netlifyNameResolver resolves the Netlify account name.
type netlifyNameResolver struct {
httpClient *http.Client
@@ -892,51 +841,6 @@ func (r *netlifyNameResolver) ResolveInstanceName(ctx context.Context) (string,
return resp.Name, nil
}
-// rampNameResolver resolves the Ramp business name.
-type rampNameResolver struct {
- httpClient *http.Client
-}
-
-func NewRampNameResolver(httpClient *http.Client) NameResolver {
- return &rampNameResolver{httpClient: httpClient}
-}
-
-func (r *rampNameResolver) ResolveInstanceName(ctx context.Context) (string, error) {
- req, err := http.NewRequestWithContext(
- ctx,
- http.MethodGet,
- "https://api.ramp.com/developer/v1/business",
- nil,
- )
- if err != nil {
- return "", fmt.Errorf("cannot create ramp business request: %w", err)
- }
- req.Header.Set("Accept", "application/json")
-
- httpResp, err := r.httpClient.Do(req)
- if err != nil {
- return "", fmt.Errorf("cannot execute ramp business request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return "", fmt.Errorf("cannot fetch ramp business: unexpected status %d", httpResp.StatusCode)
- }
-
- var resp struct {
- BusinessName string `json:"business_name"`
- LegalBusinessName string `json:"legal_business_name"`
- }
- if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
- return "", fmt.Errorf("cannot decode ramp business response: %w", err)
- }
-
- if resp.BusinessName != "" {
- return resp.BusinessName, nil
- }
- return resp.LegalBusinessName, nil
-}
-
// clickupNameResolver resolves the ClickUp team name.
type clickupNameResolver struct {
httpClient *http.Client
@@ -1102,67 +1006,6 @@ func (r *mondayNameResolver) ResolveInstanceName(ctx context.Context) (string, e
return resp.Data.Account.Name, nil
}
-// leverNameResolver returns an empty string: Lever does not expose a
-// dedicated org-name endpoint. The worker keeps the generic name and
-// the operator can rename the source manually.
-type leverNameResolver struct{}
-
-func NewLeverNameResolver() NameResolver {
- return &leverNameResolver{}
-}
-
-func (r *leverNameResolver) ResolveInstanceName(_ context.Context) (string, error) {
- return "", nil
-}
-
-// deelNameResolver resolves the Deel organization name by reading the
-// first item of /rest/v2/organizations.
-type deelNameResolver struct {
- httpClient *http.Client
-}
-
-func NewDeelNameResolver(httpClient *http.Client) NameResolver {
- return &deelNameResolver{httpClient: httpClient}
-}
-
-func (r *deelNameResolver) ResolveInstanceName(ctx context.Context) (string, error) {
- req, err := http.NewRequestWithContext(
- ctx,
- http.MethodGet,
- "https://api.letsdeel.com/rest/v2/organizations",
- nil,
- )
- if err != nil {
- return "", fmt.Errorf("cannot create deel organizations request: %w", err)
- }
- req.Header.Set("Accept", "application/json")
-
- httpResp, err := r.httpClient.Do(req)
- if err != nil {
- return "", fmt.Errorf("cannot execute deel organizations request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return "", fmt.Errorf("cannot fetch deel organizations: unexpected status %d", httpResp.StatusCode)
- }
-
- var resp struct {
- Data []struct {
- Name string `json:"name"`
- } `json:"data"`
- }
- if err := json.NewDecoder(httpResp.Body).Decode(&resp); err != nil {
- return "", fmt.Errorf("cannot decode deel organizations response: %w", err)
- }
-
- if len(resp.Data) == 0 {
- return "", nil
- }
-
- return resp.Data[0].Name, nil
-}
-
// notionNameResolver resolves the Notion workspace name via /v1/users/me.
type notionNameResolver struct {
httpClient *http.Client
diff --git a/pkg/accessreview/drivers/oauth2_scopes.go b/pkg/accessreview/drivers/oauth2_scopes.go
index e400a32f2..8d53f2fad 100644
--- a/pkg/accessreview/drivers/oauth2_scopes.go
+++ b/pkg/accessreview/drivers/oauth2_scopes.go
@@ -45,11 +45,7 @@ var providerOAuth2Scopes = map[coredata.ConnectorProvider][]string{
coredata.ConnectorProviderHeroku: {"read"},
coredata.ConnectorProviderPagerDuty: {"users.read"},
coredata.ConnectorProviderAsana: {"workspaces:read", "users:read"},
- coredata.ConnectorProviderSnyk: {"org.read", "org.membership.read", "offline_access"},
- coredata.ConnectorProviderRamp: {"users:read"},
coredata.ConnectorProviderMonday: {"users:read", "account:read"},
- coredata.ConnectorProviderLever: {"users:read:admin", "offline_access"},
- coredata.ConnectorProviderDeel: {"people:read", "organizations:read"},
// Notion and Intercom have no scopes here: Notion authorizes via
// extra-auth-params (owner=user), Intercom configures scopes at the app
// level. Bitbucket scopes are pinned on the OAuth consumer at
diff --git a/pkg/accessreview/drivers/organizations.go b/pkg/accessreview/drivers/organizations.go
index 29b0fdeca..233c7f78a 100644
--- a/pkg/accessreview/drivers/organizations.go
+++ b/pkg/accessreview/drivers/organizations.go
@@ -34,7 +34,7 @@ type Organization struct {
// ListGitHubOrganizations fetches the organizations the authenticated
// GitHub user belongs to.
func ListGitHubOrganizations(ctx context.Context, httpClient *http.Client) ([]Organization, error) {
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/user/orgs", nil)
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/user/orgs?per_page=100", nil)
if err != nil {
return nil, fmt.Errorf("cannot create github organizations request: %w", err)
}
@@ -301,58 +301,6 @@ func ListAsanaOrganizations(ctx context.Context, httpClient *http.Client) ([]Org
return result, nil
}
-// ListSnykOrganizations fetches the Snyk organizations the authenticated
-// user belongs to. The Snyk REST API is JSON:API; the org id is the
-// unique identifier surfaced as the slug.
-func ListSnykOrganizations(ctx context.Context, httpClient *http.Client) ([]Organization, error) {
- req, err := http.NewRequestWithContext(
- ctx,
- http.MethodGet,
- "https://api.snyk.io/rest/orgs?version=2024-10-15&limit=100",
- nil,
- )
- if err != nil {
- return nil, fmt.Errorf("cannot create snyk organizations request: %w", err)
- }
- req.Header.Set("Accept", "application/vnd.api+json")
-
- resp, err := httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("cannot fetch snyk organizations: %w", err)
- }
- defer func() { _ = resp.Body.Close() }()
-
- if resp.StatusCode != http.StatusOK {
- return nil, fmt.Errorf("cannot fetch snyk organizations: unexpected status %d", resp.StatusCode)
- }
-
- var body struct {
- Data []struct {
- ID string `json:"id"`
- Attributes struct {
- Slug string `json:"slug"`
- Name string `json:"name"`
- } `json:"attributes"`
- } `json:"data"`
- }
- if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
- return nil, fmt.Errorf("cannot decode snyk organizations response: %w", err)
- }
-
- result := make([]Organization, len(body.Data))
- for i, org := range body.Data {
- displayName := org.Attributes.Name
- if displayName == "" {
- displayName = org.Attributes.Slug
- }
- if displayName == "" {
- displayName = org.ID
- }
- result[i] = Organization{Slug: org.ID, DisplayName: displayName}
- }
- return result, nil
-}
-
// ListNetlifyOrganizations fetches the Netlify accounts the authenticated
// user belongs to.
func ListNetlifyOrganizations(ctx context.Context, httpClient *http.Client) ([]Organization, error) {
diff --git a/pkg/accessreview/drivers/ramp.go b/pkg/accessreview/drivers/ramp.go
deleted file mode 100644
index cec21ae74..000000000
--- a/pkg/accessreview/drivers/ramp.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "strings"
- "time"
-
- "go.probo.inc/probo/pkg/coredata"
-)
-
-// RampDriver fetches users from the Ramp Developer API using a
-// pre-authenticated HTTP client (Bearer token). Ramp grants are scoped
-// to a single business — there is no per-business picker, so this is a
-// Pattern 1 driver. Pagination is via the absolute URL exposed in
-// `page.next` on the response body.
-type RampDriver struct {
- httpClient *http.Client
-}
-
-var _ Driver = (*RampDriver)(nil)
-
-func NewRampDriver(httpClient *http.Client) *RampDriver {
- return &RampDriver{
- httpClient: &http.Client{
- Transport: &retryRoundTripper{
- next: httpClient.Transport,
- maxRetries: 3,
- },
- },
- }
-}
-
-type rampUser struct {
- ID string `json:"id"`
- Email string `json:"email"`
- FirstName string `json:"first_name"`
- LastName string `json:"last_name"`
- Role string `json:"role"`
- Status string `json:"status"`
- LastLoginAt string `json:"last_login_at"`
- IsManager bool `json:"is_manager"`
-}
-
-type rampUsersPage struct {
- Data []rampUser `json:"data"`
- Page struct {
- Next string `json:"next"`
- } `json:"page"`
-}
-
-func (d *RampDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) {
- var records []AccountRecord
-
- next := "https://api.ramp.com/developer/v1/users?page_size=100"
-
- for range maxPaginationPages {
- page, err := d.queryUsers(ctx, next)
- if err != nil {
- return nil, err
- }
-
- for _, u := range page.Data {
- fullName := strings.TrimSpace(u.FirstName + " " + u.LastName)
-
- active := u.Status == "USER_ACTIVE"
-
- record := AccountRecord{
- Email: u.Email,
- FullName: fullName,
- Role: u.Role,
- Active: &active,
- IsAdmin: u.IsManager,
- ExternalID: u.ID,
- MFAStatus: coredata.MFAStatusUnknown,
- AuthMethod: coredata.AccessEntryAuthMethodUnknown,
- AccountType: coredata.AccessEntryAccountTypeUser,
- }
-
- if u.LastLoginAt != "" {
- if t, err := time.Parse(time.RFC3339, u.LastLoginAt); err == nil {
- record.LastLogin = &t
- }
- }
-
- records = append(records, record)
- }
-
- if page.Page.Next == "" {
- return records, nil
- }
- next = page.Page.Next
- }
-
- return nil, fmt.Errorf("cannot list all ramp accounts: %w", ErrPaginationLimitReached)
-}
-
-func (d *RampDriver) queryUsers(ctx context.Context, endpoint string) (*rampUsersPage, error) {
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, fmt.Errorf("cannot create ramp users request: %w", err)
- }
- req.Header.Set("Accept", "application/json")
-
- httpResp, err := d.httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("cannot execute ramp users request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return nil, fmt.Errorf("cannot fetch ramp users: unexpected status %d", httpResp.StatusCode)
- }
-
- var page rampUsersPage
- if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil {
- return nil, fmt.Errorf("cannot decode ramp users response: %w", err)
- }
-
- return &page, nil
-}
diff --git a/pkg/accessreview/drivers/ramp_test.go b/pkg/accessreview/drivers/ramp_test.go
deleted file mode 100644
index d0f08d6cf..000000000
--- a/pkg/accessreview/drivers/ramp_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestRampDriver(t *testing.T) {
- t.Parallel()
-
- rec := newRecorder(t, "testdata/ramp", "RAMP_TOKEN")
- client := newVCRClient(rec, bearerAuth(os.Getenv("RAMP_TOKEN")))
-
- driver := NewRampDriver(client)
- records, err := driver.ListAccounts(context.Background())
- require.NoError(t, err)
- require.Len(t, records, 2)
-
- r := records[0]
- assert.Equal(t, "user-1", r.ExternalID)
- assert.Equal(t, "jane@example.com", r.Email)
- assert.Equal(t, "Jane Doe", r.FullName)
- assert.Equal(t, "BUSINESS_ADMIN", r.Role)
- require.NotNil(t, r.Active)
- assert.True(t, *r.Active)
- assert.True(t, r.IsAdmin)
- require.NotNil(t, r.LastLogin)
-
- // Suspended record should be Active=false.
- require.NotNil(t, records[1].Active)
- assert.False(t, *records[1].Active)
-}
diff --git a/pkg/accessreview/drivers/snyk.go b/pkg/accessreview/drivers/snyk.go
deleted file mode 100644
index 1d04dc35e..000000000
--- a/pkg/accessreview/drivers/snyk.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "net/http"
- "net/url"
- "strings"
-
- "go.probo.inc/probo/pkg/coredata"
-)
-
-// SnykDriver fetches organization memberships from the Snyk REST API
-// using a pre-authenticated HTTP client (Bearer token from the Snyk
-// Apps OAuth PKCE flow). Pagination is via the `links.next` field on
-// the response body (a relative URL fragment under api.snyk.io).
-//
-// Note: Snyk uses a single-use rotating refresh token (~180d TTL).
-// Persistence of the rotated refresh token is handled by the existing
-// callers — see pkg/accessreview/access_source_service.go:336-347 for
-// the campaign-fetch path and pkg/accessreview/source_name_worker.go:121-128
-// for the source-name path. Both run inside a transaction so concurrent
-// runs serialise per-row.
-type SnykDriver struct {
- httpClient *http.Client
- orgID string
-}
-
-var _ Driver = (*SnykDriver)(nil)
-
-func NewSnykDriver(httpClient *http.Client, orgID string) *SnykDriver {
- return &SnykDriver{
- httpClient: &http.Client{
- Transport: &retryRoundTripper{
- next: httpClient.Transport,
- maxRetries: 3,
- },
- },
- orgID: orgID,
- }
-}
-
-type snykMembership struct {
- ID string `json:"id"`
- Attributes struct {
- User struct {
- Email string `json:"email"`
- Name string `json:"name"`
- } `json:"user"`
- Role struct {
- Name string `json:"name"`
- } `json:"role"`
- } `json:"attributes"`
-}
-
-type snykMembershipsPage struct {
- Data []snykMembership `json:"data"`
- Links struct {
- Next string `json:"next"`
- } `json:"links"`
-}
-
-func (d *SnykDriver) ListAccounts(ctx context.Context) ([]AccountRecord, error) {
- var records []AccountRecord
-
- next := fmt.Sprintf(
- "https://api.snyk.io/rest/orgs/%s/memberships?version=2024-10-15&limit=100",
- url.PathEscape(d.orgID),
- )
-
- for range maxPaginationPages {
- page, err := d.queryMemberships(ctx, next)
- if err != nil {
- return nil, err
- }
-
- for _, m := range page.Data {
- record := AccountRecord{
- Email: m.Attributes.User.Email,
- FullName: m.Attributes.User.Name,
- Role: m.Attributes.Role.Name,
- ExternalID: m.ID,
- MFAStatus: coredata.MFAStatusUnknown,
- AuthMethod: coredata.AccessEntryAuthMethodUnknown,
- AccountType: coredata.AccessEntryAccountTypeUser,
- }
- records = append(records, record)
- }
-
- if page.Links.Next == "" {
- return records, nil
- }
-
- // Snyk surfaces `links.next` as either a path-only fragment
- // (e.g. "/rest/orgs//memberships?...&starting_after=...")
- // or an absolute URL. Normalise to absolute.
- if strings.HasPrefix(page.Links.Next, "http://") || strings.HasPrefix(page.Links.Next, "https://") {
- next = page.Links.Next
- } else {
- next = "https://api.snyk.io" + page.Links.Next
- }
- }
-
- return nil, fmt.Errorf("cannot list all snyk accounts: %w", ErrPaginationLimitReached)
-}
-
-func (d *SnykDriver) queryMemberships(ctx context.Context, endpoint string) (*snykMembershipsPage, error) {
- req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
- if err != nil {
- return nil, fmt.Errorf("cannot create snyk memberships request: %w", err)
- }
- req.Header.Set("Accept", "application/vnd.api+json")
-
- httpResp, err := d.httpClient.Do(req)
- if err != nil {
- return nil, fmt.Errorf("cannot execute snyk memberships request: %w", err)
- }
- defer func() { _ = httpResp.Body.Close() }()
-
- if httpResp.StatusCode < 200 || httpResp.StatusCode >= 300 {
- return nil, fmt.Errorf("cannot fetch snyk memberships: unexpected status %d", httpResp.StatusCode)
- }
-
- var page snykMembershipsPage
- if err := json.NewDecoder(httpResp.Body).Decode(&page); err != nil {
- return nil, fmt.Errorf("cannot decode snyk memberships response: %w", err)
- }
-
- return &page, nil
-}
diff --git a/pkg/accessreview/drivers/snyk_test.go b/pkg/accessreview/drivers/snyk_test.go
deleted file mode 100644
index 93afd84b6..000000000
--- a/pkg/accessreview/drivers/snyk_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (c) 2026 Probo Inc .
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted, provided that the above
-// copyright notice and this permission notice appear in all copies.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
-// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
-// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
-// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-// PERFORMANCE OF THIS SOFTWARE.
-
-package drivers
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestSnykDriver(t *testing.T) {
- t.Parallel()
-
- rec := newRecorder(t, "testdata/snyk", "SNYK_TOKEN")
- client := newVCRClient(rec, bearerAuth(os.Getenv("SNYK_TOKEN")))
-
- orgID := os.Getenv("SNYK_ORG_ID")
- if orgID == "" {
- orgID = "org-1234"
- }
-
- driver := NewSnykDriver(client, orgID)
- records, err := driver.ListAccounts(context.Background())
- require.NoError(t, err)
- require.Len(t, records, 2)
-
- r := records[0]
- assert.Equal(t, "membership-1", r.ExternalID)
- assert.Equal(t, "jane@example.com", r.Email)
- assert.Equal(t, "Jane Doe", r.FullName)
- assert.Equal(t, "Admin", r.Role)
-}
diff --git a/pkg/accessreview/drivers/testdata/deel.yaml b/pkg/accessreview/drivers/testdata/deel.yaml
deleted file mode 100644
index 49eda1054..000000000
--- a/pkg/accessreview/drivers/testdata/deel.yaml
+++ /dev/null
@@ -1,67 +0,0 @@
----
-version: 2
-interactions:
- - id: 0
- request:
- proto: HTTP/1.1
- proto_major: 1
- proto_minor: 1
- content_length: 0
- host: api.letsdeel.com
- form:
- limit:
- - "100"
- offset:
- - "0"
- headers:
- Accept:
- - application/json
- url: https://api.letsdeel.com/rest/v2/people?limit=100&offset=0
- method: GET
- response:
- proto: HTTP/2.0
- proto_major: 2
- proto_minor: 0
- content_length: -1
- uncompressed: true
- body: '{"data":[{"id":"d_jane","email":"jane@example.com","first_name":"Jane","last_name":"Doe","job_title":"Engineering Lead","hiring_status":"active","start_date":"2024-06-01","end_date":""},{"id":"d_bob","email":"bob@example.com","first_name":"Bob","last_name":"Smith","job_title":"Designer","hiring_status":"inactive","start_date":"2023-01-15","end_date":"2025-09-30"}]}'
- headers:
- Content-Type:
- - application/json
- Date:
- - Thu, 01 May 2026 12:00:00 GMT
- status: 200 OK
- code: 200
- duration: 100ms
- - id: 1
- request:
- proto: HTTP/1.1
- proto_major: 1
- proto_minor: 1
- content_length: 0
- host: api.letsdeel.com
- form:
- limit:
- - "100"
- offset:
- - "100"
- headers:
- Accept:
- - application/json
- url: https://api.letsdeel.com/rest/v2/people?limit=100&offset=100
- method: GET
- response:
- proto: HTTP/2.0
- proto_major: 2
- proto_minor: 0
- content_length: -1
- uncompressed: true
- body: '{"data":[]}'
- headers:
- Content-Type:
- - application/json
- Date:
- - Thu, 01 May 2026 12:00:00 GMT
- status: 200 OK
- code: 200
- duration: 100ms
diff --git a/pkg/accessreview/drivers/testdata/lever.yaml b/pkg/accessreview/drivers/testdata/lever.yaml
deleted file mode 100644
index 2fd5b14a9..000000000
--- a/pkg/accessreview/drivers/testdata/lever.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
- - id: 0
- request:
- proto: HTTP/1.1
- proto_major: 1
- proto_minor: 1
- content_length: 0
- host: api.lever.co
- form:
- limit:
- - "100"
- headers:
- Accept:
- - application/json
- url: https://api.lever.co/v1/users?limit=100
- method: GET
- response:
- proto: HTTP/2.0
- proto_major: 2
- proto_minor: 0
- content_length: -1
- uncompressed: true
- body: '{"data":[{"id":"l_jane","email":"jane@example.com","name":"Jane Doe","accessRole":"super admin","deactivatedAt":null,"lastLoggedInAt":1745000000000,"createdAt":1717200000000},{"id":"l_bob","email":"bob@example.com","name":"Bob Smith","accessRole":"admin","deactivatedAt":1735000000000,"lastLoggedInAt":null,"createdAt":1717200000000}],"hasNext":false,"next":""}'
- headers:
- Content-Type:
- - application/json
- Date:
- - Thu, 01 May 2026 12:00:00 GMT
- status: 200 OK
- code: 200
- duration: 100ms
diff --git a/pkg/accessreview/drivers/testdata/ramp.yaml b/pkg/accessreview/drivers/testdata/ramp.yaml
deleted file mode 100644
index 3370676d4..000000000
--- a/pkg/accessreview/drivers/testdata/ramp.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
----
-version: 2
-interactions:
- - id: 0
- request:
- proto: HTTP/1.1
- proto_major: 1
- proto_minor: 1
- content_length: 0
- host: api.ramp.com
- form:
- page_size:
- - "100"
- headers:
- Accept:
- - application/json
- url: https://api.ramp.com/developer/v1/users?page_size=100
- method: GET
- response:
- proto: HTTP/2.0
- proto_major: 2
- proto_minor: 0
- content_length: -1
- uncompressed: true
- body: '{"data":[{"id":"user-1","email":"jane@example.com","first_name":"Jane","last_name":"Doe","role":"BUSINESS_ADMIN","status":"USER_ACTIVE","last_login_at":"2026-04-15T10:00:00Z","is_manager":true},{"id":"user-2","email":"bob@example.com","first_name":"Bob","last_name":"Smith","role":"BUSINESS_USER","status":"USER_SUSPENDED","last_login_at":"","is_manager":false}],"page":{"next":""}}'
- headers:
- Content-Type:
- - application/json
- Date:
- - Thu, 01 May 2026 12:00:00 GMT
- status: 200 OK
- code: 200
- duration: 100ms
diff --git a/pkg/accessreview/drivers/testdata/snyk.yaml b/pkg/accessreview/drivers/testdata/snyk.yaml
deleted file mode 100644
index 8c42734b7..000000000
--- a/pkg/accessreview/drivers/testdata/snyk.yaml
+++ /dev/null
@@ -1,35 +0,0 @@
----
-version: 2
-interactions:
- - id: 0
- request:
- proto: HTTP/1.1
- proto_major: 1
- proto_minor: 1
- content_length: 0
- host: api.snyk.io
- form:
- limit:
- - "100"
- version:
- - "2024-10-15"
- headers:
- Accept:
- - application/vnd.api+json
- url: https://api.snyk.io/rest/orgs/org-1234/memberships?version=2024-10-15&limit=100
- method: GET
- response:
- proto: HTTP/2.0
- proto_major: 2
- proto_minor: 0
- content_length: -1
- uncompressed: true
- body: '{"data":[{"id":"membership-1","type":"org_membership","attributes":{"user":{"id":"user-1","email":"jane@example.com","name":"Jane Doe"},"role":{"id":"role-1","name":"Admin"}}},{"id":"membership-2","type":"org_membership","attributes":{"user":{"id":"user-2","email":"bob@example.com","name":"Bob Smith"},"role":{"id":"role-2","name":"Collaborator"}}}],"links":{"self":"/rest/orgs/org-1234/memberships?version=2024-10-15&limit=100"}}'
- headers:
- Content-Type:
- - application/vnd.api+json
- Date:
- - Thu, 01 May 2026 12:00:00 GMT
- status: 200 OK
- code: 200
- duration: 100ms
diff --git a/pkg/accessreview/review_engine.go b/pkg/accessreview/review_engine.go
index 12edaf9b6..357c77ba0 100644
--- a/pkg/accessreview/review_engine.go
+++ b/pkg/accessreview/review_engine.go
@@ -423,15 +423,6 @@ func (e *ReviewEngine) resolveDriver(
return nil, fmt.Errorf("asana connector requires workspace_gid in settings")
}
return drivers.NewAsanaDriver(httpClient, asanaSettings.WorkspaceGID), nil
- case coredata.ConnectorProviderSnyk:
- snykSettings, err := coredata.ConnectorSettings[coredata.SnykConnectorSettings](dbConnector)
- if err != nil {
- return nil, fmt.Errorf("cannot read snyk connector settings: %w", err)
- }
- if snykSettings.OrgID == "" {
- return nil, fmt.Errorf("snyk connector requires org_id in settings")
- }
- return drivers.NewSnykDriver(httpClient, snykSettings.OrgID), nil
case coredata.ConnectorProviderNetlify:
netlifySettings, err := coredata.ConnectorSettings[coredata.NetlifyConnectorSettings](dbConnector)
if err != nil {
@@ -441,8 +432,6 @@ func (e *ReviewEngine) resolveDriver(
return nil, fmt.Errorf("netlify connector requires account_slug in settings")
}
return drivers.NewNetlifyDriver(httpClient, netlifySettings.AccountSlug), nil
- case coredata.ConnectorProviderRamp:
- return drivers.NewRampDriver(httpClient), nil
case coredata.ConnectorProviderClickUp:
clickupSettings, err := coredata.ConnectorSettings[coredata.ClickUpConnectorSettings](dbConnector)
if err != nil {
@@ -463,10 +452,6 @@ func (e *ReviewEngine) resolveDriver(
return drivers.NewVercelDriver(httpClient, vercelSettings.TeamID), nil
case coredata.ConnectorProviderMonday:
return drivers.NewMondayDriver(httpClient), nil
- case coredata.ConnectorProviderLever:
- return drivers.NewLeverDriver(httpClient), nil
- case coredata.ConnectorProviderDeel:
- return drivers.NewDeelDriver(httpClient), nil
default:
return nil, fmt.Errorf("unsupported connector provider %q for access source driver", dbConnector.Provider)
}
diff --git a/pkg/accessreview/source_name_worker.go b/pkg/accessreview/source_name_worker.go
index 0bfdae6fa..e41f555bd 100644
--- a/pkg/accessreview/source_name_worker.go
+++ b/pkg/accessreview/source_name_worker.go
@@ -318,13 +318,6 @@ func (h *sourceNameHandler) buildResolver(
return nil
}
return drivers.NewAsanaNameResolver(httpClient, asanaSettings.WorkspaceGID)
- case coredata.ConnectorProviderSnyk:
- snykSettings, err := coredata.ConnectorSettings[coredata.SnykConnectorSettings](dbConnector)
- if err != nil {
- h.logger.Error("cannot read snyk connector settings", log.Error(err))
- return nil
- }
- return drivers.NewSnykNameResolver(httpClient, snykSettings.OrgID)
case coredata.ConnectorProviderNetlify:
netlifySettings, err := coredata.ConnectorSettings[coredata.NetlifyConnectorSettings](dbConnector)
if err != nil {
@@ -332,8 +325,6 @@ func (h *sourceNameHandler) buildResolver(
return nil
}
return drivers.NewNetlifyNameResolver(httpClient, netlifySettings.AccountSlug)
- case coredata.ConnectorProviderRamp:
- return drivers.NewRampNameResolver(httpClient)
case coredata.ConnectorProviderClickUp:
clickupSettings, err := coredata.ConnectorSettings[coredata.ClickUpConnectorSettings](dbConnector)
if err != nil {
@@ -350,10 +341,6 @@ func (h *sourceNameHandler) buildResolver(
return drivers.NewVercelNameResolver(httpClient, vercelSettings.TeamID)
case coredata.ConnectorProviderMonday:
return drivers.NewMondayNameResolver(httpClient)
- case coredata.ConnectorProviderLever:
- return drivers.NewLeverNameResolver()
- case coredata.ConnectorProviderDeel:
- return drivers.NewDeelNameResolver(httpClient)
default:
return nil
}
diff --git a/pkg/bootstrap/builder.go b/pkg/bootstrap/builder.go
index 8b3584746..2ded54299 100644
--- a/pkg/bootstrap/builder.go
+++ b/pkg/bootstrap/builder.go
@@ -355,13 +355,9 @@ func (b *Builder) Build() (*probodconfig.FullConfig, error) {
"HEROKU",
"PAGERDUTY",
"ASANA",
- "SNYK",
"NETLIFY",
- "RAMP",
"CLICKUP",
"MONDAY",
- "LEVER",
- "DEEL",
} {
clientID := b.getEnv("CONNECTOR_" + provider + "_CLIENT_ID")
if clientID == "" {
@@ -438,6 +434,15 @@ func (b *Builder) validateRequired() error {
{"CONNECTOR_BREX", []string{"CLIENT_SECRET"}},
{"CONNECTOR_GOOGLE_WORKSPACE", []string{"CLIENT_SECRET"}},
{"CONNECTOR_MICROSOFT_365", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_GITLAB", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_BITBUCKET", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_HEROKU", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_PAGERDUTY", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_ASANA", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_NETLIFY", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_CLICKUP", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_MONDAY", []string{"CLIENT_SECRET"}},
+ {"CONNECTOR_VERCEL", []string{"CLIENT_SECRET", "INTEGRATION_SLUG"}},
}
for _, p := range oauthProviders {
diff --git a/pkg/bootstrap/builder_test.go b/pkg/bootstrap/builder_test.go
index 177dca30f..f4f5192ac 100644
--- a/pkg/bootstrap/builder_test.go
+++ b/pkg/bootstrap/builder_test.go
@@ -442,8 +442,7 @@ func TestBuilder_Build_AccessReviewConnectors(t *testing.T) {
// CONNECTOR_VERCEL_INTEGRATION_SLUG env var.
providers := []string{
"GITLAB", "BITBUCKET", "HEROKU", "PAGERDUTY",
- "ASANA", "SNYK", "NETLIFY", "RAMP", "CLICKUP",
- "MONDAY", "LEVER", "DEEL",
+ "ASANA", "NETLIFY", "CLICKUP", "MONDAY",
}
env := requiredEnv()
diff --git a/pkg/connector/oauth2_test.go b/pkg/connector/oauth2_test.go
index 4fba0dce8..59e9bb2e0 100644
--- a/pkg/connector/oauth2_test.go
+++ b/pkg/connector/oauth2_test.go
@@ -882,12 +882,12 @@ func TestGeneratePKCEVerifier(t *testing.T) {
}
// TestApplyProviderDefaults_PKCEDefaults asserts that the registered
-// PAGERDUTY and SNYK provider defaults flip RequiresPKCE on so the
-// downstream Initiate/Complete flow generates a verifier and replays it.
+// PAGERDUTY provider defaults flip RequiresPKCE on so the downstream
+// Initiate/Complete flow generates a verifier and replays it.
func TestApplyProviderDefaults_PKCEDefaults(t *testing.T) {
t.Parallel()
- for _, provider := range []string{"PAGERDUTY", "SNYK"} {
+ for _, provider := range []string{"PAGERDUTY"} {
t.Run(provider, func(t *testing.T) {
t.Parallel()
@@ -899,31 +899,6 @@ func TestApplyProviderDefaults_PKCEDefaults(t *testing.T) {
}
}
-// TestApplyProviderDefaults_TokenExtraParamsDeepCopy guards against the
-// shared-map aliasing bug class. Two connectors using the same provider
-// (LEVER carries a non-empty TokenExtraParams) must not share the
-// underlying map; mutating one must not be observable on the other or
-// in the package-level providerDefinitions.
-func TestApplyProviderDefaults_TokenExtraParamsDeepCopy(t *testing.T) {
- t.Parallel()
-
- c1 := &OAuth2Connector{ClientID: "id1", ClientSecret: "s1"}
- c2 := &OAuth2Connector{ClientID: "id2", ClientSecret: "s2"}
-
- ApplyProviderDefaults("LEVER", "https://example.com/cb", c1)
- ApplyProviderDefaults("LEVER", "https://example.com/cb", c2)
-
- require.NotNil(t, c1.TokenExtraParams)
- require.NotNil(t, c2.TokenExtraParams)
- require.Equal(t, "https://api.lever.co/v1/", c1.TokenExtraParams["audience"])
-
- c1.TokenExtraParams["sentinel"] = "mutated"
- assert.NotContains(t, c2.TokenExtraParams, "sentinel",
- "second connector must not see mutations on the first")
- assert.NotContains(t, providerDefinitions["LEVER"].TokenExtraParams, "sentinel",
- "shared providerDefinitions map must remain pristine")
-}
-
// TestCompleteWithState_PKCEMismatch confirms that a token endpoint
// rejecting a stale or mismatched code_verifier (the standard PKCE
// failure path) surfaces as an error from CompleteWithState rather
diff --git a/pkg/connector/providers.go b/pkg/connector/providers.go
index 7a375d37a..e2f06b8c7 100644
--- a/pkg/connector/providers.go
+++ b/pkg/connector/providers.go
@@ -128,20 +128,10 @@ var (
AuthURL: "https://app.asana.com/-/oauth_authorize",
TokenURL: "https://app.asana.com/-/oauth_token",
},
- "SNYK": {
- AuthURL: "https://app.snyk.io/oauth2/authorize",
- TokenURL: "https://api.snyk.io/oauth2/token",
- RequiresPKCE: true,
- },
"NETLIFY": {
AuthURL: "https://app.netlify.com/authorize",
TokenURL: "https://api.netlify.com/oauth/token",
},
- "RAMP": {
- AuthURL: "https://app.ramp.com/v1/authorize",
- TokenURL: "https://api.ramp.com/developer/v1/token",
- TokenEndpointAuth: "basic-form",
- },
"CLICKUP": {
AuthURL: "https://app.clickup.com/api",
TokenURL: "https://api.clickup.com/api/v2/oauth/token",
@@ -159,31 +149,6 @@ var (
AuthURL: "https://auth.monday.com/oauth2/authorize",
TokenURL: "https://auth.monday.com/oauth2/token",
},
- // Lever runs on Auth0: the `audience` parameter is required in
- // BOTH the authorize URL and the token-exchange POST body. The
- // trailing slash on the audience value is mandatory.
- "LEVER": {
- AuthURL: "https://auth.lever.co/authorize",
- TokenURL: "https://auth.lever.co/oauth/token",
- ExtraAuthParams: map[string]string{
- "audience": "https://api.lever.co/v1/",
- "prompt": "consent",
- },
- TokenExtraParams: map[string]string{
- "audience": "https://api.lever.co/v1/",
- },
- },
- // Deel: the token endpoint path is "/oauth2/tokens" (plural) —
- // Deel's docs are inconsistent on the singular vs plural form.
- // The API base host (api.letsdeel.com) differs from the auth host
- // (app.deel.com). Deel's token endpoint requires HTTP Basic auth
- // (base64(client_id:client_secret)); credentials placed in the
- // form body are rejected with 401 invalid basic credentials.
- "DEEL": {
- AuthURL: "https://app.deel.com/oauth2/authorize",
- TokenURL: "https://app.deel.com/oauth2/tokens",
- TokenEndpointAuth: "basic-form",
- },
}
)
diff --git a/pkg/connector/registry.go b/pkg/connector/registry.go
index deb4221de..f3e06bbf9 100644
--- a/pkg/connector/registry.go
+++ b/pkg/connector/registry.go
@@ -142,17 +142,13 @@ var (
"HEROKU": "https://api.heroku.com/account",
"PAGERDUTY": "https://api.pagerduty.com/users/me",
"ASANA": "https://app.asana.com/api/1.0/users/me",
- "SNYK": "https://api.snyk.io/rest/self?version=2024-10-15",
"NETLIFY": "https://api.netlify.com/api/v1/user",
- "RAMP": "https://api.ramp.com/developer/v1/business",
"CLICKUP": "https://api.clickup.com/api/v2/user",
"VERCEL": "https://api.vercel.com/v2/user",
// Monday's primary API is GraphQL POST, but the probe handler
// is GET-only. Use the OIDC userinfo endpoint as a GET probe
// that returns 200/401 with the same Bearer token.
"MONDAY": "https://auth.monday.com/oauth2/userinfo",
- "LEVER": "https://api.lever.co/v1/users?limit=1",
- "DEEL": "https://api.letsdeel.com/rest/v2/people?limit=1",
}
)
diff --git a/pkg/coredata/connector_provider.go b/pkg/coredata/connector_provider.go
index 76499c887..fa84aa6f5 100644
--- a/pkg/coredata/connector_provider.go
+++ b/pkg/coredata/connector_provider.go
@@ -45,14 +45,10 @@ const (
ConnectorProviderHeroku ConnectorProvider = "HEROKU"
ConnectorProviderPagerDuty ConnectorProvider = "PAGERDUTY"
ConnectorProviderAsana ConnectorProvider = "ASANA"
- ConnectorProviderSnyk ConnectorProvider = "SNYK"
ConnectorProviderNetlify ConnectorProvider = "NETLIFY"
- ConnectorProviderRamp ConnectorProvider = "RAMP"
ConnectorProviderClickUp ConnectorProvider = "CLICKUP"
ConnectorProviderVercel ConnectorProvider = "VERCEL"
ConnectorProviderMonday ConnectorProvider = "MONDAY"
- ConnectorProviderLever ConnectorProvider = "LEVER"
- ConnectorProviderDeel ConnectorProvider = "DEEL"
)
func ConnectorProviders() []ConnectorProvider {
@@ -79,14 +75,10 @@ func ConnectorProviders() []ConnectorProvider {
ConnectorProviderHeroku,
ConnectorProviderPagerDuty,
ConnectorProviderAsana,
- ConnectorProviderSnyk,
ConnectorProviderNetlify,
- ConnectorProviderRamp,
ConnectorProviderClickUp,
ConnectorProviderVercel,
ConnectorProviderMonday,
- ConnectorProviderLever,
- ConnectorProviderDeel,
}
}
@@ -150,22 +142,14 @@ func (cp *ConnectorProvider) Scan(value any) error {
*cp = ConnectorProviderPagerDuty
case "ASANA":
*cp = ConnectorProviderAsana
- case "SNYK":
- *cp = ConnectorProviderSnyk
case "NETLIFY":
*cp = ConnectorProviderNetlify
- case "RAMP":
- *cp = ConnectorProviderRamp
case "CLICKUP":
*cp = ConnectorProviderClickUp
case "VERCEL":
*cp = ConnectorProviderVercel
case "MONDAY":
*cp = ConnectorProviderMonday
- case "LEVER":
- *cp = ConnectorProviderLever
- case "DEEL":
- *cp = ConnectorProviderDeel
default:
return fmt.Errorf("invalid ConnectorProvider value: %q", s)
}
diff --git a/pkg/coredata/connector_settings.go b/pkg/coredata/connector_settings.go
index 221e6f9db..e31d3c1bf 100644
--- a/pkg/coredata/connector_settings.go
+++ b/pkg/coredata/connector_settings.go
@@ -70,10 +70,6 @@ type (
WorkspaceGID string `json:"workspace_gid"`
}
- SnykConnectorSettings struct {
- OrgID string `json:"org_id"`
- }
-
NetlifyConnectorSettings struct {
AccountSlug string `json:"account_slug"`
}
diff --git a/pkg/coredata/migrations/20260508T729406Z.sql b/pkg/coredata/migrations/20260508T729406Z.sql
index abc09da3d..9d648f656 100644
--- a/pkg/coredata/migrations/20260508T729406Z.sql
+++ b/pkg/coredata/migrations/20260508T729406Z.sql
@@ -17,11 +17,7 @@ ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'BITBUCKET';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'HEROKU';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'PAGERDUTY';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'ASANA';
-ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'SNYK';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'NETLIFY';
-ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'RAMP';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'CLICKUP';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'VERCEL';
ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'MONDAY';
-ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'LEVER';
-ALTER TYPE connector_provider ADD VALUE IF NOT EXISTS 'DEEL';
diff --git a/pkg/server/api/console/v1/access_source_provider_config.go b/pkg/server/api/console/v1/access_source_provider_config.go
index 31065108f..c3bebd6b8 100644
--- a/pkg/server/api/console/v1/access_source_provider_config.go
+++ b/pkg/server/api/console/v1/access_source_provider_config.go
@@ -92,14 +92,6 @@ var providerOrgConfigs = map[coredata.ConnectorProvider]providerOrgConfig{
},
NeedsPicker: true,
},
- coredata.ConnectorProviderSnyk: {
- ListOrgs: drivers.ListSnykOrganizations,
- SelectedSlug: func(c *coredata.Connector) string {
- s, _ := coredata.ConnectorSettings[coredata.SnykConnectorSettings](c)
- return s.OrgID
- },
- NeedsPicker: true,
- },
coredata.ConnectorProviderNetlify: {
ListOrgs: drivers.ListNetlifyOrganizations,
SelectedSlug: func(c *coredata.Connector) string {
diff --git a/pkg/server/api/console/v1/graphql/connector.graphql b/pkg/server/api/console/v1/graphql/connector.graphql
index 43beeb69f..3de9c2aa9 100644
--- a/pkg/server/api/console/v1/graphql/connector.graphql
+++ b/pkg/server/api/console/v1/graphql/connector.graphql
@@ -38,16 +38,12 @@ enum ConnectorProvider
PAGERDUTY
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderPagerDuty")
ASANA @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderAsana")
- SNYK @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderSnyk")
NETLIFY
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderNetlify")
- RAMP @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderRamp")
CLICKUP
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderClickUp")
VERCEL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderVercel")
MONDAY @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderMonday")
- LEVER @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderLever")
- DEEL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ConnectorProviderDeel")
}
type ConnectorProviderInfo {