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]+)?\b").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]").expect("Invalid regex"),
"recursive chown",
),
(
Regex::new(r"(curl|wget)\s+[^|]*\|\s*(sh|bash|zsh|ksh|dash)")
.expect("Invalid regex"),
"pipe remote content to shell",
),
(
Regex::new(r"wget\s+(-[a-z]+\s+)*-O\s*-\[^|]*\|\s*(sh|bash)")
.expect("Invalid regex"),
"wget -O- | sh",
),
(
Regex::new(r"curl\s+.*\|\s*(sh|bash|zsh)").expect("Invalid regex"),
"curl | sh",
),
(
Regex::new(r#"(python|perl|ruby)\s+(-[a-z]+\s+)*-c\s*['"].*import\s+urllib"#)
.expect("Invalid regex"),
"remote code execution via scripting language",
),
(
Regex::new(r"\bnc\s+.*-e\s+(/bin/)?(sh|bash)").expect("Invalid regex"),
"netcat reverse shell",
),
(
Regex::new(r#"\beval\s+.*(\$\(|`|curl|wget|nc)"#).expect("Invalid regex"),
"eval with command substitution",
),
]
});
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*(sh|bash|zsh|perl|python|exec|ruby)"#)
.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")
});
#[allow(dead_code)]
pub(crate) static SUSPICIOUS_SUBSTITUTION_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"\$['"][^'"]*['"]|\$\{[^}]+\}|\$[a-zA-Z_][a-zA-Z0-9_]*"#).expect("Invalid regex")
});
#[allow(dead_code)]
pub(crate) static STANDING_VARIABLE_EXEC_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r#"^\s*\$\s*\{?\s*[A-Za-z_][A-Za-z0-9_]*\s*\}?\s*$"#).expect("Invalid regex")
});