diff --git a/packages/api/src/middleware/__tests__/tenant.spec.ts b/packages/api/src/middleware/__tests__/tenant.spec.ts index 6a78ad3098..ff9ddd1a1a 100644 --- a/packages/api/src/middleware/__tests__/tenant.spec.ts +++ b/packages/api/src/middleware/__tests__/tenant.spec.ts @@ -168,6 +168,20 @@ describe('tenantContextMiddleware', () => { expect(tenantId).toBe('tenant-y'); }); + it('rejects a normalized system tenant sentinel for authenticated requests', async () => { + const req = mockReq({ tenantId: ` ${SYSTEM_TENANT_ID} `, role: 'user' }); + const res = mockRes(); + const next: NextFunction = jest.fn(); + + await tenantContextMiddleware(req, res, next); + + expect(res.status).toHaveBeenCalledWith(403); + expect(res.json).toHaveBeenCalledWith({ + error: 'System tenant is not allowed for request-scoped routes', + }); + expect(next).not.toHaveBeenCalled(); + }); + it('different requests get independent tenant contexts', async () => { const runRequest = (tid: string) => { const req = mockReq({ tenantId: tid, role: 'user' }); diff --git a/packages/api/src/middleware/tenant.ts b/packages/api/src/middleware/tenant.ts index 41af195584..e194e42cd5 100644 --- a/packages/api/src/middleware/tenant.ts +++ b/packages/api/src/middleware/tenant.ts @@ -20,6 +20,7 @@ type ContextRequest = { }; const REQUEST_ID_HEADERS = ['x-request-id', 'x-correlation-id'] as const; +const SYSTEM_TENANT_REJECTION_MESSAGE = 'System tenant is not allowed for request-scoped routes'; let _checkedThread = false; @@ -119,14 +120,21 @@ export function tenantContextMiddleware( const user = req.user; const context = buildTenantContext(req); + const { tenantId } = context; + + if (tenantId === SYSTEM_TENANT_ID) { + logger.warn('[tenantContextMiddleware] Rejected system tenant for request route', { + path: req.path, + }); + res.status(403).json({ error: SYSTEM_TENANT_REJECTION_MESSAGE }); + return; + } if (!user) { runWithTenantContext(context, next); return; } - const { tenantId } = context; - if (!tenantId) { if (isStrict()) { res.status(403).json({ error: 'Tenant context required in strict isolation mode' }); @@ -255,7 +263,7 @@ export function restoreTenantContextFromReq( return rejectRequestWithUploadCleanup( req, res, - 'System tenant is not allowed for request-scoped routes', + SYSTEM_TENANT_REJECTION_MESSAGE, ); }