Skip to content

Security: tighten OPTIONS preflight on /api/git-proxy to echo only allowed origins #28

@thetechjon

Description

@thetechjon

Context

The /api/git-proxy/[...path] route was hardened on 2026-05-30 to add isOriginAllowed + checkRateLimit guards on every GET / POST (see SECURITY.md audit log). The CORS preflight handler OPTIONS still echoes a permissive Access-Control-Allow-Origin: * regardless of the calling origin. The actual GET / POST handler will refuse a cross-origin request, so the headline risk is closed — but the preflight currently advertises that ANY origin would be accepted, which is misleading and a defense-in-depth gap.

This issue is a small, scoped, fully-self-contained hardening that mirrors the pattern already used by the real handler.

Where to look

  • src/app/api/git-proxy/[...path]/route.ts lines 105-115 — the OPTIONS export.
  • Same file lines 40-55 — the existing isOriginAllowed + checkRateLimit pattern in handle().
  • src/utils/originAllowlist.ts — the allowlist logic; returns { ok, origin } | { ok: false, reason }.

Acceptance criteria

  • OPTIONS reads the request Origin header.
  • When isOriginAllowed(request).ok === true: respond 204 with Access-Control-Allow-Origin: <origin> (echoing the allowed origin, NOT *) plus a Vary: Origin header.
  • When not allowed: respond 403 with no CORS headers (the browser will then block the request).
  • Existing browser callers from the noteser app keep working (isomorphic-git push / fetch).
  • New unit test in src/__tests__/ covers both branches with mocked Request objects.

Complexity

Small.

For first-time contributors

Read CONTRIBUTING.md and SECURITY.md (the audit-log section explains the prior hardening). npm install, npm test, PR against dev.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions