Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions crates/ironclaw_safety/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,4 +468,24 @@ mod tests {
"Strings within depth limit should still be validated"
);
}

/// Regression: empty input is rejected by validate(), but image-only messages
/// (empty text + attachments) should bypass this check at the caller level.
/// This test confirms the validator correctly rejects empty strings — the
/// bypass logic lives in thread_ops.rs, not in the validator itself.
#[test]
fn test_empty_input_rejected() {
let validator = Validator::new();
let result = validator.validate("");
assert!(!result.is_valid, "Empty input must be rejected");
assert_eq!(result.errors[0].code, ValidationErrorCode::Empty);
}

/// Non-empty input with valid content should pass validation.
#[test]
fn test_nonempty_input_passes() {
let validator = Validator::new();
let result = validator.validate("Hello, world!");
assert!(result.is_valid, "Non-empty valid input must pass");
}
}
12 changes: 10 additions & 2 deletions src/agent/thread_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,16 @@ impl Agent {
}
}

// Safety validation for user input
let validation = self.safety().validate_input(content);
// Safety validation for user input.
// Skip empty-content check when the message has attachments (e.g., image-only messages).
// The attachment pipeline (augment_with_attachments) will provide content downstream.
let has_attachments = !message.attachments.is_empty();
let validation = if content.is_empty() && has_attachments {
// Image/file-only message — bypass empty-input validation
ironclaw_safety::ValidationResult::ok()
} else {
self.safety().validate_input(content)
};
if !validation.is_valid {
let details = validation
.errors
Expand Down
Loading