Skip to content

fix(security): prevent path traversal and restrict file size + types in file content endpoint#1606

Open
atul-upadhyay-7 wants to merge 3 commits into
nisshchayarathi:mainfrom
atul-upadhyay-7:fix/issue-1589
Open

fix(security): prevent path traversal and restrict file size + types in file content endpoint#1606
atul-upadhyay-7 wants to merge 3 commits into
nisshchayarathi:mainfrom
atul-upadhyay-7:fix/issue-1589

Conversation

@atul-upadhyay-7
Copy link
Copy Markdown
Collaborator

@atul-upadhyay-7 atul-upadhyay-7 commented Jun 1, 2026

This PR fixes a critical security vulnerability in the repository file content endpoint. Previously, the endpoint did not perform any validation or encoding on the path query parameter, allowing attackers to access arbitrary internal configuration files or sensitive files (like .env) using path traversal sequences.

Changes:

  1. Path Validation: Rejects paths containing traversal sequences (..), starting with /, containing null bytes (\0), or ending with .env, .pem, .key (sensitive files).
  2. Path Encoding: Encodes individual path segments with encodeURIComponent to prevent bypasses while preserving directory structure.
  3. File Type Restriction: Only allows previewing text files/code/configurations; blocks common binary, executable, and archive file types.
  4. File Size Limitation: Enforces a strict 1MB limit check (via headers and download length) to prevent memory exhaustion and DoS.
  5. Unit Tests: Created a suite of 6 new unit tests testing path traversal, sensitive files blocking, and size limitations. All tests pass successfully.

Closes #1589

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened file fetch endpoint: path validation blocks traversal, absolute/null-byte paths, sensitive filenames and disallowed binary/media/archive/font types; URL-encodes path segments and enforces a 1MB content limit; clearer 400/404 error responses and robust handling of upstream HTTP errors.
  • Tests

    • Added extensive tests covering success, invalid inputs, traversal and injection payloads, allowed extensions, size limits, encoding/edge cases, and upstream failure scenarios.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 1, 2026

@anshika1179 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
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds validateFilePath and encodePathSegments to the file content GET handler, rejects unsafe paths/extensions, URL-encodes path segments for raw.githubusercontent.com requests, enforces a 1MB size limit, and expands Jest tests to cover traversal, encoding, size, allowed/blocked extensions, and upstream errors.

Changes

Path Traversal Mitigation

Layer / File(s) Summary
Path Validation and Encoding Helpers
app/api/repositories/[id]/files/content/route.ts
validateFilePath() rejects traversal (..), absolute paths (/), null bytes, sensitive filenames, and blocked binary/media/archive/font extensions. encodePathSegments() URL-encodes each path segment for safe GitHub raw URL construction.
Route Handler Security Integration
app/api/repositories/[id]/files/content/route.ts
GET handler performs early validation (400 on invalid), uses encoded segments in raw.githubusercontent.com URL, checks Content-Length and fetched text length against a 1MB limit, and returns appropriate 400/404/500 responses.
Test scaffolding and mocks
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Adds test environment shims and Jest mocks for auth, repositoryService, and fetch.
Core tests: happy path, missing params
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Shared fixtures and describe blocks covering success, missing repo id, and missing file path.
Path traversal and encoding tests
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Traversal payloads, absolute paths, null-byte injection, double-encoding bypass attempts, and per-segment encoding checks asserting 400 for invalid cases.
Sensitive filenames and blocked extensions
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Rejects secret-like filenames and a broad set of blocked extensions (images, archives, binaries, documents, fonts) with 400.
Size enforcement tests
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Tests Content-Length over-limit, missing header with oversized body, and allows exactly 1MB content.
Upstream and repo error handling
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Covers upstream 404/403/502/503 and repository lookup failures (500), plus tenant/isolation checks and unsupported-host rejection.
Whitelist and edge-case validations
app/api/repositories/[id]/files/content/__tests__/route.test.ts
Asserts 200 for allowed text/code/config extensions, and covers filename edge cases, long paths, query/hash sanitization, spaces-only rejection, and response JSON/header shape.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Handler as GET Handler
  participant Validator as validateFilePath
  participant Encoder as encodePathSegments
  participant GitHub as raw.githubusercontent.com
  participant HandlerInternal as Handler(size checks)
  Client->>Handler: GET /api/repositories/[id]/files/content?path=...
  Handler->>Validator: validateFilePath(path)
  alt invalid
    Validator-->>Handler: error
    Handler->>Client: 400 Bad Request
  else valid
    Handler->>Encoder: encodePathSegments(path)
    Encoder-->>Handler: encodedPath
    Handler->>GitHub: GET encoded raw URL
    GitHub-->>Handler: content + headers
    Handler->>HandlerInternal: check content-length and text size ≤ 1MB
    alt size too large
      Handler->>Client: 400 Payload Too Large
    else ok
      Handler->>Client: 200 OK with JSON { content, path }
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Suggested labels

security, type:security, bug, level:critical

Poem

