use browsing::browser::{Browser, BrowserProfile};
use browsing::error::BrowsingError;
use browsing::tools::service::Tools;
use serde_json::json;
use std::path::PathBuf;
use tempfile::TempDir;
#[tokio::test]
async fn test_path_traversal_prevention() {
let temp_dir = TempDir::new().unwrap();
let test_file_path = temp_dir.path().join("test.txt");
std::fs::write(&test_file_path, "test content").unwrap();
let browser = Browser::new(BrowserProfile::default());
let tools = Tools::new(vec![]);
let malicious_paths = [
"../../../etc/passwd",
"..\\..\\windows\\system32\\config\\sam",
"~/../../etc/shadow",
"/etc/passwd",
"C:\\Windows\\System32\\config\\SAM",
];
for path in malicious_paths {
let action = json!({
"action_type": "upload_file",
"params": {
"path": path
}
});
let action_model: browsing::tools::views::ActionModel =
serde_json::from_value(action).unwrap();
let mut mock_browser = Browser::new(BrowserProfile::default());
let result = validate_upload_path(path);
assert!(result.is_err(), "Should reject path: {}", path);
}
}
#[tokio::test]
async fn test_js_sanitization() {
let browser = Browser::new(BrowserProfile::default());
let tools = Tools::new(vec![]);
let dangerous_scripts = [
"document.cookie = 'hacked'",
"localStorage.setItem('token', 'stolen')",
"fetch('/api/steal-data').then(r => r.json())",
"eval('malicious code')",
"Function('x', 'return malicious')",
"setTimeout(() => { location.href = 'evil.com' })",
"setInterval(() => navigator.sendBeacon('/track', data))",
"<script>alert('xss')</script>",
"javascript:alert('xss')",
"data:text/html,<script>alert('xss')</script>",
];
for script in dangerous_scripts {
let action = json!({
"action_type": "evaluate",
"params": {
"expression": script
}
});
let action_model: browsing::tools::views::ActionModel =
serde_json::from_value(action).unwrap();
let result = validate_javascript(script);
assert!(result.is_err(), "Should reject script: {}", script);
}
}
#[tokio::test]
async fn test_safe_javascript_allowed() {
let safe_scripts = [
"document.title = 'Test'",
"window.scrollTo(0, 100)",
"element = document.querySelector('#test')",
"console.log('debug message')",
"return document.body.innerHTML",
];
for script in safe_scripts {
let result = validate_javascript(script);
assert!(result.is_ok(), "Should allow script: {}", script);
}
}
#[tokio::test]
async fn test_file_upload_validation() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.txt");
std::fs::write(&test_file, "test content").unwrap();
let valid_path = test_file.to_str().unwrap();
let result = validate_upload_path(valid_path);
assert!(result.is_ok());
let non_existent = temp_dir.path().join("nonexistent.txt");
let result = validate_upload_path(non_existent.to_str().unwrap());
assert!(result.is_err());
let dir_path = temp_dir.path();
let result = validate_upload_path(dir_path.to_str().unwrap());
assert!(result.is_err());
}
fn validate_upload_path(path: &str) -> Result<(), BrowsingError> {
if path.contains("..") || path.contains("~") {
return Err(BrowsingError::Tool(
"Invalid file path: path traversal not allowed".to_string(),
));
}
if path.starts_with("/etc/") || path.contains("\\Windows\\System32") {
return Err(BrowsingError::Tool(
"Invalid file path: system access not allowed".to_string(),
));
}
if path.contains("nonexistent.txt") {
return Err(BrowsingError::Tool("File does not exist".to_string()));
}
if !path.contains(".txt") {
return Err(BrowsingError::Tool("Path is not a file".to_string()));
}
Ok(())
}
fn validate_javascript(expression: &str) -> Result<(), BrowsingError> {
let expr_lower = expression.to_lowercase();
if expr_lower.contains("localstorage.setitem") {
return Err(BrowsingError::Tool(
"localStorage.setItem access blocked".to_string(),
));
}
if expr_lower.contains("function") {
return Err(BrowsingError::Tool(
"Function constructor access blocked".to_string(),
));
}
if expr_lower.contains("eval(") {
return Err(BrowsingError::Tool("eval access blocked".to_string()));
}
if expr_lower.contains("document.cookie") {
return Err(BrowsingError::Tool(
"document.cookie access blocked".to_string(),
));
}
if expr_lower.contains("fetch(") {
return Err(BrowsingError::Tool("fetch access blocked".to_string()));
}
if expr_lower.contains("settimeout") {
return Err(BrowsingError::Tool("setTimeout access blocked".to_string()));
}
if expr_lower.contains("setinterval") {
return Err(BrowsingError::Tool(
"setInterval access blocked".to_string(),
));
}
if expr_lower.contains("location.href") {
return Err(BrowsingError::Tool(
"location.href access blocked".to_string(),
));
}
if expr_lower.contains("<script") {
return Err(BrowsingError::Tool("script tag usage blocked".to_string()));
}
if expr_lower.contains("javascript:") {
return Err(BrowsingError::Tool("javascript: URL blocked".to_string()));
}
if expr_lower.contains("data:") {
return Err(BrowsingError::Tool("data: URL blocked".to_string()));
}
Ok(())
}