Scope GetLastSnapshot query by teamID to prevent cross-team data access
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 1.6k
- Forks
- 438
- PR merge metrics
- No merged PRs in 30d
Description
Problem
The current GetLastSnapshot SQL query (packages/db/queries/snapshots/get_last_snapshot.sql) only filters by sandboxID without considering teamID. This means:
-
Security concern: The database returns snapshot data for any team's sandbox. Ownership is only validated after the query returns, via a post-fetch
TeamID != teamIDcheck. This pattern exists in 4 handlers:sandbox_connect.go:119sandbox_get.go:178sandbox_pause.go:97sandbox_resume.go:123
-
Unnecessary data exposure: Even though the post-fetch check prevents unauthorized access, the snapshot data is still loaded from the database and cached in Redis without team scoping. A malicious actor could potentially exploit timing or cache behavior.
-
Cache key collision risk:
SnapshotCacheusessandboxIDas the sole cache key. If two teams somehow reference the same sandboxID (e.g., during snapshot sharing or migration), the cache could serve stale or incorrect data.
Proposed Solution
-
Add a new SQL query
GetLastSnapshotByTeamthat includesteamIDin the WHERE clause:WHERE s.sandbox_id = $1 AND s.team_id = $2 -
Add a
GetByTeam(ctx, sandboxID, teamID)method toSnapshotCacheusing a composite cache key (sandboxID:teamID). -
Migrate the 4 handlers to use the new method, removing the post-fetch ownership checks.
-
Update
Invalidateto handle both key formats during the migration period.
Affected Code
packages/db/queries/snapshots/get_last_snapshot.sqlpackages/api/internal/cache/snapshots/snapshot_cache.gopackages/api/internal/handlers/sandbox_connect.gopackages/api/internal/handlers/sandbox_get.gopackages/api/internal/handlers/sandbox_pause.gopackages/api/internal/handlers/sandbox_resume.go
References
- Internal tracking: ENG-3544
- All 4 handlers have the same TODO comment:
// TODO: ENG-3544 scope GetLastSnapshot query by teamID to avoid post-fetch ownership check.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/db/queries/snapshots/get_last_snapshot.sql and packages/api/internal/cache/snapshots/snapshot_cache.go, then trace the TODOs in sandbox_connect.go, sandbox_get.go, sandbox_pause.go, and sandbox_resume.go. Done means team-scoped snapshot queries and cache access are used by all four handlers, ownership checks are removed, and Invalidate supports both cache key formats during migration.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, redis, sql
- Domain
- api, backend, databases, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100