#![allow(deprecated)]
use oxcache::backend::l2::L2Backend;
use oxcache::config::{L2Config, RedisMode};
use oxcache::security::{validate_lua_script, validate_redis_key, validate_scan_pattern};
use crate::common;
#[tokio::test]
async fn test_redis_tls_config_parsing() {
common::setup_logging();
let l2_config = L2Config {
mode: RedisMode::Standalone,
connection_string: std::env::var("REDIS_TLS_URL")
.unwrap_or_else(|_| "redis://127.0.0.1:6380".to_string())
.into(),
enable_tls: true,
..Default::default()
};
let result = L2Backend::new(&l2_config).await;
match result {
Ok(_) => {
}
Err(e) => {
let msg = e.to_string();
println!(
"Got expected error connecting to non-existent TLS redis: {}",
msg
);
}
}
}
#[tokio::test]
async fn test_lua_script_injection_attempts() {
common::setup_logging();
let result = validate_lua_script("return redis.call('FLUSHALL')", 0);
assert!(result.is_err(), "FLUSHALL should be rejected");
let result = validate_lua_script("return redis.call('FLUSHDB')", 0);
assert!(result.is_err(), "FLUSHDB should be rejected");
let result = validate_lua_script("return redis.call('KEYS', '*')", 0);
assert!(result.is_err(), "KEYS command should be rejected");
let result = validate_lua_script("return redis.call('SHUTDOWN')", 0);
assert!(result.is_err(), "SHUTDOWN should be rejected");
let result = validate_lua_script("return redis.call('CONFIG', 'GET', '*')", 0);
assert!(result.is_err(), "CONFIG command should be rejected");
let result = validate_lua_script("return redis.call('DEBUG', 'SEGFAULT')", 0);
assert!(result.is_err(), "DEBUG command should be rejected");
let result = validate_lua_script("return redis.call('flushall')", 0);
assert!(
result.is_err(),
"Case-insensitive FLUSHALL should be rejected"
);
let result = validate_lua_script("return redis.call('FlushAll')", 0);
assert!(result.is_err(), "Mixed case FLUSHALL should be rejected");
let result = validate_lua_script("--[[ malicious ]] return redis.call('FLUSHALL')", 0);
assert!(
result.is_err(),
"Comment-hidden FLUSHALL should be rejected"
);
let result = validate_lua_script("local x = 'FLUSHALL'; return 'safe'", 0);
assert!(
result.is_ok(),
"String containing FLUSHALL should be allowed"
);
println!("✓ All Lua script injection attempts were correctly rejected");
}
#[tokio::test]
async fn test_scan_pattern_redos_attempts() {
common::setup_logging();
let result = validate_scan_pattern(&"*".repeat(20));
assert!(result.is_err(), "Too many wildcards should be rejected");
assert!(result.unwrap_err().to_string().contains("wildcard"));
let result = validate_scan_pattern(&"x".repeat(300));
assert!(result.is_err(), "Pattern too long should be rejected");
assert!(result.unwrap_err().to_string().contains("length"));
let result = validate_scan_pattern("*:*:*:*:*:*:*:*:*:*:*:*");
assert!(
result.is_err(),
"Too many nested wildcards should be rejected"
);
let result = validate_scan_pattern(&"*".repeat(10));
assert!(result.is_ok(), "Exactly 10 wildcards should be allowed");
let result = validate_scan_pattern(&"*".repeat(11));
assert!(result.is_err(), "11 wildcards should be rejected");
let result = validate_scan_pattern("(a+)+test");
assert!(
result.is_ok(),
"Complex pattern should be allowed (not ReDoS vulnerable)"
);
let result = validate_scan_pattern("user:*:data:*:profile");
assert!(result.is_ok(), "Mixed wildcards pattern should be allowed");
println!("✓ All SCAN ReDoS attempts were correctly handled");
}
#[tokio::test]
async fn test_redis_key_injection_attempts() {
common::setup_logging();
let result = validate_redis_key("key\r\nvalue");
assert!(result.is_err(), "CRLF injection should be rejected");
let result = validate_redis_key("key\nvalue");
assert!(result.is_err(), "LF injection should be rejected");
let result = validate_redis_key("key\rvalue");
assert!(result.is_err(), "CR injection should be rejected");
let result = validate_redis_key("key\0value");
assert!(result.is_err(), "Null byte injection should be rejected");
let result = validate_redis_key("");
assert!(result.is_err(), "Empty key should be rejected");
let result = validate_redis_key(&"x".repeat(512 * 1024 + 1));
assert!(result.is_err(), "Key exceeding 512KB should be rejected");
let result = validate_redis_key("key\r\n*3\r\n$3\r\nSET\r\n");
assert!(
result.is_err(),
"Protocol injection attempt should be rejected"
);
assert!(validate_redis_key("user:123").is_ok());
assert!(validate_redis_key("cache:data:value").is_ok());
assert!(validate_redis_key("test_key").is_ok());
assert!(validate_redis_key("a:b:c:d:e").is_ok());
println!("✓ All Redis key injection attempts were correctly rejected");
}
#[tokio::test]
async fn test_connection_string_redaction() {
common::setup_logging();
use oxcache::database::normalize_connection_string_with_redaction;
let _mysql_no_pass = "mysql://user@localhost:3306";
let mysql_with_pass = "mysql://user:password123@localhost:3306";
println!("Testing: {}", mysql_with_pass);
let result = normalize_connection_string_with_redaction(mysql_with_pass, true);
println!("Result: {}", result);
assert!(result.contains("****"), "Password should be redacted");
assert!(
!result.contains("password123"),
"Original password should not be present"
);
assert!(result.contains("user"), "Username should be preserved");
let postgres_with_pass = "postgres://admin:secret456@host:5432/db";
let result = normalize_connection_string_with_redaction(postgres_with_pass, true);
assert!(result.contains("****"), "Password should be redacted");
assert!(
!result.contains("secret456"),
"Original password should not be present"
);
let result = normalize_connection_string_with_redaction(mysql_with_pass, false);
assert!(
result.contains("password123"),
"Password should be preserved when redact=false"
);
let redis_with_pass = "redis://:mypassword@localhost:6379";
println!("Testing Redis: {}", redis_with_pass);
let result = normalize_connection_string_with_redaction(redis_with_pass, true);
println!("Redis Result: {}", result);
assert!(result.contains("****"), "Password should be redacted");
println!("✓ Connection string redaction works correctly");
}