#![cfg(kani)]
use crate::ast::{Expr, Stmt, Type};
use crate::emitter::escape::{escape_shell_string, escape_shell_value};
use crate::services::parser;
use crate::verifier::properties::{is_valid_rust0, validate_rust0_ast};
#[kani::proof]
#[kani::unwind(10)]
fn verify_parser_soundness() {
let input: &str = kani::any();
kani::assume(input.len() < 1000);
match parser::parse(input) {
Ok(ast) => {
kani::assert!(validate_rust0_ast(&ast));
}
Err(_) => {
kani::assert!(!is_valid_rust0(input));
}
}
}
#[kani::proof]
#[kani::unwind(20)]
fn verify_escape_safety() {
let input: String = kani::any();
kani::assume(input.len() < 100);
let escaped = escape_shell_string(&input);
kani::assert!(escaped.starts_with('\'') && escaped.ends_with('\''));
kani::assert!(!contains_unescaped_metachar(&escaped));
let unescaped = unescape_shell_string(&escaped);
kani::assert!(unescaped == input);
}
#[kani::proof]
#[kani::unwind(15)]
fn verify_variable_expansion_safety() {
let var_name: String = kani::any();
kani::assume(var_name.len() < 50);
kani::assume(is_valid_identifier(&var_name));
let expansion = format!("\"${{{}}}\"", var_name);
kani::assert!(expansion.starts_with('"') && expansion.ends_with('"'));
kani::assert!(expansion.contains("${") && expansion.contains("}"));
}
#[kani::proof]
#[kani::unwind(10)]
fn verify_injection_safety() {
let user_input: String = kani::any();
kani::assume(user_input.len() < 100);
let contexts = vec![
format!("echo {}", escape_shell_string(&user_input)),
format!("VAR={}", escape_shell_value(&user_input)),
format!(
"if [ \"${{VAR}}\" = {} ]; then",
escape_shell_string(&user_input)
),
];
for context in contexts {
kani::assert!(!can_inject_command(&context, &user_input));
}
}
#[kani::proof]
#[kani::unwind(5)]
fn verify_array_bounds_safety() {
let array_size: usize = kani::any();
kani::assume(array_size > 0 && array_size < 100);
let index: usize = kani::any();
if index < array_size {
kani::assert!(true);
} else {
let check = format!("if [ {} -lt {} ]; then", index, array_size);
kani::assert!(check.contains("-lt"));
}
}
fn contains_unescaped_metachar(s: &str) -> bool {
let mut in_quotes = false;
let mut escaped = false;
for ch in s.chars() {
if escaped {
escaped = false;
continue;
}
match ch {
'\\' => escaped = true,
'\'' => in_quotes = !in_quotes,
';' | '&' | '|' | '`' | '$' | '(' | ')' | '<' | '>' | '\n' => {
if !in_quotes {
return true;
}
}
_ => {}
}
}
false
}
fn unescape_shell_string(s: &str) -> String {
if s.starts_with('\'') && s.ends_with('\'') {
let inner = &s[1..s.len() - 1];
inner.replace("'\"'\"'", "'")
} else {
s.to_string()
}
}
fn is_valid_identifier(s: &str) -> bool {
!s.is_empty()
&& s.chars().all(|c| c.is_alphanumeric() || c == '_')
&& s.chars()
.next()
.map_or(false, |c| c.is_alphabetic() || c == '_')
}
fn can_inject_command(shell_code: &str, user_input: &str) -> bool {
let dangerous_patterns = [";", "&&", "||", "|", "`", "$(", "\n"];
for pattern in &dangerous_patterns {
if shell_code.contains(pattern) && !is_properly_escaped(shell_code, pattern) {
return true;
}
}
false
}
fn is_properly_escaped(code: &str, pattern: &str) -> bool {
code.contains(&format!("'{}'", pattern)) || code.contains(&format!("\"{}\"", pattern))
}