Skip to content

feat: outbound file attachments from agent → Discord (with security hardening) #355

@masami-agent

Description

@masami-agent

Summary

Agents running through OpenAB can receive images/files from users, but have no native path to send files back to Discord. The only workaround is agents calling the Discord REST API directly with the bot token, which breaks session/thread management and requires exposing credentials to the agent.

PR #300 proposed a solution using markdown ![alt](/path) extraction, but was deferred due to security concerns. This issue tracks the feature with the required security hardening.

Prior Art

See PR #300 for detailed research on OpenClaw (MEDIA: directive + CVE), Hermes Agent, and cross-agent testing with Claude Code, Codex, Cursor, and Copilot.

Security Requirements

The following must be addressed before this feature can ship:

  1. Symlink resolution — Use std::fs::canonicalize() before checking path prefixes. Without this, ln -s /etc/passwd /tmp/innocent.txt bypasses the allowlist.

  2. Path traversal![img](/tmp/../etc/passwd) must be blocked. canonicalize() resolves .. components, covering this case.

  3. Configurable allowlist — Hardcoded /tmp/ and /var/folders/ does not work for containerized deployments. Add outbound.allowed_dirs to config.toml:

    [outbound]
    enabled = false
    allowed_dirs = ["/tmp/", "/home/agent/output/"]
    max_file_size_mb = 25
  4. Opt-in by default — Feature must be disabled unless outbound.enabled = true is explicitly set. Operators should consciously allow filesystem → Discord uploads.

  5. Rate limiting — Cap outbound attachments per message or per minute to prevent channel flooding.

Implementation Notes

The core implementation from PR #300 is solid and reusable:

  • Regex extraction of ![alt](/path) markers
  • serenity::CreateAttachment::path() for upload
  • Marker stripping from text response
  • 5 unit tests covering extraction, allowlist blocking, missing files, and multi-file

Related

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions