Skip to content

[Security] Require privileged role for workspace renames#38149

Open
gauravbsinghal wants to merge 1 commit into
langgenius:mainfrom
gauravbsinghal:niro/workspace-settings-auth-tc-a737c3e6
Open

[Security] Require privileged role for workspace renames#38149
gauravbsinghal wants to merge 1 commit into
langgenius:mainfrom
gauravbsinghal:niro/workspace-settings-auth-tc-a737c3e6

Conversation

@gauravbsinghal

Copy link
Copy Markdown

Summary

Require admin or owner privileges before accepting workspace rename requests through the console workspace settings endpoint. This prevents normal workspace members from reaching the tenant mutation path while preserving owner/admin behavior.

Consequence

A non-admin workspace member can rename the shared workspace, altering tenant-wide metadata visible to every member without admin or owner approval.

Reproduction

Workspace rename authorization bypass — actor: a normal workspace member (substitute your own equivalent)

  1. Authenticate the normal workspace member:

    curl -i -X POST "$TARGET/console/api/login" \
      -H 'Content-Type: application/json' \
      --data '{"email":"<normal-member-email>","password":"<base64-normal-member-password>","remember_me":true}'

    -> Observed login success with access, refresh, and CSRF cookies.

  2. Attempt to rename the shared workspace as that normal member:

    curl -i -X POST "$TARGET/console/api/workspaces/info" \
      -H 'Content-Type: application/json' \
      -H 'Authorization: Bearer <normal-member-access-token>' \
      -H 'X-CSRF-Token: <normal-member-csrf-token>' \
      --cookie 'access_token=<normal-member-access-token>; refresh_token=<normal-member-refresh-token>; csrf_token=<normal-member-csrf-token>' \
      --data '{"name":"Niro Tenant Alpha POC1"}'

    -> Observed vulnerable output: HTTP 200 with {"result":"success","tenant":{"name":"Niro Tenant Alpha POC1","role":"normal",...}}.

  3. Control — read the workspace as an owner:

    curl -i -X POST "$TARGET/console/api/workspaces/current" \
      -H 'Content-Type: application/json' \
      -H 'Authorization: Bearer <owner-access-token>' \
      -H 'X-CSRF-Token: <owner-csrf-token>' \
      --cookie 'access_token=<owner-access-token>; refresh_token=<owner-refresh-token>; csrf_token=<owner-csrf-token>' \
      --data '{}'

    -> Observed owner output showed the shared workspace name changed to Niro Tenant Alpha POC1.

Expected secure: the normal member receives 403/permission denial and the workspace name remains unchanged; an owner can still rename the workspace.

What changed

  • Added the existing is_admin_or_owner_required guard to WorkspaceInfoApi.post before tenant lookup and mutation.
  • Documented the endpoint's 403 response in the Flask-RESTX route metadata.
  • Added route-level regression coverage for normal-member denial and owner success.

Regression test

api/tests/unit_tests/controllers/console/workspace/test_workspace.py::TestWorkspaceInfoApi now asserts that a normal workspace member receives Forbidden, no tenant is loaded, no commit occurs, and the tenant name stays unchanged. The same group includes an owner control that proves privileged users can still rename the workspace.

Validation

Red on unfixed code:

FAILED api/tests/unit_tests/controllers/console/workspace/test_workspace.py::TestWorkspaceInfoApi::test_post_rejects_normal_member_without_mutating_workspace
E           Failed: DID NOT RAISE <class 'werkzeug.exceptions.Forbidden'>
1 failed, 3 passed, 3 warnings in 28.30s

Green after the fix:

api/tests/unit_tests/controllers/console/workspace/test_workspace.py::TestWorkspaceInfoApi
4 passed, 3 warnings in 26.79s

Additional focused validation:

api/tests/unit_tests/controllers/console/workspace/test_workspace.py
32 passed, 3 warnings in 27.70s

Scoped Ruff checks passed for both touched files:

uv run --project api --dev ruff format --check api/controllers/console/workspace/workspace.py api/tests/unit_tests/controllers/console/workspace/test_workspace.py
uv run --project api --dev ruff check api/controllers/console/workspace/workspace.py api/tests/unit_tests/controllers/console/workspace/test_workspace.py

Full suite

make test passed: main backend unit phase 12777 passed, 5 skipped, 601 warnings in 269.52s; controller phase 3016 passed, 1 skipped, 6 warnings in 212.03s.

Residual risk

This PR proves the console workspace rename route authorization at unit level and the existing backend unit suite. It does not add an end-to-end browser/API harness test.

Pentested and fixed by Niro

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS This PR changes 0-9 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant