Skip to content

fix(api): conceal cross-tenant resource denials on internal routes - #6586

Merged
waleedlatif1 merged 1 commit into
stagingfrom
fix/concealment-parity
Aug 12, 2026
Merged

fix(api): conceal cross-tenant resource denials on internal routes#6586
waleedlatif1 merged 1 commit into
stagingfrom
fix/concealment-parity

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • createV2ResourceConcealmentPolicy rewrites DelegatedWorkspaceAuthorizationError, NoWorkspaceAccessError, and WorkspaceApiKeyScopeAuthorizationError to a 404 on v2 single-resource routes. The internal routes reach the same application use cases and still answered 403, so the existence probe the v2 policy closes still worked from the other surface.
  • Added createInternalResourceConcealmentPolicy (and concealCrossTenantResourceError for the raw withRouteHandler routes) beside the v2 factory, and renamed v2-resource-concealment.tsresource-concealment.ts since it now owns both surfaces. The concealed failure is re-projected through each domain's existing base policy as a not_found orchestration error, so every body shape (success: false, the legacy workflow code field, the table 423 lock branch) is exactly what that route's real 404 already returns.
  • Same-workspace denials are untouched: InsufficientWorkspacePermissionsError, PersonalApiKeysDisabledError, WorkspaceApiKeyAuthorizationError, and PrincipalKindAuthorizationError all stay 403 on both surfaces, and generic OrchestrationError('forbidden', …) is never classified by message.

Routes whose status changed (cross-tenant denial only: 403404)

