Skip to content

Unauthenticated /api/health-proxy actively strips X-Frame-Options and CSP from upstream responses and serves third-party HTML under application origin #65

Description

@tg12

Summary

GET /api/health-proxy is an unauthenticated open HTTP reverse proxy deployed in production. It fetches an arbitrary hardcoded external URL, actively strips X-Frame-Options and Content-Security-Policy security headers from the proxied response, rewrites all relative URLs to point back at the upstream host, and serves the result as text/html from the application's own domain. This breaks the security posture of the proxied site, turns the server into a content-laundering proxy, and establishes a pattern that, if generalised to accept user-controlled URLs, becomes a full SSRF vector.

Evidence

File: src/app/api/health-proxy/route.ts:

export async function GET() {
  const res = await fetch('https://hantavirustracker.pplx.app/', { ... });

  let html = await res.text();

  // Actively strips the upstream site's security headers
  html = html
    .replace(/<meta[^>]*http-equiv=["']X-Frame-Options["'][^>]*>/gi, '')
    .replace(/<meta[^>]*http-equiv=["']Content-Security-Policy["'][^>]*>/gi, '');

  // Strips frame-busting JavaScript
  html = html
    .replace(/if\s*\(\s*top\s*!==\s*self\s*\)[^;]*;/gi, '')
    .replace(/if\s*\(\s*window\.top\s*!==\s*window\.self\s*\)[^;]*;/gi, '');

  // Injects base tag and rewrites relative URLs
  html = html.replace(/<head>/i,
    `<head>\n    <base href="https://hantavirustracker.pplx.app/" target="_blank">`
  );

  return new NextResponse(html, {
    headers: { 'Content-Type': 'text/html; charset=utf-8' }
  });
}

Key issues:

  1. Security header stripping: The proxy deliberately removes X-Frame-Options and Content-Security-Policy from the upstream response — headers the upstream site set to protect its users.
  2. Frame-busting script removal: Regex patterns specifically target and delete JavaScript code that prevents framing — breaking the upstream site's clickjacking defences.
  3. Content served under application domain: The response is served as text/html from feed.xdc.network, so scripts in the proxied page run under the application's origin.
  4. No URL validation: If the hardcoded URL is ever parameterised (a natural next step for a "proxy" endpoint), this becomes a full SSRF vector.

Why this matters

  • The upstream site (hantavirustracker.pplx.app) set X-Frame-Options specifically to prevent unauthorised embedding. This proxy defeats that protection without the site owner's consent.
  • Any JavaScript in the proxied HTML now executes in the application's security context (same origin as feed.xdc.network), potentially gaining access to cookies and storage.
  • The endpoint is fully public with no rate limiting — it can be used to proxy-hammer the upstream site.
  • The regex-based HTML rewriting is fragile and can produce malformed HTML, broken relative URLs, or content injection if the upstream HTML structure changes.

Root cause

A quick-and-dirty iframe alternative was built by fetching upstream HTML server-side and stripping its security headers. No security review was done, no auth was added, and no consideration was given to the upstream site's rights to control its own security posture.

Recommended fix

  1. Delete this endpoint. If the upstream content needs to be displayed, use an <iframe> directly (if the upstream permits it) or link to the upstream site.
  2. If a proxy is genuinely required, implement it with strict URL allowlisting, authentication, rate limiting, and never strip security headers from upstream responses.
  3. Never run regex on HTML to strip security controls — this is a red flag pattern.

Acceptance criteria

  • /api/health-proxy removed or protected behind authentication.
  • No code path strips X-Frame-Options, Content-Security-Policy, or frame-busting scripts from any proxied content.
  • If a proxy is retained, target URL is validated against an explicit allowlist and cannot be user-controlled.

Suggested labels

security, bug

Priority

P0

Severity

High — production proxy actively defeats upstream site's security controls; scripts in proxied content execute under application origin.

Confidence

Confirmed — header-stripping and script-removal code is present in current HEAD.


This issue was filed as part of the AI Slop Intelligence Dashboard Awareness Program.
Repo under review: https://github.com/OpenScanAI/GlobeNewsLive
Programme: https://labs.jamessawyer.co.uk/ai-slop-intelligence-dashboards/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions