Skip to content

Conversation

@jonasyr
Copy link
Owner

@jonasyr jonasyr commented Nov 17, 2025

Summary

Implements CSRF protection for resource-intensive endpoints to prevent cross-site request abuse. Addresses security vulnerability where attackers could force victims to trigger expensive Git clone operations.

Changes

Backend

  1. New Middleware: strictContentType (apps/backend/src/middlewares/strictContentType.ts)

    • Enforces application/json content type for POST/PUT/DELETE requests
    • Requires X-Requested-With header to prevent simple CSRF attacks
    • Returns 415 for invalid content types
    • Returns 403 for missing custom header
    • Bypasses validation for GET requests (non state-changing methods)
  2. Applied Protection (apps/backend/src/index.ts)

    • Applied middleware to /api/repositories (handles cloning)
    • Applied middleware to /api/commits (handles streaming)
  3. Unit Tests (apps/backend/__tests__/unit/middlewares/strictContentType.unit.test.ts)

    • 4 comprehensive test cases covering all scenarios
    • Happy path with valid JSON and custom header
    • Rejection of invalid content types
    • Rejection of missing custom header
    • Bypass for non state-changing methods

Frontend

  1. API Client Update (apps/frontend/src/services/api.ts)
    • Added X-Requested-With: XMLHttpRequest header to all API requests
    • Ensures legitimate frontend requests pass CSRF protection

Security Impact

Blocks preflightless CSRF attacks: Requests with application/x-www-form-urlencoded or text/plain are rejected
Requires CORS preflight: application/json content type triggers browser preflight checks
Custom header protection: X-Requested-With header cannot be set by HTML forms
Resource protection: Prevents unauthorized triggering of expensive Git operations

Testing

Automated Tests

  • ✅ All 825 backend tests pass (including 4 new CSRF protection tests)
  • ✅ Linting passes (0 errors, 7 warnings - pre-existing)
  • ✅ Build successful

Manual Validation (as per issue)

Test 1: Block form-encoded requests

curl -X POST http://localhost:3001/api/repositories \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "repoUrl=https://github.com/octocat/Hello-World.git"
# Expected: 415 Unsupported Media Type

Test 2: Block text/plain requests

curl -X POST http://localhost:3001/api/repositories \
  -H "Content-Type: text/plain" \
  -d '{"repoUrl":"https://github.com/octocat/Hello-World.git"}'
# Expected: 415 Unsupported Media Type

Test 3: Block JSON without custom header

curl -X POST http://localhost:3001/api/repositories \
  -H "Content-Type: application/json" \
  -d '{"repoUrl":"https://github.com/octocat/Hello-World.git"}'
# Expected: 403 Forbidden

Test 4: Allow valid requests

curl -X POST http://localhost:3001/api/repositories \
  -H "Content-Type: application/json" \
  -H "X-Requested-With: XMLHttpRequest" \
  -d '{"repoUrl":"https://github.com/octocat/Hello-World.git"}'
# Expected: 200 or validation error (not CSRF error)

Breaking Changes

⚠️ External API clients must now include:

  • Content-Type: application/json header
  • X-Requested-With: XMLHttpRequest header (or any value)

This is intentional to prevent CSRF attacks. The frontend has been updated accordingly.

Related

Closes #98


Validation Checklist (from AGENTS.md)

  • pnpm lint passes
  • pnpm build succeeds
  • pnpm test:backend passes (825/825 tests)
  • Security vulnerability mitigated

@jonasyr jonasyr linked an issue Nov 17, 2025 that may be closed by this pull request
@jonasyr jonasyr self-assigned this Nov 17, 2025
@jonasyr jonasyr requested a review from NiklasSkulll November 17, 2025 14:37
@sonarqubecloud
Copy link

@NiklasSkulll NiklasSkulll merged commit 76c5f19 into dev Nov 18, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

type(security): CSRF Protection Missing for Resource-Intensive Endpoints

3 participants