| Crates.io | palisade_errors |
| lib.rs | palisade_errors |
| version | 0.2.2 |
| created_at | 2026-01-06 21:39:13.319335+00 |
| updated_at | 2026-01-11 17:24:54.837666+00 |
| description | Security-conscious error handling with operational security principles |
| homepage | |
| repository | https://github.com/guivernoir/palisade_errors |
| max_upload_size | |
| id | 2026912 |
| size | 309,973 |
Security-conscious error handling for high-assurance Rust applications.
Palisade Errors is designed for systems where information leakage is a security vulnerability. Built as the foundational error handling layer for the Palisade Honeypot System, it enforces a strict separation between "what happened" (forensics) and "what the user sees" (sanitization), while guaranteeing that sensitive data in memory is zeroized immediately after use.
In a honeypot, every error is intelligence:
Palisade Errors ensures attackers see only walls, while defenders see everything.
Display) are automatically sanitized to reveal only error codes and categoriesZeroizeOnDrop types. Secrets are wiped from memory as soon as the error is droppedCow<'static, str> to handle string literals without heap allocationBenchmarked on Dell Latitude E6410 (Intel Core i5 M560 @ 2.66GHz, circa 2010):
| Operation | Time | Honeypot Capacity |
|---|---|---|
| Simple error creation | 209ns | 4.8M errors/sec |
| Dynamic string error | 253ns | 3.9M errors/sec |
| Sensitive context error | 224ns | 4.5M errors/sec |
| I/O error (split source) | 359ns | 2.8M errors/sec |
| Operation | Time | Throughput |
|---|---|---|
| Internal log access | 59ns | 16.9M ops/sec |
| Log write to buffer | 673ns | 1.5M ops/sec |
| Callback logging pattern | 30ns | 33.3M ops/sec |
| Scenario | Time | Capacity |
|---|---|---|
| Auth failure (full pipeline) | 1.39ฮผs | 719k/sec |
| Path traversal detection | 1.23ฮผs | 813k/sec |
| Rate limit response | 540ns | 1.9M/sec |
| Burst Size | Time | Bursts/sec |
|---|---|---|
| 10 errors | 8.8ฮผs | 114k/sec |
| 50 errors | 45.7ฮผs | 21.9k/sec |
| 100 errors | 86.6ฮผs | 11.5k/sec |
| 500 errors | 446ฮผs | 2.2k/sec |
| Entries | Time | Impact |
|---|---|---|
| 1 entry | 326ns | Inline storage |
| 2 entries | 404ns | Inline storage |
| 4 entries | 527ns | Inline storage |
| 8 entries | 1.17ฮผs | Heap allocation |
Typical honeypot load: 1,000-5,000 errors/second during coordinated attacks
Safety margin: 140-800x on 15-year-old hardware
CPU overhead at 5,000 errors/sec: <0.7% (estimated 5ms CPU time)
On modern hardware (Ryzen 9, M-series), expect 3-5x better performance.
use palisade_errors::{AgentError, definitions, Result};
fn check_access(user: &str) -> Result<()> {
if user == "admin" {
return Ok(());
}
// This creates an error that logs the user input internally
// but only shows "Configuration operation failed" externally.
Err(AgentError::config(
definitions::CFG_PERMISSION_DENIED,
"check_access",
format!("User '{}' denied", user)
))
}
What the attacker sees:
Configuration operation failed [permanent] (E-CFG-104)
What your logs contain:
[1704652800] [E-CFG-104] operation='check_access'
details="User 'attacker' denied" source_ip=192.168.1.100
When handling passwords, keys, file paths, or PII, use the sensitive constructors to ensure data is sequestered and zeroized:
use palisade_errors::{AgentError, definitions};
let err = AgentError::config_sensitive(
definitions::CFG_INVALID_VALUE,
"login_flow", // Operation name
"Password verification failed", // Generic details
password_input // SENSITIVE: Zeroized on drop, never shown in Display
);
Security guarantee: When err is dropped, password_input is zeroized in memory. Core dumps cannot recover it.
To access the internal details for your secure logs, use the internal_log() method. This returns a short-lived structure that prevents data retention:
// In your logging middleware
if let Err(e) = result {
// Write full details to secure log file
let mut log_buf = String::new();
e.internal_log().write_to(&mut log_buf).unwrap();
secure_logger.info(log_buf);
// Return sanitized error to client
return Err(e);
}
Track attack patterns across errors:
let err = AgentError::config_sensitive(
definitions::CFG_VALIDATION_FAILED,
"ssh_authenticate",
"Authentication failed",
format!("username={} password={}", username, password)
)
.with_metadata("source_ip", attacker_ip)
.with_metadata("protocol", "ssh")
.with_metadata("campaign_id", detected_campaign);
// Process through correlation engine
correlator.track_error(&err, attacker_ip);
# Basic usage patterns
cargo run --example basic_usage
# Sensitive data handling
cargo run --example sensitive_context
# Complete honeypot pipeline (attack simulation)
cargo run --example honeypot_pipeline
The honeypot_pipeline example demonstrates:
Convenient scripts are provided for comprehensive testing, benchmarking, and fuzzing:
# Run all tests with multiple feature combinations
./run_all_tests.sh
This script runs:
# Run comprehensive performance benchmarks
./run_benchmarks.sh
Generates detailed performance reports in target/criterion/report/index.html covering:
Note: Fuzz testing requires Rust nightly toolchain. Install with:
rustup install nightly
# Run all fuzz targets for 60 seconds each (uses nightly automatically)
./run_fuzz.sh all 60
# Run specific fuzz target
./run_fuzz.sh truncation 300
# Available targets: error_context, truncation, metadata, ring_buffer
Fuzz testing targets:
All external errors use identical format regardless of failure reason:
{Category} operation failed [{permanence}] ({ERROR-CODE})
Attackers cannot distinguish between:
Threat: Post-compromise memory dumps reveal sensitive data
Mitigation: Automatic zeroization via ZeroizeOnDrop
{
let err = AgentError::config_sensitive(..., "password=Secret123");
// Use error...
} // <- Memory zeroized here, unrecoverable
Threat: Response timing reveals authentication logic paths
Mitigation: Optional timing normalization
fn authenticate(user: &str, pass: &str) -> Result<Session> {
let result = check_credentials(user, pass);
if let Err(e) = result {
// All auth failures take exactly 100ms
return Err(e.with_timing_normalization(Duration::from_millis(100)));
}
result
}
Threat: Attacker triggers errors with massive payloads to exhaust memory
Mitigation: All log fields truncated to 1024 characters with clear indicators
Threat: Distributed attacks evade correlation
Mitigation: Metadata-based tracking across IPs, protocols, and time windows
Attacker Request
โ
Application Logic (fails)
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ AgentError (palisade_errors) โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ External (Display) โ โโโโ Sanitized response to attacker
โ โ "Configuration failed (E-101)" โ โ (zero information leakage)
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Internal (InternalLog) โ โโโโ Forensic logs
โ โ Full context + metadata โ โ (complete audit trail)
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Sensitive (ZeroizeOnDrop) โ โโโโ Restricted access logs
โ โ Credentials, paths, PII โ โ (encrypted, HSM-backed)
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ (on drop)
Memory Zeroization
impl HttpHoneypot {
async fn handle_request(&self, req: HttpRequest, addr: SocketAddr) -> HttpResponse {
let correlation_id = uuid::Uuid::new_v4().to_string();
match self.process_request(&req).await {
Ok(response) => response,
Err(err) => {
// Add tracking metadata
let err = err
.with_metadata("source_ip", &addr.ip().to_string())
.with_metadata("correlation_id", &correlation_id)
.with_metadata("path", req.uri().path());
// Log to forensic system
self.logger.log_error(&err);
// Update attack correlation
self.correlator.process_error(&err, &addr.ip().to_string());
// Return sanitized error
HttpResponse::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(format!("{}", err)) // Sanitized via Display
.unwrap()
}
}
}
}
// Structured JSON for SIEM ingestion
err.with_internal_log(|log| {
let event = json!({
"timestamp": Utc::now().to_rfc3339(),
"severity": "ERROR",
"code": log.code().to_string(),
"namespace": log.code().namespace(),
"category": format!("{:?}", err.category()),
"operation": log.operation(),
"details": log.details(),
"metadata": log.metadata().iter()
.map(|(k, v)| (k.to_string(), v.as_str().to_string()))
.collect::<HashMap<_, _>>(),
});
siem.send_event(event);
});
# Run benchmarks
cargo bench
# View detailed HTML report
open target/criterion/report/index.html
# Specific benchmark groups
cargo bench creation_benches # Error creation
cargo bench honeypot_benches # Attack scenarios
cargo bench attack_burst # Burst simulation
# All tests
cargo test
# Run comprehensive test suite
./run_all_tests.sh
# With coverage
cargo install cargo-tarpaulin
cargo tarpaulin --out Html --output-dir coverage/
# Memory leak verification
cargo install cargo-valgrind
cargo valgrind test --release
# Fuzz testing (requires cargo-fuzz)
./run_fuzz.sh all 60
Test Coverage:
Fuzz Testing Targets:
Any system where error messages could aid an adversary.
default: Standard error handlingtrusted_debug: Enable detailed debug output (โ ๏ธ debug builds only, never in production)external_signaling: Reserved for future capabilitiesContributions welcome! Areas of interest:
Licensed under Apache-2.0.
We assume attackers:
We guarantee:
This library is ONE layer of security. Combine with:
Current State: Production-ready for honeypot deployment
Performance: Validated on hardware from 2010 to present
Security: Designed under adversarial threat model
Testing: Comprehensive test suite + benchmarks + examples
Documentation: Complete API docs + integration guides
Built for the Palisade Honeypot System
Where every error is a strategic deception.