{"record":{"id":"2f166ad457c33958","repo":"linera-io/linera-protocol","slug":"unauthorizedhttprequest","errorCode":"UnauthorizedHttpRequest","errorMessage":"ExecutionError::UnauthorizedHttpRequest(url)","messagePattern":"ExecutionError::UnauthorizedHttpRequest\\(url\\)","errorType":"exception","errorClass":"ExecutionError","httpStatus":null,"severity":"error","filePath":"linera-execution/src/execution_state_actor.rs","lineNumber":539,"sourceCode":"                            .headers\n                            .into_iter()\n                            .map(|http::Header { name, value }| {\n                                Ok((name.parse()?, value.try_into()?))\n                            })\n                            .collect::<Result<HeaderMap, ExecutionError>>()?;\n\n                        let url = Url::parse(&request.url)?;\n                        let host = url\n                            .host_str()\n                            .ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;\n\n                        let (_epoch, committee) = system\n                            .current_committee()\n                            .await?\n                            .ok_or_else(|| ExecutionError::UnauthorizedHttpRequest(url.clone()))?;\n                        let allowed_hosts = &committee.policy().http_request_allow_list;\n\n                        ensure!(\n                            allowed_hosts.contains(host),\n                            ExecutionError::UnauthorizedHttpRequest(url)\n                        );\n\n                        let request = Client::new()\n                            .request(request.method.into(), url)\n                            .body(request.body)\n                            .headers(headers);\n                        #[cfg(not(web))]\n                        let request = request.timeout(linera_base::time::Duration::from_millis(\n                            committee.policy().http_request_timeout_ms,\n                        ));\n\n                        let response = request.send().await?;\n\n                        let mut response_size_limit =\n                            committee.policy().maximum_http_response_bytes;\n","sourceCodeStart":521,"sourceCodeEnd":557,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-execution/src/execution_state_actor.rs#L521-L557","documentation":"The HTTP oracle (PerformHttpRequest) only permits requests whose host appears in the current committee's http_request_allow_list. UnauthorizedHttpRequest is raised in three cases: the URL parses without a host, the chain has no current committee, or the host is not in the allow-list (execution_state_actor.rs:531-541). This keeps deterministic, replayable oracle calls bounded to approved endpoints.","triggerScenarios":"A contract performs an HTTP request to a URL whose host is absent from the committee policy's http_request_allow_list, or the request executes before the chain has an active committee or epoch. Also triggered by malformed URLs that yield no host component.","commonSituations":"Pointing the oracle at an API host never added to committee policy; local or dev networks regenerated without the host; URL scheme quirks (e.g. data: or scheme-relative URLs with no host); requesting during epoch transitions when current_committee() is None.","solutions":["Add the request host to the committee policy's http_request_allow_list and activate the epoch or committee that includes it","Route requests through an already allow-listed host or gateway that proxies the target API","Validate the URL has a host before issuing the request from the contract","Confirm system.current_committee() resolves (the chain is in an active epoch) when the request runs"],"exampleFix":"// before: committee policy\nhttp_request_allow_list: [\"api.gateway.example\"].into(),\n// contract requests https://prices.example/feed -> UnauthorizedHttpRequest\n\n// after\nhttp_request_allow_list: [\"api.gateway.example\", \"prices.example\"].into(),","handlingStrategy":"validation","validationCode":"// Client-side: check the host against a locally mirrored allow-list before the contract call\nfn host_allowed(url: &str, allow_list: &BTreeSet<String>) -> Result<bool, url::ParseError> {\n    let parsed = url::Url::parse(url)?;\n    Ok(parsed.host_str().map(|h| allow_list.contains(h)).unwrap_or(false))\n}\n\nif !host_allowed(&req_url, &committee_policy.http_request_allow_list)? {\n    return Err(anyhow!(\"host not in http_request_allow_list\"));\n}\nsubmit_http_oracle_request(req_url)?;","typeGuard":"fn is_unauthorized_http_request(err: &ExecutionError) -> bool {\n    matches!(err, ExecutionError::UnauthorizedHttpRequest(_))\n}","tryCatchPattern":"match client.perform_http_request(req).await {\n    Ok(resp) => resp,\n    Err(ref e) if is_unauthorized_http_request(e) => {\n        // deterministic policy failure: surface which host needs allow-listing\n        return Err(anyhow!(\"request host is not allow-listed by committee policy\"));\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Keep the committee allow-list mirrored in CI and validate every oracle URL against it before deployment","Parse and sanity-check URLs (scheme and host) in client code before they reach the contract","Never depend on ad-hoc hosts; route through an approved gateway host"],"tags":["http-oracle","committee-policy","allow-list","network","linera"],"backgroundTag":"host-not-allowlisted","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}