msgbyte / msgbyte/tianji

Security: Cross-workspace survey data IDOR in allResultCount (missing workspaceId filter)

Open
#252 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

The allResultCount endpoint in the survey router returns result counts from ALL surveys across ALL workspaces, because the handler doesn't use the workspaceId from workspaceProcedure.

Details

File: src/server/trpc/routers/survey.ts lines 110-133

allResultCount: workspaceProcedure
  .output(z.record(z.string(), z.number()))
  .query(async () => {  // ← No input destructuring, workspaceId unused
    const res = await prisma.surveyResult.groupBy({
      by: ['surveyId'],
      _count: true,  // ← No workspace filter!
    });
    return res.reduce<Record<string, number>>((prev, item) => {
      if (item.surveyId) { prev[item.surveyId] = item._count; }
      return prev;
    }, {});
  }),

Secure comparison (same file, line 43-48):

all: workspaceProcedure.query(async ({ input }) => {
  const { workspaceId } = input;  // ✓ workspaceId destructured
  return prisma.survey.findMany({
    where: { workspaceId },  // ✓ Filtered by workspace
  });
}),

Every other handler in the file (lines 43, 73, 153, 365, 393, 440) destructures workspaceId and uses it in Prisma queries. allResultCount is the only one that doesn't.

Similarly, the insights filterParams endpoint queries prisma.survey.findFirst({ where: { id: insightId } }) without workspaceId when insightType === 'survey'.

Impact

Any authenticated user can enumerate survey result counts and survey field configurations across all workspaces.

Recommended Fix

.query(async ({ input }) => {
  const { workspaceId } = input;
  const res = await prisma.surveyResult.groupBy({
    by: ['surveyId'],
    where: { survey: { workspaceId } },
    _count: true,
  });

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

Read src/server/trpc/routers/survey.ts around lines 110-133 and compare allResultCount with the workspace-scoped handlers in the same file. Then inspect the insights filterParams query for the survey case. Done means both endpoints restrict returned survey data to the current workspace, including result counts and field configurations.

Written by the indexing model from the issue text.

Assessment

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.