← Back to PRs

#16761: sessions_spawn: inline attachments + redaction + hardening

by napetrov open 2026-02-15 02:46 View on GitHub →
agents size: L
Fixes #17214 Image-specific request remains tracked in #16344 as a special case of general file attachments. ## Summary This Pull Request introduces robust support for passing inline attachments to sub-agents via `sessions_spawn`. This enables more complex workflows where agents require specific files, such as images for analysis (e.g., in response to a user's request to "analyze this image"), code snippets, or other structured data, making it a more comprehensive solution for inter-agent communication. This feature moves beyond basic text exchange, addressing a crucial need for agents to operate on concrete file inputs. ### Core Idea The goal is to allow a spawning agent (or user) to provide files directly with a `sessions_spawn` call. These files are then safely materialized into the target sub-agent's isolated workspace, making them accessible to the sub-agent's tools and thought process. This allows for scenarios like: - An agent analyzing an image provided by a user. - An agent receiving a configuration file for processing. - An agent getting a dataset for a specific task. ### Implemented Details - Adds `attachments[]` support to `sessions_spawn` (accepts inline content as `utf8` or `base64` encoded strings). - Materializes attachments into the child sub-agent's isolated workspace at a deterministic relative path: - `.openclaw/attachments/<uuid>/` - Includes a `.manifest.json` file within this directory, containing metadata for all attached files (name, size, SHA256 hash). - Returns an attachment receipt (`count`, `totalBytes`, `files`, `relDir`) in the `sessions_spawn` tool result, allowing the spawning agent to confirm successful transfer. - Appends a system note to the child sub-agent's context, informing it of the canonical `relDir` where attachments can be found (the `mountPath` concept is explicitly reserved for future, more advanced mount-based implementations and is not used as a runtime path authority in this implementation). ### Security and Robustness Hardening Extensive measures have been taken to ensure the security and integrity of attachment handling: - **Strict Transcript Redaction**: `sessions_spawn` attachment `content` is strictly redacted from transcript persistence (both `arguments` and `input` tool-call shapes) to prevent accidental logging of sensitive data. - **Robust Base64 Validation**: Implements strict base64 validation (checking alphabet, padding, and roundtrip consistency). Critically, it includes a **pre-decode encoded-size guard** to prevent memory exhaustion (DoS) from overly large base64 inputs before full decoding. - **Atomic Materialization & Rollback**: Attachment staging includes atomic file writes and a comprehensive rollback mechanism: if any error occurs during materialization (e.g., validation failure, disk write issue), the partially created attachment directory is fully removed. - **Spawn Failure Cleanup**: If the `agent.callGateway` (the actual sub-agent spawn) fails after attachments have been materialized, the staged attachment directory is removed according to the defined cleanup policy. - **Lifecycle-Aware Cleanup**: The cleanup policy (`cleanup=delete` or `retainOnSessionKeep=false`) is fully wired into the sub-agent registry's `finalize` process, ensuring attachment directories are reliably removed after the sub-agent run completes or its session is archived. - **Constrained Deletion**: Attachment directory deletion is strictly constrained to the canonical attachments root within the child sub-agent's workspace, preventing accidental deletion of files outside the intended scope (path traversal protection). - **Tightened File Permissions**: File system permissions for materialized attachments are highly restricted: directories are set to `0700` and files/manifests to `0600`, ensuring only the owner (the sub-agent process) can access them. ### Tests Run The following tests have been run and passed: - `pnpm lint --silent` (static code analysis) - `pnpm exec vitest run --config vitest.unit.config.ts src/agents/session-transcript-repair.attachments.test.ts` (unit tests for transcript redaction and base64 handling) - `pnpm exec vitest run --config vitest.e2e.config.ts src/agents/openclaw-tools.sessions.e2e.test.ts` (end-to-end tests for `sessions_spawn` functionality) --- ## Reviewer Guide **What to look for:** 1. **Security (Critical):** * **`sessions_spawn-tool.ts`**: * `decodeStrictBase64` logic: Confirm the `maxEncodedBytes` calculation and its enforcement *before* `Buffer.from` to prevent memory DoS. * Attachment file/dir creation (`fs.mkdir`, `fs.writeFile`): Verify `mode` (0700/0600) and `flag: "wx"` (no overwrite). * Error handling logic in the attachment materialization `try/catch` block: Ensure `fs.rm(absDir, { recursive: true, force: true })` is called on *any* failure to prevent residue. * **`subagent-registry.ts`**: * `safeRemoveAttachmentsDir`: Verify the `fs.realpath` and `startsWith(rootWithSep)` checks are robust against path traversal for deletion. * `finalizeSubagentCleanup`: Ensure `shouldDeleteAttachments` logic correctly triggers `safeRemoveAttachmentsDir` based on `cleanup` and `retainAttachmentsOnKeep`. * **`session-transcript-repair.ts`**: * `sanitizeToolCallInputs`: Confirm `attachments[].content` is redacted for both `arguments` and `input` fields in `sessions_spawn` tool calls. 2. **Correctness (High):** * **`sessions_spawn-tool.ts`**: * `decodeStrictBase64`: Test with various valid, invalid, and edge-case base64 strings (e.g., malformed padding, non-base64 chars, empty, very long inputs). * Attachment limits (`maxFiles`, `maxTotalBytes`, `maxFileBytes`): Ensure these are correctly enforced at various stages. * Filename validation: Check edge cases for `.` `..` `/` `\` `\\^@` and ensure they are rejected. * `relDir` generation: Confirm it is deterministic and reliably points to the attachment location in the child workspace. * System prompt message: Verify it clearly states the `relDir` and the untrusted nature of attachments. * **`subagent-registry.ts`**: * `registerSubagentRun`: Ensure `attachmentsDir` and `retainAttachmentsOnKeep` are correctly recorded. * `finalizeSubagentCleanup`: Verify the timing and conditions for `safeRemoveAttachmentsDir` are correct (i.e., it doesn't try to delete before creation, and doesn't leave residue). 3. **Architecture / Design (Medium):** * **`sessions_spawn-tool.ts`**: * Confirm that the `attachAs.mountPath` field is indeed *not* used for path resolution in the MVP, but retained as a placeholder (and documented as such in the code comments). * Attachment lifecycle (creation, use, cleanup) is clear and robust. * **Overall**: Ensure that this inline attachment mechanism is a solid foundation for future extensions (e.g., streaming, more complex storage). **Suggested Manual Test Cases:** 1. **Successful Attachment**: Spawn a sub-agent with a small UTF8 text file. Verify the sub-agent can `read` the file at the reported `relDir`. 2. **Successful Base64 Image**: Spawn a sub-agent with a base64 encoded image (within limits). Verify the sub-agent can `read` the file (e.g., check its size or first few bytes). 3. **Attachment Limits**: * Send a single file exceeding `maxFileBytes`. Expect rejection. * Send multiple files whose `totalBytes` exceed `maxTotalBytes`. Expect rejection (even if individual files are fine). * Send more than `maxFiles`. Expect rejection. 4. **Invalid Base64**: Send an attachment with malformed base64 `content`. Expect `attachments_invalid_base64_or_too_large` error. 5. **Invalid Filenames**: Send attachments with `../` `//` `\` `\\^@` or null bytes in the `name`. Expect `attachments_invalid_name` error. 6. **Duplicate Filenames**: Send multiple attachments with the same valid `name`. Expect `attachments_duplicate_name` error. 7. **Rollback on Materialization Error**: Simulate a disk full or permission error during file writing (manually block `fs.writeFile` in a test environment, if possible). Verify that no residual attachment directory or files are left. 8. **Spawn Failure Cleanup**: Trigger `sessions_spawn` with valid attachments, but ensure the actual sub-agent spawn (`callGateway<...>(method: "agent", ...)` fails (e.g., invalid `agentId`). Verify that the attachment directory is removed. 9. **`cleanup=delete`**: Spawn an agent with attachments and `cleanup="delete"`. Verify the attachment directory is removed after the sub-agent completes successfully. 10. **`cleanup=keep` (default)**: Spawn an agent with attachments and `cleanup="keep"`. Verify the attachment directory *is not* removed after the sub-agent completes. Mentions: #16344 ## AI Assistance & Contributing Transparency - [x] AI-assisted: yes (Codex/Claude) - [x] Testing level: lightly tested (unit/e2e targeted + CI) - [x] Prompts/session logs: Session logs available on request - [x] I understand what this code does and can maintain it <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds inline attachment support to `sessions_spawn`, enabling sub-agents to receive files (UTF-8 or base64) materialized into isolated workspace directories. Changes include comprehensive security hardening: pre-decode size validation, strict base64 verification with roundtrip checks, atomic file writes with rollback, transcript redaction of attachment content, and path-traversal-protected cleanup lifecycle. - Attachments materialized to `.openclaw/attachments/<uuid>/` with deterministic paths and 0700/0600 permissions - Base64 validation includes pre-allocation size guard (`maxEncodedBytes * 2` check before regex) and post-decode verification - UTF-8 path now pre-checks `Buffer.byteLength(content, "utf8")` before allocation to prevent memory...

Most Similar PRs