Skip to content

fix: resolve Insecure Direct Object Reference (IDOR) in annotations API#2356

Open
Abhishek2005-ard wants to merge 7 commits into
nisshchayarathi:mainfrom
Abhishek2005-ard:fix/idor-annotations
Open

fix: resolve Insecure Direct Object Reference (IDOR) in annotations API#2356
Abhishek2005-ard wants to merge 7 commits into
nisshchayarathi:mainfrom
Abhishek2005-ard:fix/idor-annotations

Conversation

@Abhishek2005-ard

@Abhishek2005-ard Abhishek2005-ard commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Description This PR addresses a critical Insecure Direct Object Reference (IDOR) vulnerability in the GET /api/annotations endpoint. Previously, the endpoint fetched map annotations solely based on the provided repositoryId query parameter, without verifying if the authenticated user actually had read access or ownership of that repository. This allowed unauthorized users to view sensitive annotations on private repositories.

Changes Included

Added an authorization check using prisma.repository.findFirst in the GET endpoint of app/api/annotations/route.ts.
The endpoint now strictly verifies that the requested repositoryId belongs to the authenticated user.userId before returning the mapAnnotation records.
Returns a 403 Forbidden response if the repository does not exist or if the user lacks the necessary permissions.
Security Impact

Prevents unauthorized data disclosure of repository-specific notes, map annotations, and private discussions.
Resolves IDOR exposure on the /api/annotations endpoint.

close #2353

Summary by CodeRabbit

  • Bug Fixes

    • Added repository access verification to the annotations endpoint to ensure users can only retrieve annotations for repositories they have proper permissions to access.
  • Chores

    • Enhanced build configuration to conditionally load the bundle analyzer module, with automatic fallback when unavailable, improving build process stability.

@vercel

vercel Bot commented Jun 16, 2026

Copy link
Copy Markdown

@Abhishek2005-ard is attempting to deploy a commit to the Nisshchaya's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Abhishek2005-ard, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 9 minutes and 53 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 193aa69b-3248-43ee-9dc1-d5c6c5c1937c

📥 Commits

Reviewing files that changed from the base of the PR and between ebed0ae and 0f17664.

⛔ Files ignored due to path filters (5)
  • package-lock.json is excluded by !**/package-lock.json
  • public/uploads/avatars/1001/1780676986116_emhbae.jpg is excluded by !**/*.jpg
  • public/uploads/avatars/1001/1781627118139_jtnw3e.jpg is excluded by !**/*.jpg
  • public/uploads/avatars/1001/1781627224731_8t7syn.jpg is excluded by !**/*.jpg
  • public/uploads/avatars/1001/1781627523291_lqgcv7.jpg is excluded by !**/*.jpg
📒 Files selected for processing (13)
  • app/api/auth/sessions/__tests__/route.test.ts
  • app/api/upload/avatar/__tests__/route.test.ts
  • app/api/users/profile/__tests__/route.test.ts
  • diff.txt
  • jest.config.cjs
  • lib/services/__tests__/data-residency.test.ts
  • lib/services/__tests__/repository-idor.test.ts
  • lib/services/gitService.ts
  • next.config.js
  • package.json
  • prs.json
  • src/components/layout/__tests__/Navbar.test.tsx
  • test-results.json
📝 Walkthrough

Walkthrough

The GET handler in app/api/annotations/route.ts gains an authorization guard that queries the repository by both repositoryId and the authenticated userId, returning 403 if no match is found. In next.config.js, the @next/bundle-analyzer import is wrapped in a try/catch with a pass-through fallback.

Changes

Annotations authorization and config resilience

Layer / File(s) Summary
Repository ownership check in annotations GET
app/api/annotations/route.ts
Before executing the mapAnnotation.findMany query, GET now calls prisma.repository.findFirst scoped to both repositoryId and userId. Returns 403 when no matching repository is found for the authenticated user.
Optional bundle analyzer loading
next.config.js
The @next/bundle-analyzer require is wrapped in a try/catch. When the module is unavailable, withBundleAnalyzer falls back to (config) => config, preventing a hard crash on missing dependency.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested labels

bug, gssoc:approved, level:beginner, mentor:nisshchayarathi, GSSoC'26

Poem

