use thiserror::Error;
const SENSITIVE_PATTERNS: &[(&str, &str)] = &[
("(?i)(AKIA|ABIA|ACCA|ASIA)[0-9A-Z]{16}\\b", "[AWS_ACCESS_KEY_ID]"),
("[0-9a-zA-Z+/]{40}={0,2}\\b", "[AWS_SECRET_ACCESS_KEY]"),
("\\beyJ[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+\\.[a-zA-Z0-9_-]+\\b", "[JWT_TOKEN]"),
("(?i)(postgres|postgresql)://[^@]+:[^@]+@", "$1://***:***@"),
("(?i)mysql://[^@]+:[^@]+@", "mysql://***:***@"),
("(?i)sqlite://[^?]*\\?[^&]*", "sqlite://***"),
("(?i)(api[_-]?key|access[_-]?key|secret[_-]?key)[\"']?\\s*[=:]\\s*[\"']?[a-zA-Z0-9_\\-]{20,}", "$1=***REDACTED***"),
("(?i)(bearer|authorization)\\s*:\\s*[a-zA-Z0-9_\\-\\.]+", "$1: ***REDACTED***"),
("/home/[a-zA-Z0-9_-]+/", "[USER_HOME_PATH]"),
("/etc/inklog/", "[CONFIG_PATH]"),
("/run/secrets/", "[SECRETS_PATH]"),
("(?i)(password|passwd|pwd)=[^&\\s]+", "$1=***"),
("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}", "***@***.***"),
("\\b1[3-9]\\d{9}\\b", "***-****-****"),
("\\b\\d{4}[ -]?\\d{4}[ -]?\\d{4}[ -]?\\d{4}\\b", "****-****-****-****"),
];
fn sanitize_message(msg: &str) -> String {
let mut result = msg.to_string();
for (pattern, replacement) in SENSITIVE_PATTERNS {
if let Ok(re) = regex::Regex::new(pattern) {
result = re.replace_all(&result, *replacement).to_string();
}
}
result
}
#[derive(Error, Debug)]
pub enum InklogError {
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Serialization error: {0}")]
SerializationError(#[from] serde_json::Error),
#[error("Database error: {0}")]
DatabaseError(String),
#[error("Encryption error: {0}")]
EncryptionError(String),
#[error("Shutdown error: {0}")]
Shutdown(String),
#[error("Channel error: {0}")]
ChannelError(String),
#[error("S3 error: {0}")]
S3Error(String),
#[error("Compression error: {0}")]
CompressionError(String),
#[error("Runtime error: {0}")]
RuntimeError(String),
#[error("HTTP server error: {0}")]
HttpServerError(String),
#[error("Unknown error: {0}")]
Unknown(String),
}
#[cfg(feature = "confers")]
impl From<confers::error::ConfigError> for InklogError {
fn from(err: confers::error::ConfigError) -> Self {
InklogError::ConfigError(err.to_string())
}
}
#[cfg(feature = "confers")]
impl From<toml::de::Error> for InklogError {
fn from(err: toml::de::Error) -> Self {
InklogError::ConfigError(err.to_string())
}
}
#[cfg(feature = "aws")]
impl From<tokio_cron_scheduler::JobSchedulerError> for InklogError {
fn from(err: tokio_cron_scheduler::JobSchedulerError) -> Self {
InklogError::ConfigError(format!("Scheduler error: {}", err))
}
}
impl InklogError {
pub fn safe_message(&self) -> String {
match self {
InklogError::ConfigError(msg) => {
format!("Configuration error: {}", sanitize_message(msg))
}
InklogError::IoError(e) => {
format!("IO error: {}", sanitize_message(&e.to_string()))
}
InklogError::SerializationError(e) => {
format!("Serialization error: {}", sanitize_message(&e.to_string()))
}
InklogError::DatabaseError(msg) => {
format!("Database error: {}", sanitize_message(msg))
}
InklogError::EncryptionError(msg) => {
format!("Encryption error: {}", sanitize_message(msg))
}
InklogError::Shutdown(msg) => {
format!("Shutdown error: {}", sanitize_message(msg))
}
InklogError::ChannelError(msg) => {
format!("Channel error: {}", sanitize_message(msg))
}
InklogError::S3Error(msg) => {
format!("S3 error: {}", sanitize_message(msg))
}
InklogError::CompressionError(msg) => {
format!("Compression error: {}", sanitize_message(msg))
}
InklogError::RuntimeError(msg) => {
format!("Runtime error: {}", sanitize_message(msg))
}
InklogError::HttpServerError(msg) => {
format!("HTTP server error: {}", sanitize_message(msg))
}
InklogError::Unknown(msg) => {
format!("Unknown error: {}", sanitize_message(msg))
}
}
}
}