Skip to content

Commit f947d95

Browse files
authored
feat(credentials): audit fixes and non-OAuth auth for CrowdStrike, Datadog, Gong, PlanetScale, SailPoint, ServiceNow, Zendesk, ZoomInfo, Shopify, Atlassian, Linear and Twilio (#9911)
* fix(credentials): accept permission-scoped AgentMail keys at connect A valid AgentMail key that lacks inbox_read or is scoped to a pod or inbox gets a 403 coded missing_permission or forbidden on the inbox listing, and was refused as invalid. Accept a coded 403 as proof of the key, and keep rejecting the gateway's bare Forbidden and the key-naming 401 codes. * fix(credentials): offer Atlassian API tokens for the Bitbucket saved credential Bitbucket takes an Atlassian API token created for Bitbucket as a Bearer token, so the existing repository read already verifies one. Say so in the connect copy and setup guide, and note that app passwords no longer work. * fix(credentials): ask for a reconnect when a Microsoft app certificate can no longer sign An expired or unreadable stored certificate failed before any request reached Entra, carried no status, and was reported at run time as the provider being temporarily unavailable (502). Local configuration failures now classify as invalid_credentials, and only an unreachable Entra keeps provider_unavailable. Saving also refuses a certificate that is not valid yet. * fix(credentials): warn that saved Infisical access tokens expire Sim stores the machine identity access token as pasted and never renews it, so it stops at its Access Token TTL (30 days by default). Tell users to set a long TTL and Max TTL, and not an Access Token Period, which needs renewal each period. * fix(credentials): retry keyless Microsoft token requests while a federated credential propagates Entra can refuse a new federated identity credential with AADSTS70021 for several minutes after it is added, even after a token was issued, and Microsoft asks callers to retry. A run now retries that refusal (and AADSTS700213) twice with jittered backoff before reporting it; the save-time subject-pin probe, which expects the refusal, is unchanged. * fix(infisical): keep the instance URL out of the model's reach and normalize it like a saved credential The legacy Infisical tools let the model set baseUrl, so a prompt-injected agent call could send the inline Infisical token to any public host. baseUrl is now user-only (matching Grafana and Upstash), and inline instance URLs go through the same normalizer the saved-credential validator stores, which moves from the validator module into input-validation so tools can share it. * feat(credentials): accept HubSpot service keys for the saved HubSpot credential HubSpot stopped new legacy private apps on October 26, 2026 and points direct integrations at service keys, which are Bearer tokens for the same REST APIs. The connect copy now asks for a service key or an existing private app token. The token-info route covers only private apps, so a non-transient refusal there defers to the Account Information API, and that fallback accepts a 403 only with a JSON body, so a firewall page never passes as a live token. Both checks refuse redirects. * feat(credentials): accept Microsoft app credentials for Intune Every Intune operation the block offers (managed devices, detected apps, compliance policies and their device statuses, device configurations, and the sync, restart, remote lock, and retire actions) documents application permissions in Microsoft Graph v1.0, so the Intune picker now lists Microsoft app credentials and the resolver mints a Graph token for them. OAuth Intune connections are unchanged. The setup docs name the application permissions each operation needs. * fix(credentials): refuse rotating Slack tokens for custom bot credentials A Slack app with token rotation on issues xoxe.-prefixed tokens that expire in 12 hours and can only be refreshed with the app's client ID and secret, which Sim never holds. Such a token passed auth.test at connect and failed every run after. Refuse xoxe tokens up front with a message that asks for the xoxb- token of an app with rotation off. * docs(credentials): correct the Devin key check comment to match Devin's docs Devin documents 401 for an invalid key and one 403 for both a key outside the organization and a key without ViewOrgSessions. Behavior is unchanged: nothing documented separates the two 403s, so a 403 still rejects. * fix(credentials): refuse redirects in every per-vendor saved-key check The shared token-check engine already refuses redirects, but the per-vendor validators followed them. Fetch keeps custom headers such as x-api-key and X-Shopify-Access-Token on a cross-origin redirect, so a redirected check would hand the key to another host. Every per-vendor check now refuses a redirect, which surfaces as the provider being unavailable. * fix(credentials): treat a Webflow token that reaches no site as the user's error GET /v2/sites lists every site the token can access, so an empty list is a token the user must replace, not a retryable outage. A token that reaches several sites is no longer pinned to whichever site the list returns first. * feat(credentials): pick and connect Google service accounts for Vertex AI The Vertex AI runtime already resolves Google service account credentials, keyless or JSON key, but the Google Cloud Account picker on model blocks only listed OAuth accounts, so a saved service account could be used only by pasting its ID in advanced mode. Vertex AI now names google-service-account as its service-account provider, and its picker lists both kinds with a connect action for each, so the keyless option is offered for Vertex. Existing OAuth selections and the advanced credential ID field are unchanged. * fix(copilot): let Copilot pass a saved credential to hybrid tools Copilot's integration schema only offered credentialId for tools that require OAuth, so a tool that takes an inline key or an optional saved credential always demanded the inline secret and the gateway never forwarded a saved credential. On the copilot surface those tools now declare an optional credentialId and stop requiring the params a saved credential supplies; the workflow surface and OAuth-required tools are unchanged. The executor now rejects a Copilot call to such a tool that sends neither, instead of calling the provider without a key. * fix(credentials): report a NerdGraph error answer as New Relic being unavailable New Relic refuses an unknown or wrong-region User key with a 401, so a 200 whose errors array left the fixed actor query unanswered is NerdGraph failing, not a verdict on the key. Report it as provider_unavailable instead of telling the user their key is wrong. A 200 with no user and no errors still rejects. * fix(aws): refuse S3 presigned URLs that outlive the saved role session signing them A presigned URL signed with STS credentials stops working when the role session ends, whatever expiry it asks for, but the S3 tool reported the requested expiry. Resolved sessions now carry the STS expiration, and presigning with a saved assume_role or oidc_role credential refuses an expiry past the session's end with a 400 that names the remaining time and the session duration. Long-lived access keys and inline keys are unchanged. * fix(guardrails): resolve the hallucination check's Vertex credential like every other model call The hallucination check looked the Vertex credential up as a raw account ID, so a saved credential (whose ID is a credential row, OAuth or Google service account) resolved no token and the scoring call ran without one. It now uses the shared Vertex resolver, which handles credential rows, Google service accounts, and the legacy account IDs the old lookup accepted, and fails the check instead of calling the model when the credential cannot be resolved. * fix(aws): report transient STS failures on a saved credential as 503, not a denial Every failure to resolve a saved AWS credential came back as a 403, so an STS outage, throttling, or STS failing to reach Sim's OIDC issuer (IDPCommunicationError) read as a permission problem and was never retried. Failures STS models as retryable, throttling, STS 5xx, and network errors now return 503; real denials (AccessDenied, invalid or expired identity tokens, rejected claims) stay 403. No extra retry loop is added: the pinned STS client already retries IDPCommunicationError and throttling up to three times with backoff. * fix(databricks): report an unreachable workspace at token mint as provider_unavailable A connection failure, timeout, or refused redirect while minting a run's Databricks token escaped as a raw fetch error, which the executor reported as a generic 401 and whose cause can carry connection details. It is now a 502 DatabricksCredentialError, which resolves to provider_unavailable; a cancelled run still rethrows its abort, and personal access tokens are unaffected. * feat(credentials): connect Shopify Dev Dashboard apps with client credentials Shopify Dev Dashboard apps never reveal an Admin API token, so the Shopify saved credential gains a client-credentials mode beside the pasted shpat_ token. A credential saved with authMethod client_credentials stores the client ID, secret, and store domain; execution mints the 24-hour token at the store's /admin/oauth/access_token endpoint. Shopify does not document whether a new token retires earlier ones, so every saved credential for one app and store shares a single token kept under an advisory lock in client_credential_token, instead of minting per worker. shop_not_permitted surfaces as grant_not_permitted with copy that names the same-organization requirement. Pasted-token credentials, their blob, and their resolution are unchanged; the minted-token cache in the credential service is factored out so the client-credential providers and this mode share it. * fix(credentials): keep keyless off when a pinned workload identity issuer has an unserved path Sim serves workload identity discovery and keys only under /oidc/workload, and clouds fetch <issuer>/.well-known/openid-configuration, so a WORKLOAD_IDENTITY_ISSUER with any other path advertised keyless credentials that no cloud could verify. A pinned issuer must now have the /oidc/workload path (its host may differ), or keyless stays off with a log line naming the requirement. The default issuer derived from the app URL is unchanged, and the self-hosting and AWS credential docs state the requirement. * feat(credentials): saved CrowdStrike API client credential and credential-only block A CrowdStrike credential holds a Falcon API client id and secret, the cloud (from the existing Falcon cloud allowlist), and an optional MSSP member CID. The minter posts them to the cloud's /oauth2/token with redirects refused and returns the token with its expires_in lifetime (30 minutes when unusable) and the cloud, which the credential cache keys by credential id and secret fingerprint, so changing the cloud or secret re-mints. - clientCredentialOAuth: a policy for tools that mint their own token from inline client credentials. The credential fills the hidden accessToken and the region param; tokenReplacesParams names the inline secrets the token replaces, and the saved-credential contract check accepts exactly those. - crowdstrike_v2 is the credential-only successor; the legacy block keeps its inline client id, secret, and cloud and is hidden from the toolbar. - The operation uses a credential's token as is. Inline credentials now reuse a cached token (keyed by cloud, client id, and secret hash) until a minute before it expires, and a 401 drops it so the next run re-mints. - Client-credential mints can bind a domain (the cloud), carried through the token cache to tools. * fix(credentials): accept only documented scoped-key answers from AgentMail and New Relic AgentMail now proves a key only on the two 403 codes its docs give for a valid key with the wrong scope (missing_permission, forbidden); a suspended account, a gateway deny, and any other refusal reject the key. New Relic reports an outage only for NerdGraph errors in the server classes its own client retries; BAD_API_KEY and unclassified errors reject the key. * fix(credentials): settle HubSpot service keys on the account-info route A token-info 200 without a portal now falls back to account-info instead of failing as an outage, and a token-info 401 falls back only for pat- keys, so other tokens keep being rejected there as before. The vendor-JSON 403 check moves to one shared helper. * docs(credentials): align HubSpot, Infisical, and Bitbucket saved-key copy with vendor docs * feat(credentials): saved Zendesk credentials with client credentials or an API token Zendesk gets a zendesk-service-account credential with two sign-ins: a confidential OAuth client exchanged through the client-credentials grant (POST /oauth/tokens, JSON, an explicit read or read write scope, a one-hour expires_in), or an agent email and API token sent as the documented email/token Basic credential. The subdomain is normalized and must match ^[a-z0-9][a-z0-9-]*$ before any request, and connecting requires users/me to answer with a real (non-anonymous) agent or admin. The credential resolves to its token, its subdomain as domain, and authStyle basic for the API-token sign-in. zendesk_v2 is the credential-only successor; every Zendesk tool reads the token through one header helper and takes its subdomain from the credential. The legacy zendesk block and its inline email, API token, and subdomain keep signing in exactly as before. * feat(credentials): PlanetScale saved service token credential A PlanetScale credential holds a service token and its ID, verified by listing the organizations the token reaches with the same ID:token Authorization the tools send; a JSON 403 is a valid token without that read. Every PlanetScale tool accepts it, the selectors gain saved-credential twins that read the pair from the credential, and planetscale_v2 is the credential-only successor. The legacy block keeps its inline inputs. * feat(credentials): Gong saved access key credential A Gong credential holds an access key and its secret, verified by listing workspaces on api.gong.io with the Basic key:secret the tools send. Gong documents its 403 for this read without telling a bad key from a missing permission, so any refusal rejects. Every Gong tool accepts the credential and gong_v2 is the credential-only successor; the legacy block keeps its inline inputs. * feat(credentials): ServiceNow saved username and password credential A ServiceNow credential holds an instance, a username, and its password, verified by reading one user id from the Table API with the Basic auth the tools send. The instance is reduced to the https origin of a ServiceNow-owned host before anything is sent, and the check goes through the guarded egress; 401 rejects, a JSON 403 is a login its ACLs limit, and only a Table API result list proves the login. Every ServiceNow tool accepts the credential, servicenow_v3 is the credential-only successor, and servicenow and servicenow_v2 keep their inline inputs as legacy blocks. * feat(credentials): Twilio API key mode for the saved Twilio credential The Twilio credential and every Twilio tool now also take an API key, as the key SID and secret joined by a colon (SKxxx:secret), the exact Basic username and password Twilio documents for a key, while the URL keeps the Account SID. A key is verified by listing one of the account's phone numbers, a read a Standard key may make, since only Main keys and auth tokens may read the Accounts resource. Stored auth-token credentials and inline auth tokens keep their shape and their account-resource check; a value with a colon that is not a key pair is refused before any request. * fix(credentials): retry only the documented Entra propagation refusal, within the shared mint deadline AADSTS700213 is Microsoft's documented case-sensitive federated credential mismatch, not a propagation refusal, so a mismatched keyless credential no longer makes three token requests per run. The AADSTS70021 retries now stop when a slow answer would push the run past the 30 s deadline that concurrent runs sharing the mint wait for. The Microsoft app service list is declared once for both its type and the resolver's check, and the Intune and Vertex copy names the app credential and the Google service account. * feat(credentials): Atlassian service-account OAuth 2.0 credentials An Atlassian service account can now connect with its OAuth 2.0 credential (client ID and secret) as well as a scoped API token. Saved with authMethod client_credentials, the credential stores the client credentials, the site, its cloudId from tenant_info, and the product; execution mints a 60-minute token at auth.atlassian.com with the client credentials grant and calls api.atlassian.com/ex/{product}/{cloudId} exactly as a scoped token does. Connecting proves the minted token against the product's current-user endpoint. Atlassian documents these credentials for Jira and Confluence only, so other products are refused. It does not say whether a new token retires earlier ones, so every saved credential for one OAuth client shares one coordinated token. Scoped-token credentials, their blob, and their resolution are unchanged; the identity request now refuses redirects. * feat(credentials): saved Datadog API and application key credential and credential-only block A Datadog credential holds the site (from the DATADOG_SITES allowlist), the API key, and the application key. Datadog authenticates every request with the two static headers, so the "mint" resolves the API key as the token, the application key beside it, and the site as the credential's domain. - Save verifies the API key with GET https://api.{site}/api/v1/validate, sending DD-API-KEY alone, redirects refused: 403 is invalid_credentials, and a 200 must report valid: true. The application key is not probed: Datadog publishes no reference for an application-key check, and any other read would reject a correctly scoped key with a permission 403. Keys with control characters or whitespace are refused before any request. - Credential resolution carries a new applicationKey field (credential cache, token payload contract, executor credentialParams source), and the Assistant registers it as a resolved secret beside the token. - Every Datadog tool takes the saved credential: the credential fills apiKey, applicationKey (where the tool sends it), and site, overriding caller values. - datadog_v2 is the credential-only successor; the legacy block keeps its inline keys and site and is hidden from the toolbar. withSavedCredential accepts condition: null for a picker shown in every operation when the replaced fields show differently. * fix(aws): bound S3 presigned URLs by the role session's duration, not its time left The presign guard compared the requested expiry with the seconds left before the STS session ended, floored. A session is minted for each call, so by the time the guard runs the STS round trip has used part of it and a default 3600s session reports 3599s left, which refused the default 3600s expiry for every saved assume_role or oidc_role credential on default settings. Resolved sessions now carry the duration Sim requested from STS, and the guard refuses only an expiry longer than that duration. The reported expiresAt is clamped to the session's end, so it is never later than the time the URL stops working. The session lookup that the signer and the guard share is one helper. * fix(aws): classify wrapped STS socket failures as transient with the shared network-error helper isTransientAwsFailure kept its own list of socket codes and checked only the top-level code, so a socket failure the SDK request handler wrapped in a cause, or an undici code, was reported as a 403 denial instead of a 503. It now uses isRetryableNetworkError, which walks the cause chain and errno. * feat(credentials): connect Linear OAuth apps with client credentials The Linear saved credential gains a client-credentials mode beside the personal API key. Saved with authMethod client_credentials, it stores the app's client ID and secret and mints an app-actor token at api.linear.app/oauth/token with the fixed scope read,write, so two credentials for one app never request different scopes (which Linear treats as replacing the app's tokens). Linear documents that tokens with the same scopes coexist, so each worker caches its own, and a token is reused for at most an hour so one revoked early is replaced quickly. A client-credentials token acts as the app, so connecting falls back to the workspace (organization.id) as the principal when Linear returns no viewer. The connect copy warns that requesting a token replaces an existing authorization-code installation of the app and that rotating the secret invalidates its tokens. Personal API keys and Linear OAuth credentials are unchanged. * fix(credentials): warn instead of turning keyless off for a pinned issuer on another path Refusing a pinned WORKLOAD_IDENTITY_ISSUER whose path was not exactly /oidc/workload turned keyless off on upgrade for a deployment that routes a different issuer path to Sim through a proxy, and it also refused pinning the default issuer of a path-prefixed app URL, which the default itself accepts. Trust policies name the issuer, so those operators could only recover by recreating the identity provider and every trust policy. A pinned issuer whose path ends in /oidc/workload is now accepted the same way the default is. Any other path logs a warning that clouds can verify its tokens only if a proxy routes it to /oidc/workload; saving a keyless credential still proves the route end to end. The https, port, query, and fragment refusals are unchanged, and the docs describe the rule. * fix(copilot): exempt a hybrid tool's key only when the hosted key applies to the call The Copilot credential check waived a hybrid tool's hosted key param whenever the deployment was hosted, but injectHostedKeyIfNeeded skips the hosted key when the tool's hosting.enabled predicate rejects the params, so such a call could reach the provider with neither a key nor a saved credential. Both now use one hostedKeyMayFill predicate. No hybrid tool declares hosting today, so this is latent. * fix(databricks): stop repeating 'Try again' when the save-time probe cannot reach the workspace The unreachable-workspace error ended in 'Try again.', and the federation-policy check that wraps it adds its own, so a save showed 'Try again. Try again.'. The low-level message now only names the failure. * fix(credentials): refuse redirects by default in fetchProvider Every fetchProvider caller sends a secret, and fetch replays custom headers and 307/308 bodies to whatever host a redirect names. Default the helper to redirect: 'error' so validators and client-credential mints inherit it, and drop the per-call copies this branch had added. * fix(credentials): settle a HubSpot key account-info refuses with a contacts read HubSpot documents service keys only against its CRM REST routes, not the token-info or account-info routes. When account-info answers 401, confirm on a one-contact read, where a 200 or JSON 403 proves the key is live and a 401 rejects it. * test(security): move the typed base URL normalizer test next to input-validation normalizeHttpBaseUrl and refuseUnsafeTypedUrl moved from the token validator module into input-validation; their test now lives with them. * feat(credentials): saved SailPoint API client credential and credential-only block A SailPoint credential holds a tenant plus an API client or personal access token client id and secret. The minter shares the legacy block's token exchange (now requestSailPointToken in lib/internal/sailpoint/client: the client_credentials grant at the tenant's /oauth/token, redirects refused, bounded 429 retries, and a SailPointAuthError carrying the status), so 4xx maps to invalid_credentials and 5xx, throttling, or network failures to provider_unavailable. The token lifetime comes from expires_in. - The tenant is normalized to {tenant}.api.identitynow.com, the host SailPoint documents for API and token calls, from a tenant name, the API host, or the tenant's UI URL. Anything else (other domains, credentials, ports, more than one tenant label) is refused before the secret is sent. The legacy block's inline tenants, including identitynowgov.com hosts, are unchanged. - The credential fills the hidden accessToken and the tenant (its API host); the operation sends that token as is and never re-mints it on a 401. Inline credentials keep their cached token and single 401 refresh. - sailpoint_v2 is the credential-only successor; the legacy block keeps its inline tenant, client id, and secret and is hidden from the toolbar. * feat(credentials): saved ZoomInfo client credentials and credential-only block A ZoomInfo credential holds a client credentials app's client id and secret. The tools already authenticate with that grant (Basic auth at api.zoominfo.com/gtm/oauth/v1/token), so the minter shares the legacy block's exchange, now requestZoomInfoToken: a refusal throws ZoomInfoTokenError, which the operation answers exactly as before (HTTP 500 naming the provider status) and the minter maps to invalid_credentials for a 4xx or provider_unavailable for throttling, 5xx, or network failures. - Save verifies by minting a token only; the token endpoint uses no credits and no data endpoint is called. Granted scopes are recorded as metadata. - A client id with a colon or control characters is refused before any request, since it would corrupt the Basic credential. - The token exchange and API calls refuse redirects (maxRedirects: 0); both go only to the fixed api.zoominfo.com host. - The credential fills the tools' hidden accessToken, which replaces the inline client id and secret; zoominfo_v2 is the credential-only successor, and the legacy block keeps its inline credentials and is hidden from the toolbar. Search filters named like hosts are recorded as request data in the saved-credential contract check. * refactor(gong): share one Basic auth header builder between the tools and the credential check * fix(credentials): stop accepting an undocumented PlanetScale 403 as proof of a service token PlanetScale documents 401 for a bad token and 404 for missing access, but gives no meaning to a 403 from listing organizations, so a JSON 403 now rejects like any other refusal. The token ID is checked in the account-id normalizer with the same printable, colon-free rule the tools' header builder applies. * test(credentials): prove refused saved-credential hosts never reach the guarded egress fetch The steer cases asserted only on the global fetch stub, but ServiceNow sends through the guarded egress transport, so its 'before sending the key' half never ran. Both transports now answer, so only the refusal can keep the key unsent. * fix(credentials): tell ServiceNow and Gong users which hosts the saved credential supports ServiceNow custom URLs are aliases of the instance's original service-now.com address, so the help text points custom-URL tenants there; on-premises instances are named as unsupported. Gong's help text names the fixed api.gong.io host the tools call. * docs(credentials): document the in-band Twilio API key form and hint a half-entered key pair The saved Twilio credential keeps an API key in the auth-token field as SKxxx:secret, Twilio's documented Basic username and password for a key. The provider id now records that choice and warns that a consumer needing the account auth token itself, such as webhook signature validation, must refuse a key pair. The connect form shows a hint while an SK value is not a complete SID:secret pair. * fix(credentials): share the CrowdStrike token exchange and report a wrong Falcon cloud - Legacy inline tokens are also dropped on a 403, so a scope granted after a failed run applies on the next one, as it did before tokens were cached. - The saved-credential minter and the legacy path call one exchange, requestCrowdStrikeToken; the legacy path keeps following redirects. - The minter never follows a redirect and reports one as invalid_credentials naming the API client's cloud from X-Cs-Region, instead of a provider outage. - Drop the unused instanceUrl param and cap a saved-credential accessToken. * fix(credentials): expire shared client-credentials tokens the provider rejects A token served from the shared client_credential_token row was never expired early, so a Shopify or Atlassian token revoked before its stated expiry kept being served, and reconnecting with the same secret got the same dead token. - invalidateCoordinatedClientToken expires exactly the rejected token - saving or reconnecting re-mints once when the provider rejects the served token - a 401 from a tool call on a service-account credential expires the token - expired failure memos are swept by the shared coordinator, which Vanta now reuses - drop the Atlassian product check that could never refuse anything * fix(credentials): report a mistyped SailPoint tenant as not found and share site lists - A SailPoint tenant host that does not resolve (ENOTFOUND) is site_not_found, not a provider outage; a transient resolver failure stays provider_unavailable. - The tenant normalizer refuses `api`, SailPoint's own API domain, in every input form instead of minting at api.api.identitynow.com. - Datadog site labels and the isDatadogSite guard live once in tools/datadog and serve the block, the connect modal, and the minter; CrowdStrike cloud labels derive from the cloud ids. * fix(credentials): drop the unverified ZoomInfo token-credits claim and cap saved tokens ZoomInfo's client-credentials docs do not say whether a token request uses credits, so the docs and comments describe only what Sim does: connecting requests a token and calls no data endpoint. A saved-credential accessToken is capped at 16 KiB like SailPoint's. * fix(credentials): share one Linear client-credentials token per app Linear asks clients to reuse a token until shortly before it expires and to request a new one after a 401, and caps an app at 1,000 tokens per workspace through cleanup. Each worker minted a new 30-day token every hour instead. Linear now goes through the coordinated token for its full lifetime, and the 401 path replaces it early; every token grant now coordinates, so the grant registry maps providers straight to minters. * fix(credentials): keep a credential's client-credentials mode on reconnect A reconnect that sent only a new client secret for a Shopify, Linear, or Atlassian client-credentials credential was routed to the pasted-token builder and failed. The stored mode now carries forward unless the reconnect sends a token. The connect modals no longer preselect a mode on reconnect, so the admin restates it instead of silently switching modes. * docs(credentials): align Zendesk, Linear, and Shopify copy with vendor docs - mark Zendesk API tokens as deprecated by Zendesk in the picker, hint, and docs - warn that a Linear client-credentials request with other scopes revokes tokens - say where Shopify's 24-hour token is kept instead of claiming it is never stored - reword comments that stated undocumented Linear and Zendesk behavior as fact * fix(credentials): drop exports that only their own module uses * fix(credentials): keep shared tokens on scope 401s and rate-limit tool-call expiry A 401 whose body reports a scope mismatch (Atlassian's 'Unauthorized; scope does not match', RFC 6750 insufficient_scope) comes with a valid token, so it no longer expires the application's shared token. A tool-call rejection also leaves a token minted within the last minute in service, so a call that keeps failing re-mints at most once a minute. Connect-time identity proofs still expire a rejected token unconditionally. * fix(credentials): replace the shared Linear client-credentials token daily Linear tokens live 30 days, but a revocation that does not surface as a 401 would otherwise keep a dead token in service until then. Same-scope tokens coexist, so a daily replacement leaves in-flight calls valid. * chore(credentials): regenerate block successors, tool metadata, deployment config, integration docs, OpenAPI and CLI API for credential auth wave 2 * test(credentials): drive the slow Entra answer from the propagation suite's fake clock * fix(credentials): declare the Zendesk email and API token as replaced by the saved credential's token * test(credentials): wait for the slow Entra answer with the shared sleep helper * fix(crowdstrike): mint a new token for each legacy inline request again The inline token cache held a Falcon token for up to 29 minutes, so a token Falcon had since revoked or rescoped failed the next run. Legacy blocks mint per request as they did before the saved credential. * fix(credentials): drop a saved client-credential token a provider refuses Internal operations such as CrowdStrike and SailPoint answer 401 or 403 when the provider refuses a saved credential's token. The executor now drops that token from the per-instance cache so the next call mints a replacement instead of reusing it until it nears expiry. * test(credentials): serve the stored coordinated token until it is invalidated The coordinated-token mock minted on every call, so the revoked Atlassian token test passed with the invalidation removed. The mock now keeps one token per application until invalidated. * refactor(credentials): share one client-credentials token exchange Atlassian, Linear, and Shopify each carried the same form POST, refusal mapping, payload check, and lifetime cap. They now call one exchangeClientCredentialsGrant, tested once, and keep only their URL, extra fields, lifetime cap, and Shopify's refusal classifier. * fix(credentials): read and decrypt a client-credentials secret once per resolution Token providers and Atlassian read and decrypted the stored secret to pick its mode, then read and decrypted it again to mint. The mode read now carries into the mint, and both blob parsers share one mode check. * refactor(credentials): share the credential-text check across minters Fivetran, ZoomInfo, and Datadog each carried the same empty, control character, and size check. They now call one assertCredentialText, with Datadog also refusing spaces. * refactor(zendesk): encode the API-token Basic credential in one place The saved-credential minter and the tools each built the {email}/token:{apiToken} Basic value. Both now call zendeskApiTokenCredential, so the saved and inline sign-ins cannot differ. * refactor(tools): declare the saved-credential token param once CrowdStrike, ZoomInfo, and SailPoint tools each repeated the hidden accessToken param a saved credential fills. They now use savedCredentialTokenParam, with the same descriptions. * fix(tools): name a missing credential field without naming Datadog The generic credential-field labels named Datadog for applicationKey, so any other tool mapping it would blame a Datadog key. Labels now carry their article and stay provider-neutral. * fix(security): redact Datadog application keys in logged inputs No key-name pattern matched applicationKey, so a key typed into the legacy Datadog block showed in plaintext in execution input logs. * fix(credentials): match the Atlassian OAuth 2.0 hints to what Atlassian supports In client-credentials mode the Jira client ID hint said one credential covers Jira Service Management, which Atlassian does not list for OAuth 2.0 credentials. It now points Service Management at a scoped API token. Also corrects the reconnect note on clientCredentials.preferred. * refactor(credentials): share the client ID and secret fields across connect forms The Atlassian and token-provider forms each built the client-credentials fields, and only one labelled its secret input for assistive tech. Both now render ClientCredentialsFields. * docs(credentials): describe the saved credentials on their setup-guide pages The PlanetScale, ServiceNow, Gong, and Twilio pages are the connect modal's setup guides but still described only the legacy inline inputs. Each now says how to add the credential, the hosts it accepts, and that earlier blocks keep their inline values. SailPoint v2 names both the PAT and the client-credentials API client. * test(credentials): mock credential-service through the central @sim/testing mock * fix(sailpoint): accept government tenant hosts on the saved credential, as the earlier block does * fix(servicenow): accept custom and self-hosted https instance URLs on the saved credential, as the earlier block does * fix(credentials): type the new test fixtures, unexport file-local types, and regenerate CLI docs * fix(credentials): drop refused local tokens, keep exact ServiceNow passwords, and refuse non-http URL forms - Forget a client-credential token cached on this instance when an external request answers 401, alongside expiring the coordinated token. - Add preserveWhitespace to token descriptor fields; ServiceNow passwords keep their surrounding spaces through the connect form, contracts, and builder. Contracts no longer trim apiToken; Atlassian and GitLab builders trim it. - Refuse any scheme other than http(s), and protocol-relative or backslash authorities, in the shared typed-URL helpers while bare host:port still works. - Refuse client-credentials extra fields that would replace grant_type, client_id, or client_secret. - Report a Datadog validate answer without a boolean valid field as a provider fault rather than invalid credentials. * fix(credentials): report an aborted grant refusal as the abort, and keep the CrowdStrike cloud required * fix(credentials): name a dropped connection separately from a malformed token response
1 parent 5bbf4e7 commit f947d95

364 files changed

Lines changed: 10001 additions & 1632 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/docs/components/ui/icon-mapping.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
409409
credential: CredentialIcon,
410410
credential_group: GridOffset,
411411
crowdstrike: CrowdStrikeIcon,
412+
crowdstrike_v2: CrowdStrikeIcon,
412413
crunchbase: CrunchbaseIcon,
413414
crunchbase_v2: CrunchbaseIcon,
414415
cursor: CursorIcon,
@@ -418,6 +419,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
418419
databricks: DatabricksIcon,
419420
databricks_v2: DatabricksIcon,
420421
datadog: DatadogIcon,
422+
datadog_v2: DatadogIcon,
421423
datagma: DatagmaIcon,
422424
daytona: DaytonaIcon,
423425
daytona_v2: DaytonaIcon,
@@ -487,6 +489,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
487489
gmail: GmailIcon,
488490
gmail_v2: GmailIcon,
489491
gong: GongIcon,
492+
gong_v2: GongIcon,
490493
google_ads: GoogleAdsIcon,
491494
google_appsheet: GoogleAppsheetIcon,
492495
google_bigquery: GoogleBigQueryIcon,
@@ -658,6 +661,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
658661
plane: PlaneIcon,
659662
plane_v2: PlaneIcon,
660663
planetscale: PlanetScaleIcon,
664+
planetscale_v2: PlanetScaleIcon,
661665
polymarket: PolymarketIcon,
662666
postgresql: PostgresIcon,
663667
posthog: PosthogIcon,
@@ -703,6 +707,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
703707
s3: S3Icon,
704708
s3_v2: S3Icon,
705709
sailpoint: SailPointIcon,
710+
sailpoint_v2: SailPointIcon,
706711
salesforce: SalesforceIcon,
707712
sap_concur: SapConcurIcon,
708713
sap_s4hana: SapS4HanaIcon,
@@ -719,6 +724,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
719724
serper: SerperIcon,
720725
servicenow: ServiceNowIcon,
721726
servicenow_v2: ServiceNowIcon,
727+
servicenow_v3: ServiceNowIcon,
722728
ses: SESIcon,
723729
ses_v2: SESIcon,
724730
sftp: SftpIcon,
@@ -823,12 +829,14 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
823829
youtube: YouTubeIcon,
824830
youtube_v2: YouTubeIcon,
825831
zendesk: ZendeskIcon,
832+
zendesk_v2: ZendeskIcon,
826833
zep: ZepIcon,
827834
zep_v2: ZepIcon,
828835
zerobounce: ZeroBounceIcon,
829836
zoho_desk: ZohoDeskIcon,
830837
zoom: ZoomIcon,
831838
zoominfo: ZoomInfoIcon,
839+
zoominfo_v2: ZoomInfoIcon,
832840
}
833841

