use std::collections::HashSet;
use std::sync::LazyLock;
use hayai::engine::{Prefilter, contains_ascii_ci};
const DANGEROUS_PREFIXES: &[&str] = &[
"rm", "dd", "mkfs", "chmod", "chown", "mv", "truncate", "shred",
"git",
"psql", "mysql", "sqlite3", "sqlcmd", "sqlx", "diesel", "prisma",
"liquibase", "flyway", "knex", "rails", "rake", "python", "django-admin",
"mongosh", "mongo",
"kubectl", "helm", "flux",
"aws", "gcloud", "gsutil", "az", "bq",
"nix", "nix-collect-garbage",
"docker",
"sops", "echo",
"terraform", "pulumi", "ansible-playbook",
"akeyless", "aky",
"kill", "killall", "pkill", "shutdown", "poweroff", "halt", "reboot",
"systemctl", "launchctl",
"iptables", "ufw", "ip", "nft",
"redis-cli",
"curl", "wget",
"mysqladmin",
"sh", "bash", "zsh", "fish", "dash",
"env", "sudo", "doas", "nohup", "nice", "timeout",
"eval", "xargs", "find",
"crontab", "at",
"fdisk", "parted", "wipefs",
"npm", "cargo", "gem", "pip", "twine",
"rsync", "rclone",
"journalctl",
"ssh",
];
static PREFIX_SET: LazyLock<HashSet<&'static str>> =
LazyLock::new(|| DANGEROUS_PREFIXES.iter().copied().collect());
const SQL_KEYWORDS: &[&[u8]] = &[
b"DROP ", b"TRUNCATE ", b"DELETE FROM", b"REVOKE ",
b"FLUSHALL", b"FLUSHDB", b"VACUUM FULL",
b"BASE64", b"| BASH", b"| SH",
];
#[derive(Debug, Clone, Copy, Default)]
pub struct PrefixPrefilter;
impl PrefixPrefilter {
#[must_use]
pub fn prefix_set() -> &'static HashSet<&'static str> {
&PREFIX_SET
}
}
impl Prefilter for PrefixPrefilter {
fn is_safe(&self, command: &str) -> bool {
let trimmed = command.trim_start();
if trimmed.starts_with('$') || trimmed.contains('`') {
return false;
}
let has_dangerous_prefix = command
.split_whitespace()
.take(3)
.any(|word| PREFIX_SET.contains(word) || PREFIX_SET.iter().any(|p| word.starts_with(p)));
if has_dangerous_prefix {
return false;
}
let bytes = command.as_bytes();
if SQL_KEYWORDS.iter().any(|kw| contains_ascii_ci(bytes, kw)) {
return false;
}
if bytes.windows(2).any(|w| w == b"/*")
|| bytes.windows(3).any(|w| w == b"-- " || w == b"--\t")
{
return false;
}
true
}
}