msgbyte / msgbyte/tianji

Security: Multiple Cross-Workspace IDOR Vulnerabilities in tRPC Endpoints

Open
#250 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
3.1k
Forks
194
Avg merge
18m
Merged PRs (30d)
3

Description

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

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the affected handlers in survey.ts, ai.ts, application.ts, and insights/index.ts, comparing their Prisma queries with the workspace-scoped application.info pattern. Verify that each endpoint scopes reads and updates to the workspace, then confirm that resources from another workspace cannot be accessed or modified across all six endpoints.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend-api-design, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.