The API v1 endpoints use cookie-based session auth without CSRF protection. A malicious website could make authenticated requests (delete apps, trigger deploys, read secrets) via the user's session cookie.
Changes
APICSRFProtection middleware — requires X-Requested-With header on state-changing requests (POST/PUT/DELETE/PATCH). Browsers cannot send custom headers cross-origin without CORS preflight, blocking CSRF.
Session cookie SameSite=Strict — upgraded from Lax as defense-in-depth.
CORS allowed headers — added X-Requested-With for legitimate cross-origin API clients.
Tests — 4 test cases covering safe methods, blocked POST/DELETE, and allowed POST with header.
- Add APICSRFProtection middleware requiring X-Requested-With header on
state-changing API requests (POST, PUT, DELETE, PATCH)
- Apply middleware to all /api/v1 routes
- Upgrade session cookie SameSite from Lax to Strict (defense-in-depth)
- Add X-Requested-With to CORS allowed headers
- Add tests for the new middleware
Browsers cannot send custom headers cross-origin without CORS preflight,
which effectively blocks CSRF attacks via cookie-based session auth.
Approach: Requires X-Requested-With custom header on non-safe API methods. Browsers cannot send custom headers cross-origin without CORS preflight approval, which prevents CSRF. This is the same pattern used by frameworks like Django REST and Angular.
Reviewed items:
✅ Middleware correctly distinguishes safe (GET/HEAD/OPTIONS) vs unsafe methods
✅ Returns proper JSON 403 with descriptive error message
✅ Applied at the /api/v1 route group level (covers all API endpoints including login)
✅ CORS config updated to allow X-Requested-With for legitimate cross-origin clients
✅SameSite=Strict as defense-in-depth (was Lax)
✅ 4 test cases with good coverage
✅make check passes with zero failures
✅ No linter config changes
Note: The X-Requested-With header requirement applies to the login endpoint too (POST /api/v1/login). This is intentional — API clients must include the header. The web UI login form uses the HTML routes (/login), not the API.
## Self-Review
**Approach:** Requires `X-Requested-With` custom header on non-safe API methods. Browsers cannot send custom headers cross-origin without CORS preflight approval, which prevents CSRF. This is the same pattern used by frameworks like Django REST and Angular.
**Reviewed items:**
- ✅ Middleware correctly distinguishes safe (GET/HEAD/OPTIONS) vs unsafe methods
- ✅ Returns proper JSON 403 with descriptive error message
- ✅ Applied at the `/api/v1` route group level (covers all API endpoints including login)
- ✅ CORS config updated to allow `X-Requested-With` for legitimate cross-origin clients
- ✅ `SameSite=Strict` as defense-in-depth (was `Lax`)
- ✅ 4 test cases with good coverage
- ✅ `make check` passes with zero failures
- ✅ No linter config changes
**Note:** The `X-Requested-With` header requirement applies to the login endpoint too (`POST /api/v1/login`). This is intentional — API clients must include the header. The web UI login form uses the HTML routes (`/login`), not the API.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Fixes #112 — API v1 CSRF vulnerability.
The API v1 endpoints use cookie-based session auth without CSRF protection. A malicious website could make authenticated requests (delete apps, trigger deploys, read secrets) via the user's session cookie.
Changes
APICSRFProtectionmiddleware — requiresX-Requested-Withheader on state-changing requests (POST/PUT/DELETE/PATCH). Browsers cannot send custom headers cross-origin without CORS preflight, blocking CSRF.SameSite=Strict— upgraded fromLaxas defense-in-depth.X-Requested-Withfor legitimate cross-origin API clients.make checkoutputAll checks pass with zero failures.
Self-Review
Approach: Requires
X-Requested-Withcustom header on non-safe API methods. Browsers cannot send custom headers cross-origin without CORS preflight approval, which prevents CSRF. This is the same pattern used by frameworks like Django REST and Angular.Reviewed items:
/api/v1route group level (covers all API endpoints including login)X-Requested-Withfor legitimate cross-origin clientsSameSite=Strictas defense-in-depth (wasLax)make checkpasses with zero failuresNote: The
X-Requested-Withheader requirement applies to the login endpoint too (POST /api/v1/login). This is intentional — API clients must include the header. The web UI login form uses the HTML routes (/login), not the API.Pull request closed