conductor-oss/conductor · critical · DocumentAccessDeniedException

Access denied: host '{host}' is blocked

Error message

Access denied: host '{host}' is blocked

What it means

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).

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/document/DocumentAccessPolicy.java:333

        for (String blocked : blockedFileNames) {
            if (lowerFileName.equals(blocked.toLowerCase())) {
                throw new DocumentAccessDeniedException(
                        "Access denied: file name '" + fileName + "' is blocked");
            }
        }
    }

    private void checkBlockedHosts(String location) {
        String host = extractHost(location);
        if (host == null || host.isEmpty()) {
            return;
        }
        String lowerHost = host.toLowerCase();

        // Check against explicit blocklist
        for (String blocked : DEFAULT_BLOCKED_HOSTS) {
            if (lowerHost.equals(blocked.toLowerCase())) {
                throw new DocumentAccessDeniedException(
                        "Access denied: host '" + host + "' is blocked");
            }
        }
        for (String blocked : blockedHosts) {
            if (lowerHost.equals(blocked.toLowerCase())) {
                throw new DocumentAccessDeniedException(
                        "Access denied: host '" + host + "' is blocked");
            }
        }

        // Resolve hostname to IP and check for link-local / metadata ranges.
        // This catches obfuscated IPs (hex, octal, decimal encoding) and DNS
        // rebinding because InetAddress.getByName normalizes all representations.
        checkResolvedAddress(host);
    }

    /**
     * Resolves the host to an IP address and blocks link-local (169.254.0.0/16) and other dangerous

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Point the loader at the legitimate external URL instead of a metadata/Kubernetes endpoint.
  2. Treat any such URL in your data as an injection attempt and reject it upstream.
  3. Never feed untrusted/LLM-generated URLs directly to the document loader.

Example fix

// before
loader.download("http://169.254.169.254/latest/meta-data/iam/...")
// after — use the real external resource
loader.download("https://api.example.com/v1/data")
Defensive patterns

Strategy: validation

Validate before calling

// Reject metadata/in-cluster endpoints before any HTTP loader call
private static final Set<String> META = Set.of(
    "169.254.169.254","169.254.170.2","metadata.google.internal",
    "100.100.100.200","kubernetes.default");
String host = java.net.URI.create(url).getHost();
if (host != null && META.contains(host.toLowerCase())) {
    throw new SecurityException("Refusing metadata endpoint: " + url);
}

Try / catch

try {
    loader.download(url);
} catch (SecurityException e) {
    // built-in host denylist — never bypass; treat as SSRF/injection
    throw new SecurityException("Blocked host in URL: " + url, e);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/37e76f46f08cfe36. Report an issue: GitHub.