use std::collections::HashMap;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use env_modify::{LuaApis, SHookApis, S_YNHookTask};
use crate::config::entity_ro;
pub mod env_modify;
pub(crate) mod check_exec;
pub mod detailed_restrict;
mod deep_extern_hook;
pub static DEFAULT_DENY_MODULES_LIST: [&str; 3] = [
"debug", "package", "require"
];
pub static DEFAULT_PRECISION_DENY_CONFIG: &[(&str, &[&str])] = &[
("string", &["dump"]),
];
#[derive(Debug, Clone, PartialEq)]
pub enum RiskLevel {
Critical, High, Medium, }
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Permission {
Dofile, Load, Loadfile, Require,
OsExecute, OsExit, OsGetenv, OsRemove, OsRename, OsSetenv,
IoOpen, IoRead, IoWrite, IoPopen,
DebugGetinfo, DebugGetlocal, DebugGetmetatable, DebugGetregistry, DebugGetupvalue, DebugGetuservalue, DebugSethook, DebugSetlocal, DebugSetupvalue, DebugSetuservalue,
PackageLoadlib,
PackagePath, PackageLoaded, PackagePreload, PackageSearchpath,
MathRandom, MathRandomseed, StringDump, }
impl Permission {
pub fn risk_level(&self) -> RiskLevel {
match self {
Permission::Dofile | Permission::Load | Permission::Loadfile |
Permission::Require | Permission::OsExecute | Permission::OsExit |
Permission::OsGetenv | Permission::OsRemove | Permission::OsRename |
Permission::OsSetenv | Permission::IoOpen | Permission::IoRead |
Permission::IoWrite | Permission::IoPopen |
Permission::DebugGetinfo | Permission::DebugGetlocal |
Permission::DebugGetmetatable | Permission::DebugGetregistry |
Permission::DebugGetupvalue | Permission::DebugGetuservalue |
Permission::DebugSethook | Permission::DebugSetlocal |
Permission::DebugSetupvalue | Permission::DebugSetuservalue |
Permission::PackageLoadlib => RiskLevel::Critical,
Permission::PackagePath | Permission::PackageLoaded |
Permission::PackagePreload | Permission::PackageSearchpath => RiskLevel::High,
Permission::MathRandom | Permission::MathRandomseed |
Permission::StringDump => RiskLevel::Medium,
}
}
pub fn description(&self) -> &'static str {
match self {
Permission::Dofile => "执行任意 Lua 文件",
Permission::Load => "将字符串加载为函数(执行任意代码)",
Permission::Loadfile => "从文件加载函数(执行任意文件)",
Permission::Require => "加载任意模块",
Permission::OsExecute => "执行任意系统命令(如 rm -rf /)",
Permission::OsExit => "终止当前进程",
Permission::OsGetenv => "获取环境变量(如 PASSWORD、API_KEY)",
Permission::OsRemove => "删除任意文件(包括系统文件)",
Permission::OsRename => "重命名任意文件(包括系统文件)",
Permission::OsSetenv => "设置环境变量(如 LD_PRELOAD)",
Permission::IoOpen => "打开任意文件(包括 /etc/passwd)",
Permission::IoRead => "读取任意文件内容",
Permission::IoWrite => "写入任意文件内容",
Permission::IoPopen => "打开系统进程(类似 execute)",
Permission::DebugGetinfo => "获取函数信息(包括闭包、上下文)",
Permission::DebugGetlocal => "获取局部变量(包括敏感变量)",
Permission::DebugGetmetatable => "获取元表(修改对象行为)",
Permission::DebugGetregistry => "获取注册表(Lua 内部数据结构)",
Permission::DebugGetupvalue => "获取闭包上值(包括敏感数据)",
Permission::DebugGetuservalue => "获取用户值(自定义数据)",
Permission::DebugSethook => "设置钩子(修改运行时行为)",
Permission::DebugSetlocal => "设置局部变量(修改运行时状态)",
Permission::DebugSetupvalue => "设置闭包上值(修改闭包状态)",
Permission::DebugSetuservalue => "设置用户值(修改自定义数据)",
Permission::PackageLoadlib => "加载动态库(执行任意 C 代码)",
Permission::PackagePath => "获取/修改模块路径",
Permission::PackageLoaded => "获取已加载模块",
Permission::PackagePreload => "预加载模块",
Permission::PackageSearchpath => "搜索模块路径",
Permission::MathRandom => "生成随机数",
Permission::MathRandomseed => "设置随机数种子",
Permission::StringDump => "生成字节码(可能用于代码注入)",
}
}
pub fn module(&self) -> &'static str {
match self {
Permission::Dofile | Permission::Load | Permission::Loadfile |
Permission::Require => "global",
Permission::OsExecute | Permission::OsExit | Permission::OsGetenv |
Permission::OsRemove | Permission::OsRename | Permission::OsSetenv => "os",
Permission::IoOpen | Permission::IoRead | Permission::IoWrite |
Permission::IoPopen => "io",
Permission::DebugGetinfo | Permission::DebugGetlocal |
Permission::DebugGetmetatable | Permission::DebugGetregistry |
Permission::DebugGetupvalue | Permission::DebugGetuservalue |
Permission::DebugSethook | Permission::DebugSetlocal |
Permission::DebugSetupvalue | Permission::DebugSetuservalue => "debug",
Permission::PackageLoadlib | Permission::PackagePath |
Permission::PackageLoaded | Permission::PackagePreload |
Permission::PackageSearchpath => "package",
Permission::MathRandom | Permission::MathRandomseed => "math",
Permission::StringDump => "string",
}
}
pub fn query_auth(func_name: &str) -> Option<Permission> {
match func_name {
"dofile" => Some(Permission::Dofile),
"load" => Some(Permission::Load),
"loadfile" => Some(Permission::Loadfile),
"require" => Some(Permission::Require),
"execute" => Some(Permission::OsExecute),
"exit" => Some(Permission::OsExit),
"getenv" => Some(Permission::OsGetenv),
"remove" => Some(Permission::OsRemove),
"rename" => Some(Permission::OsRename),
"setenv" => Some(Permission::OsSetenv),
"open" => Some(Permission::IoOpen),
"read" => Some(Permission::IoRead),
"write" => Some(Permission::IoWrite),
"popen" => Some(Permission::IoPopen),
"getinfo" => Some(Permission::DebugGetinfo),
"getlocal" => Some(Permission::DebugGetlocal),
"getmetatable" => Some(Permission::DebugGetmetatable),
"getregistry" => Some(Permission::DebugGetregistry),
"getupvalue" => Some(Permission::DebugGetupvalue),
"getuservalue" => Some(Permission::DebugGetuservalue),
"sethook" => Some(Permission::DebugSethook),
"setlocal" => Some(Permission::DebugSetlocal),
"setupvalue" => Some(Permission::DebugSetupvalue),
"setuservalue" => Some(Permission::DebugSetuservalue),
"loadlib" => Some(Permission::PackageLoadlib),
"path" => Some(Permission::PackagePath),
"loaded" => Some(Permission::PackageLoaded),
"preload" => Some(Permission::PackagePreload),
"searchpath" => Some(Permission::PackageSearchpath),
"random" => Some(Permission::MathRandom),
"randomseed" => Some(Permission::MathRandomseed),
"dump" => Some(Permission::StringDump),
_ => None,
}
}
pub fn api_name(&self) -> &'static str {
match self {
Permission::Dofile => "dofile",
Permission::Load => "load",
Permission::Loadfile => "loadfile",
Permission::Require => "require",
Permission::OsExecute => "execute",
Permission::OsExit => "exit",
Permission::OsGetenv => "getenv",
Permission::OsRemove => "remove",
Permission::OsRename => "rename",
Permission::OsSetenv => "setenv",
Permission::IoOpen => "open",
Permission::IoRead => "read",
Permission::IoWrite => "write",
Permission::IoPopen => "popen",
Permission::DebugGetinfo => "getinfo",
Permission::DebugGetlocal => "getlocal",
Permission::DebugGetmetatable => "getmetatable",
Permission::DebugGetregistry => "getregistry",
Permission::DebugGetupvalue => "getupvalue",
Permission::DebugGetuservalue => "getuservalue",
Permission::DebugSethook => "sethook",
Permission::DebugSetlocal => "setlocal",
Permission::DebugSetupvalue => "setupvalue",
Permission::DebugSetuservalue => "setuservalue",
Permission::PackageLoadlib => "loadlib",
Permission::PackagePath => "path",
Permission::PackageLoaded => "loaded",
Permission::PackagePreload => "preload",
Permission::PackageSearchpath => "searchpath",
Permission::MathRandom => "random",
Permission::MathRandomseed => "randomseed",
Permission::StringDump => "dump",
}
}
}
struct SAuthority {
info: String,
permission: Permission,
enabled: bool,
}
pub type SAuthBasicCtrl = HashMap<Permission, bool>;
pub type SDetailedAccessAllow = HashMap<String, Vec<String>>;
pub type SAnnounceClaim = HashMap<&'static str, String>;
pub fn get_default_auth_config() -> SAuthBasicCtrl {
let mut config = SAuthBasicCtrl::new();
config.insert(Permission::Dofile, false);
config.insert(Permission::Load, false);
config.insert(Permission::Loadfile, false);
config.insert(Permission::Require, false);
config.insert(Permission::OsExecute, false);
config.insert(Permission::OsExit, false);
config.insert(Permission::OsGetenv, false);
config.insert(Permission::OsRemove, false);
config.insert(Permission::OsRename, false);
config.insert(Permission::OsSetenv, false);
config.insert(Permission::IoOpen, false);
config.insert(Permission::IoRead, false);
config.insert(Permission::IoWrite, false);
config.insert(Permission::IoPopen, false);
config.insert(Permission::DebugGetinfo, false);
config.insert(Permission::DebugGetlocal, false);
config.insert(Permission::DebugGetmetatable, false);
config.insert(Permission::DebugGetregistry, false);
config.insert(Permission::DebugGetupvalue, false);
config.insert(Permission::DebugGetuservalue, false);
config.insert(Permission::DebugSethook, false);
config.insert(Permission::DebugSetlocal, false);
config.insert(Permission::DebugSetupvalue, false);
config.insert(Permission::DebugSetuservalue, false);
config.insert(Permission::PackageLoadlib, false);
config.insert(Permission::PackagePath, false);
config.insert(Permission::PackageLoaded, false);
config.insert(Permission::PackagePreload, false);
config.insert(Permission::PackageSearchpath, false);
config.insert(Permission::MathRandom, true);
config.insert(Permission::MathRandomseed, true);
config.insert(Permission::StringDump, false);
config
}
pub fn get_authorized_auth_config(changes: SAuthBasicCtrl) -> SAuthBasicCtrl {
let mut default = get_default_auth_config();
for (permission, enabled) in changes {
default.insert(permission, enabled);
}
default
}
pub fn generate_basic_ctrl_hook_task(auth: &SAuthBasicCtrl) -> S_YNHookTask {
let mut deny_list: LuaApis = HashMap::new();
let mut hook_callbacks: SHookApis = HashMap::new();
deny_list.insert("global".to_string(), vec![
"dofile".to_string(),
"load".to_string(),
"loadfile".to_string(),
"require".to_string(),
]);
deny_list.insert("os".to_string(), vec![
"execute".to_string(),
"exit".to_string(),
"getenv".to_string(),
"remove".to_string(),
"rename".to_string(),
"setenv".to_string(),
]);
deny_list.insert("io".to_string(), vec![
"open".to_string(),
"read".to_string(),
"write".to_string(),
"popen".to_string(),
]);
deny_list.insert("debug".to_string(), vec![
"getinfo".to_string(),
"getlocal".to_string(),
"getmetatable".to_string(),
"getregistry".to_string(),
"getupvalue".to_string(),
"getuservalue".to_string(),
"sethook".to_string(),
"setlocal".to_string(),
"setupvalue".to_string(),
"setuservalue".to_string(),
]);
deny_list.insert("package".to_string(), vec![
"loadlib".to_string(),
]);
hook_callbacks.insert("random".to_string(), (math_random_hook, true));
hook_callbacks.insert("randomseed".to_string(), (math_randomseed_hook, true));
for (module, apis) in &deny_list {
for api in apis {
let if_enabled = Permission::query_auth(api)
.and_then(|p| auth.get(&p).copied()) .unwrap_or(false);
let hook_fn = if if_enabled {
security_hook_allowed
} else {
security_hook_denied
};
hook_callbacks.insert(api.clone(), (hook_fn, if_enabled));
}
}
deny_list.insert("string".to_string(), vec![
"dump".to_string(),
]);
(deny_list, hook_callbacks)
}
fn math_random_hook(whole_api:&str) {
let _ = whole_api;
warn!("[Security Hook] math.random called");
}
fn math_randomseed_hook(whole_api:&str) {
let _ = whole_api;
warn!("[Security Hook] math.randomseed called - seed validation applied");
}
fn security_hook_allowed(api_name: &str) {
warn!("[Security Hook] {} called and was allowed.", api_name);
}
fn security_hook_denied(api_name: &str) {
warn!("[Security Hook] {} called and was rejected as an error.", api_name);
}
pub fn security_audit_log(config: &SAuthBasicCtrl) {
let mut critical_count = 0;
let mut high_count = 0;
let mut medium_count = 0;
for (permission, enabled) in config.iter() {
let status = if *enabled { "ENABLED" } else { "DISABLED" };
let risk = permission.risk_level();
match risk {
RiskLevel::Critical => {
critical_count += 1;
println!("[CRITICAL][{}] {} - {} [{}]",
permission.module(), permission.api_name(),
permission.description(), status);
},
RiskLevel::High => {
high_count += 1;
println!("[HIGH] [{}] {} - {} [{}]",
permission.module(), permission.api_name(),
permission.description(), status);
},
RiskLevel::Medium => {
medium_count += 1;
println!("[MEDIUM] [{}] {} - {} [{}]",
permission.module(), permission.api_name(),
permission.description(), status);
},
}
}
info!("\n=== Security ===");
info!("Critical: {}, High: {}, Medium: {}", critical_count, high_count, medium_count);
info!("Total permissions controlled: {}", config.len());
}