alyssaxuu/screenity · critical · Error

Untrusted TUS location host: ${parsed.host}

Error message

Untrusted TUS location host: ${parsed.host}

What it means

Defense-in-depth check: after resolving the TUS Location header, the uploader refuses any session URL whose host is not video.bunnycdn.com. A malicious or misbehaving redirect could otherwise send recording chunks plus the AuthorizationSignature header to an attacker-controlled host.

Source

Thrown at src/pages/CloudRecorder/bunnyTusUploader.js:1225

        VideoId: this.videoId,
        "Upload-Metadata": `filetype ${btoa(this.container || "video/webm")},title ${btoa(
          this.metadata.title,
        )}`,
      },
    });

    if (!res.ok) throw new Error("Failed to start TUS upload session");
    const location = res.headers.get("location");
    const resolved = location.startsWith("/")
      ? `https://video.bunnycdn.com${location}`
      : location;
    // Defense-in-depth: TUS Location header must stay on Bunny's host. Without
    // this, a redirect to attacker.com would receive subsequent PATCHes
    // carrying recording chunks plus the AuthorizationSignature header.
    try {
      const parsed = new URL(resolved);
      if (parsed.host !== "video.bunnycdn.com") {
        throw new Error(`Untrusted TUS location host: ${parsed.host}`);
      }
    } catch (err) {
      throw new Error(`Invalid TUS location: ${err?.message || err}`);
    }
    this.uploadUrl = resolved;

    // Persist BEFORE save-upload-meta: the local journal is the only recovery
    // path if the extension crashes before the backend records the URL.
    await this.persistUploadJournal({ force: true });

    if (this.userToken) {
      fetch(`${API_BASE}/bunny/videos/save-upload-meta`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${this.userToken}`,
        },
        body: JSON.stringify({

View on GitHub (pinned to 512606387b)

Solutions

  1. Check for proxies, VPNs, or corporate MITM tools rewriting response headers
  2. Log the raw Location header to confirm what Bunny actually returned
  3. If Bunny changes its upload hostname, update the allowlist constant deliberately after verifying the new host
  4. Report unexpected hosts to Bunny support; treat the recording/signature as compromised

Example fix

// before
if (parsed.host !== "video.bunnycdn.com") {
  throw new Error(`Untrusted TUS location host: ${parsed.host}`);
}
// after
const ALLOWED_HOSTS = new Set(["video.bunnycdn.com"]);
if (!ALLOWED_HOSTS.has(parsed.host) || parsed.protocol !== "https:") {
  throw new Error(`Untrusted TUS location host: ${parsed.host}`);
}
Defensive patterns

Strategy: validation

Validate before calling

const host = new URL(location, "https://video.bunnycdn.com").host;
if (host !== "video.bunnycdn.com") {
  alertHostMismatch(host); // investigate proxy/MITM before retrying
}

Type guard

function isTrustedTusLocation(loc) {
  try { return new URL(loc).host === "video.bunnycdn.com"; }
  catch { return false; }
}

Try / catch

try {
  await uploader.init();
} catch (e) {
  if (String(e.message).startsWith("Untrusted TUS location host")) {
    reportSecurityIncident(e.message); // do NOT auto-retry
  }
}

Prevention

When it happens

Trigger: Bunny (or an intercepting proxy/CDN) returns a Location header pointing at a different host, or the resolved location string is malformed such that new URL() parses a non-Bunny host.

Common situations: Corporate proxy rewriting Location headers; a man-in-the-middle or compromised endpoint returning attacker.com; Bunny changing/aliasing its upload hostname after a config update.

Related errors


AI-assisted analysis of alyssaxuu/screenity@512606387b (2026-09-02). Data as JSON: /api/errors/2963c919da3fdb1a. Report an issue: GitHub.