use std::process::Command;
use crate::types::{CodeSuggestion, VerificationStatus};
pub fn verify_suggestion(suggestion: &mut CodeSuggestion) {
let code = &suggestion.suggested;
let lang = detect_language(code, suggestion.file_path.as_deref());
let status = match lang {
Language::Rust => verify_rust(code),
Language::Python => verify_python(code),
Language::JavaScript | Language::TypeScript => verify_js(code),
Language::Unknown => VerificationStatus::NotVerified,
};
suggestion.verification = status;
}
#[derive(Debug, Clone, Copy)]
enum Language {
Rust,
Python,
JavaScript,
TypeScript,
Unknown,
}
fn detect_language(code: &str, file_path: Option<&str>) -> Language {
if let Some(path) = file_path {
if path.ends_with(".rs") {
return Language::Rust;
}
if path.ends_with(".py") {
return Language::Python;
}
if path.ends_with(".js") {
return Language::JavaScript;
}
if path.ends_with(".ts") || path.ends_with(".tsx") {
return Language::TypeScript;
}
}
if code.contains("fn ")
&& (code.contains("->") || code.contains("let ") || code.contains("pub "))
{
return Language::Rust;
}
if code.contains("def ") || code.contains("import ") && code.contains(":") {
return Language::Python;
}
if code.contains("function ") || code.contains("const ") || code.contains("=>") {
return Language::JavaScript;
}
Language::Unknown
}
fn verify_rust(code: &str) -> VerificationStatus {
let tmp_dir = std::env::temp_dir().join("car-verify");
let _ = std::fs::create_dir_all(&tmp_dir);
let src_file = tmp_dir.join("check.rs");
let out_file = tmp_dir.join("check.rlib");
if std::fs::write(&src_file, code).is_err() {
return VerificationStatus::NotVerified;
}
let result = Command::new("rustc")
.args([
"--edition",
"2021",
"--crate-type",
"lib",
"-o",
&out_file.to_string_lossy(),
&src_file.to_string_lossy(),
])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.output();
let _ = std::fs::remove_file(&src_file);
let _ = std::fs::remove_file(&out_file);
match result {
Ok(output) if output.status.success() => VerificationStatus::Passed,
Ok(_) => VerificationStatus::PartiallyVerified, Err(_) => VerificationStatus::NotVerified,
}
}
fn verify_python(code: &str) -> VerificationStatus {
let tmp_dir = std::env::temp_dir().join("car-verify");
let _ = std::fs::create_dir_all(&tmp_dir);
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let id = COUNTER.fetch_add(1, Ordering::Relaxed);
let tmp_file = tmp_dir.join(format!("check_{}_{}.py", std::process::id(), id));
if std::fs::write(&tmp_file, code).is_err() {
return VerificationStatus::NotVerified;
}
let result = Command::new("python3")
.args(["-c", "import ast, sys; ast.parse(open(sys.argv[1]).read())"])
.arg(&tmp_file)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.output();
let _ = std::fs::remove_file(&tmp_file);
match result {
Ok(output) if output.status.success() => VerificationStatus::Passed,
Ok(_) => VerificationStatus::Failed,
Err(_) => VerificationStatus::NotVerified,
}
}
fn verify_js(code: &str) -> VerificationStatus {
let result = Command::new("node")
.args(["--check", "-e", code])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.output();
match result {
Ok(output) if output.status.success() => VerificationStatus::Passed,
Ok(_) => VerificationStatus::Failed,
Err(_) => VerificationStatus::NotVerified,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detect_rust() {
assert!(matches!(
detect_language("fn main() { let x = 5; }", None),
Language::Rust
));
assert!(matches!(
detect_language("code here", Some("src/main.rs")),
Language::Rust
));
}
#[test]
fn detect_python() {
assert!(matches!(
detect_language("def hello():\n print('hi')", None),
Language::Python
));
}
#[test]
fn verify_valid_rust() {
let status = verify_rust("pub fn add(a: i32, b: i32) -> i32 { a + b }");
assert!(matches!(
status,
VerificationStatus::Passed | VerificationStatus::NotVerified
));
}
#[test]
fn verify_valid_python() {
let status = verify_python("def add(a, b):\n return a + b");
assert!(matches!(
status,
VerificationStatus::Passed | VerificationStatus::NotVerified
));
}
#[test]
fn verify_python_injection_does_not_execute() {
let sentinel = std::env::temp_dir().join("car-verify-injection-pwned");
let _ = std::fs::remove_file(&sentinel);
let payload = format!(
"''')\nimport os; os.system('touch {}')#",
sentinel.display()
);
let _ = verify_python(&payload);
assert!(
!sentinel.exists(),
"verify_python executed an injected `os.system(...)`; the sandbox is broken"
);
}
}