mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 23:50:17 +00:00
fix: rewrite fuzz_config_env to exercise IronClaw safety code directly
Replace SafetyLayer wrapper usage with direct Sanitizer, Validator, and LeakDetector instantiation and invocation. Adds meaningful consistency assertions (non-empty output, valid-means-no-errors, scan/clean agreement). Removes the config construction that was only exercising struct instantiation. [skip-regression-check] Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
@@ -1,29 +1,55 @@
|
||||
#![no_main]
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
use ironclaw::config::SafetyConfig;
|
||||
use ironclaw::safety::{LeakDetector, Sanitizer, Validator};
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(s) = std::str::from_utf8(data) {
|
||||
// SafetyConfig fields are parsed from env vars. We cannot safely set
|
||||
// env vars in a multi-threaded fuzzer, but we can exercise the types
|
||||
// that config parsing produces by constructing SafetyConfig directly
|
||||
// and feeding the fuzzed string through the safety layer it creates.
|
||||
if let Ok(input) = std::str::from_utf8(data) {
|
||||
// Exercise Sanitizer: detect and neutralize prompt injection attempts.
|
||||
let sanitizer = Sanitizer::new();
|
||||
let sanitized = sanitizer.sanitize(input);
|
||||
// The sanitized content must never be empty when input is non-empty,
|
||||
// because sanitization wraps/escapes rather than deleting.
|
||||
if !input.is_empty() {
|
||||
assert!(
|
||||
!sanitized.content.is_empty(),
|
||||
"sanitize() produced empty content for non-empty input"
|
||||
);
|
||||
}
|
||||
// If no modification occurred, content must equal input.
|
||||
if !sanitized.was_modified {
|
||||
assert_eq!(sanitized.content, input);
|
||||
}
|
||||
|
||||
// Parse the fuzzed input as a potential max_output_length value.
|
||||
let max_len: usize = s.parse().unwrap_or(100_000);
|
||||
// Exercise Validator: input validation (length, encoding, patterns).
|
||||
let validator = Validator::new();
|
||||
let result = validator.validate(input);
|
||||
// ValidationResult must always be well-formed: if valid, no errors.
|
||||
if result.is_valid {
|
||||
assert!(
|
||||
result.errors.is_empty(),
|
||||
"valid result should have no errors"
|
||||
);
|
||||
}
|
||||
|
||||
let config = SafetyConfig {
|
||||
max_output_length: max_len,
|
||||
injection_check_enabled: true,
|
||||
};
|
||||
|
||||
// Build a SafetyLayer from the config and exercise it.
|
||||
let layer = ironclaw::safety::SafetyLayer::new(&config);
|
||||
|
||||
// Use the fuzzed string as tool output content.
|
||||
let _ = layer.sanitize_tool_output("fuzz_tool", s);
|
||||
let _ = layer.validate_input(s);
|
||||
let _ = layer.check_policy(s);
|
||||
// Exercise LeakDetector: secret detection (API keys, tokens, etc.).
|
||||
let detector = LeakDetector::new();
|
||||
let scan = detector.scan(input);
|
||||
// scan_and_clean must not panic and must return valid UTF-8.
|
||||
let cleaned = detector.scan_and_clean(input);
|
||||
if let Ok(ref clean_str) = cleaned {
|
||||
// Cleaned output must never be longer than original + redaction markers.
|
||||
// At minimum it should be valid UTF-8 (guaranteed by String type).
|
||||
let _ = clean_str.len();
|
||||
}
|
||||
// If scan found no matches, scan_and_clean should return the input unchanged.
|
||||
if scan.matches.is_empty() {
|
||||
if let Ok(ref clean_str) = cleaned {
|
||||
assert_eq!(
|
||||
clean_str, input,
|
||||
"scan_and_clean changed content despite no matches"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user