Skip to content

v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, workspace members endpoint, trigger health and pinned versions, proxy support, search source permissions - #9871

Open
waleedlatif1 wants to merge 43 commits into
mainfrom
staging

Conversation

@waleedlatif1

@waleedlatif1 waleedlatif1 commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

waleedlatif1 and others added 30 commits November 9, 2026 22:36
…quoting, retire the legacy simstudio CLI (#9830)

* fix(cli): bind sim-setup's containers to loopback, fix reset and env quoting, retire the legacy simstudio CLI

* fix(setup): rebind containers an earlier sim-setup published on every interface, and stop a dev reset that failed to tear down

* fix(setup): warn about containers an earlier sim-setup exposed instead of replacing them
…ent asks for more chunks (#9823)

* improvement(search): return a longer live read window when an MCP client asks for more chunks

* improvement(search): share one character budget across the documents of a scaled read call

* docs(search): say where the scaled read budget stops bounding a call's size

* improvement(search): size a scaled read window by its JSON-encoded length
…ns, with keyless AWS/Databricks OIDC (#9763)

* feat(credentials): saved AWS and Databricks credentials with keyless OIDC

Adds an optional saved credential to every AWS block and the Databricks
block, alongside the existing inline key fields, plus Sim's own
workload-identity (OIDC) issuer so a credential can assume a customer's
AWS role or federate into a Databricks service principal without any
stored secret.

Existing workflows are unchanged: the inline fields stay, show, and
validate exactly as before until someone selects a saved credential.

- Issuer at /oidc/workload (discovery + JWKS), signing key from
  WORKLOAD_IDENTITY_SIGNING_KEY; 404 and keyless option off when unset
- Credential-scoped subjects, HMAC-bound to the preparing user and
  workspace, so a member cannot reuse another credential's subject
- aws-service-account: access keys, keys + AssumeRole (external ID), or
  keyless AssumeRoleWithWebIdentity; resolved server-side per call
- databricks-service-account: PAT, service principal M2M secret, or
  keyless RFC 8693 token federation
- AWS families accept the executor-authorized credential through trusted
  operation context; clients add the session token only for that pair
- S3 get_object signs x-amz-security-token for temporary credentials;
  inline signatures are byte-identical to before
- Tool credential binding honors optional (required: false) policies

* fix(credentials): close review findings on saved AWS/Databricks credentials

- Models can no longer inject a credential selector (or the token, session
  token, or host derived from one) into a hybrid tool call, which would
  have swapped an agent tool's inline keys for another workspace credential
- Databricks connections own the workspace host (authoritative `domain`)
- Drop the advanced credential-ID input: with a single picker, "is a
  credential selected" can no longer disagree with the active canonical mode
- OIDC subject proofs are bound to their provider
- Databricks token and identity fetches refuse redirects
- Correct the AWS module doc on presigned URLs carrying a session token

* chore(credentials): satisfy test-pattern, unused-export, and test-type audits

* fix(credentials): keyless reconnect, availability, and shared credential plumbing

- Reconnecting a keyless AWS role or Databricks principal keeps its trusted
  subject by default instead of issuing a new one that breaks the
  customer's trust policy
- The connect dialogs offer the keyless mode only where the deployment can
  sign tokens; the prepare endpoint reports availability instead of failing
- Register AWS (all 19 block types) and Databricks as stored-credential
  integrations, so org settings, the v2 credentials API, and Copilot can
  create and see them
- Optional STS region for GovCloud and China access keys
- One subject binder and config parser shared by both providers (neutral
  copy), one modal core for submit/errors/trust display
- Generic `authorizedServiceAccountCredential` operation context instead of
  an AWS-specific field; Databricks resolver checks credential type and
  honors cancellation
- Model-blocked params derive from each tool's authoritative params

* test(credentials): type the handler mock in the AWS credential seam test

* feat(credentials): credential-only block versions instead of hybrid inline keys

Existing AWS and Databricks blocks are left exactly as on staging, only
marked legacy and hidden from the toolbar; placed instances keep running
and show an upgrade badge. Each gets a credential-only successor
(s3_v2 ... databricks_v2, textract_v3) built by spreading the legacy block
and swapping its secret fields for a required saved-credential picker.

- withSavedCredential derives the successor's fields, inputs, and params
  builder; S3 and Textract split their builders so legacy key checks stay
- tools declare credentialSuppliedParams so serializer validation and the
  canonical-id check stop asking credential-only blocks for inline keys
- CloudWatch gets saved-credential log group/stream selectors that sign
  inside the credential's STS session
- docs generator resolves multi-level spreads and same-file param builders
- the AWS/Databricks providers are derived from the catalog again, so the
  hand-maintained token-credential mapping is reverted

* docs(credentials): document credential-only blocks by their credential, not inline keys

The generator omits a tool's credential-supplied params on pages for
blocks that sign in with a saved credential, as it already omits
accessToken. Intros now point at the AWS/Databricks credential guides.
Regenerates OpenAPI, CLI API, and CLI docs for the new selector keys.

* fix(blocks): never replace trigger fields that share a secret's id

A webhook trigger can register with a field named like the block's
secret (PagerDuty, Fathom, EmailBison, GitLab). withSavedCredential now
leaves trigger-mode fields, and their dependencies, exactly as they are.

* feat(credentials): generic saved-token plumbing for credential-only blocks

Wave 0 of moving inline-secret blocks onto saved credentials.

- tokenCredentialOAuth(provider, credentialParams) declares which tool
  params a saved token fills; credentialSuppliedParams is derived from it
- the executor fills those params after authorization, overriding any
  caller value, and fails closed when the credential lacks a field
- hosted keys are never injected when a saved credential supplies the key
- v2 tool execute no longer demands credential-supplied params when a
  credentialId is passed (fixes AWS/Databricks direct calls)
- withSavedCredential accepts an optional/conditional requirement
- check-block-registry verifies every credential-only successor's tools
  authorize the picker's provider and actually fill what they declare
- redact secretAccessKey/awsSecretAccessKey keys in logs
- serializer contract test covers every operation of each successor

* feat(credentials): Tavily saved credential and credential-only tavily_v2

Reference migration for wave 1. A generic tokenCheckValidator verifies a
token with one documented authenticated request (no redirects); Tavily
uses GET /usage. Legacy tavily is unchanged apart from its legacy marker.

* fix(credentials): close wave-0 audit findings

- agent tool-input no longer synthesizes inline secret fields a block's
  saved-credential picker supplies; one shared rule
  (credentialSuppliedParamsFor) now drives tool-input, serializer
  validation, and the canonical-id check
- the picker inherits the replaced secret's condition and requirement, so
  sts_v2's unsigned calls (web identity, SAML) need no credential
- withSavedCredential replaces both halves of a canonical secret pair and
  refuses options that would silently do nothing
- Databricks fills its inline apiKey/host through credentialParams, so its
  tools are byte-identical to staging apart from the policy line
- AWS operation families record their tool ids; a test and an executor
  guard ensure every AWS-credential tool resolves the credential
- the registry contract check fails on unpinned host params, optional
  credentials over required params, malformed pickers, and kind mismatch
- hosted-key admission (v2) and pre-resolution validation honor a selected
  saved credential; credential errors name fields in plain words

* feat(credentials): wave 1 — saved token credentials for 48 single-token blocks

Each block keeps its legacy definition (only hideFromToolbar + sunset) and
gains a credential-only <type>_v2. Every service gets a token-paste
provider with a one-request validator against a documented, read-only,
credit-free endpoint, and its tools fill the inline key from the saved
credential via tokenCredentialOAuth.

Blocks: affinity, agentmail, agentphone, ahrefs, apify, apollo, brex,
brightdata, browser_use, buffer, circleback, clerk, cloudflare,
crunchbase, daytona, elevenlabs, enrich, gamma, hex, huggingface,
incidentio, instantly, langsmith, launchdarkly, linq, loops, luma,
mailchimp, otter, persona, pinecone, profound, quartr, rb2b, rippling,
rocketlane, semrush, sendgrid, similarweb, sixtyfour, smartlead,
sportmonks, square, tailscale, trigger_dev, uptimerobot, youcom, youtube.

The registry contract check now probes each request tool's real URL
builder to find params that steer the host, and requires the saved
credential to fill them.

* fix(credentials): close wave-1 audit findings

- Pinecone and Semrush leave wave 1: Pinecone's per-call indexHost would
  let a shared key reach any host, and Semrush has no credit-free check
  that rejects a wrong key. Both are back to staging exactly.
- tokenCheckValidator: forbiddenMeansScoped accepts a valid key that lacks
  the check endpoint's scope (Apollo, Brex, Bright Data, Apify, Rippling,
  Persona); acceptsBody proves keys whose rejection arrives in a 200 body
  (Buffer GraphQL)
- the host-steering probe lives in scripts/saved-credential-contract.ts:
  typed placeholders for required params only, id/host shapes, bare and
  scheme probes, and a broad destination-name fallback where the URL
  cannot be built; covered by a fixture test
- S3 refuses a bucket or region that would move the signed request's host
- spread-inheriting docs keep their base block's outputs (restores the
  Smartlead, Trigger.dev, and Instantly output tables)
- incident.io's credential is optional for alert events, which use their
  own alert source token
- withSavedCredential refuses replaced secrets that show or require
  differently; copy, intro, and hint fixes from the audit

* fix(credentials): close wave-1 re-audit findings

- host probe also defeats unparsed suffix checks and finds hosts behind
  optional switches, probing every placeholder shape; broader destination
  names where a URL cannot be built; the contract check fails when a
  successor's tool is missing from the executable registry
- the optional-credential rule evaluates a conditional picker per
  operation; incident.io hides its picker on alert events instead
- withSavedCredential takes an explicit picker condition and refuses one
  that depends on a replaced secret at any nesting depth
- a scoped 403 counts only when it is the vendor's JSON response, never a
  firewall or CDN page
- S3 names a bad bucket or region in its error
- pinecone/semrush catalog dates restored to staging

* feat(credentials): wave 1b — Mem0, Zep, and Discord saved credentials

Their legacy params builders throw without an inline secret, so each is
extracted into a module function with a requireKey flag: the legacy block
keeps identical validation and messages, the credential-only v2 skips only
the key check. discord_v2 hides its picker for webhook posts, which never
use the bot token. BlockToolParams is now a shared type for extracted
builders (S3, Textract, Discord).

* feat(credentials): wave 1b — v3 credential-only versions and Flint

Blocks that already had a v2 get a credential-only v3 that spreads it:
cursor, dub, extend, fireflies, intercom, pulse, quiver, reducto. The v2
becomes legacy and every older version's replacedBy points at v3. Extend,
Pulse, and Reducto's v2 builders trimmed the key inline, so each is
extracted into a builder that takes the key as an argument; the legacy v2
passes the trimmed key exactly as before.

Flint gets flint_v2 with a dedicated validator: its API has no read
endpoint, so an unknown task id answering 404 proves the key.

tokenCheckValidator gains rejectsKey for vendors that answer a bad key
with a server error (Fireflies returns 500 auth_failed), so the user is
told the key is wrong rather than the vendor down.

* fix(credentials): close wave-1b audit findings and CI boundary failures

- token checks for the 59 new providers live in one module
  (validators/token-checks.ts) and their tool policies in
  tools/shared/saved-credentials.ts, so the tool-registry boundary stays
  within its baselines instead of adding ~60 modules to every graph
- the host-steering probe builds URLs through the shared transport
  (new prepareToolRequestUrl) and also flags a host validated against a
  vendor allowlist when two accepted values reach different tenants
- the AWS routing test reads typed metadata through getToolMetadata
- Flint accepts only its documented JSON 'Task not found' 404; Intercom no
  longer treats a 403 (suspended workspace, plan without API) as valid;
  Fireflies keeps its error text in logs when a 5xx isn't auth_failed
- Cursor help text, placeholder wording, Pulse intro, sorted secret fields

* fix(credentials): address pre-ship verification findings

- Refuse a keyless AWS role whose trust policy doesn't pin the credential's
  subject, so no other workspace can assume it
- Treat a malformed workload-identity signing key as not configured
- Brex: reject an unknown token's scope-style 403; the token-check factory
  now runs rejectsKey before accepting a scoped 403
- Flint: probe with a docs-shaped task id and a size-capped body read
- Drop the versioned BlockMeta entries that doubled templates; alias bare
  block types in the app icon map so template icon clusters resolve
- Widen the host-probe fallback names and rotate digits in alternates
- Copy: picker placeholder casing, Databricks v2 description, Pulse and
  Tailscale skills, AWS/Persona/Rippling intros, AWS credential docs
- Tests: AWS executor signing, AWS/Databricks credential setup, legacy
  params builders, serializer coverage for blocks without operations

* refactor(credentials): one saved-credential table and shared helpers

- withSavedCredential reads the replaced fields from SAVED_CREDENTIAL_OAUTH,
  so blocks and tools share one table instead of two hand-kept copies
- Reuse isTriggerModeSubBlock, getDependsOnFields, and a shared
  isSavedCredentialPicker; build the replacedBy map once in the registry check
- newWorkloadIdentitySubject owns the subject format; the AWS trust-pin probe
  runs alongside verification
- The issuer parses a bad signing key once instead of per request
- Token checks share the accepted result and the capped JSON reader

* chore(credentials): cleanup pass

- Keyless setup query gets its own key root, so broad credential
  invalidations can't refetch an open setup and mint a new subject
- Named staleTime constant for the setup query
- Correct stale comments (relying parties, Databricks param names)
- Tighten tests: assert the refusal reason, make the BYOK guard reachable,
  drop a duplicate and a test that could not fail

* fix(credentials): address review round 1

- A scoped 403 whose body can't be read is provider_unavailable, never accepted
- Provider error snippets read through the bounded reader
- Props interfaces for KeylessTrustFields and useWorkloadIdentitySetup; TSDoc on issuer responses

* fix(credentials): never cache the issuer's not-configured 404
Bumps the actions group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [docker/login-action](https://github.com/docker/login-action) | `4.7.0` | `4.8.0` |
| [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) | `4.1.2` | `4.1.3` |
| [actions/setup-java](https://github.com/actions/setup-java) | `6.1.0` | `6.2.0` |
| [actions/setup-dotnet](https://github.com/actions/setup-dotnet) | `6.0.0` | `6.1.0` |
| [helm/kind-action](https://github.com/helm/kind-action) | `1.15.1` | `1.15.2` |
| [softprops/action-gh-release](https://github.com/softprops/action-gh-release) | `3.0.3` | `3.0.4` |

Bumps the actions group with 1 update in the /.github/actions/image directory: [useblacksmith/setup-docker-builder](https://github.com/useblacksmith/setup-docker-builder).


Updates `docker/login-action` from 4.7.0 to 4.8.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](docker/login-action@81c6f93...8aa5831)

Updates `sigstore/cosign-installer` from 4.1.2 to 4.1.3
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](sigstore/cosign-installer@6f9f177...756fe2d)

Updates `actions/setup-java` from 6.1.0 to 6.2.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](actions/setup-java@b843ebf...77dd5b2)

Updates `actions/setup-dotnet` from 6.0.0 to 6.1.0
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](actions/setup-dotnet@a98b568...0f4a354)

Updates `helm/kind-action` from 1.15.1 to 1.15.2
- [Release notes](https://github.com/helm/kind-action/releases)
- [Commits](helm/kind-action@1544676...ea822c7)

Updates `softprops/action-gh-release` from 3.0.3 to 3.0.4
- [Release notes](https://github.com/softprops/action-gh-release/releases)
- [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md)
- [Commits](softprops/action-gh-release@efb3536...8a0cd65)

Updates `useblacksmith/setup-docker-builder` from 2.2.0 to 2.3.0
- [Release notes](https://github.com/useblacksmith/setup-docker-builder/releases)
- [Commits](useblacksmith/setup-docker-builder@1921511...06dd9c8)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 4.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: actions/setup-java
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: actions/setup-dotnet
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
- dependency-name: helm/kind-action
  dependency-version: 1.15.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: softprops/action-gh-release
  dependency-version: 3.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions
- dependency-name: useblacksmith/setup-docker-builder
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…h Tools in 2026 (#9842)

* feat(library): AI Agent Builders With Built-In Web Search and Research Tools in 2026

* Pi Babysit: address PR #9842 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
…in 2026 (#9843)

* feat(library): Best AI Agent Platforms That Support the A2A Protocol in 2026

* Pi Babysit: address PR #9843 feedback

---------

Co-authored-by: Sim Pi Agent <pi@sim.ai>
… to Decide (#9844)

Co-authored-by: Sim Pi Agent <pi@sim.ai>
* feat(search): update GitLab permission CSVs through the API

* fix(search): align GitLab CSV updates with CLI and API policies

* test(cli): cover confirmation for GitLab permission replacement
…9845)

* feat(search): manage source permissions through a shared API and CLI

* chore(search): document the shared permission grant format
…s, and keyless hardening (#9840)

* fix(credentials): accept scoped keys on save, isolate the retiring signing key, surface Databricks token errors

* improvement(credentials): report Databricks token failures in the shared service-account codes

* feat(credentials): saved credentials and credential-only versions for 17 more token blocks

Ashby, Calendly, Checkr, Fathom, Grain (v3), Granola, Lemlist, PagerDuty,
Resend, Rootly, Semrush, Sentry, Stripe, Telegram, Typeform, Vercel and
WhatsApp get a token credential with a vendor check on save and a
credential-only successor. Legacy blocks only gain hideFromToolbar and a
sunset; Telegram's params builder is extracted so the legacy block keeps
its bot-token check.

* fix(credentials): close batch-2 audit findings

Restricted keys that name their missing permission (Ashby, Resend,
ElevenLabs) are accepted through a provesKey check; Typeform rejects every
403; WhatsApp keeps the phone number ID in its own path segment; Semrush is
held back because its free balance check accepts unknown keys.

* improvement(credentials): put the credential picker under the operation picker and share the provider-failure split

Credential-only blocks now show their saved-credential picker right under
the operation picker. Validators reuse the tools' header builders, one
providerFailureCode decides caller vs provider errors (a Databricks token
reply without a token is the provider misbehaving), and new providers sit in
alphabetical order.

* fix(credentials): harden keyless sign-in for AWS and Databricks

Saving a keyless Databricks connection now refuses a federation policy that
trusts every Sim subject, as AWS roles already were. Keyless stays off when
the issuer isn't https on the default port, and an unpinned issuer is logged.
Docs cover a zero-downtime key rotation, the issuer requirements, finding the
service principal ID, and troubleshooting.

* feat(credentials): saved GitLab token credential and credential-only gitlab_v2

A GitLab credential holds a personal, project, or group access token and the
instance host (gitlab.com or self-managed). Saving verifies the token's
self-lookup at that exact host over the SSRF-guarded fetch with no redirects
and requires an active token with an API scope; runs only reach the verified
host. Successors may now state the picker's required rule when the replaced
fields differ (GitLab's host was optional).

* fix(credentials): close final-audit findings on keyless wording, issuer logs, and WhatsApp IDs

The Databricks refusal names the real cause (a subject_claim other than sub),
the issuer failure log points at the key or issuer, the AWS troubleshooting
leads with the message users see, rotation steps cover background workers,
and a WhatsApp phone number ID can no longer leave its path segment.

* fix(credentials): keep GitLab personal tokens available after GitLab moves to gitlab_v2

Personal-token visibility resolved the GitLab integration by the legacy block
type, so hiding that block and renaming its catalog entry hid every existing
personal token and refused new ones. It now resolves the current GitLab
version, and the integration page lists personal tokens beside saved GitLab
credentials.

* chore(credentials): cleanup pass on the batch-2 changes

* chore(credentials): shrink the unused-exports baseline

* fix(credentials): type the GitLab check's secure-fetch response

* fix(credentials): address review round 1

GitLab's integration page offers personal tokens and saved access tokens
side by side, a malformed GitLab token lookup reports the provider as
unavailable, and the keyless issuer refuses a query string or fragment.

* fix(credentials): close the PR-wide audit findings

Docs describe credential-only Telegram and Typeform actions and the issuer's
query/fragment refusal, PagerDuty and Grain copy name every token type that
works, Granola accepts a key whose note scope an admin turned off, a GitLab
reply that isn't a token lookup reports the provider, and Semrush's catalog
entry matches staging exactly.

* improvement(credentials): simplify and clean up the review-round changes

* fix(credentials): address review round 2

Ashby's restricted-key check reads the missing permission from object-shaped
error entries too, and a GitLab host that doesn't resolve is reported as not
found instead of the provider being unavailable.
* fix(firecrawl): complete v2 response and depth compatibility

* fix(firecrawl): preserve index pages with crawl path filters
…one host-aware check factory (#9849)

* refactor(credentials): host-aware token checks in the shared validator factory

tokenCheckValidator now takes a domain normalizer (stored as the credential's
domain), an account-id normalizer, an SSRF-guarded egress profile for hosts
the user chose, and a flag that keeps token-echoing error bodies out of logs.
GitLab becomes a declaration on it, so every token check reads the same way.

* feat(credentials): saved credentials for 20 token-and-host blocks

Algolia, Azure DevOps, Convex, Devin, Dynatrace, Email Bison, Grafana,
Infisical, Jotform, Jupyter (v3), Logfire, Mailgun, New Relic, Okta, Plane,
Supabase, Tinybird, Twilio SMS and Voice (one shared Twilio credential), and
Upstash get a credential that carries the token plus its host, region, or
account id, and a credential-only successor. Every host-deciding param comes
from the credential; a typed host is normalized as the tools do it and
verified through the guarded egress, a region or account id is checked
before any request, and Databricks subject tokens last up to an hour so
long Genie polls keep their token.

* feat(credentials): Thrive saved credential, and close the batch-3 audit findings

Thrive gets a credential holding its key, tenant id, and region host (one of
its four API hosts). Audit fixes: the connect form sends URL-shaped domains
as typed so the provider check keeps their scheme and path; credentials
default to a name with their domain or account id (GitLab keeps its token
name) and record it in the audit log; Upstash accepts http and a read-only
token refused PING; Infisical keeps http and a proxy path; Devin, Dynatrace,
and Logfire refuse dot segments, non-http schemes, and mistyped regions.

* improvement(credentials): simplify and clean up the token-and-host checks

One normalizeHttpBaseUrl and one allowedDomain replace five URL normalizers
and four region checks (fixing Grafana and Tinybird treating a non-http
scheme as a host); region tables come from the tools; acceptsBody can name
the credential (GitLab); Email Bison and Jupyter refuse credentials in the
URL instead of dropping them; the Supabase public-URL policy is inlined; the
Databricks token lifetime uses the issuer's cap.

* fix(credentials): address review round 1

Azure DevOps tools build every project-scoped URL through one traversal-safe
helper, so a project or build id can't climb into another organization with a
saved token; Tinybird and Upstash restricted-token replies are covered by
tests.

* fix(credentials): address the final audit

An Azure DevOps project or organization pasted already encoded is decoded
once before the traversal-safe segment, so it isn't encoded twice; a base
URL with a single-slash scheme is refused; Upstash accepts a proxy path like
the legacy block; a Thrive tenant id must be printable ASCII.

* docs(skills): teach the saved token credential pattern in the integration skills

add-integration, add-block, add-tools, and validate-integration now describe
the token credential (descriptor, tokenCheckValidator declaration with its
host and account normalizers, shared policy mapping every host-deciding
param) and credential-only successors for blocks that take a secret inline.

* docs(skills): address review round 2

The token-credential skill steps name the real tokenCredentialOAuth factory,
scope the checklist to token-credential and successor blocks, and keep the
AuthMode.ApiKey path for token-only blocks; normalizeEmailBisonBaseUrl gains
its TSDoc.

* fix(credentials): refuse an Email Bison instance typed with a non-https scheme

normalizeEmailBisonBaseUrl now keeps any typed scheme, so ftp://host is
refused instead of becoming a host named ftp.

* fix(credentials): close release-audit findings in the saved-credential executor

- A policy that maps credential fields owns them: the generic domain,
  instanceUrl, apiDomain and cloudId fills no longer write a stored host
  into a same-named user param (Thrive create/update user sent its region
  host as the user's domain).
- An agent tool with a saved credential selected is neither asked for nor
  allowed to send the params the credential supplies (Azure DevOps
  organization, Infisical base URL).
- ENOTFOUND is read from the error or its cause, so a mistyped host or
  account id is site_not_found under Bun as well as Node.
- A service-account secret is released only to a tool bound to its
  provider; a bare credential ID is refused.

* fix(credentials): refuse non-http schemes on typed hosts; correct docs and copy

- One refuseUnsafeTypedUrl guard refuses a typed instance URL with
  credentials or a non-http(s) scheme, for Email Bison, Jupyter and
  Logfire, before their normalizers can read the scheme as a host. The
  Email Bison tool normalizer is back to its staging behavior; the check
  lives in the credential validator, so legacy blocks are unchanged.
- Docs for successor users: Jotform region, Dynatrace scopes, Logfire
  host, Plane instance, Infisical, Fathom and Supabase now point at the
  saved credential; self-hosting no longer says API keys live in blocks;
  the AWS keyless step names the dialog's Issuer.
- ElevenLabs and Instantly copy no longer asks for a permission their
  checks don't need; Okta lists trexcloud.com.
… requests, AWS SDK clients and OTLP export (#9851)

* feat(network): honor HTTP_PROXY, HTTPS_PROXY and NO_PROXY for guarded outbound requests

* fix(network): limit environment proxies to http and https, cover an https proxy

* feat(network): route AWS SDK clients and OTLP exporters through the environment proxy

* fix(network): record the proxy dependencies under apps/sim and register the proxy agent as egress infrastructure
… Mistral Parser (#9856)

* feat(mistral): extract PDF images as files and expose table format in Mistral Parser

* fix(mistral): type the v3 images output as optional for the shared v2 base

* fix(mistral): name extracted images as storage does, drop inline table format, align image switch with tool param

* docs(mistral): describe Extract Images and Tables options in the Mistral Parser page
… from the start URL, and drop stale route cache on upgrade (#9860)

* fix(release): cap proxied plain-http responses, count Firecrawl depth from the start URL, and drop stale route cache on upgrade

* fix(firecrawl): read a scheme-less start URL as https when counting depth
…pose don't expand them (#9861)

* fix(sim-setup): quote env values containing $ so dotenv, Bun, and Compose don't expand them

* fix(sim-setup): write $ as \$ in double quotes, the one form Next, Bun, and Compose all read literally
…LI key-creation errors, show GitLab tokens as the current block (#9862)
… saved-credential contract, and drop SDK copy (#9864)
…t paths from the release audit (#9865)

* fix(security): harden saved-credential, proxy egress, and response-cap paths from the release audit

- Refuse session callers resolving saved service-account secrets on POST /api/auth/oauth/token
- Refuse a proxied hostname when any resolved record is blocked (the proxy re-resolves)
- Apply the response cap to every body, not only decoded ones (undici's http proxy wrapper ignores maxResponseSize)
- Bind workload-identity subjects to their owner at mint time
- Bound Databricks token/identity JSON reads; refuse single-slash https:/ in typed URLs; case-insensitive Dynatrace /api/vN strip
- Twilio recordings: stay on the Twilio API origin and bound fetch concurrency
- Firecrawl: count crawl maxDepth below the start URL; stop sending unused search sources
- sim-setup reset refuses when Docker is installed but unreachable
- Remove the dead requireDeployment branch from workflow access middleware; reuse requireMutableWorkflow; move a misplaced TSDoc
- Docs: CONTRIBUTING setup via sim-setup, sim-setup README, Helm read-only-root mounts, proxy and NO_PROXY behavior, GitLab CSV body limit

* fix(security): stop queued Twilio recordings fetches on cancel, type token-route auth mocks, bump chart

- Twilio list calls takes the run's signal: queued recordings requests stop once it aborts, and fetch receives it
- Token route tests type the hybrid-auth mock through the real AuthResult and pass a route context
- Chart version 1.11.13 for the README and helper comment changes
…k crawl depth as unset (#9857)

* fix(release): measure Firecrawl crawl depth from the start URL and reuse only full-access delegation keys

* fix(release): leave Firecrawl crawl depth to #9864

* fix(firecrawl): treat a blank crawl maxDepth as unset

* test(release): prove delegation refuses read-only keys and type a blank crawl depth
* fix(release): small release-audit cleanups

- Read a paused run's pause points once per status read instead of
  querying paused_executions twice.
- Log a polling webhook's auto-disable only when this failure disables
  it, in the same race-free statement.
- /api/v2/meta reports the scopes a credential can actually reach,
  using the same check enforcement uses. Enforcement is unchanged.
- MACHINE_ACCESS.md names MCP TypeScript SDK 2.4.0.

* test(webhooks): overlapping poll failures count every failure and disable once

* test(mcp): expect the search:read scope a full-access key reaches in v2 meta
… boot secret, and remaining audit follow-ups (#9866)

* docs(api-keys): add rollback guidance to the api-key-read-only flag

* docs(api): say v2 workflow execution needs a full-access API key

* chore(api): regenerate MCP operations for the execute scope wording

* fix(hitl): stop duplicating input fields across listed pause points

* fix(api): refuse a personal v2 key with a missing owner as invalid, not a 500

* fix(credentials): refuse a keyless subject another live credential already holds

A keyless subject proof stays valid for 24 hours and is not bound to one
credential, so a member removed from a keyless AWS or Databricks credential
could save a second credential with the same subject and proof and keep
assuming the customer's role.

Creating or reconnecting a credential onto a subject it did not already hold
now refuses (409 conflict) when another non-revoked credential of the same
owner holds it. The subject lives only in the encrypted secret, so the check
decrypts the owner's keyless credentials inside the write transaction, under a
per-subject advisory lock so concurrent saves serialize. A reconnect keeping
its stored subject binds nothing new and skips the check.

* refactor(tools): detect the saved-credential picker with isSavedCredentialPicker

credentialSuppliedParamsFor matched any oauth-input feeding oauthCredential,
without the credentialKind === 'service-account' check isSavedCredentialPicker
applies. It now uses that predicate. No registered block changes: every block
whose loose-match picker lacks the service-account kind has no tool that
declares credentialSuppliedParams.

* fix(runtime-secrets): fetch the boot secret through the configured proxy

The Secrets Manager client used the SDK's Node handler, which ignores
HTTPS_PROXY, so a container whose only egress is a proxy could not load its
env and never booted. Under Bun with a proxy set, send through Bun's fetch,
which honors HTTPS_PROXY/HTTP_PROXY/NO_PROXY. apps/sim's awsRequestHandler
is out of reach for packages and the realtime bootstrap needs the same path.

* fix(models): mark NVIDIA Nemotron 3 Super retired and move the NVIDIA default

NVIDIA's model page states the nvidia/nemotron-3-super-120b-a12b API is no
longer supported after 2026-10-19, and Pi 1.0.1 dropped it as its NVIDIA
default for the same reason. Mark it deprecated (hidden from pickers, still a
known id for pinned workflows) and default the NVIDIA provider to Nemotron 3
Ultra, which the Pi catalog also serves.

The Mistral IDs Pi 1.5 dropped (mistral-large-2411, devstral-small-2507,
devstral-medium-2507) were retired by Mistral on 2026-05-31 and are already
deprecated here, so saved Pi blocks fail before credentials with the
choose-a-model error instead of being mapped onto a different model.

* fix(credentials): lock keyless subject checks through the advisory-lock helper and refuse on unreadable siblings

assertWorkloadIdentitySubjectUnclaimed now takes a DbTransaction and locks
through acquireAdvisoryXactLock, so the lock carries its SQLCommenter tag and
cannot be taken on the pool by mistake. A sibling keyless credential whose
secret does not decrypt or parse used to throw a generic error, failing every
keyless create or rotation for the owner with a 500. Since it might hold the
subject, the save is now refused as a conflict naming that credential, with a
warning logged, and stored subjects are read with a schema safeParse.

* fix(network): match NO_PROXY entries like curl, including bare IPv6 and CIDR

A bare IPv6 entry such as ::1 was read as host ':' port 1 and never matched,
IP entries only matched their exact spelling, and CIDR ranges were ignored.
Entries now follow curl: domains cover their subdomains, bracketed IPv6 may
carry a port, IP entries match any spelling, and CIDR ranges match IP-literal
destinations. NO_PROXY is parsed once per distinct value. The comments and
the self-hosting networking page describe these rules, and note that Bun's
default fetch applies its own.

* docs(network): note the boot secret fetch follows Bun's NO_PROXY rules

* fix(credentials): decide subject reuse under the lock on every keyless reconnect

A reconnect that kept its subject skipped the uniqueness check, but whether
it kept the subject was decided from the secret read before provider
verification. If another reconnect moved the credential to a new subject and a
second credential claimed the old one meanwhile, the stale reconnect wrote the
old subject back unchecked and two credentials held it.

Every keyless reconnect now locks the subject it is writing, then the
credential row (FOR UPDATE), re-reads the row's current subject, and runs the
sibling check whenever the two differ, with the decision and the write in one
transaction.

* fix(runtime-secrets): pick the boot fetch's proxy with the app's rules

Bun's fetch picks a proxy by scheme only, so with just HTTP_PROXY set the
https Secrets Manager request went direct and a proxy-only container still
could not boot. Move proxy selection (HTTPS_PROXY falling back to HTTP_PROXY,
curl-style NO_PROXY) from apps/sim into @sim/security/environment-proxy, and
have runtime-secrets pass the selected proxy to Bun's fetch explicitly, or
send through the Node handler when NO_PROXY excludes the endpoint.

* docs(api): state the credential and role a manual v2 run requires

Manual runs need api:write, which read-only personal keys lack, workspace
write access, and a personal key or OAuth token; workspace API keys are
refused. Regenerate the OpenAPI document and the MCP operation catalog.
…normalized account ids, cap Databricks reads (#9859)

* fix(credentials): report a refused host as the user's input, persist normalized account ids, cap Databricks reads

- A saved-credential check whose typed host the egress policy refuses (private address, plain http where https is required) now fails as invalid_credentials instead of provider_unavailable, which rendered as a retryable 503
- The stored account id is the validator's normalized value, matching how the domain is stored
- Databricks token and identity responses are read through the capped JSON reader
- Comparison data no longer says Sim can be built in code via an SDK

* fix(credentials): fail Databricks verification on an oversized identity reply, cite the CLI docs

* fix(credentials): report an oversized Databricks token reply as oversized

* fix(credentials): tell an interrupted Databricks token reply apart from an oversized one
… literals (#9868)

* fix(sim-setup): mirror env values as written so ${VAR} references survive doctor --fix

* fix(sim-setup): leave $ references for a hand copy instead of mirroring them, drop the single-quote rewrite

* fix(sim-setup): never generate a shared secret doctor can't copy, and report unwritable values instead of aborting

* fix(sim-setup): give a placeholder shared secret the same fix as a missing one
…ecret release, and final release-audit cleanups (#9870)

* fix(api): tell a missing key owner from an owner whose ban state is null

user.banned is nullable, so a real owner with banned = NULL had every
personal key refused as orphaned. Select the joined user id and test that
instead; the ban and suspension checks are unchanged.

* fix(credentials): report a refused connection host as host_not_allowed

- egress validation names its refusal (dns or policy) instead of callers
  inferring it from a missing cause; an empty DNS answer is ENOTFOUND
- the token check judges every DNS record when an environment proxy will
  carry the request, so a mixed public/private host is refused up front
  rather than failing in the transport as a retryable 502; the transport's
  own refusal is a typed EgressRefusedError and maps the same way
- new host_not_allowed code (400) with its own connect-modal message
- Flow and GitLab personal-token verification classify a refused or
  missing host the same way
- the typed-URL scheme guard is shared and ignores the tabs and newlines
  the URL parser drops

* fix(credentials): report an oversized Databricks token success as a 502

An oversized 2xx token reply carried status 200 into provider_unavailable
and its logs. A refusal keeps its own status.

* docs(api): say v2 execute options need a full-access key or api:write

run.version, async and waitTimeoutSeconds refuse read-only keys; the meta
example lists search:read with the full-access scopes.

* chore(scripts): scope the payload-param allowlist to internal operations

Every listed tool is an internal operation, so the probed-request fallback
no longer builds or consults it.

* fix(redaction): keep Infisical secret names visible; redact only deployKey

* docs(networking): the SSRF guard matches NO_PROXY like curl, not like Bun

* fix(api-keys): never echo an internal key-creation failure to the caller

The unauthenticated CLI poll route and the settings routes returned the
raw error message for an internal failure. Return a fixed message and keep
the cause in the server log; a conflict still names the existing key. The
CLI's comment now says a 400 can also be a refused key creation.

* chore: log a failed route-cache clear and spell out the read-only key rollback

The route-cache clear stays non-fatal but warns when it fails for any
reason other than a missing build id or cache. The api-key-read-only flag
says to revoke or replace read-only keys before rolling back.

* fix(credentials): release saved secrets only to internal-JWT callers

The token route refused only session callers; any other auth type would
have been handed a saved secret. Allow internal JWT and refuse the rest.

Tools never execute in the browser (the registry boundary keeps the tool
registry out of every workspace client graph, and nothing else imports the
executor), so the browser-only token fetch through the route is deleted.

* fix(firecrawl): treat a blank maxDiscoveryDepth as unset

A cleared field arrived as '' and was sent as 0, a crawl of the start page
only. It now follows the blank maxDepth handling.

* chore(credentials): log a broken JWKS and keep the successor map out of credential display

The JWKS route still answers 404 when the signing key is broken, but warns
with the error. The GitLab personal-token display reads its block type
from the catalog entry instead of importing the generated successor map
into a client-imported module.

* test: drop or tighten assertions that could not fail

- delegation: the read-only replacement case decided its filter by
  scanning bound params in a fake DB; no real-boundary check exists, so
  it is dropped with the fake's filter
- Twilio: assert how many recordings requests were sent, so an empty
  loop cannot pass
- Pi: the removed-model case restated the absent-from-catalog case

* fix(security): ignore a malformed CIDR NO_PROXY entry

10.0.0.0/8/junk parsed as /8. An entry with extra segments is dropped like
any other unparseable entry, and the TSDoc says IP entries match within
their address family.

* chore(workflows): scope the paused-row read to its workflow and reuse the mutability guard

- the execution-status paused-row read filters by workflowId as well
- delete-workflow uses requireMutableWorkflow instead of an inline copy
- polling webhook logs carry the webhook id in metadata

* fix(credentials): never name a sibling credential in a trust-setup refusal

The caller may not be able to see the credential that holds or blocks the
subject, so both refusals name only the workspace or organization. The
uniqueness check documents its limits.

* docs: correct the Helm readOnlyRootFilesystem note and the deployed-run variables TSDoc

* test(credentials): rely on the shared env reset in the proxy validator tests

* chore(helm): bump the chart version for the README correction

* fix(redaction): keep prefixed secret keys masked while leaving Infisical's bare secretKey visible

* fix(release): keep bare secretKey redacted, leave the proxy every-address rule to the transport, and report unknown GitLab hosts as input errors
@waleedlatif1
waleedlatif1 requested a review from a team as a code owner November 11, 2026 01:53
@vercel

vercel Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Building Building Preview Nov 11, 2026 5:32am UTC

Request Review

@greptile-apps

greptile-apps Bot commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The reviewed changes appear safe to merge; no actionable findings were accepted.

Summary

This release adds saved credentials, keyless AWS and Databricks connections, read-only API keys, and new v2 management endpoints.

  • Workflow tools can use saved credentials for supported providers.
  • The CLI can read and replace search-source permissions.
  • The TypeScript and Python SDK packages and guides are removed.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  Request[API request] --> Auth[Authenticate caller]
  Auth --> Policy[Check role and operation scope]
  Policy --> Version[Choose active or pinned version]
  Version --> Run[Run now or enqueue]
  Run --> Credentials[Authorize saved credential]
  Credentials --> Provider[Resolve provider credentials]
  Provider --> Tool[Run tool]
  Tool --> State[Record result or pause]
  State --> Resume[Resume using recorded version]
Loading

Reviews (1) · Last reviewed commit: "fix(release): host_not_allowed for refus..." · Reviewed by Greptile

@waleedlatif1 waleedlatif1 changed the title v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, v2 workspace members, trigger health and pinned versions, HTTP(S)_PROXY support, search source permissions, release-audit hardening v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, v2 workspace members, trigger health and pinned versions, HTTP(S)_PROXY support, search source permissions Nov 11, 2026
@waleedlatif1 waleedlatif1 changed the title v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, v2 workspace members, trigger health and pinned versions, HTTP(S)_PROXY support, search source permissions v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, workspace members endpoint, trigger health and pinned versions, HTTP(S)_PROXY support, search source permissions Nov 11, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3000 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

Comment thread apps/sim/blocks/blocks/tinybird.ts Resolved
Comment thread apps/sim/blocks/blocks/apify.ts Resolved
Comment thread .agents/skills/add-integration/SKILL.md Resolved
Comment thread apps/sim/blocks/blocks/mistral_parse.ts Resolved
Comment thread apps/sim/blocks/blocks/mistral_parse.ts Resolved
…ng, and the NO_PROXY reference (#9874)

* docs(release): correct the Discord allowlist note, Mistral image naming, and the NO_PROXY reference

* docs(networking): state the NO_PROXY rules without the curl comparison
…eyless, GitHub and Bitbucket tokens, DocuSign JWT (#9869)

* feat(credentials): keyless Snowflake connections through workload identity federation

A Snowflake connection can now trust Sim's OIDC issuer instead of holding a
programmatic access token. Sim signs a short-lived token (audience
snowflakecomputing.com) for the credential's own random subject; the customer
creates a SERVICE user with WORKLOAD_IDENTITY = (TYPE = OIDC ISSUER SUBJECT).

- Save verifies by minting a token and running the identity statement at the
  pinned account host through the guarded egress path; Snowflake's refusals
  keep the shared codes (invalid_credentials, site_not_found,
  provider_unavailable). Subjects bind to their creator like AWS/Databricks.
- Runtime: the resolver returns authStyle 'snowflake-workload-identity' for a
  keyless credential, and the tools and selectors send Bearer WIF.OIDC.<token>
  with the WORKLOAD_IDENTITY_FEDERATION token type. authStyle and domain are
  authoritative on every Snowflake tool, so a caller can no longer choose the
  token type or steer the account host of a saved credential.
- PAT credentials resolve and verify exactly as before.
- Connect dialog offers the keyless service user with the CREATE USER
  statement; it falls back to the token dialog where keyless is unavailable.
- Docs: Snowflake connections page with keyless setup, troubleshooting, and
  the 60-minute Snowflake key cache on signing-key rotation.

* fix(snowflake): run Cortex Analyst SQL with the resolved credential and reopen keyless reconnects as keyless

Cortex Analyst's "run the SQL" went through a nested snowflake_execute_sql
call carrying the already-resolved token and host. With authStyle and domain
now authoritative, the executor cleared both before the nested call, so it
failed for every credential (and a keyless token would have been sent as a
PAT). The generated SQL now runs as one guarded statement request built from
the parent call's credential-derived host, token, and token type, through the
same requestTarget egress the tool transport uses, with redirects refused.

The Snowflake connect dialog opened every reconnect on the programmatic
access token. The workload-identity prepare call now takes the reconnected
credential and reports whether it is keyless (only to an admin of that
credential in the same workspace), and the dialog opens on that mode.

Docs: the 60-minute wait applies before signing with a new key; removing the
old key needs 10 minutes with keyless Snowflake connections.

* fix(snowflake): cancel Cortex Analyst's generated SQL with the run

The generated SQL used to run as a nested tool call, which inherited the run's
abort signal; running it directly dropped that. postProcess now receives the
executor's effective abort signal as an optional fourth argument (the same
signal the tool's own request used), and Cortex Analyst forwards it to the DNS
check, the guarded fetch, and the response read. Other tools' postProcess
hooks are unchanged.

* feat(credentials): saved GitHub personal access tokens and Bitbucket access tokens

GitHub: a saved personal access token credential (fine-grained or classic),
verified against api.github.com/user with the tools' own headers. GitHub's
workflow block authenticates with an inline token rather than OAuth, so it
follows the token-block pattern: github_v3 is the credential-only successor
of github_v2, which stays exactly as it was, marked legacy and hidden from the
toolbar. Every GitHub tool declares the optional saved-token policy; inline
tokens keep working. The GitHub App installation credential (Search) is a
separate service and is untouched: the Assistant still projects GitHub tools
onto the person's connected GitHub account, and Search policy now follows the
visible GitHub block (github_v3).

Bitbucket: a workspace, project, or repository access token as a second
option next to OAuth (the Linear pattern), verified by listing one repository
of the typed workspace on api.bitbucket.org; the slug is validated so it
cannot steer the path. List Workspaces needs a signed-in user, so it now
refuses a saved access token before any request.

Also: isHostNotFound no longer lets an outer error code mask ENOTFOUND on its
cause.

* fix(credentials): close wave 2a review findings on GitHub and Bitbucket tokens

- The GitHub token service is `github-token`, so it no longer shares an id
  with the GitHub sign-in provider: parseProvider and getProviderIdFromServiceId
  answer for sign-in exactly as before, and a token-only service is never
  marked connected from a sign-in account whose id prefixes it.
- Suggested connect rows offer each integration once and count it connected
  when any of its services' OAuth or service-account credentials is saved
  (GitHub's Search App and saved tokens share one integration).
- Assistant GitHub calls bind through the connected-account policy the
  Assistant projects (the person's github-repositories OAuth account) in the
  token resolver too, on both the ordinary and managed paths; workflow runs
  and saved workspace tokens are unaffected.
- Bitbucket verification reads one repository (`workspace/repository`), the
  request Atlassian documents for workspace, project, and repository access
  tokens alike; listing a workspace is not documented for repository tokens.
- prepare-workload-identity test uses the central credentials-access mock.

* feat(credentials): DocuSign JWT Grant credentials next to OAuth

A saved DocuSign credential that authenticates with JWT Grant (an
integration key, an impersonated user ID, the app's RSA private key, and an
environment), offered as a second option beside the existing OAuth
connection, which is unchanged.

The minter signs an RS256 assertion (iss = integration key, sub = user,
aud = the environment's authentication host without a scheme, scope
"signature impersonation"), exchanges it at that host's /oauth/token, then
reads /oauth/userinfo on the same host to bind the pinned account (or the
user's default) and returns its eSignature REST base. Both hosts come only
from the stored environment; a base_uri outside that environment's
docusign.net shape is never trusted with the token. GUIDs and the key are
checked before any request, and tokens are re-minted about 15 minutes
before they expire, as DocuSign advises.

DocuSign tools take the account base as an authoritative credential param,
so a caller can never supply it, and DocuSignClient skips its
deployment-host userinfo lookup when one is bound. OAuth tokens resolve
exactly as before.

Save-time failures map to consent_required (the connect modal shows the
exact consent URL for the key and environment) and account_not_found (a
pinned account the user does not belong to), alongside the shared
invalid_credentials and provider_unavailable codes.

* fix(credentials): close wave 2b review findings on DocuSign JWT Grant

- The DocuSign knowledge connector now honours a JWT Grant credential's
  bound account. The resolved token's instanceUrl reaches the connector as
  credentialInstanceUrl in the sync context (seeded on every path through
  syncContextForToken). The connector re-validates it with the same parser
  as DocuSignClient, lists and fetches from that account in the
  credential's environment, links envelopes to that environment's web app,
  and validates without asking the deployment host. A seeded value of the
  wrong shape fails closed. OAuth syncs are unchanged.
- RSA keys under 2048 bits are refused as invalid_credentials with copy
  that names the size, instead of jose's raw TypeError.
- A short expires_in no longer makes every call re-mint: the cached
  lifetime floors at six minutes while staying a minute under the real
  expiry.
- The connect modal shows the consent URL as a link with a copy button,
  and account_not_found says "pinned account" only when one was entered.

* feat(credentials): Microsoft Entra app-only credentials for Entra ID, SharePoint, Dataverse, and Dynamics 365

A saved Microsoft app credential (OAuth 2.0 client credentials) as an
additional option next to Microsoft OAuth, which is unchanged. One app
registration serves the four services whose tools run without a signed-in
user; Outlook, OneDrive, Excel, Word, Planner, and Teams never offer it and
token resolution refuses it for them (provider mismatch).

- Modes: client secret; certificate (PS256 client assertion with the
  certificate's x5t#S256 thumbprint, aud = the tenant token endpoint, 5-minute
  lifetime; key/certificate pairing, RSA size, and expiry checked before any
  request); keyless federated credential (a Sim workload-identity token for the
  credential's own subject, audience api://AzureADTokenExchange, subject bound
  to the saving user, refused if the app also accepts an unheld subject).
- The token resource comes from the tool's service, passed from the
  server-validated tool policy: Graph for Entra ID and SharePoint, the stored
  environment for Dataverse and Dynamics 365. Any other service is refused.
- The environment URL is stored on the credential (public-cloud Dynamics
  hosts, https root only). Dataverse tools take credentialType and instanceUrl
  as authoritative params; an app credential's request goes only to its own
  environment and a block environment that names another is refused. OAuth
  requests keep the block environment exactly as before. Dynamics 365 already
  matched instanceUrl against the block environment.
- Save check: a Graph token reading the app's own service principal (no
  permission needed), plus Dataverse WhoAmI when an environment is set. Entra
  AADSTS codes map to actionable copy; runtime failures keep the shared
  invalid_credentials / provider_unavailable split. Tokens cache per
  credential, resource, and stored-secret version in an LRU.
- Azure AD Reset password refuses app-only credentials: Graph does not
  support application permissions for resetPassword.
- Connect dialog with tenant ID, client ID, environment URL, and the
  issuer/audience/subject to paste; docs page with per-mode setup.

* fix(credentials): close wave 3 review findings on Microsoft app credentials

- SharePoint selectors (workspace and organization paths) and knowledge
  connectors now pass the service they resolve for (the selector's own
  serviceId, the connector's declared auth provider), so a Microsoft app
  credential browses SharePoint with a Graph token instead of being refused.
  The resolution option is renamed from toolService to service; it is only
  ever set from server-side policy.
- AADSTS700211 and 700212 are no longer mapped: Microsoft documents only
  70021 and 700213 for federated credential mismatches. Other keyless
  refusals still name the mismatch.
- DocuSign minter test mocks carry account_name, and the connector
  access-token test narrows its tokens with a guard instead of non-null
  assertions.

* feat(credentials): keyless Google Cloud credentials via Workload Identity Federation

A keyless mode of the existing Google service account credential
(google-service-account) for Google Cloud APIs. JSON-key credentials, their
connect flow, and their token minting are unchanged.

- Flow: Sim signs a short-lived token for the credential's own subject with
  aud = the customer's provider resource name
  (https://iam.googleapis.com/projects/<number>/locations/global/
  workloadIdentityPools/<pool>/providers/<provider>), exchanges it at
  sts.googleapis.com/v1/token (token-exchange, jwt subject token,
  cloud-platform scope, audience //iam.googleapis.com/...), then calls
  iamcredentials generateAccessToken for the service account with the tool's
  scopes and a 1h lifetime. Impersonation rather than direct federated access:
  some Cloud APIs reject federated tokens, and the service account keeps the
  same identity and roles a JSON key uses.
- Inputs validated against Google's documented formats before any request:
  numeric project number, pool/provider IDs (4-32 [a-z0-9-], no gcp-), and a
  user-managed *.iam.gserviceaccount.com email, so nothing can steer the STS
  audience or IAM URL. Requests never follow redirects.
- Scope: refuses domain-wide delegation (an impersonated user) and any service
  but BigQuery and Vertex AI before reading further or contacting Google, with
  a new shared unsupported_use code and copy pointing to a JSON key. Workspace
  blocks never offer the mode.
- Save check: STS exchange, impersonation, and tokeninfo (no IAM role or
  enabled API needed), plus a probe that a subject no credential holds cannot
  impersonate the service account. Subject bound to the saving user with the
  signed proof; kept across reconnects.
- STS/IAM failures map to actionable copy (provider settings, issuer
  reachability, the exact workloadIdentityUser binding) without Google's
  descriptions or any token; runtime keeps the invalid_credentials /
  provider_unavailable split. Tokens cache per credential, secret version, and
  scopes in an LRU.
- Per-credential audiences: audiences.ts gains google without a fixed
  audience; the setup response omits audience for it and reports a keyless
  reconnect. Connect dialog shows the issuer, audience, subject, and filled-in
  gcloud commands (pool, OIDC provider with attribute condition on the
  subject, IAM binding); docs section added.

* fix(credentials): close wave 4 review findings on keyless Google Cloud credentials

- Drop the tokeninfo check: a token from generateAccessToken already proves
  the provider accepted Sim's token and the impersonation binding, and
  tokeninfo puts a live token in a query string.
- Test that saving fails closed when the stray-subject pin probe hits an STS
  outage or an unreachable IAM endpoint.
- Pass --project=<project number> on all three gcloud setup commands, drop
  the unused Resource Manager API from the docs, and raise the shared
  validation error for an empty scope list.

* fix(credentials): keep Snowflake, Microsoft app, and Google keyless subjects unique

The subject-uniqueness scan from #9866 covered only AWS and Databricks. It now
also scans Snowflake, Microsoft app, and Google service account credentials,
so no two live credentials of an owner hold one keyless subject. Their keyed
secrets that carry no config (a Google JSON key, a Snowflake programmatic
access token) hold no subject and no longer read as unreadable. The three
keyless save paths report a newly bound subject so create checks it.

* fix(credentials): close round-1 CI and review findings on alt auth

- Knowledge connector tests expect the service now passed with Google
  service-account token requests; a new test proves a JSON-key mint signs the
  same request with or without it.
- Re-record the tool-registry-boundary baseline: the keyless Snowflake,
  Microsoft app, and Google modules add 5 modules behind credential-service,
  which tipped credit-usage past its allowance on top of staging's drift.
- Docs compatibility baseline: record the new code examples in the Google and
  Snowflake service account pages.
- Generate the Microsoft certificate test fixture at run time instead of
  committing a PEM key and certificate.
- A Microsoft app credential reconnect opens on keyless only when the stored
  credential is federated (the prepare step now reports it), like Snowflake;
  new connections still default to keyless.
- TSDoc on the Google project number, pool/provider ID, and service account
  email validators.

* fix(credentials): offer keyless Google setup only in a Google Cloud context

Reconnecting a keyless Google service account from a Workspace integration
(Gmail, Drive, ...) offered the keyless form, saving a credential that
integration can never use. The dialog now offers keyless only for a Google
Cloud service (new or reconnect) or on the credential's own page; from a
Workspace integration it explains the credential works only with BigQuery and
Vertex AI and does not save. The chat connect control passes its service so
it gets the same gate.

* test(credentials): match the stable part of the subject-in-use refusal
@gitguardian

gitguardian Bot commented Nov 11, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
39921752 Triggered Generic High Entropy Secret 9ab4e6f apps/sim/lib/credentials/google-workload-identity.server.test.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

…d Sim MCP (#9875)

Keyless (workload identity) credentials could only be set up in the web
connect dialog. Platform teams scripting their cloud trust now get the
same two steps as an API:

1. POST /api/v2/credentials/workload-identity/subjects
   (`sim credentials keyless-subject <providerId>`) issues a random,
   per-credential subject and a 24-hour proof bound to the caller, plus
   the keylessConfig to fill in and the provider's required fields.
2. The existing create (`sim credentials create … --credentials
   @keyless.json`) submits the completed config; Sim verifies the trust
   before saving.

- The existing prepare use case now also admits personal API keys and
  OAuth tokens with api:write. Workspace keys and delegated callers stay
  refused, because the subject must belong to a person.
- Every keyless refusal carries one stable `error.details.code`
  (WORKLOAD_IDENTITY_NOT_CONFIGURED, _PROOF_EXPIRED, _PROOF_INVALID,
  _SUBJECT_REQUIRED, _SUBJECT_IN_USE, _SUBJECT_UNVERIFIABLE) on a single
  refusal error, from create, reconnect and the subjects route alike.
- The provider catalog advertises a `keyless` object (authType and
  required/optional config fields) only when the deployment can sign,
  and the CLI reads it instead of keeping its own provider table.
  Snowflake's keyless config is now accepted by the CLI.
- Provider hints, OpenAPI, MCP tool descriptions, CLI help and the five
  credential docs pages describe the flow the same way.

- AWS and Databricks keyless create and reconnect also refuse with
  WORKLOAD_IDENTITY_NOT_CONFIGURED when the deployment cannot sign, and
  their verification steps pass keyless refusals through unchanged. Each
  provider's refusal names a fallback credential type.
- The Google Cloud keyless hint says it covers Cloud services (BigQuery,
  Vertex AI), not Google Workspace.
@waleedlatif1 waleedlatif1 changed the title v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, workspace members endpoint, trigger health and pinned versions, HTTP(S)_PROXY support, search source permissions v0.9.53: saved credentials with keyless AWS and Databricks OIDC, read-only API keys, workspace members endpoint, trigger health and pinned versions, proxy support, search source permissions Nov 11, 2026
…saved-credential contract on every credential block (#9878)

* fix(credentials): enforce the saved-credential contract on every credential block, reopen key-based reconnects on their mode, and tighten fixtures and skills

- check-block-registry runs the saved-credential contract for every block with a
  saved-credential picker, not only successors; the per-block contract moves to
  saved-credential-contract.ts with tests. The host probe gains a path-shaped id and a
  per-param shape so mixed-validation builders (Jama, Higgsfield) are probed, not guessed.
- Blocks the widened check caught: Coda, Harmonic, and Oracle Database tools declare
  credentialKind 'service-account'; NetSuite pins instanceUrl in authoritativeParams;
  Vanta's assetExternalAccountId is a filter, not a host.
- prepareWorkloadIdentity covers every keyless provider (AWS, Databricks added) and
  reuses audiences.ts for the provider-id mapping; the AWS and Databricks dialogs reopen
  a reconnect on the stored mode.
- Snowflake keyless verification maps an egress refusal to host_not_allowed.
- Neutral token fixtures in new credential tests; subject uniqueness TSDoc; skills
  describe native token blocks, the picker shape, keyless setup, and the successor naming
  exception.

* docs(credentials): fix keyless setup instructions and copy

- AWS: enter the Role ARN before setting the trust policy, so it never carries
  the <AWS_ACCOUNT_ID> placeholder IAM rejects (dialog hint and docs).
- Databricks: the federation-policy command needs an account-level CLI login.
- Google: a valid sample pool ID, ID format hints, user-managed service accounts
  only, skip the pool command when the pool exists, and the API list from
  Google's guide (adds Resource Manager).
- Bitbucket: workspace and project access tokens need Premium; repository
  tokens do not.
- Snowflake: rename SIM_WORKFLOWS if it exists; USERADMIN runs the statement.
- Microsoft keyless dialog says where the federated credential goes.
- Server errors name the trust values for API and CLI callers too; Google
  Workspace and Account ID wording; AWS catalog hint mentions stsRegion; the
  CLI keyless example uses aws-service-account throughout.
- Self-hosting env vars list every keyless provider.

* fix(credentials): reopen every keyless-capable reconnect on its stored auth mode and reject native credential tools without a policy

- prepareWorkloadIdentity reports the reconnected credential's stored authType
  (reconnectAuthType), for an admin of a credential in the same workspace, whether or
  not this deployment can sign. reconnectsKeyless is removed from the contract and hook.
  The AWS, Databricks, Snowflake, Microsoft and Google dialogs reopen on that mode, so
  an assume_role, personal_access_token or certificate credential keeps its auth.
- The saved-credential contract fails a native credential block whose tool has no
  oauth policy: the executor would never fill its token.
- The Snowflake hint and docs say where to change SIM_WORKFLOWS; the DocuSign
  account_not_found message covers a credential with no Account ID; the docs
  baseline's reviewed CLI example entries carry their new byte length; the contract
  test fixtures use a real OAuth service id.

This branch was previously deployed

1 inactive (outdated) deployment
Preview — 219b39ca Deployed Nov 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants