{"record":{"id":"3b9944c109f94b4c","repo":"zeroclaw-labs/zeroclaw","slug":"blocked-cloud-metadata-host-host","errorCode":null,"errorMessage":"Blocked cloud metadata host: {host}","messagePattern":"Blocked cloud metadata host: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/http_request.rs","lineNumber":143,"sourceCode":"\n        if url.chars().any(char::is_whitespace) {\n            anyhow::bail!(\"URL cannot contain whitespace\");\n        }\n\n        if !url.starts_with(\"http://\") && !url.starts_with(\"https://\") {\n            anyhow::bail!(\"Only http:// and https:// URLs are allowed\");\n        }\n\n        if self.allowed_domains.is_empty() {\n            anyhow::bail!(\n                \"HTTP request tool is enabled but no allowed_domains are configured. Add [http_request].allowed_domains in config.toml\"\n            );\n        }\n\n        let host = extract_host(url)?;\n        if let Ok(ip) = host.parse::<IpAddr>() {\n            if domain_guard::is_known_cloud_metadata_endpoint(ip) {\n                anyhow::bail!(\"Blocked cloud metadata host: {host}\");\n            }\n            if domain_guard::is_cloud_metadata_ip(ip) {\n                anyhow::bail!(\n                    \"Blocked link-local host: {host}; 169.254.0.0/16 is blocked unconditionally \\\n                     because cloud metadata services are hosted in that range\"\n                );\n            }\n        }\n        let port = extract_port(url)?;\n\n        let private_host = domain_guard::is_private_or_local_host(&host);\n        let private_host_explicitly_allowed = private_host\n            && domain_guard::host_matches_allowlist(&host, &self.allowed_private_hosts);\n\n        if private_host && !private_host_explicitly_allowed && !self.allow_private_hosts {\n            anyhow::bail!(\"Blocked local/private host: {host}\");\n        }\n","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/http_request.rs#L125-L161","documentation":"Thrown by HttpRequestTool::validate_url_policy (crates/zeroclaw-tools/src/http_request.rs:143) when the URL host is a literal IP address that domain_guard::is_known_cloud_metadata_endpoint recognizes as a cloud metadata service endpoint (e.g. 169.254.169.254 and equivalent well-known addresses). This is an unconditional SSRF guard: no allowlist entry, wildcard, or allow_private_hosts setting can override it, because hitting the metadata service would leak instance credentials.","triggerScenarios":"Passing url = \"http://169.254.169.254/latest/meta-data/\" or the equivalent known metadata IP in any cloud VM/container; an agent following a prompt-injection link that points at the metadata endpoint; integrations that try to read IAM role credentials over HTTP instead of the provider SDK.","commonSituations":"AWS/GCP/Azure/Aliyun instances where tooling tries to fetch instance metadata manually; penetration tests exercising the agent; SSRF attempts through LLM-driven URL input.","solutions":["Do not target the metadata service from http_request; obtain credentials via the cloud provider SDK, instance profile, or environment variables instead.","If you control the URL pipeline, reject known metadata IPs before the tool call and log the attempt as a security event.","If this fires unexpectedly, treat it as a prompt-injection or SSRF signal and audit where the URL came from."],"exampleFix":"// before\nlet args = json!({\"url\": \"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"});\n\n// after\n// fetch credentials via the provider SDK / instance profile, not via http_request\nlet creds = aws_sdk_default_chain().provide_credentials().await?;","handlingStrategy":"validation","validationCode":"fn is_known_metadata_target(host: &str) -> bool {\n    host.parse::<std::net::IpAddr>().is_ok_and(|ip| {\n        ip.to_string().starts_with(\"169.254.\")\n    })\n}\n// reject before calling the tool and alert on it","typeGuard":null,"tryCatchPattern":"let result = tool.execute(args).await?;\nif let Some(err) = &result.error {\n    if err.contains(\"Blocked cloud metadata host\") {\n        // security event: audit URL provenance; never retry or bypass\n    }\n}","preventionTips":["Strip or reject literal IPs of 169.254.169.254 and known metadata addresses at URL-intake time.","Give the agent its credentials via env/instance profile so it never needs the metadata endpoint.","Alert on this error: it frequently indicates prompt injection or SSRF probing."],"tags":["http","security","ssrf","cloud-metadata","zeroclaw"],"backgroundTag":"ssrf-metadata-endpoint-blocked","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}