mintrt 0.1.2

Security-featured runtime for lua.
Documentation
//     <<*>> Revenge of snowflake.
//     -^v-  SASWE
//     @ This source code is licensed under a dual license model:
//       1. MPL-2.0 for non-commercial use only
//       2. Commercial License for commercial use
//       See LICENSE and LICENSE_COMMERCIAL files for details.
use mlua::{Function, Lua, Table, Value};
use crate::security::{DEFAULT_DENY_MODULES_LIST, DEFAULT_PRECISION_DENY_CONFIG};
use crate::security;

use std::collections::HashMap;
use log::{info, warn};
use crate::security::SAuthBasicCtrl;

pub type LuaApis = HashMap<String, Vec<String>>;
pub type Modules_no_suffix = Vec<String>;

///实际上是在执行禁用操作之前执行,通过操作两个函数之间的全局变量,来达到目的
pub fn deny_module_except(entity: &Lua, except_modules_list: LuaApis) -> Result<(), mlua::Error>{
    let entity_globals = entity.globals();
    for lib in &DEFAULT_DENY_MODULES_LIST {
        if except_modules_list.contains_key(*lib) {
            continue
        } else {entity_globals.set(*lib, Value::Nil)?}
    }
    for (lib, apis) in DEFAULT_PRECISION_DENY_CONFIG {
        let table = entity_globals.get::<Table>(*lib)?;
        for api in *apis {
            if except_modules_list.contains_key(*lib) {
                if except_modules_list[*lib].contains( &String::from(*api) ) {
                    continue
                }
            }
            table.set(*api, Value::Nil)?;
        }
        entity_globals.set(*lib, table)?;
    }
    Ok(())
}

pub type SHookApis = HashMap<String, (fn(module_api: &str), bool)>;
pub type S_YNHookTask = (LuaApis, SHookApis);
///若要hook全局函数,请设置module为global
pub fn uni_hook_func_anonym(lua: &Lua, module: &str, target_func: &str, hook_func: fn()) -> Result<(), mlua::Error> {
    let entity_globals = lua.globals();
    let target_table = match module{
        "global" => entity_globals,
        _ => entity_globals.get::<Table>(module)?
    };
    let backup_func = target_table.get::<Function>(target_func)?;
    let new_func = lua.create_function(move |_, args: mlua::MultiValue| {
        hook_func();
        // 【通义修复】添加类型注解 `let _: () =` 来明确指定不关心返回值
        // 原因:mlua 的 Function::call 是泛型方法,需要明确返回类型
        // 如果不标注类型,编译器无法推断应该使用哪个 FromLuaMulti 实现
        // 这里使用 () 表示忽略原函数的返回值
        let _: () = backup_func.call(args)?;
        Ok(())
    })?;
    target_table.set(target_func, new_func)?;
    Ok(())
}

///HookTask.0是需要hook的函数表,为module,apis、HookTask.1是函数指针表,为函数名,函数指针
/// 批量hook,用于安全审计,运行时权限认证执行
/// 若要hook全局函数,请设置module为global
pub fn batch_hook_sec(lua: &Lua, hook_task: S_YNHookTask) -> Result<(), mlua::Error> {
    let target_apis = hook_task.0;
    let hook_apis = hook_task.1;//解包任务
    let global_table = lua.globals();
    let lua_error: mlua::Function = lua.globals().get("error")?;
    for (module, apis) in target_apis {
        let target_module_result: Result<Table, mlua::Error> = match module.as_str() {
            "global" => {
                Ok(global_table.clone())
            },
            _ => {
                // 如果模块不存在,跳过整个模块
                global_table.get::<Table>(&*module)
            }
        };
        
        let target_module = match target_module_result {
            Ok(tbl) => tbl,
            Err(_) => continue, // 模块不存在,跳过
        };//获取函数所在的模块(表)
        
        for api in apis {
            // 如果函数不存在,跳过
            let backup_func_result: Result<Function, mlua::Error> = target_module.get(&*api);
            let backup_func = match backup_func_result {
                Ok(func) => func,
                Err(_) => continue, // 函数不存在,跳过
            };//备份目标函数
            // 检查 hook_apis 中是否存在该 API
            let (hook_func, allow_or_not) = match hook_apis.get(&api) {
                Some(hook) => *hook,
                None => {
                    // API 不在 hook 列表中,跳过
                    continue;
                }
            };
            let whole_api = format!("{}.{}", module.clone(), api.clone());
            let error_lua_api_to_func = lua_error.clone();
            let new_func = lua.create_function(move |lua_ctx, args: mlua::MultiValue| -> Result<mlua::MultiValue, mlua::Error> {
                hook_func(&whole_api);
                if !allow_or_not {
                    // 【通义修复】添加类型参数 ::<_> 来明确 call 方法的返回类型
                    // 原因:error 函数不需要返回值,使用 _ 让编译器推断为 ()
                    // 注意:error() 会立即终止执行并抛出错误,下面的 backup_func.call() 不会被执行
                    info!("Denied {}", &whole_api);
                    error_lua_api_to_func.call::<_>("Rejected by mint.")?
                }

                // 【通义修复】调用原始备份的函数,传递参数并返回结果
                // 这是本次修复的核心:确保被 hook 的函数在授权后能正常执行
                // 如果上面 error() 已执行,这里永远不会到达
                warn!("Allowed {}", &whole_api);
                // 通义添加:显式指定 call 的返回类型为 mlua::MultiValue,满足 IntoLuaMulti trait 约束
                backup_func.call::<mlua::MultiValue>(args)
            })?;//构造新的函数:hook 回调 -> 原函数
            target_module.set(&*api, new_func)?;//启用新的函数
        }
    }
    Ok(())
}

///重写一些可能要用到的危险功能
///runtime.call_exec( <function>, <table> )
//这个 api 会临时启用 executor 的权限并且 call 目标函数
/// runtime.log.trace; runtime.log.info;
/// runtime.log.error; runtime.log.warn;
//日志
//手动触发解释器的 panic
/// runtime.ctrl.panic_and_deadlock;
/// runtime.ctrl.panic_and_abort;
/// runtime.ctrl._panic_and_unwind_
/// runtime.ctrl.set_panic_hook

//TODO
/// 应用完整的安全沙箱配置
pub fn apply_security_sandbox(lua: &Lua) -> Result<(), mlua::Error> {
    // 获取默认安全配置
    let security_config = security::get_default_auth_config();

    // 打印安全审计报告(可选)
    security::security_audit_log(&security_config);
    
    // 生成 hook 任务
    //allow: empty
    let (deny_list, _hook_callbacks) = security::generate_basic_ctrl_hook_task(&SAuthBasicCtrl::new());
    
    // 禁用危险模块
    deny_module_except(lua, deny_list)?;
    
    // TODO: 如果需要应用 hook,可以在这里调用 batch_hook
    // batch_hook(lua.clone(), (hook_targets, hook_callbacks))?;
    
    Ok(())
}