eris: move lua API to standalone module
Easier to work with now, yay.
This commit is contained in:
parent
a2fc2bf2bc
commit
54f858aee9
5 changed files with 297 additions and 127 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -636,6 +636,7 @@ dependencies = [
|
||||||
"rlua",
|
"rlua",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -646,9 +647,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
@ -1580,7 +1587,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1740,6 +1747,19 @@ dependencies = [
|
||||||
"syn 2.0.101",
|
"syn 2.0.101",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.19.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
|
|
|
@ -19,3 +19,4 @@ serde_json = "1.0.96"
|
||||||
tokio = { version = "1.28.0", features = ["full"] }
|
tokio = { version = "1.28.0", features = ["full"] }
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
env_logger = "0.11.8"
|
env_logger = "0.11.8"
|
||||||
|
tempfile = "3.19.1"
|
||||||
|
|
17
resources/default_script.lua
Normal file
17
resources/default_script.lua
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
function generate_honeytoken(token)
|
||||||
|
local token_types = { "API_KEY", "AUTH_TOKEN", "SESSION_ID", "SECRET_KEY" }
|
||||||
|
local prefix = token_types[math.random(#token_types)]
|
||||||
|
local suffix = string.format("%08x", math.random(0xffffff))
|
||||||
|
return prefix .. "_" .. token .. "_" .. suffix
|
||||||
|
end
|
||||||
|
|
||||||
|
function enhance_response(text, response_type, path, token)
|
||||||
|
local result = text
|
||||||
|
local honeytoken = generate_honeytoken(token)
|
||||||
|
|
||||||
|
-- Add some fake sensitive data
|
||||||
|
result = result .. "\n<!-- DEBUG: " .. honeytoken .. " -->"
|
||||||
|
result = result .. "\n<div style='display:none'>Server ID: " .. token .. "</div>"
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
247
src/lua/mod.rs
Normal file
247
src/lua/mod.rs
Normal file
|
@ -0,0 +1,247 @@
|
||||||
|
use rlua::{Function, Lua};
|
||||||
|
use std::fs;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub struct ScriptManager {
|
||||||
|
script_content: String,
|
||||||
|
scripts_loaded: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScriptManager {
|
||||||
|
pub fn new(scripts_dir: &str) -> Self {
|
||||||
|
let mut script_content = String::new();
|
||||||
|
let mut scripts_loaded = false;
|
||||||
|
|
||||||
|
// Try to load scripts from directory
|
||||||
|
let script_dir = Path::new(scripts_dir);
|
||||||
|
if script_dir.exists() {
|
||||||
|
log::debug!("Loading Lua scripts from directory: {scripts_dir}");
|
||||||
|
if let Ok(entries) = fs::read_dir(script_dir) {
|
||||||
|
for entry in entries.filter_map(Result::ok) {
|
||||||
|
let path = entry.path();
|
||||||
|
if path.extension().and_then(|ext| ext.to_str()) == Some("lua") {
|
||||||
|
if let Ok(content) = fs::read_to_string(&path) {
|
||||||
|
log::debug!("Loaded Lua script: {}", path.display());
|
||||||
|
script_content.push_str(&content);
|
||||||
|
script_content.push('\n');
|
||||||
|
scripts_loaded = true;
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to read Lua script: {}", path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::warn!("Lua scripts directory does not exist: {scripts_dir}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no scripts were loaded, use a default script
|
||||||
|
if !scripts_loaded {
|
||||||
|
log::info!("No Lua scripts found, loading default scripts");
|
||||||
|
script_content = include_str!("../../resources/default_script.lua").to_string();
|
||||||
|
scripts_loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
script_content,
|
||||||
|
scripts_loaded,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing only
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn with_content(content: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
script_content: content.to_string(),
|
||||||
|
scripts_loaded: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand_response(
|
||||||
|
&self,
|
||||||
|
text: &str,
|
||||||
|
response_type: &str,
|
||||||
|
path: &str,
|
||||||
|
token: &str,
|
||||||
|
) -> String {
|
||||||
|
if !self.scripts_loaded {
|
||||||
|
return format!("{text}\n<!-- Token: {token} -->");
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
if let Err(e) = lua.load(&self.script_content).exec() {
|
||||||
|
log::warn!("Error loading Lua script: {e}");
|
||||||
|
return format!("{text}\n<!-- Error: Failed to load Lua script -->");
|
||||||
|
}
|
||||||
|
|
||||||
|
let globals = lua.globals();
|
||||||
|
match globals.get::<_, Function>("enhance_response") {
|
||||||
|
Ok(enhance_func) => {
|
||||||
|
match enhance_func.call::<_, String>((text, response_type, path, token)) {
|
||||||
|
Ok(result) => result,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Error calling Lua function enhance_response: {e}");
|
||||||
|
format!("{text}\n<!-- Error calling Lua enhance_response -->")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Lua enhance_response function not found: {e}");
|
||||||
|
format!("{text}\n<!-- Lua enhance_response function not found -->")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::io::Write;
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_script_manager_default_script() {
|
||||||
|
let script_manager = ScriptManager::new("/nonexistent_directory");
|
||||||
|
assert!(script_manager.scripts_loaded);
|
||||||
|
assert!(
|
||||||
|
script_manager
|
||||||
|
.script_content
|
||||||
|
.contains("generate_honeytoken")
|
||||||
|
);
|
||||||
|
assert!(script_manager.script_content.contains("enhance_response"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_script_manager_custom_scripts() {
|
||||||
|
let temp_dir = TempDir::new().unwrap();
|
||||||
|
let script_dir = temp_dir.path().to_str().unwrap();
|
||||||
|
|
||||||
|
// Create a test script
|
||||||
|
let script_path = temp_dir.path().join("test_script.lua");
|
||||||
|
let mut file = fs::File::create(&script_path).unwrap();
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
"function enhance_response(text, response_type, path, token)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(file, " return text .. ' - Enhanced with token: ' .. token").unwrap();
|
||||||
|
writeln!(file, "end").unwrap();
|
||||||
|
|
||||||
|
let script_manager = ScriptManager::new(script_dir);
|
||||||
|
assert!(script_manager.scripts_loaded);
|
||||||
|
assert!(
|
||||||
|
!script_manager
|
||||||
|
.script_content
|
||||||
|
.contains("generate_honeytoken")
|
||||||
|
); // Default script not loaded
|
||||||
|
assert!(
|
||||||
|
script_manager
|
||||||
|
.script_content
|
||||||
|
.contains("Enhanced with token")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expand_response_successful() {
|
||||||
|
let lua_code = r#"
|
||||||
|
function enhance_response(text, response_type, path, token)
|
||||||
|
return text .. " | Type: " .. response_type .. " | Path: " .. path .. " | Token: " .. token
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let script_manager = ScriptManager::with_content(lua_code);
|
||||||
|
let result =
|
||||||
|
script_manager.expand_response("Test content", "test_type", "/test/path", "12345");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
"Test content | Type: test_type | Path: /test/path | Token: 12345"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expand_response_syntax_error() {
|
||||||
|
let lua_code = r#"
|
||||||
|
function enhance_response(text, response_type, path, token)
|
||||||
|
This is an invalid Lua syntax
|
||||||
|
return "Something"
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let script_manager = ScriptManager::with_content(lua_code);
|
||||||
|
let result =
|
||||||
|
script_manager.expand_response("Test content", "test_type", "/test/path", "12345");
|
||||||
|
|
||||||
|
assert!(result.contains("Test content"));
|
||||||
|
assert!(result.contains("<!-- Error: Failed to load Lua script -->"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expand_response_runtime_error() {
|
||||||
|
let lua_code = r#"
|
||||||
|
function enhance_response(text, response_type, path, token)
|
||||||
|
-- This will cause a runtime error
|
||||||
|
return nonexistent_variable
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let script_manager = ScriptManager::with_content(lua_code);
|
||||||
|
let result =
|
||||||
|
script_manager.expand_response("Test content", "test_type", "/test/path", "12345");
|
||||||
|
|
||||||
|
assert!(result.contains("Test content"));
|
||||||
|
assert!(result.contains("<!-- Error calling Lua enhance_response -->"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expand_response_missing_function() {
|
||||||
|
let lua_code = r#"
|
||||||
|
-- This script doesn't define enhance_response function
|
||||||
|
function some_other_function()
|
||||||
|
return "Hello, world!"
|
||||||
|
end
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let script_manager = ScriptManager::with_content(lua_code);
|
||||||
|
let result =
|
||||||
|
script_manager.expand_response("Test content", "test_type", "/test/path", "12345");
|
||||||
|
|
||||||
|
assert!(result.contains("Test content"));
|
||||||
|
assert!(result.contains("<!-- Lua enhance_response function not found -->"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expand_response_multiple_scripts() {
|
||||||
|
let temp_dir = TempDir::new().unwrap();
|
||||||
|
let script_dir = temp_dir.path().to_str().unwrap();
|
||||||
|
|
||||||
|
// Create first script with helper function
|
||||||
|
let script1_path = temp_dir.path().join("01_helpers.lua");
|
||||||
|
let mut file1 = fs::File::create(script1_path).unwrap();
|
||||||
|
writeln!(file1, "function create_prefix(token)").unwrap();
|
||||||
|
writeln!(file1, " return 'PREFIX_' .. token").unwrap();
|
||||||
|
writeln!(file1, "end").unwrap();
|
||||||
|
|
||||||
|
// Create second script that uses the helper
|
||||||
|
let script2_path = temp_dir.path().join("02_responder.lua");
|
||||||
|
let mut file2 = fs::File::create(script2_path).unwrap();
|
||||||
|
writeln!(
|
||||||
|
file2,
|
||||||
|
"function enhance_response(text, response_type, path, token)"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(
|
||||||
|
file2,
|
||||||
|
" return text .. ' [' .. create_prefix(token) .. ']'"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(file2, "end").unwrap();
|
||||||
|
|
||||||
|
let script_manager = ScriptManager::new(script_dir);
|
||||||
|
let result =
|
||||||
|
script_manager.expand_response("Test content", "test_type", "/test/path", "12345");
|
||||||
|
|
||||||
|
assert_eq!(result, "Test content [PREFIX_12345]");
|
||||||
|
}
|
||||||
|
}
|
135
src/main.rs
135
src/main.rs
|
@ -1,7 +1,6 @@
|
||||||
use actix_web::{App, HttpResponse, HttpServer, web};
|
use actix_web::{App, HttpResponse, HttpServer, web};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use ipnetwork::IpNetwork;
|
use ipnetwork::IpNetwork;
|
||||||
use rlua::{Function, Lua};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
@ -17,9 +16,11 @@ use tokio::process::Command;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
mod lua;
|
||||||
mod markov;
|
mod markov;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
|
|
||||||
|
use lua::ScriptManager;
|
||||||
use markov::MarkovGenerator;
|
use markov::MarkovGenerator;
|
||||||
use metrics::{
|
use metrics::{
|
||||||
ACTIVE_CONNECTIONS, BLOCKED_IPS, HITS_COUNTER, PATH_HITS, UA_HITS, metrics_handler,
|
ACTIVE_CONNECTIONS, BLOCKED_IPS, HITS_COUNTER, PATH_HITS, UA_HITS, metrics_handler,
|
||||||
|
@ -356,10 +357,11 @@ impl BotState {
|
||||||
}
|
}
|
||||||
|
|
||||||
let hit_cache_file = format!("{}/hit_counters.json", self.cache_dir);
|
let hit_cache_file = format!("{}/hit_counters.json", self.cache_dir);
|
||||||
let mut hit_map = HashMap::new();
|
let hit_map: HashMap<String, u32> = self
|
||||||
for (ip, count) in &self.hits {
|
.hits
|
||||||
hit_map.insert(ip.to_string(), *count);
|
.iter()
|
||||||
}
|
.map(|(ip, count)| (ip.to_string(), *count))
|
||||||
|
.collect();
|
||||||
|
|
||||||
match fs::File::create(&hit_cache_file) {
|
match fs::File::create(&hit_cache_file) {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
|
@ -376,116 +378,11 @@ impl BotState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lua scripts for response generation and customization
|
|
||||||
struct ScriptManager {
|
|
||||||
script_content: String,
|
|
||||||
scripts_loaded: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScriptManager {
|
|
||||||
fn new(scripts_dir: &str) -> Self {
|
|
||||||
let mut script_content = String::new();
|
|
||||||
let mut scripts_loaded = false;
|
|
||||||
|
|
||||||
// Try to load scripts from directory
|
|
||||||
let script_dir = Path::new(scripts_dir);
|
|
||||||
if script_dir.exists() {
|
|
||||||
log::debug!("Loading Lua scripts from directory: {scripts_dir}");
|
|
||||||
if let Ok(entries) = fs::read_dir(script_dir) {
|
|
||||||
for entry in entries {
|
|
||||||
if let Ok(entry) = entry {
|
|
||||||
let path = entry.path();
|
|
||||||
if path.extension().and_then(|ext| ext.to_str()) == Some("lua") {
|
|
||||||
if let Ok(content) = fs::read_to_string(&path) {
|
|
||||||
log::debug!("Loaded Lua script: {}", path.display());
|
|
||||||
script_content.push_str(&content);
|
|
||||||
script_content.push('\n');
|
|
||||||
scripts_loaded = true;
|
|
||||||
} else {
|
|
||||||
log::warn!("Failed to read Lua script: {}", path.display());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::warn!("Lua scripts directory does not exist: {scripts_dir}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no scripts were loaded, use a default script
|
|
||||||
if !scripts_loaded {
|
|
||||||
log::info!("No Lua scripts found, loading default scripts");
|
|
||||||
script_content = r#"
|
|
||||||
function generate_honeytoken(token)
|
|
||||||
local token_types = {"API_KEY", "AUTH_TOKEN", "SESSION_ID", "SECRET_KEY"}
|
|
||||||
local prefix = token_types[math.random(#token_types)]
|
|
||||||
local suffix = string.format("%08x", math.random(0xffffff))
|
|
||||||
return prefix .. "_" .. token .. "_" .. suffix
|
|
||||||
end
|
|
||||||
|
|
||||||
function enhance_response(text, response_type, path, token)
|
|
||||||
local result = text
|
|
||||||
local honeytoken = generate_honeytoken(token)
|
|
||||||
|
|
||||||
-- Add some fake sensitive data
|
|
||||||
result = result .. "\n<!-- DEBUG: " .. honeytoken .. " -->"
|
|
||||||
result = result .. "\n<div style='display:none'>Server ID: " .. token .. "</div>"
|
|
||||||
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
"#
|
|
||||||
.to_string();
|
|
||||||
scripts_loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
script_content,
|
|
||||||
scripts_loaded,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lua is a powerful configuration language we can use to expand functionality of
|
|
||||||
// Eris, e.g., with fake tokens or honeytrap content.
|
|
||||||
fn expand_response(&self, text: &str, response_type: &str, path: &str, token: &str) -> String {
|
|
||||||
if !self.scripts_loaded {
|
|
||||||
return format!("{text}\n<!-- Token: {token} -->");
|
|
||||||
}
|
|
||||||
|
|
||||||
let lua = Lua::new();
|
|
||||||
if let Err(e) = lua.load(&self.script_content).exec() {
|
|
||||||
log::warn!("Error loading Lua script: {e}");
|
|
||||||
return format!("{text}\n<!-- Error: Failed to load Lua script -->");
|
|
||||||
}
|
|
||||||
|
|
||||||
let globals = lua.globals();
|
|
||||||
match globals.get::<_, Function>("enhance_response") {
|
|
||||||
Ok(enhance_func) => {
|
|
||||||
match enhance_func.call::<_, String>((text, response_type, path, token)) {
|
|
||||||
Ok(result) => result,
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Error calling Lua function enhance_response: {e}");
|
|
||||||
format!("{text}\n<!-- Error calling Lua enhance_response -->")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::warn!("Lua enhance_response function not found: {e}");
|
|
||||||
format!("{text}\n<!-- Lua enhance_response function not found -->")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find end of HTTP headers
|
// Find end of HTTP headers
|
||||||
// XXX: I'm sure this could be made less fragile.
|
|
||||||
fn find_header_end(data: &[u8]) -> Option<usize> {
|
fn find_header_end(data: &[u8]) -> Option<usize> {
|
||||||
for i in 0..data.len().saturating_sub(3) {
|
data.windows(4)
|
||||||
if data[i] == b'\r' && data[i + 1] == b'\n' && data[i + 2] == b'\r' && data[i + 3] == b'\n'
|
.position(|window| window == b"\r\n\r\n")
|
||||||
{
|
.map(|pos| pos + 4)
|
||||||
return Some(i + 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract path from raw request data
|
// Extract path from raw request data
|
||||||
|
@ -1379,18 +1276,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_script_manager_default_script() {
|
|
||||||
let script_manager = ScriptManager::new("/nonexistent_directory");
|
|
||||||
assert!(script_manager.scripts_loaded);
|
|
||||||
assert!(
|
|
||||||
script_manager
|
|
||||||
.script_content
|
|
||||||
.contains("generate_honeytoken")
|
|
||||||
);
|
|
||||||
assert!(script_manager.script_content.contains("enhance_response"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_bot_state() {
|
async fn test_bot_state() {
|
||||||
let state = BotState::new("/tmp/eris_test", "/tmp/eris_test_cache");
|
let state = BotState::new("/tmp/eris_test", "/tmp/eris_test_cache");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue