Repository navigation
feat(credentials): audit fixes and non-OAuth auth for CrowdStrike, Datadog, Gong, PlanetScale, SailPoint, ServiceNow, Zendesk, ZoomInfo, Shopify, Atlassian, Linear and Twilio - #9911
Conversation
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.
…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.
…e 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.
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.
…rated 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.
…rmalize 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.
…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.
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.
…ials 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.
…'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.
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.
…ser'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.
…x 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.
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.
…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.
…n 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.
…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.
… 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.
…vider_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.
…dentials 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.
…suer 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.
…tial-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.
…tMail 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.
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.
…copy with vendor docs
…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.
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.
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.
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.
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.
There was a problem hiding this comment.
1 issue found across 356 files
Confidence score: 4/5
- In
datadog.ts, a successful validation response with a missing or non-booleanvalidfield is reported as invalid credentials. Treat onlyvalid: falseas invalid and classify malformed responses asprovider_unavailable.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/sim/lib/credentials/client-credential-accounts/minters/datadog.ts">
<violation number="1" location="apps/sim/lib/credentials/client-credential-accounts/minters/datadog.ts:94">
P2: Malformed successful validate responses are reported as invalid credentials. Treat only an explicit `valid: false` as invalid credentials and classify a missing or non-boolean field as `provider_unavailable`.</violation>
</file>
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
…es, and regenerate CLI docs
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 359 files
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
…sswords, 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.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 364 files
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
…eep the CrowdStrike cloud required
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 364 files
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
Turn on auto-fix | Re-trigger cubic
…ed token response
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 364 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Note: This PR contains a large number of files. cubic selects up to 200 of the highest-priority eligible files for this review, so some files may not have been reviewed.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Turn on auto-fix | Re-trigger cubic
Summary
Fixes from a vendor-docs audit of the saved-credential and keyless work, plus new non-OAuth auth modes for integrations that still took inline secrets. Every change is additive: existing provider ids keep their stored secret shape, and earlier blocks keep their inline inputs.
Fixes to shipped auth
user-onlyand normalized like the saved credential, so the model can't send the token to another host.oauth.required: false) offer an optionalcredentialIdand no longer require the params a saved credential supplies.expiresAtis the real expiry.provider_unavailable.WORKLOAD_IDENTITY_ISSUERmust use the served/oidc/workloadpath, or keyless stays off.AADSTS70021/700213during trust propagation, at run time only.resolveVertexAccessToken.provider_unavailable.xoxe.) bot tokens at connect.New saved credentials and modes
ID:tokenform.client-credentials-grant.ts);_v2/_v3blocks use the picker. The earlier blocks keep their inline inputs and run unchanged.Type of Change
Testing
bun run lintpasses, and so docheck:api-validation:strict,check:boundaries,check:test-patterns,check:comment-hygiene,check:explicit-any,check:client-boundary,check:integration-catalog,check:integration-accessandcheck:docs.check:openapi,check:cli-api,check:mcp-operations,check:v2-route-table,check:cli-docs,check:block-successors,check:tool-metadata,check:deployment-config,check:docs-manifestandcheck:skillspass.check:tool-registry-boundary --check,check:file-names,check:utilsandcheck-block-registry.ts origin/stagingpass.Checklist
test-auditauthoring gate)