# Securing recursive_directory_iterator loops in agent sandboxes

Using std::filesystem::recursive_directory_iterator in C++ agent sandboxes can expose host systems to path traversal attacks if symbolic links and relative path segments are not validated. This how-to guide explains how to secure C++ filesystem iteration using canonical path validation and directory options.

Source: https://fast.io/resources/cpp-std-filesystem-recursive-directory-iterator-sandboxes/
Last reviewed: 2026-08-23

## Why naive directory traversals vulnerate agent sandboxes

When an AI coding agent runs with local filesystem tools, a standard recursive directory iterator can follow a symbolic link out of its sandbox and expose system configuration files or source code. The security threat is not a failure of the language model's reasoning, but rather the behavior of C++ filesystem utilities that default to traversing directories without validating sandbox constraints. If an agent receives instructions containing a prompt injection, the model might execute local tools with relative path segments or resolve system directories, leading to unauthorized access. Developers must configure their systems to handle these requests safely, as described in the [agent onboarding guidelines](https://fast.io/llms.txt).

Many developer implementations validate files using basic prefix matching on path strings. If the tool verifies that the target path begins with the sandbox root string, it will fail to block access when the path contains parent directory segments like dot-dot. A path containing a traversal sequence will resolve outside the sandbox when processed by the system filesystem. The execution tool inherits the permissions of the underlying worker process, which means a security leak can expose user keys, credentials, or proprietary code on the host machine.

To prevent these vulnerabilities, developers must build sandbox checks directly into the C++ tool definitions. This involves verifying that the target directory and all traversed files resolve within the permitted boundaries before performing any filesystem operations. By implementing canonical path validation, the host application maintains absolute control over what the model can inspect or modify.

## How recursive_directory_iterator traverses subdirectories

To build a secure directory traversal tool in C++, developers should understand the standard class template std::filesystem::recursive_directory_iterator, which was introduced in C++17. This class iterates over the directory entry elements of a directory and, recursively, over the entries of all subdirectories. Unlike standard directory iterators that only visit immediate folder contents, the recursive iterator maintains an internal stack of directory streams to descend into nested folders.

By default, the C++ recursive directory iterator does not follow symbolic links unless directory_options::follow_directory_symlink is passed. When the iterator encounters a symbolic link pointing to a directory, it treats it as a regular file and does not descend into the target directory. If the agent requires the tool to follow symbolic links to resolve files in other project folders, the developer must explicitly configure the iterator options.

Configuring the iterator to follow symlinks increases the risk of sandbox escapes. If a symbolic link points to a location outside the sandbox, such as the system directory on a Unix system, the iterator will follow that link. This behavior allows the agent to traverse the entire host filesystem. The standard library provides observers like recursion_pending to check status, and modifiers like disable_recursion_pending or pop to control the traversal flow. Using these modifiers enables the application to stop descending when it detects unsafe directory boundaries.

## Preventing directory traversal with canonical path validation

Preventing directory traversal requires resolving the requested path to its canonical form and verifying that it falls within the boundaries of the canonical sandbox root. A common mistake is using standard prefix checks on raw string paths. For example, if the sandbox root path is "/workspace/sandbox" and the target path is "/workspace/sandbox_secrets/config.txt", a naive prefix check will approve the access because the prefix matches. This allows the agent to read directories that should remain hidden.

To solve this, developers must ensure that the target path is either identical to the sandbox root or starts with the sandbox root followed immediately by the preferred directory separator. More importantly, the check must be performed on the resolved path. The C++ standard library std::filesystem::canonical function converts a path to an absolute path by resolving symbolic links and relative path elements, requiring that the path exists. Because canonical throws an exception if the file does not exist, it cannot validate new files that the agent wants to create.

C++17 provides std::filesystem::weakly_canonical to solve this problem. This function resolves the existing portion of the path using canonical and appends the remaining non-existent elements in normal form. The following helper function demonstrates how to validate sandbox boundaries securely in C++ using weakly_canonical:

```cpp
#include <filesystem>
#include <string>
#include <system_error>
namespace fs = std::filesystem;
bool is_path_inside_sandbox(const fs::path& target, const fs::path& sandbox_root) {
    try {
        fs::path canonical_target = fs::weakly_canonical(target);
        fs::path canonical_root = fs::weakly_canonical(sandbox_root);
        std::string target_str = canonical_target.string();
        std::string root_str = canonical_root.string();
        if (target_str.size() < root_str.size()) {
            return false;
        }
        if (target_str.compare(0, root_str.size(), root_str) != 0) {
            return false;
        }
        if (target_str.size() > root_str.size()) {
            if (target_str[root_str.size()] != fs::path::preferred_separator) {
                return false;
            }
        }
        return true;
    } catch (const fs::filesystem_error&) {
        return false;
    }
}
```

On Windows systems, path resolution contains additional complexities such as volume mount points, junction points, and short filename aliases (the 8.3 format). If an agent attempts to access a path using a short name alias like `C:\\PROGRA~1` instead of `C:\\Program Files`, a naive prefix check will fail to identify the match. Both `canonical` and `weakly_canonical` query the operating system file tables to resolve these aliases to their long-form representation, ensuring that name-based comparisons remain accurate. Furthermore, `weakly_canonical` handles paths that refer to non-existent files by only resolving the existing prefix. This makes it particularly effective for checking files that are about to be written, preventing directory traversal attacks before a single byte is committed to disk.

## How directory options protect recursive loops

When iterating through a directory tree, validating the root path is not enough. If the sandbox contains symbolic links created by the user or the agent, the recursive directory iterator could traverse out of the sandbox mid-loop. To prevent this, the validation check must verify every entry during the iteration. If the loop encounters a subdirectory that resolves outside the sandbox, the application must skip descending into that folder.

