fix(api): PostTemplatesTags queries database and begins transaction before team ownership check (tag enumeration oracle)
还没有人认领这个 Issue。
评估
- 难度
- 2/5
- 预计耗时
- 1-3 小时
- 新手友好度
- 76/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 冷清
- 技术栈
- go, postgres
调研方向
从 packages/api/internal/handlers/template_tags.go 中的 PostTemplatesTags 开始,将其授权顺序与 GetTemplatesTemplateIDTags 和 DeleteTemplatesTags 进行比较。然后检查或添加 packages/api/internal/handlers/template_tags_test.go 中针对该问题的用例,并运行相关的 API 测试。未授权请求能够一致地返回 403,且不会查询 tag 或启动事务,即表示完成。
由索引模型根据 Issue 内容生成。
描述
Problem
In POST /templates/tags (packages/api/internal/handlers/template_tags.go), the control plane handler initiates a database transaction via a.sqlcDB.WithTx(ctx) and executes client.GetTemplateWithBuildByTag before validating whether the authenticated user's team owns the requested template (if aliasInfo.TeamID != team.ID).
This ordering defect causes two distinct issues:
-
Tag Enumeration / Information Disclosure Oracle:
When a user sendsPOST /templates/tagstargeting a template owned by a different team:- If the requested
tagdoes not exist on that template:GetTemplateWithBuildByTagreturnssql.ErrNoRows, which triggersErrTemplateNotFound, responding with404 Not Found("Template '<target>' with tag '<tag>' not found"). - If the requested
tagexists on that template: the query succeeds, and only afterwards at line 115 isaliasInfo.TeamID != team.IDchecked, responding with403 Forbidden("You don't have access to sandbox template '%s'").
An unauthorized tenant can therefore determine whether arbitrary private build tags (e.g.,staging,v2.0.0-rc1,hotfix) exist on other teams' templates by probing the endpoint and observing the difference between HTTP404and HTTP403.
- If the requested
-
Unnecessary Database Load and Transaction Allocation:
Every unauthorized or invalid tag request against another tenant's template allocates a Postgres transaction connection from the pool (WithTx), acquires read locks, executes queries, and then rolls back upon failure.
Root Cause
In packages/api/internal/handlers/template_tags.go:L75-L122, tag resolution and database transaction creation precede the team ownership authorization check:
// Current flow in template_tags.go:
aliasInfo, err := a.templateCache.ResolveAlias(ctx, templateID)
if err != nil { ... }
// BUG: Transaction started and DB queried BEFORE checking aliasInfo.TeamID == team.ID
txErr := a.sqlcDB.WithTx(ctx).Exec(func(queries *queries.Queries) error {
build, err := queries.GetTemplateWithBuildByTag(ctx, ...)
if err != nil {
return ErrTemplateNotFound // Returns 404 to unauthorized caller if tag missing
}
...
if aliasInfo.TeamID != team.ID {
return a.sendAPIStoreError(c, http.StatusForbidden, ...) // Returns 403 only if tag exists
}
})
In contrast, peer handlers such as DeleteTemplatesTags (template_tags.go:L139) and GetTemplatesTemplateIDTags (template_tags.go:L21) enforce if aliasInfo.TeamID != team.ID immediately after alias resolution:
| Handler | Authorization Check Timing | Tag Enumeration Vulnerable? |
|---|---|---|
GET /templates/{templateID}/tags |
Immediate after ResolveAlias |
No |
DELETE /templates/tags |
Immediate after ResolveAlias |
No |
POST /templates/tags (Current) |
Deferred after GetTemplateWithBuildByTag DB query |
Yes (404 vs 403 Oracle) |
POST /templates/tags (Expected) |
Immediate after ResolveAlias |
No (403 Forbidden) |
Reproduction Steps
- Create a template
template-Aunderteam-1with tagv1.0.0. - Authenticate as an unrelated user
team-2. - Send
POST /templates/tagstargetingtemplate-Awithtag: "v9.9.9"(non-existent).- Observed:
HTTP 404 Not Found({"code": 404, "message": "Template 'template-A' with tag 'v9.9.9' not found"})
- Observed:
- Send
POST /templates/tagstargetingtemplate-Awithtag: "v1.0.0"(existing tag).- Observed:
HTTP 403 Forbidden({"code": 403, "message": "You don't have access to sandbox template 'template-A'"})
- Observed:
- Expected: Both requests must return
HTTP 403 Forbiddenwithout leaking metadata about tag existence.
Technical Context
- File affected:
packages/api/internal/handlers/template_tags.go - Subsystem: Control Plane API / Templates & Tags
- Impact: Medium (Security/Privacy: cross-tenant metadata disclosure & unnecessary database transaction overhead)
Proposed Changes
| # | Change | File(s) Affected | Complexity |
|---|---|---|---|
| 1 | Move if aliasInfo.TeamID != team.ID check immediately after ResolveAlias before WithTx |
packages/api/internal/handlers/template_tags.go |
Trivial |
| 2 | Add unit test TestPostTemplatesTags_RejectsOtherTeamTemplate verifying 403 Forbidden |
packages/api/internal/handlers/template_tags_test.go |
Low |
- 主要语言
- Go
- 星标
- 1.6k
- 派生
- 438
- PR 合并指标
- 30 天内没有已合并 PR
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
e2b-dev/runtime 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 88/100
-
sandbox cache: StartRemoving state transition not broadcast, all allocations see stale Running state 未关闭
难度 2/5 1-3 小时 新手友好度 86/100
-
难度 1/5 1 小时以内 新手友好度 86/100
-
难度 2/5 1-3 小时 新手友好度 86/100
-
难度 2/5 1-3 小时 新手友好度 88/100
相似的 Issue
-
难度 1/5 1 小时以内 新手友好度 84/100
-
enhancement needs triage
难度 2/5 1-3 小时 新手友好度 68/100
-
kind/cleanup
难度 2/5 1-3 小时 新手友好度 88/100
kubernetes-sigs/kueue#15947 ·
-
难度 2/5 1-3 小时 新手友好度 78/100
sympozium-ai/sympozium#627 ·
-
难度 2/5 1-3 小时 新手友好度 86/100