Skip to content

Block viewer workspace mutations; enforce tenant ownership references - #175

Merged
GoddeyUwamari merged 2 commits into
mainfrom
fix/workspace-mutation-security
Oct 5, 2026
Merged

GoddeyUwamari merged 2 commits into
mainfrom
fix/workspace-mutation-security

Conversation

@GoddeyUwamari

Copy link
Copy Markdown
Owner

Two commits. (1) requireMember on POST/DELETE /api/teams, POST/DELETE /api/deployments, POST/DELETE /api/infrastructure, POST /api/infrastructure/sync-aws and POST /api/services/discover, so viewers get 403 while member/admin/owner behavior is unchanged; /api/services/discover shares the existing per-organization discovery limiter, and sync-aws gets its own 10/hour/org limiter. (2) team_id on POST /api/services and service_id on POST /api/infrastructure are validated against the caller's organization in the same statement as the insert; foreign and nonexistent ids return the same 404. No policy changes, no frontend, schema, webhook or IAM changes.

GoddeyUwamari and others added 2 commits October 5, 2026 14:01
Creating and deleting teams, deployments and infrastructure rows, the AWS
cost sync, and POST /api/services/discover checked only that the caller was
authenticated, so a viewer could do all of them. They now require owner,
admin or member (requireMember, on the caller's current membership role).
Nothing changes for those three roles.

POST /api/services/discover also takes the existing discoveryRateLimiter,
so it shares the per-organization discovery budget with
POST /api/aws-resources/discover instead of being unlimited.

POST /api/infrastructure/sync-aws gets a per-organization limit of its own:
a sync whose result is not already cached makes a billed Cost Explorer call.

The GitHub webhook and the scheduled discovery job carry no user and do not
pass through these checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
POST /api/services stored whatever team_id it was given, and
POST /api/infrastructure whatever service_id: the foreign key only proved
the row existed somewhere, so a service could point at another
organization's team and a resource at another organization's service.

Both inserts now select from the referenced row with the caller's
organization in the predicate, in the same statement as the insert (the
shape the deployments insert already uses). An id that belongs to another
organization is answered exactly like one that exists nowhere: 404, same
body, nothing written.

The organization still comes only from the authenticated request. An id
that exists nowhere used to surface as a foreign-key 500; it is now the
same 404.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
devcontrol Ready Ready Preview Oct 5, 2026 6:32pm UTC

@GoddeyUwamari
GoddeyUwamari merged commit 5541cd4 into main Oct 5, 2026
6 checks passed

This branch was successfully deployed

1 active deployment
Preview — 8bdd8bde Deployed Oct 5, 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.

1 participant