Skip to content

Security inspection and policy gateway

Duckbill™ treats inbound mail as untrusted. Rendering, fetching, and side effects go through application services in @inbox-agent/application before any provider adapter runs.

Source: MAIL-35, MAIL-34, MAIL-108, MAIL-107, MAIL-87, MAIL-86. Pipeline §§31–35, §§57–62, §§114–118; 01 AI §8 / §12; 03 NFR §3; 04 MVP §3.

Untrusted HTML

sanitizeUntrustedHtml is the only HTML render path. Script, iframe, form, object, meta-refresh, and event-handler markup are stripped. Remote images are not preserved, so tracking pixels do not auto-load. If the sanitizer throws or residual unsafe markup remains, the result is plain text (html: null). Raw provider HTML is never returned.

SSRF and attachments

inspectUrl / assertSafeFetchUrl deny localhost, RFC1918 and link-local ranges, IPv6 unique-local and loopback, cloud metadata hosts, and non-http(s) schemes. Redirect targets are inspected independently.

Stream 04 already stores attachment scan_state values unknown and held (migration 0014_attachment_hold_states.sql). Scanner or fetch failure holds the item. scanFailureDisposition records unknown / held and never clean. Quarantined or held items fail mayAutoArchive — ordinary automation does not archive them.

Policy gateway

evaluatePolicy returns ALLOW or DENY in this MVP (APPROVAL_REQUIRED is reserved) and never invokes an adapter (adapterInvoked: false). Actors are USER, RULE, AI, and SYSTEM.

Denied calls use the POLICY_DENIED API envelope from @inbox-agent/api-contracts.

Deterministic rules

Conservative backend rules match sender, sender domain, subject substring, or List-Id. Evaluation emits COMMAND_REQUEST records that still pass the policy gateway. Matching a rule never calls a provider adapter.

AGENT_SUGGESTED rules start as DRAFT and cannot be created ACTIVE. Activation requires explicit approval. Security and user rules outrank system defaults and skip AI. Simple list / create / pause is the MVP surface — there is no visual rules builder.