use crate::config::SafetyConfig;
use regex::Regex;
use std::path::PathBuf;
use std::sync::LazyLock;
pub struct SafetyChecker {
pub(crate) config: SafetyConfig,
pub(crate) working_dir: PathBuf,
pub(crate) security_scanner: crate::safety::scanner::SecurityScanner,
}
#[derive(Clone, Copy, Default)]
pub(crate) struct UrlSafetyOptions {
pub allow_file_scheme: bool,
pub allow_localhost: bool,
}
pub(crate) static DANGEROUS_COMMAND_PATTERNS: LazyLock<Vec<(Regex, &'static str)>> =
LazyLock::new(|| {
vec![
(
Regex::new(r"rm\s+(--?[a-z-]+\s+)*(/\*?|\.\.(/\.\.)+/?)").expect("Invalid regex"),
"rm -rf / or ../../.. (destructive deletion)",
),
(
Regex::new(r"\bmkfs(\.[a-z0-9]+)?\s+(-\S+\s+)*(-t\s+\S+\s+)?/dev/")
.expect("Invalid regex"),
"mkfs (format filesystem)",
),
(
Regex::new(r"\bdd\s+.*\b(if|of)=\s*/dev/(sd|hd|nvme|vd|xvd)")
.expect("Invalid regex"),
"dd to disk device (data destruction)",
),
(
Regex::new(r":\s*\(\s*\)\s*\{.*:\s*\|.*:\s*&.*\}").expect("Invalid regex"),
"fork bomb",
),
(
Regex::new(r">\s*/dev/(sd|hd|nvme|vd|xvd)").expect("Invalid regex"),
"redirect to disk device",
),
(
Regex::new(r">\s*/(etc|usr|boot|sys|sbin|var)/").expect("Invalid regex"),
"redirect to system directory",
),
(
Regex::new(r"chmod\s+(-[a-zA-Z]+\s+)*777\s+/+").expect("Invalid regex"),
"chmod 777 / (remove all file permissions)",
),
(
Regex::new(r"chown\s+(-[a-zA-Z]+\s+)*\S+:\S+\s+/").expect("Invalid regex"),
"chown on system directory",
),
(
Regex::new(
r"chown\s+-[rR]\S*\s+\S*\s+(/|/etc|/usr|/var|/bin|/sbin|/lib|/boot|/sys|/proc)",
)
.expect("Invalid regex"),
"recursive chown on system path",
),
(
Regex::new(r"(curl|wget)\s+[^|]*(\|[^|]*)*\|\s*(?:ba|z|k|da)?sh(?:\s|$)")
.expect("Invalid regex"),
"pipe remote content to shell",
),
(
Regex::new(r"wget\s+(-[a-z]+\s+)*-O\s*-\[^|]*\|\s*(?:ba)?sh(?:\s|$)")
.expect("Invalid regex"),
"wget -O- | sh",
),
(
Regex::new(r"curl\s+.*\|\s*(?:ba|z)?sh(?:\s|$)").expect("Invalid regex"),
"curl | sh",
),
(
Regex::new(r"\bnc\s+.*-e\s+(/bin/)?(sh|bash)").expect("Invalid regex"),
"netcat reverse shell",
),
]
});
pub(crate) static PAYLOAD_COMMAND_PATTERNS: LazyLock<Vec<(Regex, &'static str)>> = LazyLock::new(
|| {
vec![
(
Regex::new(
r#"\b(python[0-9.]*|perl|ruby)\s+(-[a-z]+\s+)*-c\s*['"].*urllib(2|\.request).*(urlopen|request\s*\()"#,
)
.expect("Invalid regex"),
"remote code execution via scripting language",
),
(
Regex::new(r"\beval\s+[^;&\n]*\b(curl|wget|nc)\b").expect("Invalid regex"),
"eval with network tool",
),
]
},
);
pub(crate) static BASE64_EXEC_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"(base64\s+(-[a-z]+\s+)*(-d|--decode)|base64\s+-d|--decode\s+<|base64\s+<<<).*\|\s*((?:ba|z)?sh|perl|python[0-9.]*|exec|ruby)(\s|$)"#)
.expect("Invalid regex")
});
pub(crate) static HEX_EXEC_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(
r#"(xxd\s+-r|-r\s+xxd|printf\s+.*\\x[0-9a-fA-F]{2}.*\|\s*(sh|bash|zsh|perl|python|ruby))"#,
)
.expect("Invalid regex")
});
pub(crate) static ENCODED_EXEC_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"(uudecode|gunzip|zcat|gzip\s+-d)\s+.*\|\s*(base64|sh|bash|zsh|perl|python)"#)
.expect("Invalid regex")
});