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
- Valid authenticated workspace member.
- Valid member token in
X-SQLBOT-TOKEN: Bearer <member_token>.
- 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:
- member denied;
- workspace admin allowed.
Reference
d640ac31d1ce64ce90e06cf7081163915c9fc28cSummary
The fix for
CVE-2025-15597addedws_adminauthorization to the main training and terminology management routes, but did not add the same check to:GET /api/v1/system/data-training/exportGET /api/v1/system/terminology/exportAny authenticated workspace member can call these routes directly with a valid
X-SQLBOT-TOKENand export workspace data scoped tocurrent_user.oid.Preconditions
X-SQLBOT-TOKEN: Bearer <member_token>.ws_adminprivilege required.Reproduction
Use a token issued by a normal browser session. The frontend encrypts credentials before calling
/api/v1/login/access-token.Observed Result
/api/v1/system/data-training/exportreturns an XLSX file containing workspace training data, includingquestion,description,datasource_name, andadvanced_application_name./api/v1/system/terminology/exportreturns an XLSX file containing workspace terminology data, includingword,other_words,description,datasource,all_data_sources, andadvanced_application_name.Expected Result
Non-
ws_adminmembers should receive a permission error consistent with the corresponding management page endpoints.Root Cause
The export handlers accept an authenticated
CurrentUserbut do not enforcerequire_permissions(... ws_admin ...).backend/apps/system/middleware/auth.pybackend/common/core/deps.pybackend/apps/data_training/api/data_training.pybackend/apps/terminology/api/terminology.pyThe 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.pybackend/apps/terminology/curd/terminology.pyThese shared query builders do not filter on
enabled == True.Existing Access-Control Boundary
The corresponding features are already treated as workspace-admin functionality:
ws_admin,/set/trainingand/set/professional,/exportAPIs 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/exportGET /api/v1/system/terminology/exportAdd regression coverage for: