{"record":{"id":"ddcafdf8f2ef00b4","repo":"stablyai/orca","slug":"jira-attachment-url-must-use-the-configured-site-o","errorCode":null,"errorMessage":"Jira attachment URL must use the configured site origin.","messagePattern":"Jira attachment URL must use the configured site origin\\.","errorType":"exception","errorClass":"JiraApiError","httpStatus":null,"severity":"error","filePath":"src/main/jira/client.ts","lineNumber":505,"sourceCode":"  }\n  if (response.status === 204) {\n    return null as T\n  }\n  return (await response.json()) as T\n}\n\nexport async function jiraRequestBinary(\n  client: JiraClientForSite,\n  pathOrUrl: string\n): Promise<{ data: ArrayBuffer; contentType: string }> {\n  const siteUrl = new URL(client.site.siteUrl)\n  const requestUrl = /^https?:\\/\\//i.test(pathOrUrl)\n    ? new URL(pathOrUrl)\n    : new URL(`${client.site.siteUrl}${pathOrUrl}`)\n  if (requestUrl.origin !== siteUrl.origin) {\n    // Why: attachment metadata is provider-controlled; never forward Jira\n    // credentials if a malformed response points at another origin.\n    throw new JiraApiError('Jira attachment URL must use the configured site origin.', null)\n  }\n  const headers = new Headers()\n  // Why: attachment content is binary; forcing JSON Accept/Content-Type can\n  // break downloads and confuses some Atlassian edge responses.\n  headers.set('Accept', '*/*')\n  headers.set('User-Agent', JIRA_API_USER_AGENT)\n  headers.set('Authorization', client.authorization)\n  const response = await jiraFetch(requestUrl.toString(), { headers })\n  if (!response.ok) {\n    throw new JiraApiError(await readJiraError(response), response.status)\n  }\n  const contentType = response.headers.get('content-type') || 'application/octet-stream'\n  return {\n    data: await response.arrayBuffer(),\n    contentType\n  }\n}\n","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/jira/client.ts#L487-L523","documentation":"Thrown by jiraRequestBinary when a fully-qualified attachment URL (or a relative path that resolves) has an origin different from the configured Jira site origin. This is a deliberate SSRF guard: Jira attachment metadata is provider-controlled, so forwarding Authorization credentials to an unexpected origin would leak them. It is a JiraApiError with status null.","triggerScenarios":"A Jira attachment record returns a content URL on a different host (e.g. an Atlassian CDN domain not matching the site origin, a malformed/restaged attachment, or a malicious/corrupted response). Also if the user's siteUrl was changed to one origin while cached attachment URLs point at the old origin.","commonSituations":"Atlassian serving attachments from a different subdomain than the API site. Site URL reconfigured after attachments were cached. A proxy rewriting attachment hosts.","solutions":["Reconfigure the Jira site URL so its origin matches where attachments are actually served.","Refetch the attachment metadata to get a fresh URL and retry.","Do NOT bypass this guard by stripping Authorization; instead download the URL unauthenticated out-of-band if the alternate origin is trusted.","Report the malformed attachment to the Jira admin."],"exampleFix":"// before\nconst { data } = await jiraRequestBinary(client, attachment.content)\n// after\nconst attachmentUrl = new URL(attachment.content)\nif (attachmentUrl.origin !== new URL(client.site.siteUrl).origin) {\n  await refreshAttachmentMetadata(attachment.id)\n}\nconst { data } = await jiraRequestBinary(client, attachment.content)","handlingStrategy":"validation","validationCode":"const attachmentUrl = new URL(pathOrUrl.startsWith('http') ? pathOrUrl : client.site.siteUrl + pathOrUrl)\nif (attachmentUrl.origin !== new URL(client.site.siteUrl).origin) await refreshAttachmentMetadata()","typeGuard":"function isSameOrigin(clientUrl: string, candidate: string): boolean {\n  return new URL(candidate).origin === new URL(clientUrl).origin\n}","tryCatchPattern":"try { return await jiraRequestBinary(client, pathOrUrl) }\ncatch (e) {\n  if (e instanceof JiraApiError && /configured site origin/.test(e.message)) { await refreshAttachmentMetadata(); return downloadUnauthenticated(pathOrUrl) }\n  throw e\n}","preventionTips":["Keep the Jira siteUrl aligned with the origin that serves attachments.","Refresh cached attachment metadata after reconfiguring a site.","Never strip Authorization to bypass the guard on untrusted origins."],"tags":["jira","security","ssrf","attachment","auth"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}