Expand description
T-32 attachment policy layer.
The agent receives a message body containing 📎 attachment: <path>
and asks the broker to read the file. This module owns the
decision: accept (return bytes) or reject (return reason). It is
transport-agnostic — the MCP read_attachment tool is one caller;
a future REST surface or CLI debug helper would call the same
check_and_read entry point.
Three independent guards layered before any read:
- Path-traversal: canonicalize the operator-supplied path and
confirm it is a descendant of one of
allowed_roots. - Size: stat the file and reject if it exceeds
max_size_bytesbefore any bytes are read. - Scanner (optional): hand the canonical path to an external command with a timeout; non-zero exit or timeout → reject.
Bytes are returned as-is on accept. No envelope wrapping, no “treat as data” framing — those are prompt-injection mitigations and live in the hook layer per owner ratify.
enabled = false short-circuits with RejectReason::Disabled —
no filesystem cost when the operator has flipped the flag.
Structs§
- Accepted
Attachment - T-32b: outcome of a successful read — bytes plus metadata the caller surfaces to the agent (and writes to the audit log).
- Audit
Entry - T-32b audit log entry. Written as a single JSON line per attempt
so the file is
tail -f-friendly + parseable withjq. - Real
Scanner - Production scanner: spawns the operator-configured command with
the resolved path as a single argument, waits up to
timeout, captures stderr for the reject detail. The wait usesstd::thread::spawn+mpsc::recv_timeoutso team-core stays sync (no tokio dep — owner-ratify variant 4).
Enums§
- Reject
Reason - Reasons the broker can refuse to read an attachment. The agent surfaces the variant + a short string back to the operator via the originating-channel notification path; the operator never sees raw filesystem errors verbatim.
- Scan
Outcome
Traits§
- Scanner
- External-scanner abstraction. Implementations spawn the operator’s
configured command, wait up to
timeout, and return the outcome. Trait-object shape keeps the read path testable without spawning real processes — the test seam is the Mock impl in#[cfg(test)].
Functions§
- append_
audit - Append a single JSON-line audit entry. No-op when
audit_log_pathisNone. Errors creating the parent dir or opening the file are surfaced to the caller — production paths log-and-continue so a misconfigured audit dir doesn’t block real reads. - check_
and_ read - Attempt to read the file the operator pointed at, applying every
configured guard. The scanner is plumbed through as a trait object
so callers (production: real
Command; tests: mock) share the same control flow. - check_
and_ read_ with_ metadata - Wrapper that runs
check_and_readand packages the result with the metadata team-mcp’sread_attachmenttool returns to the agent. Centralises hashing so the staging-tempfile name (content- addressed) and the audit log entry stay in sync. - is_
within_ any_ root - Pure check: is
resolveda descendant of (or equal to) any ofroots? Both sides are expected canonical, so byte-equalitystarts_withis enough — no..slipping through. - now_
rfc3339 - Helper: format an RFC3339 UTC timestamp suitable for audit entries. Pulled out so tests can pin the format independently of the call site.
- resolve_
allowed_ roots - Resolve
$HOMEand other allow-list roots to canonical paths. Performed at check-time so a snapshot taken on machine A still resolves correctly when restored on machine B (different$HOME). Roots that fail to canonicalize are dropped — an operator with a stale path entry doesn’t break the whole policy. - stage_
to_ tempfile - Write
accepted.bytesto the staging dir under a content- addressed name. Idempotent: if the file already exists with the expected size, we skip the write (operator may have read the same attachment recently). Returns the staged path so the agent canread_file()it directly. - staging_
dir - T-32b staging directory under the compose root. Tempfiles live here, named by the content blake3 hash so identical content dedups to a single file across sessions and across agents.
- sweep_
expired - T-32b: drop tempfiles whose mtime is older than
now - ttl. Called on team-mcp startup as a best-effort cleanup. Returns the number of files reaped so callers can log a single summary line. Errors traversing individual entries are logged-and-skipped at the call site (kept out of this function so unit tests stay trace-free).