Skip to main content

phantomdev_detector/
lib.rs

1//! PhantomDev Detection Engine
2//!
3//! This crate provides AI detection capabilities using local models
4//! and cloud API fallbacks.
5//!
6//! Built with ❤️ by John Varghese (J0X)
7//! GitHub: https://github.com/John-Varghese-EH
8//! LinkedIn: https://linkedin.com/in/John--Varghese
9
10use phantomdev_core::{CodeBlock, DetectionResult, Detector, Pattern, PatternType, StealthScore};
11use std::sync::Arc;
12
13/// Main detector implementation
14pub struct PhantomDetector {
15    #[allow(dead_code)]
16    config: DetectorConfig,
17    local_model: Option<Arc<dyn LocalModel>>,
18    cloud_client: Option<Arc<dyn CloudClient>>,
19}
20
21/// Detector configuration
22#[derive(Clone)]
23pub struct DetectorConfig {
24    /// Whether to use local models
25    pub use_local: bool,
26    /// Whether to use cloud API as fallback
27    pub use_cloud_fallback: bool,
28    /// AI probability threshold
29    pub threshold: f32,
30}
31
32impl Default for DetectorConfig {
33    fn default() -> Self {
34        Self {
35            use_local: true,
36            use_cloud_fallback: true,
37            threshold: 0.15,
38        }
39    }
40}
41
42impl PhantomDetector {
43    /// Create a new detector with default configuration
44    pub fn new() -> anyhow::Result<Self> {
45        Self::with_config(DetectorConfig::default())
46    }
47
48    /// Create a new detector with custom configuration
49    pub fn with_config(config: DetectorConfig) -> anyhow::Result<Self> {
50        let local_model: Option<Arc<dyn LocalModel>> = if config.use_local {
51            Some(Arc::new(RobertaDetector::new()?))
52        } else {
53            None
54        };
55
56        let cloud_client: Option<Arc<dyn CloudClient>> = if config.use_cloud_fallback {
57            Some(Arc::new(CloudDetector::new()?))
58        } else {
59            None
60        };
61
62        Ok(Self {
63            config,
64            local_model,
65            cloud_client,
66        })
67    }
68
69    /// Detect using local model
70    fn detect_local(&self, code: &CodeBlock) -> anyhow::Result<Option<DetectionResult>> {
71        if let Some(model) = &self.local_model {
72            Ok(Some(model.detect(code)?))
73        } else {
74            Ok(None)
75        }
76    }
77
78    /// Detect using cloud API
79    async fn detect_cloud(&self, code: &CodeBlock) -> anyhow::Result<Option<DetectionResult>> {
80        if let Some(client) = &self.cloud_client {
81            Ok(Some(client.detect(code).await?))
82        } else {
83            Ok(None)
84        }
85    }
86
87    /// Detect patterns in code
88    fn detect_patterns(&self, code: &CodeBlock) -> Vec<Pattern> {
89        let mut patterns = Vec::new();
90
91        // Check for excessive emojis
92        let emoji_count = code.content.chars().filter(|c| is_emoji(*c)).count();
93        if emoji_count > 3 {
94            patterns.push(Pattern {
95                pattern_type: PatternType::ExcessiveEmojis,
96                confidence: (emoji_count as f32 / 10.0).min(1.0),
97                location: None,
98            });
99        }
100
101        // Check for watermarks
102        if code.content.contains("TODO") || code.content.contains("FIXME") {
103            patterns.push(Pattern {
104                pattern_type: PatternType::Watermark,
105                confidence: 0.3,
106                location: None,
107            });
108        }
109
110        // Check for uniform comments
111        let comment_lines: Vec<_> = code.content
112            .lines()
113            .filter(|l| l.trim().starts_with("//") || l.trim().starts_with("#"))
114            .collect();
115        if comment_lines.len() > 5 {
116            patterns.push(Pattern {
117                pattern_type: PatternType::UniformComments,
118                confidence: 0.4,
119                location: None,
120            });
121        }
122
123        patterns
124    }
125}
126
127impl Detector for PhantomDetector {
128    fn detect(&self, code: &CodeBlock) -> anyhow::Result<DetectionResult> {
129        // Try local detection first
130        let mut result = if let Some(local_result) = self.detect_local(code)? {
131            local_result
132        } else {
133            // Fall back to cloud detection
134            let runtime = tokio::runtime::Runtime::new()?;
135            if let Some(cloud_result) = runtime.block_on(self.detect_cloud(code))? {
136                cloud_result
137            } else {
138                // Default result if no detection available
139                DetectionResult {
140                    score: StealthScore::new(0.5, 0.5, 0.5),
141                    patterns: Vec::new(),
142                    sections: Vec::new(),
143                }
144            }
145        };
146
147        // Add pattern detection
148        let patterns = self.detect_patterns(code);
149        result.patterns.extend(patterns);
150
151        Ok(result)
152    }
153}
154
155/// Trait for local ML models
156pub trait LocalModel: Send + Sync {
157    fn detect(&self, code: &CodeBlock) -> anyhow::Result<DetectionResult>;
158}
159
160/// Trait for cloud API clients
161#[async_trait::async_trait]
162pub trait CloudClient: Send + Sync {
163    async fn detect(&self, code: &CodeBlock) -> anyhow::Result<DetectionResult>;
164}
165
166/// RoBERTa-based local detector
167pub struct RobertaDetector {
168    // Model placeholder - will be implemented with candle
169}
170
171impl RobertaDetector {
172    pub fn new() -> anyhow::Result<Self> {
173        // TODO: Initialize candle model
174        Ok(Self {})
175    }
176}
177
178impl LocalModel for RobertaDetector {
179    fn detect(&self, _code: &CodeBlock) -> anyhow::Result<DetectionResult> {
180        // TODO: Implement actual detection with RoBERTa model
181        // For now, return a placeholder result
182        Ok(DetectionResult {
183            score: StealthScore::new(0.5, 0.5, 0.5),
184            patterns: Vec::new(),
185            sections: Vec::new(),
186        })
187    }
188}
189
190/// Cloud-based detector
191pub struct CloudDetector {
192    // API client placeholder
193}
194
195impl CloudDetector {
196    pub fn new() -> anyhow::Result<Self> {
197        // TODO: Initialize API client
198        Ok(Self {})
199    }
200}
201
202#[async_trait::async_trait]
203impl CloudClient for CloudDetector {
204    async fn detect(&self, _code: &CodeBlock) -> anyhow::Result<DetectionResult> {
205        // TODO: Implement actual cloud API detection
206        // For now, return a placeholder result
207        Ok(DetectionResult {
208            score: StealthScore::new(0.5, 0.5, 0.5),
209            patterns: Vec::new(),
210            sections: Vec::new(),
211        })
212    }
213}
214
215/// Check if a character is an emoji
216fn is_emoji(c: char) -> bool {
217    // Basic emoji detection - can be improved
218    let as_u32 = c as u32;
219    (0x1F600..=0x1F64F).contains(&as_u32) // Emoticons
220        || (0x1F300..=0x1F5FF).contains(&as_u32) // Misc Symbols and Pictographs
221        || (0x1F680..=0x1F6FF).contains(&as_u32) // Transport and Map
222        || (0x1F1E0..=0x1F1FF).contains(&as_u32) // Flags
223        || (0x2600..=0x26FF).contains(&as_u32) // Misc symbols
224        || (0x2700..=0x27BF).contains(&as_u32) // Dingbats
225}
226
227#[cfg(test)]
228mod tests {
229    use super::*;
230
231    #[test]
232    fn test_detector_creation() {
233        let detector = PhantomDetector::new().unwrap();
234        assert!(detector.local_model.is_some());
235    }
236
237    #[test]
238    fn test_pattern_detection() {
239        let detector = PhantomDetector::new().unwrap();
240        let code = CodeBlock {
241            path: "test.rs".into(),
242            language: phantomdev_core::Language::Rust,
243            content: "// TODO: fix this 🎉\n// Another comment 🚀\n// Yet another 🎯".to_string(),
244            line_range: (1, 3),
245        };
246        let patterns = detector.detect_patterns(&code);
247        assert!(!patterns.is_empty());
248    }
249}