Initial commit - AiRust cyber operator
This commit is contained in:
+148
@@ -0,0 +1,148 @@
|
||||
use anyhow::{Context, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::Duration;
|
||||
use std::io::Write;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ExecutionResult {
|
||||
pub command: String,
|
||||
pub exit_code: Option<i32>,
|
||||
pub stdout: String,
|
||||
pub stderr: String,
|
||||
pub duration_ms: u64,
|
||||
pub success: bool,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ScriptExecution {
|
||||
pub language: String,
|
||||
pub code: String,
|
||||
pub result: ExecutionResult,
|
||||
}
|
||||
|
||||
pub fn execute_command(cmd: &str, args: &[&str], timeout_secs: u64) -> Result<ExecutionResult> {
|
||||
let _start = std::time::Instant::now();
|
||||
let mut child = Command::new(cmd)
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.with_context(|| format!("Failed to spawn: {} {}", cmd, args.join(" ")))?;
|
||||
|
||||
let timeout = Duration::from_secs(timeout_secs);
|
||||
let mut elapsed = Duration::ZERO;
|
||||
let check_interval = Duration::from_millis(50);
|
||||
|
||||
loop {
|
||||
match child.try_wait()? {
|
||||
Some(status) => {
|
||||
let stdout = std::io::read_to_string(child.stdout.take().unwrap()).unwrap_or_default();
|
||||
let stderr = std::io::read_to_string(child.stderr.take().unwrap()).unwrap_or_default();
|
||||
return Ok(ExecutionResult {
|
||||
command: format!("{} {}", cmd, args.join(" ")),
|
||||
exit_code: status.code(),
|
||||
stdout: stdout.trim().to_string(),
|
||||
stderr: stderr.trim().to_string(),
|
||||
duration_ms: elapsed.as_millis() as u64,
|
||||
success: status.success(),
|
||||
});
|
||||
}
|
||||
None => {
|
||||
if elapsed >= timeout {
|
||||
child.kill()?;
|
||||
return Ok(ExecutionResult {
|
||||
command: format!("{} {}", cmd, args.join(" ")),
|
||||
exit_code: None,
|
||||
stdout: String::new(),
|
||||
stderr: format!("Killed after {}s timeout", timeout_secs),
|
||||
duration_ms: timeout.as_millis() as u64,
|
||||
success: false,
|
||||
});
|
||||
}
|
||||
std::thread::sleep(check_interval);
|
||||
elapsed += check_interval;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_script(language: &str, code: &str, timeout_secs: u64) -> Result<ExecutionResult> {
|
||||
let (interpreter, ext) = match language.to_lowercase().as_str() {
|
||||
"python" | "py" => ("python", ".py"),
|
||||
"bash" | "sh" => ("bash", ".sh"),
|
||||
"powershell" | "ps1" => ("powershell", ".ps1"),
|
||||
"cmd" | "bat" => ("cmd", ".bat"),
|
||||
"ruby" => ("ruby", ".rb"),
|
||||
"perl" => ("perl", ".pl"),
|
||||
"node" | "javascript" | "js" => ("node", ".js"),
|
||||
"rust" => return compile_and_run_rust(code, timeout_secs),
|
||||
_ => return Err(anyhow::anyhow!("Unsupported language: {}. Supported: python, bash, powershell, cmd, ruby, perl, node, rust", language)),
|
||||
};
|
||||
|
||||
let temp_dir = std::env::temp_dir().join("airust_scripts");
|
||||
std::fs::create_dir_all(&temp_dir)?;
|
||||
let script_path = temp_dir.join(format!("script_{}{}", std::process::id(), ext));
|
||||
|
||||
let mut file = std::fs::File::create(&script_path)?;
|
||||
file.write_all(code.as_bytes())?;
|
||||
drop(file);
|
||||
|
||||
let result = if interpreter == "cmd" {
|
||||
execute_command("cmd", &["/C", script_path.to_str().unwrap()], timeout_secs)
|
||||
} else {
|
||||
execute_command(interpreter, &[script_path.to_str().unwrap()], timeout_secs)
|
||||
};
|
||||
|
||||
let _ = std::fs::remove_file(&script_path);
|
||||
result
|
||||
}
|
||||
|
||||
fn compile_and_run_rust(code: &str, timeout_secs: u64) -> Result<ExecutionResult> {
|
||||
let temp_dir = std::env::temp_dir().join("airust_scripts");
|
||||
std::fs::create_dir_all(&temp_dir)?;
|
||||
|
||||
let source_path = temp_dir.join(format!("script_{}.rs", std::process::id()));
|
||||
let bin_path = temp_dir.join(format!("script_{}", std::process::id()));
|
||||
|
||||
let mut file = std::fs::File::create(&source_path)?;
|
||||
file.write_all(code.as_bytes())?;
|
||||
drop(file);
|
||||
|
||||
let compile = execute_command("rustc", &[source_path.to_str().unwrap(), "-o", bin_path.to_str().unwrap(), "--quiet"], timeout_secs)?;
|
||||
if !compile.success {
|
||||
return Ok(ExecutionResult {
|
||||
command: "rustc (compile)".to_string(),
|
||||
exit_code: compile.exit_code,
|
||||
stdout: String::new(),
|
||||
stderr: compile.stderr,
|
||||
duration_ms: compile.duration_ms,
|
||||
success: false,
|
||||
});
|
||||
}
|
||||
|
||||
let run_result = if cfg!(windows) {
|
||||
execute_command(bin_path.to_str().unwrap(), &[], timeout_secs)
|
||||
} else {
|
||||
execute_command(&bin_path.to_str().unwrap(), &[], timeout_secs)
|
||||
};
|
||||
|
||||
let _ = std::fs::remove_file(&source_path);
|
||||
let _ = std::fs::remove_file(&bin_path);
|
||||
run_result
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn list_supported_languages() -> Vec<String> {
|
||||
vec![
|
||||
"python".to_string(),
|
||||
"bash".to_string(),
|
||||
"powershell".to_string(),
|
||||
"cmd".to_string(),
|
||||
"node".to_string(),
|
||||
"ruby".to_string(),
|
||||
"perl".to_string(),
|
||||
"rust".to_string(),
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user