From 617d8aad98a13ef531bc5598424f5fc2d537e11b Mon Sep 17 00:00:00 2001 From: Gabriele Pau Date: Sun, 11 Oct 2026 10:54:33 +0200 Subject: [PATCH] fix(tools): reject non-string fetch_url method and headers --- src/tools/http.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/tools/http.rs b/src/tools/http.rs index ed876ca..c8b76b6 100644 --- a/src/tools/http.rs +++ b/src/tools/http.rs @@ -250,8 +250,13 @@ impl Tool for FetchUrlTool { let url_str = arguments["url"] .as_str() .ok_or_else(|| anyhow::anyhow!("Missing 'url'"))?; - let method: Method = arguments["method"] - .as_str() + let method: Method = arguments + .get("method") + .map(|m| { + m.as_str() + .ok_or_else(|| anyhow::anyhow!("'method' must be a string")) + }) + .transpose()? .unwrap_or("GET") .to_ascii_uppercase() .parse() @@ -265,9 +270,14 @@ impl Tool for FetchUrlTool { .as_object() .map(|h| { h.iter() - .filter_map(|(k, v)| v.as_str().map(|v| (k.clone(), v.to_string()))) - .collect() + .map(|(k, v)| { + v.as_str() + .map(|v| (k.clone(), v.to_string())) + .with_context(|| format!("header '{k}' must be a string")) + }) + .collect::>>() }) + .transpose()? .unwrap_or_default(); let body = if !arguments["json_body"].is_null() { Some(RequestBody::Json(arguments["json_body"].clone())) @@ -304,3 +314,27 @@ impl Tool for FetchUrlTool { } } } + +#[cfg(test)] +mod tests { + use super::*; + + fn tool() -> FetchUrlTool { + let cfg: HttpToolConfig = serde_json::from_value(json!({"enabled": true})).unwrap(); + FetchUrlTool::new(&cfg) + } + + #[tokio::test] + async fn non_string_method_and_headers_are_rejected() { + let err = tool() + .execute(&json!({"url": "https://example.com", "method": 123})) + .await + .unwrap_err(); + assert!(err.to_string().contains("'method' must be a string")); + let err = tool() + .execute(&json!({"url": "https://example.com", "headers": {"Accept": 123}})) + .await + .unwrap_err(); + assert!(err.to_string().contains("header 'Accept' must be a string")); + } +}