diff --git a/.gitignore b/.gitignore index fd35c7da9..978bed4f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target +/target_ci +/target_review* firmware/*/target *.db *.db-journal @@ -13,6 +15,8 @@ site/.vite/ site/public/docs-content/ gh-pages/ +.idea + # Environment files (may contain secrets) .env diff --git a/src/channels/linq.rs b/src/channels/linq.rs index 287c170db..995762d0c 100644 --- a/src/channels/linq.rs +++ b/src/channels/linq.rs @@ -400,8 +400,7 @@ impl Channel for LinqChannel { /// The signature is sent in `X-Webhook-Signature` (hex-encoded) and the /// timestamp in `X-Webhook-Timestamp`. Reject timestamps older than 300s. pub fn verify_linq_signature(secret: &str, body: &str, timestamp: &str, signature: &str) -> bool { - use hmac::{Hmac, Mac}; - use sha2::Sha256; + use ring::hmac; // Reject stale timestamps (>300s old) if let Ok(ts) = timestamp.parse::() { @@ -417,10 +416,6 @@ pub fn verify_linq_signature(secret: &str, body: &str, timestamp: &str, signatur // Compute HMAC-SHA256 over "{timestamp}.{body}" let message = format!("{timestamp}.{body}"); - let Ok(mut mac) = Hmac::::new_from_slice(secret.as_bytes()) else { - return false; - }; - mac.update(message.as_bytes()); let signature_hex = signature .trim() .strip_prefix("sha256=") @@ -430,8 +425,8 @@ pub fn verify_linq_signature(secret: &str, body: &str, timestamp: &str, signatur return false; }; - // Constant-time comparison via HMAC verify. - mac.verify_slice(&provided).is_ok() + let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes()); + hmac::verify(&key, message.as_bytes(), &provided).is_ok() } #[cfg(test)] diff --git a/src/channels/mod.rs b/src/channels/mod.rs index ef279b9a8..dca5742bb 100644 --- a/src/channels/mod.rs +++ b/src/channels/mod.rs @@ -6393,6 +6393,13 @@ BTC is currently around $65,000 based on latest tool output."# let mut channels_by_name = HashMap::new(); channels_by_name.insert(channel.name().to_string(), channel); + let autonomy_cfg = crate::config::AutonomyConfig { + level: crate::config::AutonomyLevel::Full, + auto_approve: vec!["mock_price".to_string()], + ..crate::config::AutonomyConfig::default() + }; + let approval_manager = Arc::new(ApprovalManager::from_config(&autonomy_cfg)); + let runtime_ctx = Arc::new(ChannelRuntimeContext { channels_by_name: Arc::new(channels_by_name), provider: Arc::new(ToolCallingProvider), @@ -6422,9 +6429,7 @@ BTC is currently around $65,000 based on latest tool output."# non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())), query_classification: crate::config::QueryClassificationConfig::default(), model_routes: Vec::new(), - approval_manager: Arc::new(ApprovalManager::from_config( - &crate::config::AutonomyConfig::default(), - )), + approval_manager, multimodal: crate::config::MultimodalConfig::default(), hooks: None, }); @@ -6460,6 +6465,13 @@ BTC is currently around $65,000 based on latest tool output."# let mut channels_by_name = HashMap::new(); channels_by_name.insert(channel.name().to_string(), channel); + let autonomy_cfg = crate::config::AutonomyConfig { + level: crate::config::AutonomyLevel::Full, + auto_approve: vec!["mock_price".to_string()], + ..crate::config::AutonomyConfig::default() + }; + let approval_manager = Arc::new(ApprovalManager::from_config(&autonomy_cfg)); + let runtime_ctx = Arc::new(ChannelRuntimeContext { channels_by_name: Arc::new(channels_by_name), provider: Arc::new(ToolCallingProvider), @@ -6489,9 +6501,7 @@ BTC is currently around $65,000 based on latest tool output."# non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())), query_classification: crate::config::QueryClassificationConfig::default(), model_routes: Vec::new(), - approval_manager: Arc::new(ApprovalManager::from_config( - &crate::config::AutonomyConfig::default(), - )), + approval_manager, multimodal: crate::config::MultimodalConfig::default(), hooks: None, }); @@ -6541,6 +6551,13 @@ BTC is currently around $65,000 based on latest tool output."# let mut channels_by_name = HashMap::new(); channels_by_name.insert(channel.name().to_string(), channel); + let autonomy_cfg = crate::config::AutonomyConfig { + level: crate::config::AutonomyLevel::Full, + auto_approve: vec!["mock_price".to_string()], + ..crate::config::AutonomyConfig::default() + }; + let approval_manager = Arc::new(ApprovalManager::from_config(&autonomy_cfg)); + let runtime_ctx = Arc::new(ChannelRuntimeContext { channels_by_name: Arc::new(channels_by_name), provider: Arc::new(ToolCallingProvider), @@ -6568,9 +6585,7 @@ BTC is currently around $65,000 based on latest tool output."# message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS, interrupt_on_new_message: false, non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())), - approval_manager: Arc::new(ApprovalManager::from_config( - &crate::config::AutonomyConfig::default(), - )), + approval_manager, multimodal: crate::config::MultimodalConfig::default(), hooks: None, query_classification: crate::config::QueryClassificationConfig::default(), @@ -6621,6 +6636,13 @@ BTC is currently around $65,000 based on latest tool output."# let mut channels_by_name = HashMap::new(); channels_by_name.insert(channel.name().to_string(), channel); + let autonomy_cfg = crate::config::AutonomyConfig { + level: crate::config::AutonomyLevel::Full, + auto_approve: vec!["mock_price".to_string()], + ..crate::config::AutonomyConfig::default() + }; + let approval_manager = Arc::new(ApprovalManager::from_config(&autonomy_cfg)); + let runtime_ctx = Arc::new(ChannelRuntimeContext { channels_by_name: Arc::new(channels_by_name), provider: Arc::new(ToolCallingProvider), @@ -6648,9 +6670,7 @@ BTC is currently around $65,000 based on latest tool output."# message_timeout_secs: CHANNEL_MESSAGE_TIMEOUT_SECS, interrupt_on_new_message: false, non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())), - approval_manager: Arc::new(ApprovalManager::from_config( - &crate::config::AutonomyConfig::default(), - )), + approval_manager, multimodal: crate::config::MultimodalConfig::default(), hooks: None, query_classification: crate::config::QueryClassificationConfig::default(), @@ -6760,6 +6780,13 @@ BTC is currently around $65,000 based on latest tool output."# let mut channels_by_name = HashMap::new(); channels_by_name.insert(channel.name().to_string(), channel); + let autonomy_cfg = crate::config::AutonomyConfig { + level: crate::config::AutonomyLevel::Full, + auto_approve: vec!["mock_price".to_string()], + ..crate::config::AutonomyConfig::default() + }; + let approval_manager = Arc::new(ApprovalManager::from_config(&autonomy_cfg)); + let runtime_ctx = Arc::new(ChannelRuntimeContext { channels_by_name: Arc::new(channels_by_name), provider: Arc::new(ToolCallingAliasProvider), @@ -6791,9 +6818,7 @@ BTC is currently around $65,000 based on latest tool output."# non_cli_excluded_tools: Arc::new(Mutex::new(Vec::new())), query_classification: crate::config::QueryClassificationConfig::default(), model_routes: Vec::new(), - approval_manager: Arc::new(ApprovalManager::from_config( - &crate::config::AutonomyConfig::default(), - )), + approval_manager, }); process_channel_message( diff --git a/src/channels/nextcloud_talk.rs b/src/channels/nextcloud_talk.rs index 97c60815a..3fe5ed803 100644 --- a/src/channels/nextcloud_talk.rs +++ b/src/channels/nextcloud_talk.rs @@ -1,7 +1,5 @@ use super::traits::{Channel, ChannelMessage, SendMessage}; use async_trait::async_trait; -use hmac::{Hmac, Mac}; -use sha2::Sha256; use uuid::Uuid; /// Nextcloud Talk channel in webhook mode. @@ -247,6 +245,8 @@ pub fn verify_nextcloud_talk_signature( body: &str, signature: &str, ) -> bool { + use ring::hmac; + let random = random.trim(); if random.is_empty() { tracing::warn!("Nextcloud Talk: missing X-Nextcloud-Talk-Random header"); @@ -265,17 +265,15 @@ pub fn verify_nextcloud_talk_signature( }; let payload = format!("{random}{body}"); - let Ok(mut mac) = Hmac::::new_from_slice(secret.as_bytes()) else { - return false; - }; - mac.update(payload.as_bytes()); - - mac.verify_slice(&provided).is_ok() + let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_bytes()); + hmac::verify(&key, payload.as_bytes(), &provided).is_ok() } #[cfg(test)] mod tests { use super::*; + use hmac::{Hmac, Mac}; + use sha2::Sha256; fn make_channel() -> NextcloudTalkChannel { NextcloudTalkChannel::new( diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index 88dd81dd6..756fa8850 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -1728,8 +1728,7 @@ async fn handle_whatsapp_verify( /// Returns true if the signature is valid, false otherwise. /// See: pub fn verify_whatsapp_signature(app_secret: &str, body: &[u8], signature_header: &str) -> bool { - use hmac::{Hmac, Mac}; - use sha2::Sha256; + use ring::hmac; // Signature format: "sha256=" let Some(hex_sig) = signature_header.strip_prefix("sha256=") else { @@ -1741,14 +1740,8 @@ pub fn verify_whatsapp_signature(app_secret: &str, body: &[u8], signature_header return false; }; - // Compute HMAC-SHA256 - let Ok(mut mac) = Hmac::::new_from_slice(app_secret.as_bytes()) else { - return false; - }; - mac.update(body); - - // Constant-time comparison - mac.verify_slice(&expected).is_ok() + let key = hmac::Key::new(hmac::HMAC_SHA256, app_secret.as_bytes()); + hmac::verify(&key, body, &expected).is_ok() } /// POST /whatsapp — incoming message webhook diff --git a/src/main.rs b/src/main.rs index 3fd91a661..c461f52a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,9 +53,7 @@ mod agent; mod approval; mod auth; mod channels; -mod rag { - pub use zeroclaw::rag::*; -} +mod rag; mod config; mod coordination; mod cost; diff --git a/src/providers/mod.rs b/src/providers/mod.rs index bc698664e..4aec8cc2c 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -33,7 +33,8 @@ pub mod traits; #[allow(unused_imports)] pub use traits::{ ChatMessage, ChatRequest, ChatResponse, ConversationMessage, Provider, ProviderCapabilityError, - ToolCall, ToolResultMessage, + is_user_or_assistant_role, ToolCall, ToolResultMessage, ROLE_ASSISTANT, ROLE_SYSTEM, ROLE_TOOL, + ROLE_USER, }; use crate::auth::AuthService; diff --git a/src/providers/traits.rs b/src/providers/traits.rs index 6d45dcdf2..a2a06f3dd 100644 --- a/src/providers/traits.rs +++ b/src/providers/traits.rs @@ -11,31 +11,40 @@ pub struct ChatMessage { pub content: String, } +pub const ROLE_SYSTEM: &str = "system"; +pub const ROLE_USER: &str = "user"; +pub const ROLE_ASSISTANT: &str = "assistant"; +pub const ROLE_TOOL: &str = "tool"; + +pub fn is_user_or_assistant_role(role: &str) -> bool { + role == ROLE_USER || role == ROLE_ASSISTANT +} + impl ChatMessage { pub fn system(content: impl Into) -> Self { Self { - role: "system".into(), + role: ROLE_SYSTEM.into(), content: content.into(), } } pub fn user(content: impl Into) -> Self { Self { - role: "user".into(), + role: ROLE_USER.into(), content: content.into(), } } pub fn assistant(content: impl Into) -> Self { Self { - role: "assistant".into(), + role: ROLE_ASSISTANT.into(), content: content.into(), } } pub fn tool(content: impl Into) -> Self { Self { - role: "tool".into(), + role: ROLE_TOOL.into(), content: content.into(), } }