pub struct ExfiltrationGuard { /* private fields */ }Expand description
Stateless exfiltration guard. All three scanners are independently toggled via config.
Construct once from ExfiltrationGuardConfig and store on the agent. Cheap to clone.
Implementations§
Source§impl ExfiltrationGuard
impl ExfiltrationGuard
Sourcepub fn new(config: ExfiltrationGuardConfig) -> ExfiltrationGuard
pub fn new(config: ExfiltrationGuardConfig) -> ExfiltrationGuard
Create a new guard from the given configuration.
Sourcepub fn scan_output(&self, text: &str) -> (String, Vec<ExfiltrationEvent>)
pub fn scan_output(&self, text: &str) -> (String, Vec<ExfiltrationEvent>)
Scan LLM output text and strip external markdown images.
Returns the cleaned text and a list of ExfiltrationEvent::MarkdownImageBlocked
for each image that was removed.
When block_markdown_images is false, returns the input unchanged.
§Scanning coverage
- Inline images:
 - Reference-style images:
![alt][ref]+[ref]: https://evil.com/img - Percent-encoded URLs inside already-captured groups: decoded before
is_external_url()
§Not covered (Phase 5, tracked in #1195)
- Percent-encoded scheme bypass:
%68ttps://evil.com— the regex requires literalhttps?://, so a percent-encoded scheme is never captured. Fix requires pre-decoding the full input text before regex matching. - HTML
<img src="...">tags - Unicode zero-width joiner tricks (
!\u{200B}[alt](url)) - Reference definitions inside fenced code blocks (false positive risk)
§Panics
Panics if the compiled regex does not produce expected capture groups (compile-time
guarantee — the regex patterns are validated via expect in LazyLock initializers).
Sourcepub fn validate_tool_call(
&self,
tool_name: &str,
args_json: &str,
flagged_urls: &HashSet<String>,
) -> Vec<ExfiltrationEvent>
pub fn validate_tool_call( &self, tool_name: &str, args_json: &str, flagged_urls: &HashSet<String>, ) -> Vec<ExfiltrationEvent>
Validate tool call arguments against a set of URLs flagged in untrusted content.
Parses args_json as a JSON value and extracts all string leaves recursively to
avoid JSON-encoding bypasses (escaped slashes, unicode escapes, etc.).
Returns one ExfiltrationEvent::SuspiciousToolUrl per matching URL.
When validate_tool_urls is false, always returns an empty vec.
§Flag-only approach
Matching URLs are logged and counted but tool execution is NOT blocked. Blocking would break legitimate workflows where the same URL appears in both a search result and a subsequent fetch call. See design decision D1 in the architect handoff.
Sourcepub fn should_guard_memory_write(
&self,
has_injection_flags: bool,
) -> Option<ExfiltrationEvent>
pub fn should_guard_memory_write( &self, has_injection_flags: bool, ) -> Option<ExfiltrationEvent>
Check whether a memory write should skip Qdrant embedding.
Returns Some(MemoryWriteGuarded) when has_injection_flags is true and
guard_memory_writes is enabled. The caller should still save to SQLite for
conversation continuity but omit the Qdrant embedding to prevent poisoned content
from polluting semantic search results.
See design decision D2 in the architect handoff.
Trait Implementations§
Source§impl Clone for ExfiltrationGuard
impl Clone for ExfiltrationGuard
Source§fn clone(&self) -> ExfiltrationGuard
fn clone(&self) -> ExfiltrationGuard
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for ExfiltrationGuard
impl RefUnwindSafe for ExfiltrationGuard
impl Send for ExfiltrationGuard
impl Sync for ExfiltrationGuard
impl Unpin for ExfiltrationGuard
impl UnsafeUnpin for ExfiltrationGuard
impl UnwindSafe for ExfiltrationGuard
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request