RouteConcealed as
GET/PUT/DELETE /api/workflows/[id]Workflow not found
GET/POST/PATCH/DELETE /api/workflows/[id]/deployWorkflow not found
GET /api/workflows/[id]/deploymentsWorkflow not found
GET/PATCH /api/workflows/[id]/deployments/[version]Workflow not found
PATCH/DELETE /api/workspaces/[id]/files/[fileId]File not found
PUT /api/workspaces/[id]/files/[fileId]/contentFile not found
GET/PUT /api/workspaces/[id]/files/[fileId]/shareFile not found
POST /api/table/imports, POST /api/table/[tableId]/exportsTable not found
POST/PATCH/DELETE /api/table/[tableId]/groupsTable not found
GET/DELETE /api/table/imports/[importId], POST .../parts, POST .../completeTable import not found
GET/DELETE /api/table/exports/[exportId], GET .../downloadTable export not found
/api/knowledge/[id]/** (read/update/delete/restore, documents, chunks, tags, connectors, uploads, upsert) and POST /api/knowledge/searchKnowledge base not found

Deliberately left at 403

  • Workspace-level list/createGET/POST /api/knowledge name no resource, so a 403 there betrays nothing. Concealing them would turn a denied workspace into a phantom empty list.
  • /api/mcp/**, /api/skills/**, /api/tools/custom, /api/logs/[id], workflow runs/execute/resume — the v2 routes for these use v2-only use cases with no internal counterpart sharing the symbol, so there is no asymmetry to close. Several of them are also where a flip would regress: hooks/queries/mcp.ts:92,127 and hooks/queries/workflow-mcp-servers.ts:60,130 swallow 404 into [], so a denied user would see a "no servers configured" empty state instead of an error, and deploy-modal/components/mcp/mcp.tsx:400 auto-recreates a tool on 404.
  • GET /api/workspaces/[id]/host-context — untouched. workspace-host-provider.tsx:29 is the one surface that renders a dedicated <WorkspaceAccessDenied /> on 403; flipping it would drop a revoked user into a stale workspace tree.

UI impact of what did change

I swept hooks/** and app/** for 403/404 branching on the affected families. Nothing structural flips:

  • knowledge/[id]/base.tsx:1233 already renders "Knowledge base not found" for both statuses.
  • kb/knowledge.ts:113 rewrites 404 to "Document not found" — message-only change, same error layout.
  • tables/[tableId]/error.tsx copy already hedges "deleted or you may not have permission".
  • Table and custom-tool toasts render error.message verbatim, so a denied write now reads "Table not found" instead of "Insufficient workspace permissions".
  • logs.ts:208 skips retry on 404 — one fewer retry before the same error state.

Type of Change

  • Bug fix

Testing

bun run type-check, bunx vitest run app/api lib/api (342 files / 3257 tests), bun run lint, bun run check:api-validation — all green.

Tests pin the concealed status per policy and per route: resource-concealment.test.ts now covers every internal policy alongside the v2 ones, and route-level tests drive the real handlers for file rename/share/content, workflow PUT, and deployment-version PATCH. Table/group route tests assert the wiring. Verified they can fail — stubbing concealCrossTenantResourceError to a passthrough turns 60 tests red, and un-wiring a single table route fails its wiring assertion with expected { kind: 'plain' } to be { kind: 'conceal-import' }.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel

vercelBot commented Aug 12, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
docsSkippedSkippedAug 12, 2026 2:40am

Request Review

@cursor

cursorBot commented Aug 12, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Changes authorization error semantics across many internal API surfaces (workflows, tables, files, knowledge). This is security-hardening, but incorrect classification could leak existence signals or turn legitimate 403s into 404s.

Overview
Closes a cross-surface existence probe: internal single-resource routes previously returned 403 for cross-tenant denials while their v2 twins already returned 404.

Adds createInternalResourceConcealmentPolicy and concealCrossTenantResourceError beside the existing v2 factory (renamed into resource-concealment.ts), then wires them through workflow, table, file, and knowledge-base internal error policies. Cross-tenant failures (NoWorkspaceAccessError, WorkspaceApiKeyScopeAuthorizationError, DelegatedWorkspaceAuthorizationError) now project as domain-specific not-found responses; same-workspace role/key denials stay 403.

Workspace-level list/create and a few intentionally asymmetric surfaces are left unchanged.

Reviewed by Cursor Bugbot for commit c96b6b0. Configure here.

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@cursor review

@greptile-apps

greptile-appsBot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR extends the existing cross-tenant resource-concealment behavior from v2 endpoints to corresponding internal workflow, file, table, and knowledge routes.

  • Adds shared internal and raw-handler concealment helpers while preserving each route family’s existing error response shape.
  • Wires single-resource internal routes to domain-specific concealment policies while leaving workspace-level list/create operations unchanged.
  • Adds policy, route-level, and wiring tests covering concealed cross-tenant denials and unchanged same-workspace permission denials.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

FilenameOverview
apps/sim/lib/api/server/routes/resource-concealment.tsIntroduces shared internal and raw-handler concealment while preserving the existing v2 classification behavior.
apps/sim/lib/workflows/api/route-policies.tsApplies workflow-not-found concealment to internal single-workflow policies without changing same-workspace denials.
apps/sim/lib/knowledge/api/route-policies.tsConceals knowledge-base-scoped operations while deliberately retaining existing behavior for workspace-level list and create policies.
apps/sim/lib/table/api/route-policies.tsAdds internal table, group, import, and export concealment policies while retaining the table-lock response branch.
apps/sim/lib/workspace-files/api/internal-error-policies.tsAdds concealment variants for single-file and content operations while preserving specialized file errors.
apps/sim/app/api/workflows/[id]/route.tsWires declarative and raw workflow handlers through the new concealment behavior.
apps/sim/app/api/workflows/[id]/deployments/[version]/route.tsConceals cross-tenant failures in the raw deployment-version mutation before existing error classification.
apps/sim/app/api/table/table-transfer-routes.test.tsVerifies all internal table-transfer routes select the appropriate resource-specific concealment policy.
apps/sim/lib/api/server/routes/resource-concealment.test.tsCovers internal and v2 concealment, preserved same-workspace denials, specialized policy branches, and unconcealed list/create behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
Request[Internal or v2 resource request] --> UseCase[Authorized application use case]
UseCase --> Result{Outcome}
Result -->|Allowed| Success[Existing success response]
Result -->|Cross-tenant denial| Conceal[Resource concealment policy]
Conceal --> NotFound[Domain-specific 404 response]
Result -->|Same-workspace denial| Forbidden[Existing 403 response]
Result -->|Other domain error| BasePolicy[Existing domain error policy]
BasePolicy --> ExistingShape[Existing route-specific error response]
Loading

Reviews (3): Last reviewed commit: "fix(api): conceal cross-tenant resource ..." | Re-trigger Greptile

The v2 routes rewrite DelegatedWorkspaceAuthorizationError,
NoWorkspaceAccessError, and WorkspaceApiKeyScopeAuthorizationError to a
404 so a caller with no reach into a workspace cannot confirm a
resource exists. The internal routes reach the same application use
cases and still answered 403, so the same probe worked from the other
surface. Same-workspace role denials stay 403 on both.
@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@greptile

@waleedlatif1

Copy link
Copy Markdown
CollaboratorAuthor

@cursor review

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ 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 c96b6b0. Configure here.

@waleedlatif1
waleedlatif1 merged commit 083319b into stagingAug 12, 2026
30 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/concealment-parity branch August 12, 2026 02:50
Sign up for freeto 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.

1 participant

@waleedlatif1