trackslash
TRACK-48 P1

Members named access or blocks hijack the member-role route and mutate project settings

0
All issues

Description

Static route segments shadow the {username} param route, so a member-role POST for a user named access or blocks silently runs a different, project-wide action.

The routes

internal/server/ui_routes.go:159, :161, :162:

r.Post("/{owner}/projects/{key}/members/{username}", s.uiUpdateProjectMember)
r.Post("/{owner}/projects/{key}/members/access",     s.uiUpdateProjectAccess)
r.Post("/{owner}/projects/{key}/members/blocks",     s.uiBlockProjectUser)

chi resolves static segments before param segments, so the static patterns always win.

Steps to reproduce

  1. Add a project member whose username is access.
  2. On the members page, change that member's role and submit.
  3. The form posts to /{owner}/projects/{key}/members/access — the URL built by uiProjectMemberPath (internal/server/ui_paths.go:104-106), emitted by the member row form at internal/server/templates/project_member_page.html:54.

Expected

uiUpdateProjectMember sets the role of the member named access.

Actual

uiUpdateProjectAccess runs and changes the project's visibility level. A member named blocks hits uiBlockProjectUser the same way. Neither handler reads {username}, so the operator mutates project-wide settings while believing they changed one member's role.

Why it is reachable

access and blocks are both legal usernames. store.NormalizeUsername (internal/store/accounts.go:17-32) only enforces 3–32 chars of [a-z0-9_-], and there is no reserved-name list anywhere in the repo. Same applies to candidates on the GET side (ui_routes.go:157).

Fix

Move the collection-level actions out of the {username} namespace (.../member-access, .../member-blocks), and/or add a reserved-username list enforced at signup. Add route tests asserting a member named access gets uiUpdateProjectMember.

Sub-issues

0

Linked issues

0

GitHub

0

No branches or pull requests linked.

Comments

0
No comments.