fix(security): validate cloud region/project inputs and bind vertex credentials to the executing workspace - #6167
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryHigh Risk Overview Vertex and Bedrock providers now reject malicious
New unit tests cover validators, provider chokepoints, and workspace binding. Reviewed by Cursor Bugbot for commit 270ecbd. Configure here. |
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit f266e00. Configure here.
|
@cursor review |
|
@greptile review |
Greptile SummaryThe PR hardens cloud-provider execution by validating Vertex and Bedrock endpoint inputs before SDK construction and binding Vertex credentials to the executing workflow workspace.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains in the eligible follow-up review scope.
|
| Filename | Overview |
|---|---|
| apps/sim/executor/utils/vertex-credential.ts | Adds executing-workspace ownership enforcement before a Vertex credential can mint or refresh an access token. |
| apps/sim/lib/core/security/input-validation.ts | Adds constrained Google Cloud location and project validators and expands the AWS region validator for European Sovereign Cloud. |
| apps/sim/providers/vertex/index.ts | Normalizes and validates Vertex location and project inputs before constructing the authenticated Google SDK client. |
| apps/sim/providers/bedrock/index.ts | Validates the configured AWS region before deriving the Bedrock endpoint and inference profile. |
| apps/sim/executor/handlers/agent/agent-handler.ts | Supplies the executing workspace when resolving Vertex credentials for agent blocks. |
| apps/sim/executor/handlers/evaluator/evaluator-handler.ts | Supplies the executing workspace when resolving Vertex credentials for evaluator blocks. |
| apps/sim/executor/handlers/router/router-handler.ts | Supplies the executing workspace in both Vertex credential-resolution paths used by router blocks. |
Sequence Diagram
sequenceDiagram
participant Exec as Workflow Executor
participant Cred as Vertex Credential Resolver
participant Validate as Cloud Input Validators
participant SDK as Cloud Provider SDK
Exec->>Cred: credentialId, userId, workspaceId
Cred->>Cred: Check user access and workspace ownership
Cred-->>Exec: Access token
Exec->>Validate: Project, location, or region
Validate-->>Exec: Validated input
Exec->>SDK: Construct authenticated provider client
SDK-->>Exec: Provider response
Reviews (2): Last reviewed commit: "fix(security): accept AWS European Sover..." | Re-trigger Greptile
…redentials to the executing workspace
Two credential-exposure bugs on the Vertex AI path.
1. `vertexLocation` reached `new GoogleGenAI({ location })` unvalidated. The
SDK interpolates that value straight into the API hostname
(`https://${location}-aiplatform.googleapis.com/`), so a value like
`attacker.tld/x` terminates the authority component and relocates the
request — with the workspace's GCP bearer token attached by the auth
client — to an arbitrary host. Reachable from agent/router/evaluator
blocks and from `POST /api/guardrails/validate` with only a session
cookie. `vertexProject` (interpolated into the URL path) and the Bedrock
region (interpolated into the endpoint hostname) had the same shape of
problem.
Adds `validateGoogleCloudLocation` / `validateGoogleCloudProject` to the
shared input-validation module and applies them, plus the existing
`validateAwsRegion`, at the provider chokepoints. Every path to the SDK
goes through `executeRequest`, so no caller can bypass them.
`azureEndpoint` already routes through the DNS-pinning SSRF guard.
2. `resolveVertexCredential` enforced only the user↔credential predicate and
never the workflow-workspace↔credential predicate that
`authorizeCredentialUse` applies on the HTTP path. A credential held in
workspace B could be pasted into a workflow in workspace A and consumed
by workspace-A principals with no access to B — including on deployed
runs, where `enforceCredentialAccess` is false and `ctx.userId` is the
workflow owner rather than the trigger caller. Service-account
credentials mint a `cloud-platform`-scoped token, so this handed
workspace A the use of workspace B's GCP identity.
The resolver now takes the executing `workspaceId` and rejects a
credential belonging to a different workspace, mirroring
`credential-access.ts`. All four executor call sites pass
`ctx.workspaceId`.
Hostnames are case-insensitive, so a mixed-case location like US-Central1 reaches Google today. Lowercase it before the region check rather than rejecting an input that currently works.
…eAwsRegion eusc-de-east-1 is a real Bedrock-enabled region that the existing pattern did not cover, so applying the validator to bedrockRegion would have rejected a config that worked before. Adds the eusc-<country> partition.
f266e00 to
270ecbd
Compare
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 270ecbd. Configure here.
Summary
vertexLocationbefore it reaches@google/genai. The SDK builds its base URL as`https://${location}-aiplatform.googleapis.com/`— a literal interpolation into the hostname — soattacker.tld/xterminates the authority and the auth client still attachesAuthorization: Bearer <workspace GCP token>to the attacker's host. Runtime-confirmed against production viaPOST /api/guardrails/validate, which needs only a session cookie.vertexProject(interpolated into the URL path) and the Bedrockregion(interpolated into the endpoint hostname, now using the pre-existingvalidateAwsRegion).azureEndpointwas already routed through the DNS-pinning SSRF guard and needed no change.resolveVertexCredentialenforced only the user↔credential predicate;authorizeCredentialUsealso enforces workflow-workspace↔credential. A credential held in workspace B could be pasted into a workflow in workspace A and consumed by A's principals — including on deployed runs, whereenforceCredentialAccessis false andctx.userIdis the workflow owner rather than the trigger caller. Service-account credentials mint acloud-platform-scoped token, so this handed A the use of B's GCP identity.Details
Chokepoint. Two new validators (
validateGoogleCloudLocation,validateGoogleCloudProject) live next to the existingvalidateAwsRegionin the shared input-validation module and are applied in each provider'sexecuteRequest. There is exactly onevertexai: trueconstruction site in the repo, so every path — agent/router/evaluator/translate blocks,/api/providers,/api/guardrails/validate— goes through them. Nothing containing/,:,@,?,#, or whitespace can pass.Credential binding.
getCredentialActorContextresolves the workspace from the credential row itself, so it structurally cannot express the workflow-workspace predicate.resolveVertexCredentialnow takes the executingworkspaceId(params object, since the arg list grew) and rejects a mismatch with the same messagecredential-access.tsuses. All four call sites passctx.workspaceId, already in scope at each. Routing throughauthorizeCredentialUsedirectly was the other option but it needs aNextRequestthe executor does not have.Regression audit
ExecutionContext.workspaceIdis optional in the type, so the binding check is conditional — butexecution-core.ts:356throws when execution metadata lacks a workspace, so it is always present on real runs. No bypass by omission.credential.workspaceIdisNOT NULL, so there is no personal/unscoped credential the new check could wrongly reject.childWorkspaceId = childWorkflow.workspaceId) withchildUserId = loadUserId; the new check follows that boundary rather than fighting it. Regular child workflows already go throughassertChildWorkflowInWorkspace.US-Central1work today (DNS is case-insensitive), so the provider lowercases before validating instead of rejecting input that currently succeeds. Second commit.providers/vertexnorproviders/bedrockis imported from any client module, so pulling in the validation module adds nothing to a client bundle.check:client-boundarypasses.google.com:my-project) would be rejected by the project validator. They cannot be created anymore and none are expected here.Type of Change
Testing
providers/vertex/index.test.ts(new) — malicious locations and projects throw and never construct the client; valid values pass through; mixed case normalizes; theus-central1default still applies.executor/utils/vertex-credential.test.ts(new) — cross-workspace credential rejected before any token is minted; same-workspace still resolves; the user↔credential check still fires.lib/core/security/input-validation.test.ts— table-driven cases for both validators covering every authority-terminating character class.tsc -p apps/sim --noEmitclean,bun run lintclean,bun run check:api-validationpasses,check:client-boundarypasses. The one failing test in a wider run (executor/handlers/pi/cloud-review-tools.test.ts) fails identically onorigin/stagingand is unrelated.Checklist