Skip to content

Incomplete Fix for CVE-2025-15597: Non-Admin Workspace Export #1336

Description

@arbor-s

Reference

  • Original fix commit: d640ac31d1ce64ce90e06cf7081163915c9fc28c

Summary

The fix for CVE-2025-15597 added ws_admin authorization to the main training and terminology management routes, but did not add the same check to:

  • GET /api/v1/system/data-training/export
  • GET /api/v1/system/terminology/export

Any authenticated workspace member can call these routes directly with a valid X-SQLBOT-TOKEN and export workspace data scoped to current_user.oid.

Preconditions

  1. Valid authenticated workspace member.
  2. Valid member token in X-SQLBOT-TOKEN: Bearer <member_token>.
  3. No ws_admin privilege required.

Reproduction

Use a token issued by a normal browser session. The frontend encrypts credentials before calling /api/v1/login/access-token.

GET /api/v1/system/data-training/export
X-SQLBOT-TOKEN: Bearer <member_token>
GET /api/v1/system/terminology/export
X-SQLBOT-TOKEN: Bearer <member_token>

Observed Result

  • /api/v1/system/data-training/export returns an XLSX file containing workspace training data, including question, description, datasource_name, and advanced_application_name.
  • /api/v1/system/terminology/export returns an XLSX file containing workspace terminology data, including word, other_words, description, datasource, all_data_sources, and advanced_application_name.

Expected Result

Non-ws_admin members should receive a permission error consistent with the corresponding management page endpoints.

Root Cause

The export handlers accept an authenticated CurrentUser but do not enforce require_permissions(... ws_admin ...).

backend/apps/system/middleware/auth.py

validate_pass, data = await self.validateToken(token, trans)
if validate_pass:
    request.state.current_user = data
    return await call_next(request)

backend/common/core/deps.py

async def get_current_user(request: Request) -> UserInfoDTO:
    return request.state.current_user

backend/apps/data_training/api/data_training.py

@router.get("/page/{current_page}/{page_size}")
@require_permissions(permission=SqlbotPermission(role=['ws_admin']))
async def pager(...)

@router.get("/export")
async def export_excel(...)

backend/apps/terminology/api/terminology.py

@router.get("/page/{current_page}/{page_size}")
@require_permissions(permission=SqlbotPermission(role=['ws_admin']))
async def pager(...)

@router.get("/export")
async def export_excel(...)

The export routes call the same shared management query builders used by the admin list routes, with pagination disabled:

backend/apps/data_training/curd/data_training.py

def page_data_training(...):
    build_data_training_query(..., True, ...)

def get_all_data_training(...):
    build_data_training_query(..., False, ...)

backend/apps/terminology/curd/terminology.py

def page_terminology(...):
    build_terminology_query(..., True, ...)

def get_all_terminology(...):
    build_terminology_query(..., False, ...)

These shared query builders do not filter on enabled == True.

Existing Access-Control Boundary

The corresponding features are already treated as workspace-admin functionality:

  • backend page endpoints require ws_admin,
  • frontend routes are under /set/training and /set/professional,
  • frontend route generation and route guards only expose those pages to admins / workspace admins,
  • frontend export actions call the same unprotected /export APIs from those admin pages.

Impact

An authenticated non-admin workspace member can export bulk training and terminology data from the current workspace and bypass the intended admin-only management boundary.

Remediation

Add:

@require_permissions(permission=SqlbotPermission(role=['ws_admin']))

to:

  • GET /api/v1/system/data-training/export
  • GET /api/v1/system/terminology/export

Add regression coverage for:

  1. member denied;
  2. workspace admin allowed.

Metadata

Metadata

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions