{"record":{"id":"bbeba78b7346a698","repo":"phacility/phabricator","slug":"uri-s-is-not-a-valid-fetchable-resource-a-vali-bbeba7","errorCode":null,"errorMessage":"URI \"%s\" is not a valid fetchable resource. A valid fetchable resource URI must specify a domain.","messagePattern":"URI \"(.+?)\" is not a valid fetchable resource\\. A valid fetchable resource URI must specify a domain\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/infrastructure/env/PhabricatorEnv.php","lineNumber":816,"sourceCode":"        pht(\n          'URI \"%s\" is not a valid fetchable resource. A valid fetchable '.\n          'resource URI must specify a protocol.',\n          $raw_uri));\n    }\n\n    $protocols = array_fuse($protocols);\n    if (!isset($protocols[$proto])) {\n      throw new Exception(\n        pht(\n          'URI \"%s\" is not a valid fetchable resource. A valid fetchable '.\n          'resource URI must use one of these protocols: %s.',\n          $raw_uri,\n          implode(', ', array_keys($protocols))));\n    }\n\n    $domain = $uri->getDomain();\n    if (!strlen($domain)) {\n      throw new Exception(\n        pht(\n          'URI \"%s\" is not a valid fetchable resource. A valid fetchable '.\n          'resource URI must specify a domain.',\n          $raw_uri));\n    }\n\n    $addresses = gethostbynamel($domain);\n    if (!$addresses) {\n      throw new Exception(\n        pht(\n          'URI \"%s\" is not a valid fetchable resource. The domain \"%s\" could '.\n          'not be resolved.',\n          $raw_uri,\n          $domain));\n    }\n\n    foreach ($addresses as $address) {\n      if (self::isBlacklistedOutboundAddress($address)) {","sourceCodeStart":798,"sourceCodeEnd":834,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/infrastructure/env/PhabricatorEnv.php#L798-L834","documentation":"Third gate of requireValidRemoteURIForFetch(): the URI must include a non-empty domain (PhutilURI->getDomain()). Schemes with no host component (e.g. 'file:///etc/passwd', 'mailto:x@y' passed to a fetch context, or 'https:///path') pass protocol checks but give nothing to resolve, and host-less fetches are exactly the shape of filesystem/SSRF abuse, so they are rejected outright.","triggerScenarios":"requireValidRemoteURIForFetch() receiving 'file:///srv/secret' (file is not in allowed protocols normally, but any allowed scheme with empty host hits this branch), 'https:///resource', or a URI where the host was lost in templating.","commonSituations":"Attempts (accidental or hostile) to make Phabricator fetch local file:// resources; URL-building bugs that drop the host; imported data with truncated URLs; penetration tests probing the fetch validator.","solutions":["Supply a fully-qualified remote URI: 'https://example.com/resource'.","If you meant a local file, this API is the wrong tool - local assets must be handled by dedicated import mechanisms, not remote fetch.","Reject host-less URIs at the form/import boundary so users never store them."],"exampleFix":"// before\nPhabricatorEnv::requireValidRemoteURIForFetch('https:///img/logo.png', array('https'));\n// throws: no domain\n\n// after\nPhabricatorEnv::requireValidRemoteURIForFetch('https://cdn.example.com/img/logo.png', array('https'));","handlingStrategy":"validation","validationCode":"$uri = new PhutilURI($url);\nif (!strlen($uri->getDomain())) {\n  throw new Exception('Refusing to fetch host-less URI: '.$url);\n}","typeGuard":"function uriHasHost($raw) {\n  $uri = new PhutilURI($raw);\n  return strlen($uri->getProtocol()) && strlen($uri->getDomain());\n}","tryCatchPattern":"try {\n  PhabricatorEnv::requireValidRemoteURIForFetch($url, array('http', 'https'));\n} catch (Exception $ex) {\n  // host-less URIs are never fetchable; degrade to a placeholder resource\n  return $default_image;\n}","preventionTips":["Reject file:// and host-less URIs in form validation - they are always a mistake or an attack.","Build URLs from components (scheme, host, path) rather than string concatenation.","Treat 'missing host' as a data-quality defect: clean it at import time."],"tags":["phabricator","uri","domain","fetch","ssrf","security"],"backgroundTag":"uri-missing-host","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}