{"record":{"id":"4c80720ef807eb1f","repo":"xai-org/grok-build","slug":"hook-http-client-config-is-valid","errorCode":null,"errorMessage":"hook HTTP client config is valid","messagePattern":"hook HTTP client config is valid","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-hooks/src/runner/http.rs","lineNumber":134,"sourceCode":"        if is_blocked_ip(&addr.ip()) {\n            return Err(format!(\n                \"URL host {host} resolves to blocked private/internal IP: {}\",\n                addr.ip()\n            ));\n        }\n    }\n\n    Ok(())\n}\n\nfn build_hook_client(timeout_ms: u64) -> reqwest::Client {\n    xai_grok_extra_ca::build_reqwest_client(|builder| {\n        builder\n            .timeout(Duration::from_millis(timeout_ms))\n            // SECURITY: only the initial URL is SSRF-validated; do not follow redirects.\n            .redirect(reqwest::redirect::Policy::none())\n    })\n    .expect(\"hook HTTP client config is valid\")\n}\n\npub async fn run_http_hook(\n    spec: &HookSpec,\n    envelope: &HookEventEnvelope,\n    ctx: &RunContext<'_>,\n    mode: GateKind,\n) -> HookRunOutput {\n    let start = Instant::now();\n\n    let Some(ref raw_url) = spec.url else {\n        return (\n            HookRunnerResult::Failed(\"http hook has no 'url' field\".into()),\n            start.elapsed(),\n            None,\n            None,\n        );\n    };","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-hooks/src/runner/http.rs#L116-L152","documentation":"build_hook_client constructs a reqwest client for hook HTTP calls with a total timeout and redirects disabled (SSRF hardening: only the initial URL is validated) and .expect()s success. A panic means the reqwest/TLS builder rejected the configuration or the crypto backend failed to initialize.","triggerScenarios":"Calling build_hook_client (from run_http_hook or hook_client_does_not_follow_redirects) when build_reqwest_client fails - TLS provider init failure, CA load failure, or invalid builder options in the current build/environment.","commonSituations":"Environment lacking working TLS/crypto prerequisites; mismatched rustls feature flags; conflicting process-level crypto provider installed by another component before hooks run.","solutions":["Ensure rustls/aws-lc-rs features are consistent across the workspace and rebuild","Check no other component installed an incompatible global rustls CryptoProvider before hooks initialize","Build the hook client once at startup (fail fast) rather than per-hook-execution","If needed, inject a prebuilt client into the hook runner instead of relying on build_hook_client"],"exampleFix":"// before\nlet client = build_hook_client(timeout_ms); // panics if build fails\n// after\nlet client = std::panic::catch_unwind(|| build_hook_client(timeout_ms))\n    .map_err(|_| anyhow!(\"hook HTTP client unavailable: TLS init failed\"))\n    .unwrap_or_else(|_| reqwest::blocking::Client::new());","handlingStrategy":"try-catch","validationCode":"let client = std::panic::catch_unwind(|| build_hook_client(timeout_ms))\n    .map_err(|_| anyhow!(\"hook HTTP client build failed (TLS init?)\"))?;","typeGuard":"null","tryCatchPattern":"let result = std::panic::catch_unwind(|| run_http_hook(spec, envelope, ctx).await)\n    .await\n    .map_err(|_| anyhow!(\"hook runner unavailable: HTTP client build failed\"))?;","preventionTips":["Build the hook client once at startup, not per hook execution","Keep redirect Policy::none() and timeout config valid for your reqwest version","Ensure TLS/crypto prerequisites exist in the deployment image","Avoid conflicting global rustls provider installation before hooks run"],"tags":["http","tls","security","panics","hooks","rust"],"backgroundTag":"tls-client-build-failed","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}