use std::path::{Path, PathBuf};
use std::process::Command;
#[cfg(unix)]
mod unix;
#[cfg(unix)]
pub use unix::{current_uid_tasks, no_net_kernel_enforcement_available, spawn_and_wait};
#[cfg(windows)]
mod windows;
#[cfg(windows)]
pub use windows::{current_uid_tasks, no_net_kernel_enforcement_available, spawn_and_wait};
#[derive(Clone, Copy, Default)]
#[cfg_attr(not(windows), allow(dead_code))]
pub struct RawStdio {
pub stdin: isize,
pub stdout: isize,
pub stderr: isize,
}
#[derive(Clone, Copy)]
pub struct ResolvedLimits {
pub timeout_secs: u64,
#[cfg_attr(windows, allow(dead_code))]
pub cpu_secs: u64,
pub memory_bytes: u64,
#[cfg_attr(windows, allow(dead_code))]
pub fsize_bytes: u64,
#[cfg_attr(windows, allow(dead_code))]
pub nofile: u64,
pub max_processes: u64,
#[cfg_attr(unix, allow(dead_code))]
pub no_net: bool,
}
pub struct Wait {
pub exit_code: i64,
pub signal: Option<i32>,
pub timed_out: bool,
pub cpu_ms: u64,
pub peak_memory_kb: u64,
pub no_net_seccomp_enforced: bool,
pub unenforced: Vec<&'static str>,
}
pub fn no_net_shim(exe_dir: &Path) -> Option<std::path::PathBuf> {
let name = if cfg!(target_os = "macos") {
"blocknet.dylib"
} else if cfg!(target_os = "linux") {
"blocknet.so"
} else {
return None; };
let p = exe_dir.join(name);
if p.is_file() { Some(p) } else { None }
}
pub fn preload_env_var() -> Option<&'static str> {
if cfg!(target_os = "macos") {
Some("DYLD_INSERT_LIBRARIES")
} else if cfg!(target_os = "linux") {
Some("LD_PRELOAD")
} else {
None
}
}
pub fn apply_no_net(cmd: &mut Command, exe_dir: &Path) -> bool {
match (preload_env_var(), no_net_shim(exe_dir)) {
(Some(var), Some(shim)) => {
cmd.env(var, shim.to_string_lossy().into_owned());
true
}
_ => false,
}
}
#[cfg_attr(not(windows), allow(dead_code))]
pub fn quote_arg(arg: &std::ffi::OsStr) -> String {
let s = arg.to_string_lossy();
if !s.is_empty() && !s.contains([' ', '\t', '"']) {
return s.into_owned();
}
let mut out = String::with_capacity(s.len() + 2);
out.push('"');
let mut backslashes = 0usize;
for c in s.chars() {
match c {
'\\' => {
backslashes += 1;
out.push('\\');
}
'"' => {
for _ in 0..backslashes {
out.push('\\');
}
backslashes = 0;
out.push('\\');
out.push('"');
}
_ => {
backslashes = 0;
out.push(c);
}
}
}
for _ in 0..backslashes {
out.push('\\');
}
out.push('"');
out
}
#[cfg_attr(not(windows), allow(dead_code))]
pub fn is_windowsapps_alias_path(path: &Path) -> bool {
let display = path.to_string_lossy();
let mut prev_is_microsoft = false;
for part in display.split(['/', '\\']).filter(|p| !p.is_empty()) {
if prev_is_microsoft && part.eq_ignore_ascii_case("WindowsApps") {
return true;
}
prev_is_microsoft = part.eq_ignore_ascii_case("Microsoft");
}
false
}
#[cfg_attr(not(windows), allow(dead_code))]
pub enum RuntimeChoice {
Found(PathBuf),
OnlyAlias(PathBuf),
None,
}
#[cfg_attr(not(windows), allow(dead_code))]
pub fn choose_runtime<I, F, G>(candidates: I, is_file: F, is_alias: G) -> RuntimeChoice
where
I: IntoIterator<Item = PathBuf>,
F: Fn(&Path) -> bool,
G: Fn(&Path) -> bool,
{
let mut first_alias: Option<PathBuf> = None;
for candidate in candidates {
if !is_file(&candidate) {
continue;
}
if is_alias(&candidate) {
if first_alias.is_none() {
first_alias = Some(candidate);
}
continue;
}
return RuntimeChoice::Found(candidate);
}
match first_alias {
Some(alias) => RuntimeChoice::OnlyAlias(alias),
None => RuntimeChoice::None,
}
}
#[cfg(test)]
mod choose_runtime_tests {
use super::{RuntimeChoice, choose_runtime};
use std::path::{Path, PathBuf};
fn cands(list: &[&str]) -> Vec<PathBuf> {
list.iter().map(PathBuf::from).collect()
}
fn is(p: &Path, name: &str) -> bool {
p.to_str() == Some(name)
}
#[test]
fn a_real_file_after_an_alias_is_preferred_and_the_alias_skipped() {
let r = choose_runtime(cands(&["alias", "real"]), |_| true, |p| is(p, "alias"));
assert!(matches!(r, RuntimeChoice::Found(p) if p == Path::new("real")));
}
#[test]
fn a_real_file_before_an_alias_wins_immediately() {
let r = choose_runtime(cands(&["real", "alias"]), |_| true, |p| is(p, "alias"));
assert!(matches!(r, RuntimeChoice::Found(p) if p == Path::new("real")));
}
#[test]
fn only_aliases_fails_closed_naming_the_first() {
let r = choose_runtime(cands(&["alias1", "alias2"]), |_| true, |_| true);
assert!(matches!(r, RuntimeChoice::OnlyAlias(p) if p == Path::new("alias1")));
}
#[test]
fn no_file_match_is_none_not_alias() {
let r = choose_runtime(cands(&["a", "b"]), |_| false, |_| true);
assert!(matches!(r, RuntimeChoice::None));
}
#[test]
fn missing_candidates_are_stepped_over_to_reach_a_real_file() {
let r = choose_runtime(cands(&["missing", "real"]), |p| is(p, "real"), |_| false);
assert!(matches!(r, RuntimeChoice::Found(p) if p == Path::new("real")));
}
#[test]
fn a_run_of_aliases_never_masks_a_later_real_interpreter() {
let r = choose_runtime(
cands(&["a1", "a2", "real", "a3"]),
|_| true,
|p| !is(p, "real"),
);
assert!(matches!(r, RuntimeChoice::Found(p) if p == Path::new("real")));
}
}
#[cfg(test)]
mod alias_path_tests {
use super::is_windowsapps_alias_path;
use std::path::Path;
fn is_alias(s: &str) -> bool {
is_windowsapps_alias_path(Path::new(s))
}
#[test]
fn the_real_store_alias_path_is_detected() {
assert!(is_alias(
r"C:\Users\alice\AppData\Local\Microsoft\WindowsApps\python3.EXE"
));
}
#[test]
fn the_package_store_is_not_an_alias() {
assert!(!is_alias(
r"C:\Program Files\WindowsApps\SomeVendor.App\python.exe"
));
}
#[test]
fn a_normal_interpreter_is_not_an_alias() {
assert!(!is_alias(r"C:\Python312\python.exe"));
assert!(!is_alias(r"C:\Users\alice\venv\Scripts\python.exe"));
}
#[test]
fn case_is_ignored_on_both_components() {
assert!(is_alias(
r"c:\users\x\appdata\local\microsoft\windowsapps\PYTHON3.EXE"
));
}
#[test]
fn forward_slashes_are_matched_too() {
assert!(is_alias("C:/opt/Local/Microsoft/WindowsApps/python3.exe"));
}
#[test]
fn microsoft_without_windowsapps_is_not_an_alias() {
assert!(!is_alias(r"C:\Program Files\Microsoft\dotnet\dotnet.exe"));
}
}
#[cfg(test)]
mod quoting_tests {
use super::quote_arg;
use std::ffi::OsStr;
fn q(s: &str) -> String {
quote_arg(OsStr::new(s))
}
#[test]
fn a_plain_argument_is_not_quoted() {
assert_eq!(q("main.py"), "main.py");
}
#[test]
fn backslashes_not_before_a_quote_are_literal() {
assert_eq!(q(r"C:\Temp\main.py"), r"C:\Temp\main.py");
}
#[test]
fn a_space_forces_quoting() {
assert_eq!(q("John Smith"), "\"John Smith\"");
}
#[test]
fn the_backslash_run_before_the_closing_quote_is_doubled() {
assert_eq!(q(r"C:\a b\"), "\"C:\\a b\\\\\"");
}
#[test]
fn an_embedded_quote_is_escaped() {
assert_eq!(q(r#"say "hi""#), r#""say \"hi\"""#);
}
#[test]
fn a_backslash_run_before_an_embedded_quote_is_doubled() {
assert_eq!(q(r#"a\"b"#), r#""a\\\"b""#);
}
#[test]
fn the_empty_argument_survives_as_an_empty_quoted_string() {
assert_eq!(q(""), "\"\"");
}
}