The implementation below shows a secure traversal loop in C++ using recursive_directory_iterator. It configures the iterator to follow symlinks but checks each path before processing it. If a folder points outside the sandbox, the loop calls disable_recursion_pending to prevent the iterator from descending into the unsafe target directory.

```cpp
#include <filesystem>
#include <iostream>
#include <system_error>
namespace fs = std::filesystem;
void process_directory_securely(const fs::path& start_dir, const fs::path& sandbox_root) {
    if (!is_path_inside_sandbox(start_dir, sandbox_root)) {
        std::cerr << "Access denied: Start directory is outside sandbox." << std::endl;
        return;
    }
    std::error_code ec;
    fs::recursive_directory_iterator it(
        start_dir, 
        fs::directory_options::follow_directory_symlink, 
        ec
    );
    fs::recursive_directory_iterator end;
    while (it != end) {
        const auto& entry = *it;
        const fs::path& current_path = entry.path();
        if (!is_path_inside_sandbox(current_path, sandbox_root)) {
            std::cerr << "Security bypass blocked: " << current_path << std::endl;
            if (entry.is_directory()) {
                it.disable_recursion_pending();
            }
            it.increment(ec);
            if (ec) {
                std::cerr << "Iterator error: " << ec.message() << std::endl;
                break;
            }
            continue;
        }
        std::cout << "Safe entry: " << current_path << std::endl;
        it.increment(ec);
        if (ec) {
            std::cerr << "Iterator error: " << ec.message() << std::endl;
            break;
        }
    }
}
```

Developers must also consider race conditions like Time-of-Check to Time-of-Use (TOCTOU). A malicious agent could attempt to replace a safe subdirectory with a symbolic link immediately after the check but before the file is opened. To defend against this, configure the sandbox environment using containerization. Running the tool execution worker in a lightweight container ensures that the operating system enforces a hard boundary, protecting the parent host system even if the application path checks fail.

When traversing directories recursively with symlink resolution enabled, developers must also protect the application from infinite loop cycles. If a symbolic link points back to a parent folder in the directory tree, a naive iterator will follow the cycle indefinitely, eventually causing a stack overflow or exhaustion of system file descriptors. C++ filesystem iterators do not automatically detect all link loops under every operating system. To mitigate this risk, developers can use the `recursive_directory_iterator::depth()` observer to enforce a maximum recursion depth limit, skipping directories that exceed a safe nesting level. Combined with `is_path_inside_sandbox`, depth constraints prevent malicious loops from disabling the agent tool.

## Managing agent file collaboration in cloud workspaces

Running local sandbox directories on a single server introduces management complexity and security challenges when scaling agentic teams. Developers often use local folder paths or standard cloud drives like Google Drive or Dropbox to manage agent outputs. However, these tools are built for human file synchronization rather than concurrent agent operations. They lack built-in search indices, suffer from severe API quota limits under frequent updates, and fail to track concurrent writes from multiple agents.

Fast.io provides an alternative by exposing collaborative workspaces designed for agents and human teams. Instead of maintaining complex path checking scripts on a local machine, teams can connect coding frameworks to shared workspaces. Fast.io Coordination Rooms act as neutral platforms where multiple agents (using Claude Code, Codex, Cursor, Gemini, or OpenClaw) can read and write files, post activity messages, and hand off finished work to human managers. For detailed configuration instructions, refer to the [agent storage guide](/storage-for-agents/).

Integrating these workspaces is simple. Fast.io does not publish language-specific libraries or custom SDKs, avoiding dependency issues. Instead, agents connect using the Model Context Protocol (MCP) server. Developers configure their agents to connect to the remote MCP server endpoint at `https://mcp.fast.io/mcp`, using a secure organization API key. This endpoint exposes a consolidated MCP toolset that allows agents to list files, retrieve content, and upload changes directly. The configuration rules are detailed in `https://mcp.fast.io/skill.md`.

Every file written to a workspace maintains a complete version history. If an agent writes a bug or overwrites a configuration file, human developers can review the append-only audit log and restore prior versions. Enabling Intelligence Mode on the workspace automatically builds a vector and text index for all files, allowing both agents and humans to perform semantic searches over the entire document corpus. Doing real work requires an organization on a paid subscription. Every organization starts with a 14-day free trial, which requires a credit card. Subscription plans are Starter, Business, and Growth tiers. Details about these plans are available on the [Fast.io pricing page](/pricing/).

## Frequently asked questions

### How to recursively iterate directories in C++?

To recursively iterate directories in C++, use std::filesystem::recursive_directory_iterator from the <filesystem> header. This class traverses a directory and its subdirectories, returning directory entry objects that contain path details.

### How to prevent directory traversal attacks in C++ filesystem?

To prevent directory traversal attacks in C++, resolve both the target path and the sandbox root to absolute forms using std::filesystem::weakly_canonical. Verify that the resolved target path starts with the resolved sandbox root path, ensuring the character immediately following the root is a directory separator to avoid partial folder matching.

### How does recursive_directory_iterator handle symbolic links?

By default, std::filesystem::recursive_directory_iterator does not follow symbolic links, treating them as files. If you need to follow directory symlinks, pass std::filesystem::directory_options::follow_directory_symlink to the constructor, but ensure you validate every resolved path to prevent sandbox escapes.

## About Fast.io

Fast.io provides shared workspaces where people and AI agents work on the same files, with built-in semantic search and citation-backed chat over what they hold. Agents reach it through a remote MCP server at https://mcp.fast.io/mcp, a REST API at https://api.fast.io/current/, and a command line client published on npm as @vividengine/fastio-cli.
