diff --git a/src/analytics/goals.rs b/src/analytics/goals.rs index e4f2da6e7..be4d2233b 100644 --- a/src/analytics/goals.rs +++ b/src/analytics/goals.rs @@ -620,7 +620,7 @@ pub async fn create_objective( let record = new_objective.clone(); - let _result = tokio::task::spawn_blocking(move || { + tokio::task::spawn_blocking(move || { let mut conn = pool.get().map_err(|e| GoalsError::Database(e.to_string()))?; diesel::insert_into(okr_objectives::table) .values(&new_objective) @@ -718,7 +718,7 @@ pub async fn delete_objective( ) -> Result, GoalsError> { let pool = state.conn.clone(); - let _result = tokio::task::spawn_blocking(move || { + tokio::task::spawn_blocking(move || { let mut conn = pool.get().map_err(|e| GoalsError::Database(e.to_string()))?; let deleted = diesel::delete(okr_objectives::table.find(objective_id)) .execute(&mut conn) @@ -793,7 +793,7 @@ pub async fn create_key_result( let record = new_kr.clone(); - let _result = tokio::task::spawn_blocking(move || { + tokio::task::spawn_blocking(move || { let mut conn = pool.get().map_err(|e| GoalsError::Database(e.to_string()))?; diesel::insert_into(okr_key_results::table) .values(&new_kr) diff --git a/src/auto_task/intent_classifier.rs b/src/auto_task/intent_classifier.rs index 5960426e1..3a1f95d3f 100644 --- a/src/auto_task/intent_classifier.rs +++ b/src/auto_task/intent_classifier.rs @@ -884,8 +884,6 @@ END SCHEDULE .clone() .unwrap_or_else(|| "unspecified".to_string()); - let _goal_id = Uuid::new_v4(); - // Goals are more complex - they create a monitoring + action loop let basic_code = format!( r#"' Goal: {goal_name} diff --git a/src/basic/keywords/create_site.rs b/src/basic/keywords/create_site.rs index 799280178..8f50b47cd 100644 --- a/src/basic/keywords/create_site.rs +++ b/src/basic/keywords/create_site.rs @@ -16,11 +16,6 @@ use std::path::PathBuf; #[cfg(feature = "llm")] use std::sync::Arc; -// When llm feature is disabled, create a dummy trait for type compatibility -#[cfg(not(feature = "llm"))] -#[allow(dead_code)] -trait LLMProvider: Send + Sync {} - pub fn create_site_keyword(state: &AppState, user: UserSession, engine: &mut Engine) { let state_clone = state.clone(); let user_clone = user; diff --git a/src/basic/keywords/on_change.rs b/src/basic/keywords/on_change.rs index 82cf5e79c..4b4f15e66 100644 --- a/src/basic/keywords/on_change.rs +++ b/src/basic/keywords/on_change.rs @@ -1,7 +1,6 @@ use log::info; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; -use std::path::Path; use uuid::Uuid; use crate::core::shared::state::AppState; @@ -174,56 +173,56 @@ pub fn fetch_folder_changes( Ok(events) } -#[allow(dead_code)] -fn apply_filters(events: Vec, filters: &Option) -> Vec { - let Some(filters) = filters else { - return events; - }; - - events - .into_iter() - .filter(|event| { - if let Some(ref extensions) = filters.extensions { - let ext = Path::new(&event.path) - .extension() - .and_then(|e| e.to_str()) - .unwrap_or(""); - if !extensions.iter().any(|e| e.eq_ignore_ascii_case(ext)) { - return false; - } - } - - if let Some(min_size) = filters.min_size { - if event.size.unwrap_or(0) < min_size { - return false; - } - } - - if let Some(max_size) = filters.max_size { - if event.size.unwrap_or(i64::MAX) > max_size { - return false; - } - } - - if let Some(ref pattern) = filters.name_pattern { - let file_name = Path::new(&event.path) - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or(""); - if !file_name.contains(pattern) { - return false; - } - } - - true - }) - .collect() -} - #[cfg(test)] mod tests { use super::*; + fn apply_filters(events: Vec, filters: &Option) -> Vec { + let Some(ref filters) = filters else { + return events; + }; + + events + .into_iter() + .filter(|event| { + if let Some(ref extensions) = filters.extensions { + let ext = Path::new(&event.path) + .extension() + .and_then(|e| e.to_str()) + .unwrap_or(""); + if !extensions.iter().any(|e| e.eq_ignore_ascii_case(ext)) { + return false; + } + } + + if let Some(min_size) = filters.min_size { + if event.size.unwrap_or(0) < min_size { + return false; + } + } + + if let Some(max_size) = filters.max_size { + if event.size.unwrap_or(i64::MAX) > max_size { + return false; + } + } + + if let Some(ref pattern) = filters.name_pattern { + let file_name = Path::new(&event.path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(""); + if !file_name.contains(pattern) { + return false; + } + } + + true + }) + .collect() + } + use super::*; + #[test] fn test_folder_provider_from_str() { assert_eq!( diff --git a/src/compliance/code_scanner.rs b/src/compliance/code_scanner.rs index b81ad01b8..74c486da3 100644 --- a/src/compliance/code_scanner.rs +++ b/src/compliance/code_scanner.rs @@ -139,18 +139,18 @@ pub struct CodeScanner { } impl CodeScanner { - pub fn new(base_path: impl AsRef) -> Self { - let patterns = Self::build_patterns(); - Self { + pub fn new(base_path: impl AsRef) -> Result> { + let patterns = Self::build_patterns()?; + Ok(Self { patterns, base_path: base_path.as_ref().to_path_buf(), - } + }) } - fn build_patterns() -> Vec { - vec![ + fn build_patterns() -> Result, Box> { + Ok(vec![ ScanPattern { - regex: Regex::new(r#"(?i)password\s*=\s*["'][^"']+["']"#).expect("valid regex"), + regex: Regex::new(r#"(?i)password\s*=\s*["'][^"']+["']"#)?, issue_type: IssueType::PasswordInConfig, severity: IssueSeverity::Critical, title: "Hardcoded Password".to_string(), @@ -159,7 +159,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r#"(?i)(api[_-]?key|apikey|secret[_-]?key|client[_-]?secret)\s*=\s*["'][^"']{8,}["']"#).expect("valid regex"), + regex: Regex::new(r#"(?i)(api[_-]?key|apikey|secret[_-]?key|client[_-]?secret)\s*=\s*["'][^"']{8,}["']"#)?, issue_type: IssueType::HardcodedSecret, severity: IssueSeverity::Critical, title: "Hardcoded API Key/Secret".to_string(), @@ -168,7 +168,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r#"(?i)token\s*=\s*["'][a-zA-Z0-9_\-]{20,}["']"#).expect("valid regex"), + regex: Regex::new(r#"(?i)token\s*=\s*["'][a-zA-Z0-9_\-]{20,}["']"#)?, issue_type: IssueType::HardcodedSecret, severity: IssueSeverity::High, title: "Hardcoded Token".to_string(), @@ -177,7 +177,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)IF\s+.*\binput\b").expect("valid regex"), + regex: Regex::new(r"(?i)IF\s+.*\binput\b")?, issue_type: IssueType::DeprecatedIfInput, severity: IssueSeverity::Medium, title: "Deprecated IF...input Pattern".to_string(), @@ -189,7 +189,7 @@ impl CodeScanner { category: "Code Quality".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)\b(GET_BOT_MEMORY|SET_BOT_MEMORY|GET_USER_MEMORY|SET_USER_MEMORY|USE_KB|USE_TOOL|SEND_MAIL|CREATE_TASK)\b").expect("valid regex"), + regex: Regex::new(r"(?i)\b(GET_BOT_MEMORY|SET_BOT_MEMORY|GET_USER_MEMORY|SET_USER_MEMORY|USE_KB|USE_TOOL|SEND_MAIL|CREATE_TASK)\b")?, issue_type: IssueType::UnderscoreInKeyword, severity: IssueSeverity::Low, title: "Underscore in Keyword".to_string(), @@ -198,7 +198,7 @@ impl CodeScanner { category: "Naming Convention".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)POST\s+TO\s+INSTAGRAM\s+\w+\s*,\s*\w+").expect("valid regex"), + regex: Regex::new(r"(?i)POST\s+TO\s+INSTAGRAM\s+\w+\s*,\s*\w+")?, issue_type: IssueType::InsecurePattern, severity: IssueSeverity::High, title: "Instagram Credentials in Code".to_string(), @@ -209,7 +209,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)(SELECT|INSERT|UPDATE|DELETE)\s+.*(FROM|INTO|SET)\s+").expect("valid regex"), + regex: Regex::new(r"(?i)(SELECT|INSERT|UPDATE|DELETE)\s+.*(FROM|INTO|SET)\s+")?, issue_type: IssueType::FragileCode, severity: IssueSeverity::Medium, title: "Raw SQL Query".to_string(), @@ -221,7 +221,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)\bEVAL\s*\(").expect("valid regex"), + regex: Regex::new(r"(?i)\bEVAL\s*\(")?, issue_type: IssueType::FragileCode, severity: IssueSeverity::High, title: "Dynamic Code Execution".to_string(), @@ -233,7 +233,7 @@ impl CodeScanner { regex: Regex::new( r#"(?i)(password|secret|key|token)\s*=\s*["'][A-Za-z0-9+/=]{40,}["']"#, ) - .expect("valid regex"), + ?, issue_type: IssueType::HardcodedSecret, severity: IssueSeverity::High, title: "Potential Encoded Secret".to_string(), @@ -243,7 +243,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)(AKIA[0-9A-Z]{16})").expect("valid regex"), + regex: Regex::new(r"(?i)(AKIA[0-9A-Z]{16})")?, issue_type: IssueType::HardcodedSecret, severity: IssueSeverity::Critical, title: "AWS Access Key".to_string(), @@ -253,7 +253,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r"-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----").expect("valid regex"), + regex: Regex::new(r"-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----")?, issue_type: IssueType::HardcodedSecret, severity: IssueSeverity::Critical, title: "Private Key in Code".to_string(), @@ -263,7 +263,7 @@ impl CodeScanner { category: "Security".to_string(), }, ScanPattern { - regex: Regex::new(r"(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@").expect("valid regex"), + regex: Regex::new(r"(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@")?, issue_type: IssueType::HardcodedSecret, severity: IssueSeverity::Critical, title: "Database Credentials in Connection String".to_string(), @@ -450,12 +450,12 @@ impl CodeScanner { fn redact_sensitive(line: &str) -> String { let mut result = line.to_string(); - let secret_pattern = Regex::new(r#"(["'])[^"']{8,}(["'])"#).expect("valid regex"); + let secret_pattern = Regex::new(r#"(["'])[^"']{8,}(["'])"#)?; result = secret_pattern .replace_all(&result, "$1***REDACTED***$2") .to_string(); - let aws_pattern = Regex::new(r"AKIA[0-9A-Z]{16}").expect("valid regex"); + let aws_pattern = Regex::new(r"AKIA[0-9A-Z]{16}")?; result = aws_pattern .replace_all(&result, "AKIA***REDACTED***") .to_string(); diff --git a/src/core/package_manager/installer.rs b/src/core/package_manager/installer.rs index e909b6f26..22d985352 100644 --- a/src/core/package_manager/installer.rs +++ b/src/core/package_manager/installer.rs @@ -24,7 +24,9 @@ struct ThirdPartyConfig { static THIRDPARTY_CONFIG: Lazy = Lazy::new(|| { let toml_str = include_str!("../../../3rdparty.toml"); - toml::from_str(toml_str).expect("Failed to parse embedded 3rdparty.toml") + toml::from_str(toml_str).unwrap_or_else(|e| { + panic!("Failed to parse embedded 3rdparty.toml: {e}") + }) }); fn get_component_url(name: &str) -> Option { diff --git a/src/core/package_manager/setup/directory_setup.rs b/src/core/package_manager/setup/directory_setup.rs index fbfa15dfc..c9f69f664 100644 --- a/src/core/package_manager/setup/directory_setup.rs +++ b/src/core/package_manager/setup/directory_setup.rs @@ -474,12 +474,12 @@ impl DirectorySetup { .bearer_auth(&access_token) .json(&json!({ "name": app_name, - "redirectUris": [redirect_uri, "http://localhost:3000/auth/callback", "http://localhost:8080/auth/callback"], + "redirectUris": [redirect_uri, "http://localhost:3000/auth/callback", "http://localhost:8080/auth/callback", "http://localhost:9000/auth/callback"], "responseTypes": ["OIDC_RESPONSE_TYPE_CODE"], "grantTypes": ["OIDC_GRANT_TYPE_AUTHORIZATION_CODE", "OIDC_GRANT_TYPE_REFRESH_TOKEN", "OIDC_GRANT_TYPE_PASSWORD"], "appType": "OIDC_APP_TYPE_WEB", "authMethodType": "OIDC_AUTH_METHOD_TYPE_POST", - "postLogoutRedirectUris": ["http://localhost:8080", "http://localhost:3000", "http://localhost:8080"], + "postLogoutRedirectUris": ["http://localhost:8080", "http://localhost:3000", "http://localhost:9000"], "accessTokenType": "OIDC_TOKEN_TYPE_BEARER", "devMode": true, })) diff --git a/src/core/secrets/mod.rs b/src/core/secrets/mod.rs index 6163528d7..fa3a0c355 100644 --- a/src/core/secrets/mod.rs +++ b/src/core/secrets/mod.rs @@ -20,6 +20,7 @@ impl SecretPaths { pub const EMAIL: &'static str = "gbo/email"; pub const LLM: &'static str = "gbo/llm"; pub const ENCRYPTION: &'static str = "gbo/encryption"; + pub const JWT: &'static str = "gbo/jwt"; pub const MEET: &'static str = "gbo/meet"; pub const ALM: &'static str = "gbo/alm"; pub const VECTORDB: &'static str = "gbo/vectordb"; @@ -270,6 +271,10 @@ impl SecretsManager { self.get_value(SecretPaths::ENCRYPTION, "master_key").await } + pub async fn get_jwt_secret(&self) -> Result { + self.get_value(SecretPaths::JWT, "secret").await + } + pub async fn put_secret(&self, path: &str, data: HashMap) -> Result<()> { let client = self .client diff --git a/src/designer/bas_analyzer.rs b/src/designer/bas_analyzer.rs index d6bd96745..e52712390 100644 --- a/src/designer/bas_analyzer.rs +++ b/src/designer/bas_analyzer.rs @@ -1,3 +1,4 @@ +use serde::Serialize; use std::path::Path; use std::fs; @@ -83,7 +84,7 @@ impl BasFileAnalyzer { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, Serialize)] pub struct WorkflowMetadata { pub name: String, pub step_count: usize, diff --git a/src/designer/workflow_canvas.rs b/src/designer/workflow_canvas.rs index 16a590638..2dc056b4b 100644 --- a/src/designer/workflow_canvas.rs +++ b/src/designer/workflow_canvas.rs @@ -242,7 +242,7 @@ pub async fn workflow_designer_page( content = 'Parallel
Multiple branches'; break; case 'event': - content = 'Event
'; + content = 'Event
'; break; } diff --git a/src/directory/auth_routes.rs b/src/directory/auth_routes.rs index 548a426db..e6feca181 100644 --- a/src/directory/auth_routes.rs +++ b/src/directory/auth_routes.rs @@ -406,7 +406,7 @@ pub async fn get_current_user( is_anonymous: true, }) } - Some(token) if token.is_empty() => { + Some("") => { info!("get_current_user: empty authorization token - returning anonymous user"); Json(CurrentUserResponse { id: None, diff --git a/src/drive/drive_monitor/mod.rs b/src/drive/drive_monitor/mod.rs index ec3012b1e..f58aba015 100644 --- a/src/drive/drive_monitor/mod.rs +++ b/src/drive/drive_monitor/mod.rs @@ -23,7 +23,6 @@ use serde::{Deserialize, Serialize}; use tokio::fs as tokio_fs; #[cfg(any(feature = "research", feature = "llm"))] -#[allow(dead_code)] const KB_INDEXING_TIMEOUT_SECS: u64 = 60; const MAX_BACKOFF_SECS: u64 = 300; const INITIAL_BACKOFF_SECS: u64 = 30; @@ -43,7 +42,6 @@ pub struct DriveMonitor { is_processing: Arc, consecutive_failures: Arc, #[cfg(any(feature = "research", feature = "llm"))] - #[allow(dead_code)] kb_indexing_in_progress: Arc>>, } impl DriveMonitor { diff --git a/src/security/command_guard.rs b/src/security/command_guard.rs index e77686970..f88286466 100644 --- a/src/security/command_guard.rs +++ b/src/security/command_guard.rs @@ -263,7 +263,23 @@ impl SafeCommand { } cmd.env_clear(); - cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin"); + + // Build PATH with standard locations plus botserver-stack/bin/shared + let mut path_entries = vec![ + "/usr/local/bin".to_string(), + "/usr/bin".to_string(), + "/bin".to_string(), + ]; + + // Add botserver-stack/bin/shared to PATH if it exists + let stack_path = std::env::var("BOTSERVER_STACK_PATH") + .unwrap_or_else(|_| "./botserver-stack".to_string()); + let shared_bin = format!("{}/bin/shared", stack_path); + if std::path::Path::new(&shared_bin).exists() { + path_entries.insert(0, shared_bin); + } + + cmd.env("PATH", path_entries.join(":")); cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp"))); cmd.env("LANG", "C.UTF-8"); @@ -284,7 +300,23 @@ impl SafeCommand { } cmd.env_clear(); - cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin"); + + // Build PATH with standard locations plus botserver-stack/bin/shared + let mut path_entries = vec![ + "/usr/local/bin".to_string(), + "/usr/bin".to_string(), + "/bin".to_string(), + ]; + + // Add botserver-stack/bin/shared to PATH if it exists + let stack_path = std::env::var("BOTSERVER_STACK_PATH") + .unwrap_or_else(|_| "./botserver-stack".to_string()); + let shared_bin = format!("{}/bin/shared", stack_path); + if std::path::Path::new(&shared_bin).exists() { + path_entries.insert(0, shared_bin); + } + + cmd.env("PATH", path_entries.join(":")); cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp"))); cmd.env("LANG", "C.UTF-8"); @@ -305,7 +337,23 @@ impl SafeCommand { } cmd.env_clear(); - cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin"); + + // Build PATH with standard locations plus botserver-stack/bin/shared + let mut path_entries = vec![ + "/usr/local/bin".to_string(), + "/usr/bin".to_string(), + "/bin".to_string(), + ]; + + // Add botserver-stack/bin/shared to PATH if it exists + let stack_path = std::env::var("BOTSERVER_STACK_PATH") + .unwrap_or_else(|_| "./botserver-stack".to_string()); + let shared_bin = format!("{}/bin/shared", stack_path); + if std::path::Path::new(&shared_bin).exists() { + path_entries.insert(0, shared_bin); + } + + cmd.env("PATH", path_entries.join(":")); cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp"))); cmd.env("LANG", "C.UTF-8"); @@ -326,7 +374,23 @@ impl SafeCommand { } cmd.env_clear(); - cmd.env("PATH", "/usr/local/bin:/usr/bin:/bin"); + + // Build PATH with standard locations plus botserver-stack/bin/shared + let mut path_entries = vec![ + "/usr/local/bin".to_string(), + "/usr/bin".to_string(), + "/bin".to_string(), + ]; + + // Add botserver-stack/bin/shared to PATH if it exists + let stack_path = std::env::var("BOTSERVER_STACK_PATH") + .unwrap_or_else(|_| "./botserver-stack".to_string()); + let shared_bin = format!("{}/bin/shared", stack_path); + if std::path::Path::new(&shared_bin).exists() { + path_entries.insert(0, shared_bin); + } + + cmd.env("PATH", path_entries.join(":")); cmd.env("HOME", dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp"))); cmd.env("LANG", "C.UTF-8"); diff --git a/src/security/panic_handler.rs b/src/security/panic_handler.rs index 04e100044..0080a6b7c 100644 --- a/src/security/panic_handler.rs +++ b/src/security/panic_handler.rs @@ -337,8 +337,6 @@ mod tests { fn test_catch_panic_failure() { let result = catch_panic(|| { panic!("test panic"); - #[allow(unreachable_code)] - 42 }); assert!(result.is_err()); assert!(result.unwrap_err().message.contains("test panic")); diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 3340cb4e3..ba099f023 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "mail", allow(unused_imports))] pub mod audit_log; pub mod menu_config; pub mod permission_inheritance; @@ -147,13 +146,10 @@ r##"
"## .to_string(), ) } #[derive(Debug, Deserialize)] -#[allow(dead_code)] struct SearchSettingsRequest { enable_fuzzy_search: Option, search_result_limit: Option, enable_ai_suggestions: Option, -index_attachments: Option, -search_sources: Option>, } #[derive(Debug, Serialize)] @@ -183,8 +179,15 @@ Json(SearchSettingsResponse { } +#[derive(Debug, Serialize)] +struct SmtpTestResponse { +success: bool, +message: Option, +error: Option, +} + +#[cfg(feature = "mail")] #[derive(Debug, Deserialize)] -#[allow(dead_code)] struct SmtpTestRequest { host: String, port: i32, @@ -193,11 +196,14 @@ password: Option, use_tls: Option, } -#[derive(Debug, Serialize)] -struct SmtpTestResponse { -success: bool, -message: Option, -error: Option, +#[cfg(not(feature = "mail"))] +#[derive(Debug, Deserialize)] +struct SmtpTestRequest { +_host: String, +_port: i32, +_username: Option, +_password: Option, +_use_tls: Option, } #[cfg(feature = "mail")] diff --git a/src/whatsapp/mod.rs b/src/whatsapp/mod.rs index 5dd6bdcc8..8148394ee 100644 --- a/src/whatsapp/mod.rs +++ b/src/whatsapp/mod.rs @@ -344,7 +344,6 @@ async fn process_attendant_command( } async fn check_is_attendant(state: &Arc, phone: &str) -> bool { - let _conn = state.conn.clone(); let phone_clone = phone.to_string(); tokio::task::spawn_blocking(move || {