🐇 A rabbit hopped in, sniffed the code with care,
Found annotations left open — forbidden data bare!
Now userId + repoId must both align just right,
Or a 403 bounces the stranger from sight.
The config wraps safely in try/catch embrace —
No missing analyzer can crash this place! 🌿

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The change to next.config.js appears unrelated to the IDOR vulnerability fix; it modifies bundle analyzer error handling, which falls outside the stated security remediation scope. Remove the next.config.js changes or justify their necessity in relation to the IDOR vulnerability fix. If unrelated, move to a separate PR.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: fixing an IDOR vulnerability in the annotations API by adding authorization checks.
Linked Issues check ✅ Passed The PR adds authorization checks to the GET /api/annotations endpoint as required by issue #2353, preventing unauthorized access to private repository annotations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the GSSoC'26 Part of GirlScript Summer of Code 2026 label Jun 16, 2026
@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (23 lines across 2 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

1 similar comment
@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (23 lines across 2 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/api/annotations/route.ts (1)

20-20: 💤 Low value

Consider validating repositoryId is a valid integer before parsing.

If repositoryId is a non-numeric string (e.g., "abc"), parseInt returns NaN, which Prisma will reject or handle unexpectedly. A validation check could provide a clearer 400 response.

✨ Optional improvement
     if (!repositoryId) {
       return NextResponse.json({ error: "repositoryId is required" }, { status: 400 });
     }

+    const repoIdNum = parseInt(repositoryId, 10);
+    if (isNaN(repoIdNum)) {
+      return NextResponse.json({ error: "repositoryId must be a valid integer" }, { status: 400 });
+    }
+
     // Verify user has access to the repository to prevent IDOR
     const repo = await prisma.repository.findFirst({
       where: {
-        id: parseInt(repositoryId),
+        id: repoIdNum,
         userId: user.userId,
       }
     });

Then reuse repoIdNum in the mapAnnotation.findMany query as well.

Also applies to: 31-31

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/api/annotations/route.ts` at line 20, The code directly parses
repositoryId without validating it is a numeric string first, causing parseInt
to return NaN for invalid inputs which Prisma will not handle correctly. Before
calling parseInt on repositoryId, add a validation check to ensure it is a valid
numeric string and return a 400 Bad Request response if validation fails. Store
the parsed integer result in a variable (such as repoIdNum) and use this
variable in both the id field on line 20 and in the mapAnnotation.findMany query
on line 31, replacing the direct parseInt calls at both locations.
next.config.js (1)

14-19: Narrow the catch to only the missing-module case.

At line 18, the unconditional catch suppresses all initialization errors and can silently disable the analyzer when there's a real config or runtime problem. Narrow the catch to handle only the MODULE_NOT_FOUND error for the missing analyzer package, and rethrow other errors.

Suggested patch
 let withBundleAnalyzer;
 try {
   withBundleAnalyzer = require('`@next/bundle-analyzer`')({
     enabled: process.env.ANALYZE === 'true',
   });
-} catch {
-  withBundleAnalyzer = (config) => config;
+} catch (error) {
+  const isMissingAnalyzer =
+    error &&
+    error.code === 'MODULE_NOT_FOUND' &&
+    String(error.message || '').includes('`@next/bundle-analyzer`');
+
+  if (isMissingAnalyzer) {
+    withBundleAnalyzer = (config) => config;
+  } else {
+    throw error;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@next.config.js` around lines 14 - 19, The catch block in the
withBundleAnalyzer initialization is too broad and silently suppresses all
errors, masking real configuration or runtime problems. Modify the catch block
to check if the error is specifically a MODULE_NOT_FOUND error (indicating the
analyzer package is missing). If it is a missing module error, assign the
passthrough function to withBundleAnalyzer as currently done. If the error is
anything else, rethrow it so that real problems are not silently ignored and can
be properly diagnosed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/api/annotations/route.ts`:
- Line 20: The code directly parses repositoryId without validating it is a
numeric string first, causing parseInt to return NaN for invalid inputs which
Prisma will not handle correctly. Before calling parseInt on repositoryId, add a
validation check to ensure it is a valid numeric string and return a 400 Bad
Request response if validation fails. Store the parsed integer result in a
variable (such as repoIdNum) and use this variable in both the id field on line
20 and in the mapAnnotation.findMany query on line 31, replacing the direct
parseInt calls at both locations.

In `@next.config.js`:
- Around line 14-19: The catch block in the withBundleAnalyzer initialization is
too broad and silently suppresses all errors, masking real configuration or
runtime problems. Modify the catch block to check if the error is specifically a
MODULE_NOT_FOUND error (indicating the analyzer package is missing). If it is a
missing module error, assign the passthrough function to withBundleAnalyzer as
currently done. If the error is anything else, rethrow it so that real problems
are not silently ignored and can be properly diagnosed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b28f7c6-56a8-4b59-a4d1-b0f1767b84d8

📥 Commits

Reviewing files that changed from the base of the PR and between d53ee6b and ebed0ae.

📒 Files selected for processing (2)
  • app/api/annotations/route.ts
  • next.config.js

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (58 lines across 6 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (55 lines across 6 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (68 lines across 8 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (71 lines across 9 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

@github-actions

Copy link
Copy Markdown

🎉 Thanks for your contribution, @Abhishek2005-ard!

Your PR has passed our automated GSSoC quality checks. Here's a quick summary:

Check Status
PR description ✅ Provided
PR title ✅ Meaningful
Linked issue ✅ Found
Change size ✅ Looks good (81 lines across 19 file(s))

A maintainer will review your PR soon. Please be patient and available for feedback. 💪

GSSoC'26 automation · Maintainer: @nisshchayarathi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GSSoC'26 Part of GirlScript Summer of Code 2026

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Denial of Service (DoS) via Unbounded Subprocess Spawning

1 participant