Summary
Several tRPC endpoints using workspaceProcedure middleware extract workspaceId from the middleware context but do not use it in their Prisma database queries, allowing users from one workspace to access or modify resources belonging to other workspaces.
Vulnerable Endpoints
1. survey.allResultCount (survey.ts ~line 110-133)
- Queries
surveyResult.groupBy by surveyId globally without any workspaceId filter
- The handler function
async () => {} doesn't even extract input parameters
- Impact: Returns survey result counts for ALL workspaces
2. ai.classifySurvey (ai.ts ~line 114-145)
- Updates
survey record using where: { id: surveyId } only
workspaceId is extracted from input but NOT used in the Prisma update() WHERE clause
- Impact: Can modify
recentSuggestionCategory of surveys in other workspaces
3. application.storeInfoHistory (application.ts ~line 255-308)
- Queries
applicationStoreInfoHistory.findMany by applicationId without workspaceId
- Contrast: Adjacent
info endpoint correctly uses where: { id: applicationId, workspaceId }
- Impact: Reveals download counts, ratings, version history of apps in other workspaces
4. application.eventStats (application.ts ~line 309-355)
- Calls
getApplicationEventStats(applicationId) without workspace verification
- Impact: Reveals event statistics for applications in other workspaces
5. application.sessionStats (application.ts ~line 356-404)
- Queries
applicationSession.groupBy by applicationId without workspaceId
- Impact: Reveals session distribution (OS, version, country, language) for other workspaces
6. insights.eventNames (insights/index.ts ~line 44-77)
- Queries
websiteEvent.groupBy by websiteId without workspaceId
- Impact: Reveals event names and page view data for websites in other workspaces
Root Cause
The workspaceProcedure middleware correctly verifies workspace membership and provides workspaceId in the input. However, these specific endpoints don't include workspaceId in their Prisma WHERE clauses when querying associated resources.
Correct Pattern (used by most endpoints)
// ✓ application.info correctly uses workspaceId
const app = await prisma.application.findUnique({
where: { id: applicationId, workspaceId } // Both conditions
});
Broken Pattern
// ✗ application.storeInfoHistory missing workspaceId
const history = await prisma.applicationStoreInfoHistory.findMany({
where: { applicationId } // Missing workspaceId!
});
Suggested Fix
Add workspaceId to all affected Prisma queries. For example:
survey.allResultCount:
.query(async ({ input }) => {
const { workspaceId } = input;
const res = await prisma.surveyResult.groupBy({
by: ['surveyId'],
where: { survey: { workspaceId } }, // ADD workspace filter
_count: true,
});
ai.classifySurvey:
await prisma.survey.update({
where: { id: surveyId, workspaceId }, // ADD workspaceId
data: { recentSuggestionCategory: [...] },
});
application endpoints:
// Verify application belongs to workspace before querying stats
const app = await prisma.application.findUnique({
where: { id: applicationId, workspaceId }
});
if (!app) throw new Error('Application not found');
Severity
- High for
ai.classifySurvey (cross-workspace data modification)
- Medium for the read-only information disclosure endpoints
Summary
Several tRPC endpoints using
workspaceProceduremiddleware extractworkspaceIdfrom the middleware context but do not use it in their Prisma database queries, allowing users from one workspace to access or modify resources belonging to other workspaces.Vulnerable Endpoints
1.
survey.allResultCount(survey.ts ~line 110-133)surveyResult.groupBybysurveyIdglobally without anyworkspaceIdfilterasync () => {}doesn't even extract input parameters2.
ai.classifySurvey(ai.ts ~line 114-145)surveyrecord usingwhere: { id: surveyId }onlyworkspaceIdis extracted from input but NOT used in the Prismaupdate()WHERE clauserecentSuggestionCategoryof surveys in other workspaces3.
application.storeInfoHistory(application.ts ~line 255-308)applicationStoreInfoHistory.findManybyapplicationIdwithoutworkspaceIdinfoendpoint correctly useswhere: { id: applicationId, workspaceId }4.
application.eventStats(application.ts ~line 309-355)getApplicationEventStats(applicationId)without workspace verification5.
application.sessionStats(application.ts ~line 356-404)applicationSession.groupBybyapplicationIdwithoutworkspaceId6.
insights.eventNames(insights/index.ts ~line 44-77)websiteEvent.groupBybywebsiteIdwithoutworkspaceIdRoot Cause
The
workspaceProceduremiddleware correctly verifies workspace membership and providesworkspaceIdin the input. However, these specific endpoints don't includeworkspaceIdin their Prisma WHERE clauses when querying associated resources.Correct Pattern (used by most endpoints)
Broken Pattern
Suggested Fix
Add
workspaceIdto all affected Prisma queries. For example:survey.allResultCount:
ai.classifySurvey:
application endpoints:
Severity
ai.classifySurvey(cross-workspace data modification)