🐰 In tunnels of paths where dots once slipped through,
I stitched every segment, encoded each view.
No secret file hiding, no oversized tale,
One megabyte guard keeps the fetch on the trail.
Hop, hop — safe content returns to you!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
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 (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly and concisely describes the main security fixes: path traversal prevention, file size and type restrictions in the file content endpoint.
Linked Issues check ✅ Passed PR fully addresses all coding requirements from issue #1589: validates paths (rejects .., /, null bytes, sensitive files), encodes path segments with encodeURIComponent, restricts file types to text/code/config, and enforces 1MB size limit.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the security vulnerability in the file content endpoint; test additions and implementation changes are all within scope.

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

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

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🎉 Thanks for your contribution, @atul-upadhyay-7!

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 (172 lines across 2 file(s))

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

GSSoC'26 automation · Maintainer: @nisshchayarathi

@github-actions github-actions Bot added the GSSoC'26 Part of GirlScript Summer of Code 2026 label Jun 1, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🎉 Thanks for your contribution, @atul-upadhyay-7!

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 (172 lines across 2 file(s))

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

GSSoC'26 automation · Maintainer: @nisshchayarathi

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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.

Inline comments:
In `@app/api/repositories/`[id]/files/content/__tests__/route.test.ts:
- Around line 73-111: Add two new test cases in the same test suite that
exercise the GET handler: (1) duplicate the ".env" sensitive-file test but use
paths ".env.local" and "cert.PEM" (call NextRequest with ?path=.env.local and
?path=cert.PEM, invoke GET(request, { params: { id: "1" } }) and assert status
400 and error contains "Access to sensitive files is restricted"); (2) add a
test for an oversized streamed body when Content-Length is missing by mocking
global.fetch to return ok: true, headers.get -> null for "content-length", and
text() -> a string larger than 1MB (e.g. 'a'.repeat(1024*1024+1)), then call GET
with ?path=src/index.js and assert status 400 and error contains "File size
exceeds 1MB limit"; reference the GET handler, NextRequest constructor, and the
existing tests in route.test.ts to place and mirror assertion style.

In `@app/api/repositories/`[id]/files/content/route.ts:
- Around line 101-112: The current code calls response.text() and uses
content.length after optionally trusting response.headers.get("content-length"),
which is unsafe; change to stream and enforce a byte-accurate cap while reading
the response body so you never buffer the entire payload: read from
response.body (ReadableStream / getReader()) increment a byte counter (use the
Uint8Array chunk.byteLength) and abort/throw once the counter exceeds the 1MB
limit, and use that streamed data (or reject with a 413/400) instead of
response.text() or string.length; update any logic that referenced content,
contentLengthHeader, or response to use the streamed result or early-rejection
path.
- Around line 13-16: The current filename checks (using filename === ".env" and
endsWith(".env")/".pem"/".key") are bypassable by paths like
"secrets/.env.local" and by case variations like "CERT.PEM"; update the
validation to operate on the basename and normalized case: obtain the file's
basename (e.g., via path.basename(filename)) and compare against a
case-normalized value (e.g., lowercased) using a pattern that matches ".env" and
any ".env.*" (for example check basename === ".env" or basename
startsWith(".env.")) and use case-insensitive checks for ".pem" and ".key"
(e.g., lowercased basename.endsWith(".pem")/".key") so that ".env.local",
"CERT.PEM", and similar variants are correctly rejected; adjust the logic around
the filename variable in route.ts accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 27c564f4-5493-458c-88a5-3f85437273a6

📥 Commits

Reviewing files that changed from the base of the PR and between 63ecb90 and 70e0337.

📒 Files selected for processing (2)
  • app/api/repositories/[id]/files/content/__tests__/route.test.ts
  • app/api/repositories/[id]/files/content/route.ts

Comment thread app/api/repositories/[id]/files/content/__tests__/route.test.ts Outdated
Comment thread app/api/repositories/[id]/files/content/route.ts
Comment thread app/api/repositories/[id]/files/content/route.ts
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

🎉 Thanks for your contribution, @atul-upadhyay-7!

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 (700 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

github-actions Bot commented Jun 1, 2026

🎉 Thanks for your contribution, @atul-upadhyay-7!

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 (700 lines across 2 file(s))

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

GSSoC'26 automation · Maintainer: @nisshchayarathi

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/api/repositories/[id]/files/content/__tests__/route.test.ts (1)

511-530: 💤 Low value

Scenario 9.3 name and assertion disagree.

The title says "blocks long path names exceeding bounds safely" but the route enforces no length bound, so the test correctly asserts 200. Rename to reflect that long paths are allowed to avoid implying a non-existent limit for future maintainers.

🤖 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/repositories/`[id]/files/content/__tests__/route.test.ts around lines
511 - 530, The test title for Scenario 9.3 is misleading (it says "blocks long
path names exceeding bounds safely" while the assertion expects 200); update the
test's description string in the describe/it block (look for the "Scenario 9.3"
it in route.test.ts) to indicate that long paths are allowed (e.g. "permits long
path names" or similar) so the test name matches the assertion and intent.
🤖 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/repositories/`[id]/files/content/__tests__/route.test.ts:
- Around line 511-530: The test title for Scenario 9.3 is misleading (it says
"blocks long path names exceeding bounds safely" while the assertion expects
200); update the test's description string in the describe/it block (look for
the "Scenario 9.3" it in route.test.ts) to indicate that long paths are allowed
(e.g. "permits long path names" or similar) so the test name matches the
assertion and intent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 93035df5-ef47-4b3b-9271-511fce86f329

📥 Commits

Reviewing files that changed from the base of the PR and between 70e0337 and fee13da.

📒 Files selected for processing (1)
  • app/api/repositories/[id]/files/content/__tests__/route.test.ts

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.

Critical: Path Traversal in File Content Endpoint Allows Reading Sensitive Repository Files

2 participants