{"record":{"id":"2963c919da3fdb1a","repo":"alyssaxuu/screenity","slug":"untrusted-tus-location-host-parsed-host","errorCode":null,"errorMessage":"Untrusted TUS location host: ${parsed.host}","messagePattern":"Untrusted TUS location host: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/pages/CloudRecorder/bunnyTusUploader.js","lineNumber":1225,"sourceCode":"        VideoId: this.videoId,\n        \"Upload-Metadata\": `filetype ${btoa(this.container || \"video/webm\")},title ${btoa(\n          this.metadata.title,\n        )}`,\n      },\n    });\n\n    if (!res.ok) throw new Error(\"Failed to start TUS upload session\");\n    const location = res.headers.get(\"location\");\n    const resolved = location.startsWith(\"/\")\n      ? `https://video.bunnycdn.com${location}`\n      : location;\n    // Defense-in-depth: TUS Location header must stay on Bunny's host. Without\n    // this, a redirect to attacker.com would receive subsequent PATCHes\n    // carrying recording chunks plus the AuthorizationSignature header.\n    try {\n      const parsed = new URL(resolved);\n      if (parsed.host !== \"video.bunnycdn.com\") {\n        throw new Error(`Untrusted TUS location host: ${parsed.host}`);\n      }\n    } catch (err) {\n      throw new Error(`Invalid TUS location: ${err?.message || err}`);\n    }\n    this.uploadUrl = resolved;\n\n    // Persist BEFORE save-upload-meta: the local journal is the only recovery\n    // path if the extension crashes before the backend records the URL.\n    await this.persistUploadJournal({ force: true });\n\n    if (this.userToken) {\n      fetch(`${API_BASE}/bunny/videos/save-upload-meta`, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application/json\",\n          Authorization: `Bearer ${this.userToken}`,\n        },\n        body: JSON.stringify({","sourceCodeStart":1207,"sourceCodeEnd":1243,"githubUrl":"https://github.com/alyssaxuu/screenity/blob/512606387b8d07dda5e63bb428bd063f0a2a3ed0/src/pages/CloudRecorder/bunnyTusUploader.js#L1207-L1243","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check for proxies, VPNs, or corporate MITM tools rewriting response headers","Log the raw Location header to confirm what Bunny actually returned","If Bunny changes its upload hostname, update the allowlist constant deliberately after verifying the new host","Report unexpected hosts to Bunny support; treat the recording/signature as compromised"],"exampleFix":"// before\nif (parsed.host !== \"video.bunnycdn.com\") {\n  throw new Error(`Untrusted TUS location host: ${parsed.host}`);\n}\n// after\nconst ALLOWED_HOSTS = new Set([\"video.bunnycdn.com\"]);\nif (!ALLOWED_HOSTS.has(parsed.host) || parsed.protocol !== \"https:\") {\n  throw new Error(`Untrusted TUS location host: ${parsed.host}`);\n}","handlingStrategy":"validation","validationCode":"const host = new URL(location, \"https://video.bunnycdn.com\").host;\nif (host !== \"video.bunnycdn.com\") {\n  alertHostMismatch(host); // investigate proxy/MITM before retrying\n}","typeGuard":"function isTrustedTusLocation(loc) {\n  try { return new URL(loc).host === \"video.bunnycdn.com\"; }\n  catch { return false; }\n}","tryCatchPattern":"try {\n  await uploader.init();\n} catch (e) {\n  if (String(e.message).startsWith(\"Untrusted TUS location host\")) {\n    reportSecurityIncident(e.message); // do NOT auto-retry\n  }\n}","preventionTips":["Never disable the host allowlist check","Investigate proxies/VPNs if this fires; the signature must be considered leaked","Keep the allowlist constant centralized and reviewed","Alert on this error in telemetry — it may indicate MITM"],"tags":["security","ssrf","upload","tus","header-validation"],"backgroundTag":"untrusted-upload-location-host","analyzedSha":"512606387b8d07dda5e63bb428bd063f0a2a3ed0","analyzedAt":"2026-09-02T20:59:09.419Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}