From 2b7835225221fa6b56217f0930f5604dc0ed6d34 Mon Sep 17 00:00:00 2001 From: ztsalexey Date: Tue, 10 Feb 2026 13:40:31 -0700 Subject: [PATCH] fix: replace bare unwrap() with expect() in production code The project standard (CLAUDE.md) prohibits .unwrap() in production code, but 31 calls existed across the codebase. A full audit confirmed all remaining unwrap calls are in #[cfg(test)] blocks. All 31 are technically safe (hardcoded regex patterns, guarded pops, response builders with valid inputs, or state set in prior steps), but bare unwrap() hides the safety reasoning. Replacing with expect() documents the invariant and gives clear panic messages if assumptions are ever violated. Changes: - leak_detector.rs: 16 Regex::new().unwrap() -> .expect("valid regex") - sanitizer.rs: 4 Regex::new().unwrap() -> .expect("valid regex") - sandbox/proxy/http.rs: 3 Response::builder().unwrap() -> .expect("valid response") - setup/wizard.rs: 2 state.unwrap() -> .expect("set in previous step") - chunker.rs: 2 chunks.pop().unwrap() -> .expect("checked non-empty above") - repository.rs: 1 embedding.unwrap() -> .expect("checked is_some above") - settings.rs: 1 parts.last().unwrap() -> .expect("checked non-empty above") - tools/builder/core.rs: 1 to_string().unwrap() -> .expect("serializable output") - cli/tool.rs: 1 check.unwrap() -> eliminated via map_or --- src/cli/tool.rs | 2 +- src/safety/leak_detector.rs | 32 ++++++++++++++++---------------- src/safety/sanitizer.rs | 8 ++++---- src/sandbox/proxy/http.rs | 6 +++--- src/settings.rs | 2 +- src/setup/wizard.rs | 4 ++-- src/tools/builder/core.rs | 2 +- src/workspace/chunker.rs | 4 ++-- src/workspace/repository.rs | 2 +- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/cli/tool.rs b/src/cli/tool.rs index 9a86bfd783c..ff8b6eb6f49 100644 --- a/src/cli/tool.rs +++ b/src/cli/tool.rs @@ -259,7 +259,7 @@ fn build_wasm_component(source_dir: &Path, release: bool) -> anyhow::Result Vec { // OpenAI API keys LeakPattern { name: "openai_api_key".to_string(), - regex: Regex::new(r"sk-(?:proj-)?[a-zA-Z0-9]{20,}(?:T3BlbkFJ[a-zA-Z0-9_-]*)?").unwrap(), + regex: Regex::new(r"sk-(?:proj-)?[a-zA-Z0-9]{20,}(?:T3BlbkFJ[a-zA-Z0-9_-]*)?").expect("valid openai_api_key regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // Anthropic API keys LeakPattern { name: "anthropic_api_key".to_string(), - regex: Regex::new(r"sk-ant-api[a-zA-Z0-9_-]{90,}").unwrap(), + regex: Regex::new(r"sk-ant-api[a-zA-Z0-9_-]{90,}").expect("valid anthropic_api_key regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // AWS Access Key ID LeakPattern { name: "aws_access_key".to_string(), - regex: Regex::new(r"AKIA[0-9A-Z]{16}").unwrap(), + regex: Regex::new(r"AKIA[0-9A-Z]{16}").expect("valid aws_access_key regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // GitHub tokens LeakPattern { name: "github_token".to_string(), - regex: Regex::new(r"gh[pousr]_[A-Za-z0-9_]{36,}").unwrap(), + regex: Regex::new(r"gh[pousr]_[A-Za-z0-9_]{36,}").expect("valid github_token regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // GitHub fine-grained PAT LeakPattern { name: "github_fine_grained_pat".to_string(), - regex: Regex::new(r"github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}").unwrap(), + regex: Regex::new(r"github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59}").expect("valid github_fine_grained_pat regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // Stripe keys LeakPattern { name: "stripe_api_key".to_string(), - regex: Regex::new(r"sk_(?:live|test)_[a-zA-Z0-9]{24,}").unwrap(), + regex: Regex::new(r"sk_(?:live|test)_[a-zA-Z0-9]{24,}").expect("valid stripe_api_key regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // NEAR AI session tokens LeakPattern { name: "nearai_session".to_string(), - regex: Regex::new(r"sess_[a-zA-Z0-9]{32,}").unwrap(), + regex: Regex::new(r"sess_[a-zA-Z0-9]{32,}").expect("valid nearai_session regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // PEM private keys LeakPattern { name: "pem_private_key".to_string(), - regex: Regex::new(r"-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----").unwrap(), + regex: Regex::new(r"-----BEGIN\s+(?:RSA\s+)?PRIVATE\s+KEY-----").expect("valid pem_private_key regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // SSH private keys LeakPattern { name: "ssh_private_key".to_string(), - regex: Regex::new(r"-----BEGIN\s+(?:OPENSSH|EC|DSA)\s+PRIVATE\s+KEY-----").unwrap(), + regex: Regex::new(r"-----BEGIN\s+(?:OPENSSH|EC|DSA)\s+PRIVATE\s+KEY-----").expect("valid ssh_private_key regex"), severity: LeakSeverity::Critical, action: LeakAction::Block, }, // Google API keys LeakPattern { name: "google_api_key".to_string(), - regex: Regex::new(r"AIza[0-9A-Za-z_-]{35}").unwrap(), + regex: Regex::new(r"AIza[0-9A-Za-z_-]{35}").expect("valid google_api_key regex"), severity: LeakSeverity::High, action: LeakAction::Block, }, // Slack tokens LeakPattern { name: "slack_token".to_string(), - regex: Regex::new(r"xox[baprs]-[0-9a-zA-Z-]{10,}").unwrap(), + regex: Regex::new(r"xox[baprs]-[0-9a-zA-Z-]{10,}").expect("valid slack_token regex"), severity: LeakSeverity::High, action: LeakAction::Block, }, // Twilio API keys LeakPattern { name: "twilio_api_key".to_string(), - regex: Regex::new(r"SK[a-fA-F0-9]{32}").unwrap(), + regex: Regex::new(r"SK[a-fA-F0-9]{32}").expect("valid twilio_api_key regex"), severity: LeakSeverity::High, action: LeakAction::Block, }, // SendGrid API keys LeakPattern { name: "sendgrid_api_key".to_string(), - regex: Regex::new(r"SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}").unwrap(), + regex: Regex::new(r"SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}").expect("valid sendgrid_api_key regex"), severity: LeakSeverity::High, action: LeakAction::Block, }, // Bearer tokens (redact instead of block, might be intentional) LeakPattern { name: "bearer_token".to_string(), - regex: Regex::new(r"Bearer\s+[a-zA-Z0-9_-]{20,}").unwrap(), + regex: Regex::new(r"Bearer\s+[a-zA-Z0-9_-]{20,}").expect("valid bearer_token regex"), severity: LeakSeverity::High, action: LeakAction::Redact, }, // Authorization header with key LeakPattern { name: "auth_header".to_string(), - regex: Regex::new(r"(?i)authorization:\s*[a-zA-Z]+\s+[a-zA-Z0-9_-]{20,}").unwrap(), + regex: Regex::new(r"(?i)authorization:\s*[a-zA-Z]+\s+[a-zA-Z0-9_-]{20,}").expect("valid auth_header regex"), severity: LeakSeverity::High, action: LeakAction::Redact, }, @@ -516,7 +516,7 @@ fn default_patterns() -> Vec { // This catches standalone 64-char hex strings (like SHA256 hashes used as secrets). LeakPattern { name: "high_entropy_hex".to_string(), - regex: Regex::new(r"\b[a-fA-F0-9]{64}\b").unwrap(), + regex: Regex::new(r"\b[a-fA-F0-9]{64}\b").expect("valid high_entropy_hex regex"), severity: LeakSeverity::Medium, action: LeakAction::Warn, }, diff --git a/src/safety/sanitizer.rs b/src/safety/sanitizer.rs index 60ab9901cbc..701dba64c28 100644 --- a/src/safety/sanitizer.rs +++ b/src/safety/sanitizer.rs @@ -165,25 +165,25 @@ impl Sanitizer { // Regex patterns for more complex detection let regex_patterns = vec![ RegexPattern { - regex: Regex::new(r"(?i)base64[:\s]+[A-Za-z0-9+/=]{50,}").unwrap(), + regex: Regex::new(r"(?i)base64[:\s]+[A-Za-z0-9+/=]{50,}").expect("valid base64_payload regex"), name: "base64_payload".to_string(), severity: Severity::Medium, description: "Potential encoded payload".to_string(), }, RegexPattern { - regex: Regex::new(r"(?i)eval\s*\(").unwrap(), + regex: Regex::new(r"(?i)eval\s*\(").expect("valid eval_call regex"), name: "eval_call".to_string(), severity: Severity::High, description: "Potential code evaluation attempt".to_string(), }, RegexPattern { - regex: Regex::new(r"(?i)exec\s*\(").unwrap(), + regex: Regex::new(r"(?i)exec\s*\(").expect("valid exec_call regex"), name: "exec_call".to_string(), severity: Severity::High, description: "Potential code execution attempt".to_string(), }, RegexPattern { - regex: Regex::new(r"\x00").unwrap(), + regex: Regex::new(r"\x00").expect("valid null_byte regex"), name: "null_byte".to_string(), severity: Severity::Critical, description: "Null byte injection attempt".to_string(), diff --git a/src/sandbox/proxy/http.rs b/src/sandbox/proxy/http.rs index 6b0a3e82df7..90b1ee3a7b8 100644 --- a/src/sandbox/proxy/http.rs +++ b/src/sandbox/proxy/http.rs @@ -273,7 +273,7 @@ async fn handle_connect( Response::builder() .status(StatusCode::OK) .body(empty_body()) - .unwrap() + .expect("valid empty CONNECT response") } /// Forward a request to the target server. @@ -355,7 +355,7 @@ async fn forward_request( } } - Ok(builder.body(full_body(body)).unwrap()) + Ok(builder.body(full_body(body)).expect("valid proxied response")) } Err(e) => { tracing::error!("Proxy: failed to read response body: {}", e); @@ -397,7 +397,7 @@ fn error_response(status: StatusCode, message: String) -> Response Vec { // Don't create tiny trailing chunks, merge with previous if chunk_words.len() < config.min_chunk_size && !chunks.is_empty() { - let last = chunks.pop().unwrap(); + let last = chunks.pop().expect("checked non-empty above"); let combined = format!("{} {}", last, chunk_words.join(" ")); chunks.push(combined); break; @@ -176,7 +176,7 @@ pub fn chunk_by_paragraphs(content: &str, config: ChunkConfig) -> Vec { if !current_chunk.is_empty() { // If too small, merge with previous chunk if possible if current_word_count < config.min_chunk_size && !chunks.is_empty() { - let last = chunks.pop().unwrap(); + let last = chunks.pop().expect("checked non-empty above"); chunks.push(format!("{}\n\n{}", last, current_chunk.trim())); } else { chunks.push(current_chunk.trim().to_string()); diff --git a/src/workspace/repository.rs b/src/workspace/repository.rs index e350a2e3c95..db951db92eb 100644 --- a/src/workspace/repository.rs +++ b/src/workspace/repository.rs @@ -408,7 +408,7 @@ impl Repository { self.vector_search( user_id, agent_id, - embedding.unwrap(), + embedding.expect("checked is_some above"), config.pre_fusion_limit, ) .await?