Configuration
libmagic-rs provides a single configuration struct, EvaluationConfig, that controls how magic rules are evaluated. All fields have safe defaults, and the validate() method enforces security bounds before any evaluation begins.
EvaluationConfig
#![allow(unused)]
fn main() {
use libmagic_rs::EvaluationConfig;
pub struct EvaluationConfig {
pub max_recursion_depth: u32, // Default: 20, max: 1000
pub max_string_length: usize, // Default: 8192, max: 1MB (1_048_576)
pub stop_at_first_match: bool, // Default: true
pub enable_mime_types: bool, // Default: false
pub timeout_ms: Option<u64>, // Default: None, max: 300_000 (5 min)
}
}
Field Reference
| Field | Type | Default | Bounds | Purpose |
|---|---|---|---|---|
max_recursion_depth | u32 | 20 | 1 – 1000 | Limits nested rule traversal depth to prevent stack overflow |
max_string_length | usize | 8192 | 1 – 1_048_576 | Caps bytes read for string types to prevent memory exhaustion |
stop_at_first_match | bool | true | – | When true, evaluation stops after the first matching top-level rule (children of that rule are still evaluated – see below) |
enable_mime_types | bool | false | – | When true, maps file type descriptions to standard MIME types |
timeout_ms | Option<u64> | None | 1 – 300_000 | Per-file evaluation timeout in milliseconds; None disables |
stop_at_first_match Semantics
“First match” refers to the first top-level rule that matches. Children of the first matching top-level rule are always evaluated before the stop check; the stop check applies to subsequent top-level rules.
In other words, stop_at_first_match = true does not truncate the child subtree of the matching rule – it only prevents later sibling top-level rules from being evaluated. A successful top-level match therefore returns one parent match plus any descendant matches its children produced. If you need the absolute minimum work done per evaluation, combine this setting with a shallow max_recursion_depth and a magic rule set whose top-level rules are already specific enough that children add only refinement (not new classification).
Constructor Presets
EvaluationConfig::new() / EvaluationConfig::default()
Returns balanced defaults suitable for most workloads:
#![allow(unused)]
fn main() {
let config = EvaluationConfig::new();
assert_eq!(config.max_recursion_depth, 20);
assert_eq!(config.max_string_length, 8192);
assert!(config.stop_at_first_match);
assert!(!config.enable_mime_types);
assert_eq!(config.timeout_ms, None);
}
EvaluationConfig::performance()
Optimized for high-throughput scenarios where speed matters more than completeness:
#![allow(unused)]
fn main() {
let config = EvaluationConfig::performance();
assert_eq!(config.max_recursion_depth, 10);
assert_eq!(config.max_string_length, 1024);
assert!(config.stop_at_first_match);
assert!(!config.enable_mime_types);
assert_eq!(config.timeout_ms, Some(1000)); // 1 second
}
EvaluationConfig::comprehensive()
Finds all matches with deep analysis, MIME type mapping, and a generous timeout:
#![allow(unused)]
fn main() {
let config = EvaluationConfig::comprehensive();
assert_eq!(config.max_recursion_depth, 50);
assert_eq!(config.max_string_length, 32768);
assert!(!config.stop_at_first_match);
assert!(config.enable_mime_types);
assert_eq!(config.timeout_ms, Some(30000)); // 30 seconds
}
Custom Configuration
Use struct update syntax to override individual fields from any preset:
#![allow(unused)]
fn main() {
use libmagic_rs::EvaluationConfig;
let config = EvaluationConfig {
max_recursion_depth: 30,
enable_mime_types: true,
timeout_ms: Some(5000),
..EvaluationConfig::default()
};
}
Validation
Call validate() to check that all values fall within safe bounds. The MagicDatabase constructors call validate() automatically, so you only need to call it explicitly when creating a config that will be stored for later use.
#![allow(unused)]
fn main() {
use libmagic_rs::EvaluationConfig;
let config = EvaluationConfig::default();
assert!(config.validate().is_ok());
let bad = EvaluationConfig {
max_recursion_depth: 0,
..EvaluationConfig::default()
};
assert!(bad.validate().is_err());
}
Validation Rules
The validate() method enforces four categories of security constraints:
Recursion depth – must be between 1 and 1000. A value of 0 is rejected because evaluation cannot proceed without at least one level. Values above 1000 risk stack overflow.
String length – must be between 1 and 1_048_576 (1 MB). A value of 0 is rejected because no string matching could occur. Values above 1 MB risk memory exhaustion.
Timeout – if Some, must be between 1 and 300_000 (5 minutes). A value of 0 is rejected as meaningless. Values above 5 minutes risk denial-of-service. None (no timeout) is always accepted.
Resource combination – a recursion depth above 100 combined with a string length above 65_536 is rejected. Deep recursion with large string reads at every level can compound into excessive resource consumption even when each value individually falls within safe bounds.
Using Configuration with MagicDatabase
Built-in Rules with Default Config
#![allow(unused)]
fn main() {
use libmagic_rs::MagicDatabase;
let db = MagicDatabase::with_builtin_rules()?;
let result = db.evaluate_buffer(b"\x7fELF")?;
}
Built-in Rules with Custom Config
#![allow(unused)]
fn main() {
use libmagic_rs::{MagicDatabase, EvaluationConfig};
let config = EvaluationConfig {
timeout_ms: Some(5000),
..EvaluationConfig::default()
};
let db = MagicDatabase::with_builtin_rules_and_config(config)?;
}
Magic File with Custom Config
#![allow(unused)]
fn main() {
use libmagic_rs::{MagicDatabase, EvaluationConfig};
let config = EvaluationConfig::performance();
let db = MagicDatabase::load_from_file_with_config("custom.magic", config)?;
}
All three constructors call config.validate() internally and return an error if the configuration is invalid. There is no way to create a MagicDatabase with an invalid configuration.
CLI Usage
The command-line interface exposes the timeout_ms field via the --timeout-ms flag. All other configuration values use their defaults when running from the CLI.
# No timeout (default)
libmagic-rs sample.bin
# 5-second timeout per file
libmagic-rs --timeout-ms 5000 sample.bin
If evaluation exceeds the timeout, the file is skipped and an error message is printed to stderr with exit code 5.
Security Considerations
The default configuration returned by EvaluationConfig::default() (and EvaluationConfig::new()) has timeout_ms: None, meaning evaluation runs without a wall-clock limit. This is appropriate for trusted input, but a maliciously crafted file or magic rule could cause an unbounded evaluation and a denial-of-service condition.
When processing untrusted input, do one of the following:
- Use the
EvaluationConfig::performance()preset, which setstimeout_ms: Some(1000)(1 second) along with tighter recursion and string-length bounds. - Set
timeout_msexplicitly via struct update syntax, for exampleEvaluationConfig { timeout_ms: Some(5000), ..EvaluationConfig::default() }. - Pass
--timeout-ms <ms>on the CLI.
The other bounds enforced by validate() (recursion depth, string length, resource combination) protect against stack overflow and memory exhaustion regardless of the timeout setting, but only the timeout limits total evaluation wall-clock time.
Choosing a Preset
| Scenario | Preset | Why |
|---|---|---|
| General file identification | default() | Balanced depth and limits |
| Batch processing many files | performance() | Low limits, 1s timeout, early exit |
| Forensic analysis | comprehensive() | Deep traversal, all matches, MIME types |
| Untrusted input | performance() | Tight bounds reduce attack surface |
| Custom requirements | Struct update syntax | Override specific fields from any preset |