834842
export const coreBlockTypes = new Set([

‎apps/docs/content/docs/cli/reference.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5254,7 +5254,7 @@ sim selectors get [options]
52545254

52555255
| Option | Required | Description |
52565256
| --- | --- | --- |
5257-
| `--selector-key <value>` | Yes | Registered selector key from an import or sync preview, such as `gmail.labels`. Accepted values: `airtable.bases`, `airtable.tables`, `asana.workspaces`, `attio.lists`, `attio.objects`, `bigquery.datasets`, `bigquery.tables`, `bitbucket.workspaces`, `bitbucket.repositories`, `calcom.eventTypes`, `calcom.schedules`, `clickup.workspaces`, `clickup.spaces`, `clickup.folders`, `clickup.lists`, `coda.docs`, `coda.pages`, `coda.tables`, `coda.columns`, `coda.rows`, `coda.formulas`, `coda.controls`, `coda.folders`, `coda.permissions`, `confluence.spaces`, `confluence.spacesById`, `confluence.pages`, `google.tasks.lists`, `gauge.organizations`, `gauge.categories`, `gauge.topics`, `gauge.prompts`, `gauge.tags`, `gauge.answers`, `fivetran.groups`, `fivetran.connections`, `gmail.labels`, `google.calendar`, `google.drive`, `google.sheets`, `harmonic.savedSearches`, `jama.projects`, `jama.itemTypes`, `flow.projects`, `flow.categories`, `flow.branches`, `helpscout.mailboxes`, `helpscout.assignees`, `hubspot.lists`, `hubspot.owners`, `hubspot.pipelines`, `hubspot.pipelineStages`, `hubspot.properties`, `jsm.requestTypes`, `jsm.serviceDesks`, `microsoft.planner.plans`, `notion.databases`, `notion.pages`, `netsuite.recordTypes`, `netsuite.asyncTasks`, `pipedrive.pipelines`, `sharepoint.lists`, `trello.boards`, `zoho_desk.organizations`, `zoho_desk.departments`, `zoho_desk.agents`, `zoom.meetings`, `slack.channels`, `snowflake.databases`, `snowflake.schemas`, `snowflake.tables`, `snowflake.warehouses`, `snowflake.roles`, `snowflake.fileFormats`, `snowflake.procedures`, `slack.users`, `outlook.folders`, `outlook.calendars`, `microsoft.teams`, `microsoft.chats`, `microsoft.channels`, `microsoft.planner`, `onedrive.files`, `onedrive.folders`, `sharepoint.sites`, `microsoft.excel`, `microsoft.excel.drives`, `microsoft.excel.sheets`, `microsoft.word`, `wealthbox.contacts`, `jira.issues`, `jira.projects`, `jira.projectKeys`, `linear.projects`, `linear.teams`, `monday.boards`, `monday.groups`, `webflow.sites`, `webflow.collections`, `webflow.items`, `planetscale.databases`, `powerbi.workspaces`, `powerbi.datasets`, `powerbi.reports`, `planetscale.branches`, `planetscale.backups`, `planetscale.deployRequests`, `cloudwatch.logGroups`, `cloudwatch.logStreams`, `cloudwatch.savedLogGroups`, `cloudwatch.savedLogStreams`, `imap.mailboxes`, `mcp.tools`, `managedAgent.agents`, `managedAgent.environments`, `managedAgent.vaults`, `managedAgent.memoryStores`, `knowledge.documents`, `sim.workflows`, `table.columns`, `table.outputColumns`, `workspace.secretNames`, `workspace.sandboxes`, `providers.ollamaEmbeddingModels`, `providers.openrouterEmbeddingModels`. |
5257+
| `--selector-key <value>` | Yes | Registered selector key from an import or sync preview, such as `gmail.labels`. Accepted values: `airtable.bases`, `airtable.tables`, `asana.workspaces`, `attio.lists`, `attio.objects`, `bigquery.datasets`, `bigquery.tables`, `bitbucket.workspaces`, `bitbucket.repositories`, `calcom.eventTypes`, `calcom.schedules`, `clickup.workspaces`, `clickup.spaces`, `clickup.folders`, `clickup.lists`, `coda.docs`, `coda.pages`, `coda.tables`, `coda.columns`, `coda.rows`, `coda.formulas`, `coda.controls`, `coda.folders`, `coda.permissions`, `confluence.spaces`, `confluence.spacesById`, `confluence.pages`, `google.tasks.lists`, `gauge.organizations`, `gauge.categories`, `gauge.topics`, `gauge.prompts`, `gauge.tags`, `gauge.answers`, `fivetran.groups`, `fivetran.connections`, `gmail.labels`, `google.calendar`, `google.drive`, `google.sheets`, `harmonic.savedSearches`, `jama.projects`, `jama.itemTypes`, `flow.projects`, `flow.categories`, `flow.branches`, `helpscout.mailboxes`, `helpscout.assignees`, `hubspot.lists`, `hubspot.owners`, `hubspot.pipelines`, `hubspot.pipelineStages`, `hubspot.properties`, `jsm.requestTypes`, `jsm.serviceDesks`, `microsoft.planner.plans`, `notion.databases`, `notion.pages`, `netsuite.recordTypes`, `netsuite.asyncTasks`, `pipedrive.pipelines`, `sharepoint.lists`, `trello.boards`, `zoho_desk.organizations`, `zoho_desk.departments`, `zoho_desk.agents`, `zoom.meetings`, `slack.channels`, `snowflake.databases`, `snowflake.schemas`, `snowflake.tables`, `snowflake.warehouses`, `snowflake.roles`, `snowflake.fileFormats`, `snowflake.procedures`, `slack.users`, `outlook.folders`, `outlook.calendars`, `microsoft.teams`, `microsoft.chats`, `microsoft.channels`, `microsoft.planner`, `onedrive.files`, `onedrive.folders`, `sharepoint.sites`, `microsoft.excel`, `microsoft.excel.drives`, `microsoft.excel.sheets`, `microsoft.word`, `wealthbox.contacts`, `jira.issues`, `jira.projects`, `jira.projectKeys`, `linear.projects`, `linear.teams`, `monday.boards`, `monday.groups`, `webflow.sites`, `webflow.collections`, `webflow.items`, `planetscale.databases`, `powerbi.workspaces`, `powerbi.datasets`, `powerbi.reports`, `planetscale.branches`, `planetscale.backups`, `planetscale.deployRequests`, `planetscale.savedDatabases`, `planetscale.savedBranches`, `planetscale.savedBackups`, `planetscale.savedDeployRequests`, `cloudwatch.logGroups`, `cloudwatch.logStreams`, `cloudwatch.savedLogGroups`, `cloudwatch.savedLogStreams`, `imap.mailboxes`, `mcp.tools`, `managedAgent.agents`, `managedAgent.environments`, `managedAgent.vaults`, `managedAgent.memoryStores`, `knowledge.documents`, `sim.workflows`, `table.columns`, `table.outputColumns`, `workspace.secretNames`, `workspace.sandboxes`, `providers.ollamaEmbeddingModels`, `providers.openrouterEmbeddingModels`. |
52585258
| `--context <json\|@file>` | No | Only the dependencies declared by the selector, such as oauthCredential and channelId. Missing OAuth connections require human authorization. Defaults to &#123;&#125;. (JSON, or @path / @- to read a file or stdin). |
52595259
| `--id <value>` | Yes | Provider option ID to resolve with the same selector and declared context. |
52605260

@@ -5276,7 +5276,7 @@ sim selectors list [options]
52765276

52775277
| Option | Required | Description |
52785278
| --- | --- | --- |
5279-
| `--selector-key <value>` | Yes | Registered selector key from an import or sync preview, such as `gmail.labels`. Accepted values: `airtable.bases`, `airtable.tables`, `asana.workspaces`, `attio.lists`, `attio.objects`, `bigquery.datasets`, `bigquery.tables`, `bitbucket.workspaces`, `bitbucket.repositories`, `calcom.eventTypes`, `calcom.schedules`, `clickup.workspaces`, `clickup.spaces`, `clickup.folders`, `clickup.lists`, `coda.docs`, `coda.pages`, `coda.tables`, `coda.columns`, `coda.rows`, `coda.formulas`, `coda.controls`, `coda.folders`, `coda.permissions`, `confluence.spaces`, `confluence.spacesById`, `confluence.pages`, `google.tasks.lists`, `gauge.organizations`, `gauge.categories`, `gauge.topics`, `gauge.prompts`, `gauge.tags`, `gauge.answers`, `fivetran.groups`, `fivetran.connections`, `gmail.labels`, `google.calendar`, `google.drive`, `google.sheets`, `harmonic.savedSearches`, `jama.projects`, `jama.itemTypes`, `flow.projects`, `flow.categories`, `flow.branches`, `helpscout.mailboxes`, `helpscout.assignees`, `hubspot.lists`, `hubspot.owners`, `hubspot.pipelines`, `hubspot.pipelineStages`, `hubspot.properties`, `jsm.requestTypes`, `jsm.serviceDesks`, `microsoft.planner.plans`, `notion.databases`, `notion.pages`, `netsuite.recordTypes`, `netsuite.asyncTasks`, `pipedrive.pipelines`, `sharepoint.lists`, `trello.boards`, `zoho_desk.organizations`, `zoho_desk.departments`, `zoho_desk.agents`, `zoom.meetings`, `slack.channels`, `snowflake.databases`, `snowflake.schemas`, `snowflake.tables`, `snowflake.warehouses`, `snowflake.roles`, `snowflake.fileFormats`, `snowflake.procedures`, `slack.users`, `outlook.folders`, `outlook.calendars`, `microsoft.teams`, `microsoft.chats`, `microsoft.channels`, `microsoft.planner`, `onedrive.files`, `onedrive.folders`, `sharepoint.sites`, `microsoft.excel`, `microsoft.excel.drives`, `microsoft.excel.sheets`, `microsoft.word`, `wealthbox.contacts`, `jira.issues`, `jira.projects`, `jira.projectKeys`, `linear.projects`, `linear.teams`, `monday.boards`, `monday.groups`, `webflow.sites`, `webflow.collections`, `webflow.items`, `planetscale.databases`, `powerbi.workspaces`, `powerbi.datasets`, `powerbi.reports`, `planetscale.branches`, `planetscale.backups`, `planetscale.deployRequests`, `cloudwatch.logGroups`, `cloudwatch.logStreams`, `cloudwatch.savedLogGroups`, `cloudwatch.savedLogStreams`, `imap.mailboxes`, `mcp.tools`, `managedAgent.agents`, `managedAgent.environments`, `managedAgent.vaults`, `managedAgent.memoryStores`, `knowledge.documents`, `sim.workflows`, `table.columns`, `table.outputColumns`, `workspace.secretNames`, `workspace.sandboxes`, `providers.ollamaEmbeddingModels`, `providers.openrouterEmbeddingModels`. |
5279+
| `--selector-key <value>` | Yes | Registered selector key from an import or sync preview, such as `gmail.labels`. Accepted values: `airtable.bases`, `airtable.tables`, `asana.workspaces`, `attio.lists`, `attio.objects`, `bigquery.datasets`, `bigquery.tables`, `bitbucket.workspaces`, `bitbucket.repositories`, `calcom.eventTypes`, `calcom.schedules`, `clickup.workspaces`, `clickup.spaces`, `clickup.folders`, `clickup.lists`, `coda.docs`, `coda.pages`, `coda.tables`, `coda.columns`, `coda.rows`, `coda.formulas`, `coda.controls`, `coda.folders`, `coda.permissions`, `confluence.spaces`, `confluence.spacesById`, `confluence.pages`, `google.tasks.lists`, `gauge.organizations`, `gauge.categories`, `gauge.topics`, `gauge.prompts`, `gauge.tags`, `gauge.answers`, `fivetran.groups`, `fivetran.connections`, `gmail.labels`, `google.calendar`, `google.drive`, `google.sheets`, `harmonic.savedSearches`, `jama.projects`, `jama.itemTypes`, `flow.projects`, `flow.categories`, `flow.branches`, `helpscout.mailboxes`, `helpscout.assignees`, `hubspot.lists`, `hubspot.owners`, `hubspot.pipelines`, `hubspot.pipelineStages`, `hubspot.properties`, `jsm.requestTypes`, `jsm.serviceDesks`, `microsoft.planner.plans`, `notion.databases`, `notion.pages`, `netsuite.recordTypes`, `netsuite.asyncTasks`, `pipedrive.pipelines`, `sharepoint.lists`, `trello.boards`, `zoho_desk.organizations`, `zoho_desk.departments`, `zoho_desk.agents`, `zoom.meetings`, `slack.channels`, `snowflake.databases`, `snowflake.schemas`, `snowflake.tables`, `snowflake.warehouses`, `snowflake.roles`, `snowflake.fileFormats`, `snowflake.procedures`, `slack.users`, `outlook.folders`, `outlook.calendars`, `microsoft.teams`, `microsoft.chats`, `microsoft.channels`, `microsoft.planner`, `onedrive.files`, `onedrive.folders`, `sharepoint.sites`, `microsoft.excel`, `microsoft.excel.drives`, `microsoft.excel.sheets`, `microsoft.word`, `wealthbox.contacts`, `jira.issues`, `jira.projects`, `jira.projectKeys`, `linear.projects`, `linear.teams`, `monday.boards`, `monday.groups`, `webflow.sites`, `webflow.collections`, `webflow.items`, `planetscale.databases`, `powerbi.workspaces`, `powerbi.datasets`, `powerbi.reports`, `planetscale.branches`, `planetscale.backups`, `planetscale.deployRequests`, `planetscale.savedDatabases`, `planetscale.savedBranches`, `planetscale.savedBackups`, `planetscale.savedDeployRequests`, `cloudwatch.logGroups`, `cloudwatch.logStreams`, `cloudwatch.savedLogGroups`, `cloudwatch.savedLogStreams`, `imap.mailboxes`, `mcp.tools`, `managedAgent.agents`, `managedAgent.environments`, `managedAgent.vaults`, `managedAgent.memoryStores`, `knowledge.documents`, `sim.workflows`, `table.columns`, `table.outputColumns`, `workspace.secretNames`, `workspace.sandboxes`, `providers.ollamaEmbeddingModels`, `providers.openrouterEmbeddingModels`. |
52805280
| `--context <json\|@file>` | No | Only the dependencies declared by the selector, such as oauthCredential and channelId. Missing OAuth connections require human authorization. Defaults to &#123;&#125;. (JSON, or @path / @- to read a file or stdin). |
52815281
| `--search <value>` | No | Provider option search text. |
52825282
| `--cursor <value>` | No | Continue from nextCursor returned by a previous result. |

0 commit comments

Comments
 (0)