{"record":{"id":"37e76f46f08cfe36","repo":"conductor-oss/conductor","slug":"access-denied-host-host-is-blocked","errorCode":null,"errorMessage":"Access denied: host '{host}' is blocked","messagePattern":"Access denied: host '(.+?)' is blocked","errorType":"validation","errorClass":"DocumentAccessDeniedException","httpStatus":null,"severity":"critical","filePath":"ai/src/main/java/org/conductoross/conductor/ai/document/DocumentAccessPolicy.java","lineNumber":333,"sourceCode":"        for (String blocked : blockedFileNames) {\n            if (lowerFileName.equals(blocked.toLowerCase())) {\n                throw new DocumentAccessDeniedException(\n                        \"Access denied: file name '\" + fileName + \"' is blocked\");\n            }\n        }\n    }\n\n    private void checkBlockedHosts(String location) {\n        String host = extractHost(location);\n        if (host == null || host.isEmpty()) {\n            return;\n        }\n        String lowerHost = host.toLowerCase();\n\n        // Check against explicit blocklist\n        for (String blocked : DEFAULT_BLOCKED_HOSTS) {\n            if (lowerHost.equals(blocked.toLowerCase())) {\n                throw new DocumentAccessDeniedException(\n                        \"Access denied: host '\" + host + \"' is blocked\");\n            }\n        }\n        for (String blocked : blockedHosts) {\n            if (lowerHost.equals(blocked.toLowerCase())) {\n                throw new DocumentAccessDeniedException(\n                        \"Access denied: host '\" + host + \"' is blocked\");\n            }\n        }\n\n        // Resolve hostname to IP and check for link-local / metadata ranges.\n        // This catches obfuscated IPs (hex, octal, decimal encoding) and DNS\n        // rebinding because InetAddress.getByName normalizes all representations.\n        checkResolvedAddress(host);\n    }\n\n    /**\n     * Resolves the host to an IP address and blocks link-local (169.254.0.0/16) and other dangerous","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/document/DocumentAccessPolicy.java#L315-L351","documentation":"Thrown by DocumentAccessPolicy.checkBlockedHosts when the host of an HTTP(S) location exactly matches (case-insensitive) one of the built-in DEFAULT_BLOCKED_HOSTS — cloud metadata endpoints and in-cluster APIs: 169.254.169.254, 169.254.170.2, metadata.google.internal, 100.100.100.200, kubernetes.default(.svc/.svc.cluster.local), fd00:ec2::254. This is the primary SSRF guard against metadata-service theft. DocumentAccessDeniedException (SecurityException).","triggerScenarios":"An HTTP document loader (HttpDocumentLoader) is given a URL whose host is a metadata endpoint or the in-cluster Kubernetes API — e.g. http://169.254.169.254/latest/meta-data/ or http://kubernetes.default/api.","commonSituations":"Prompt injection or a misconfigured URL template points a loader at a metadata service to steal cloud tokens; a workflow tries to call the in-cluster Kubernetes API from a document task.","solutions":["Point the loader at the legitimate external URL instead of a metadata/Kubernetes endpoint.","Treat any such URL in your data as an injection attempt and reject it upstream.","Never feed untrusted/LLM-generated URLs directly to the document loader."],"exampleFix":"// before\nloader.download(\"http://169.254.169.254/latest/meta-data/iam/...\")\n// after — use the real external resource\nloader.download(\"https://api.example.com/v1/data\")","handlingStrategy":"validation","validationCode":"// Reject metadata/in-cluster endpoints before any HTTP loader call\nprivate static final Set<String> META = Set.of(\n    \"169.254.169.254\",\"169.254.170.2\",\"metadata.google.internal\",\n    \"100.100.100.200\",\"kubernetes.default\");\nString host = java.net.URI.create(url).getHost();\nif (host != null && META.contains(host.toLowerCase())) {\n    throw new SecurityException(\"Refusing metadata endpoint: \" + url);\n}","typeGuard":null,"tryCatchPattern":"try {\n    loader.download(url);\n} catch (SecurityException e) {\n    // built-in host denylist — never bypass; treat as SSRF/injection\n    throw new SecurityException(\"Blocked host in URL: \" + url, e);\n}","preventionTips":["Never feed untrusted/LLM-generated URLs to the document loader.","Treat any metadata/Kubernetes-API URL in input data as an injection attempt.","Allow-list external hosts upstream rather than relying solely on the blocklist."],"tags":["security","ssrf","access-control","document-loader","cloud-metadata","denylist"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}