How Craft-Agents Enforces File System Isolation and Path Security

Craft-Agents implements a defense-in-depth strategy that combines session-ID validation, filename sanitization, and atomic file operations to ensure every file system access is confined to a dedicated workspace directory.

The lukilabs/craft-agents-oss repository treats file system isolation as a critical security boundary. By validating every path component before it touches the OS, the codebase prevents directory traversal, path injection, and race-condition attacks. The implementation relies on three complementary mechanisms that work together to enforce strict file system isolation and path security.

Session-ID Validation and Sanitization

Every session receives a dedicated directory under the workspace root, but before any folder is created, the session identifier undergoes rigorous validation.

In packages/shared/src/sessions/validation.ts, the validateSessionId function enforces a whitelist pattern of [\\w-]+ (alphanumeric, underscores, and hyphens). This regex explicitly rejects any traversal characters such as ../ or ./. The function also applies path.basename() to strip any leading directory components, ensuring the ID is treated as a single filename segment regardless of malicious input.

// packages/shared/src/sessions/validation.ts
import { validateSessionId, sanitizeSessionId } from '@/shared/src/sessions/validation';
import { join } from 'path';

function getSessionPath(workspaceRoot: string, rawId: string): string {
  validateSessionId(rawId);               // Throws on illegal IDs containing traversal characters
  const safeId = sanitizeSessionId(rawId); // Returns basename only
  return join(workspaceRoot, 'sessions', safeId);
}

If validation fails, an error is thrown before any file system access occurs, preventing the creation of directories outside the intended workspace.

Filename Sanitization

Once a session directory is established, any files written within it must also pass security checks. The sanitizeFilename function in packages/shared/src/utils/binary-detection.ts removes characters that are illegal on the host file system, including /, \, :, *, ?, ", <, >, and |.

The function also collapses whitespace, trims leading and trailing underscores, and limits the result to 200 characters. This ensures that even if an agent or user provides a malicious filename like ../../../etc/passwd, the sanitized output is a safe, flat filename suitable for concatenation into a path.

// packages/shared/src/utils/binary-detection.ts
import { sanitizeFilename } from '@/shared/src/utils/binary-detection';
import { join } from 'path';

function safeFilePath(downloadsDir: string, rawName: string): string {
  const safeName = sanitizeFilename(rawName); // Strips traversal characters and illegal symbols
  return join(downloadsDir, safeName);
}

Controlled Path Construction and Atomic Writes

The final layer of defense occurs at the moment of file creation. All file system operations build paths using path.join(workspaceRoot, 'sessions', sanitizedSessionId, ...) so that the only variable component is the already-validated session ID.

For binary downloads and sensitive writes, Craft-Agents uses atomic file operations to prevent Time-of-Check-to-Time-of-Use (TOCTOU) race conditions. In packages/shared/src/utils/binary-detection.ts, the saveBinaryResponse function writes files with the wx flag:

// packages/shared/src/utils/binary-detection.ts (excerpt)
const filePath = join(downloadsDir, finalFilename);
writeFileSync(filePath, buffer, { flag: 'wx' }); // Fails if file exists

The wx flag (equivalent to O_EXCL|O_CREAT in POSIX) opens the file for exclusive creation. If the file already exists, the operation throws an error. The implementation catches this error and iterates with numeric suffixes (file-1.ext, file-2.ext) until a unique filename is found, ensuring that an attacker cannot swap a file between the existence check and the write operation.

Summary

  • Session-ID validation uses a strict whitelist pattern and path.basename to prevent directory traversal via malicious session identifiers.
  • Filename sanitization removes illegal characters and limits length, ensuring user-provided filenames cannot escape the session directory.
  • Controlled path construction joins only validated components, keeping all operations within workspaceRoot/sessions/<safe-id>/.
  • Atomic writes with the wx flag prevent TOCTOU race conditions and accidental overwrites.

Frequently Asked Questions

How does Craft-Agents prevent directory traversal attacks?

Craft-Agents prevents directory traversal by validating session IDs against a whitelist pattern of [\\w-]+ and applying path.basename to strip any directory components before the ID is used in path construction. Additionally, filenames are sanitized to remove traversal characters like ../ and slashes, ensuring only flat, safe filenames are concatenated into the final path.

What is the workspace root and how is it determined?

The workspace root is the base directory under which all session data is stored, typically located at $HOME/.craft-agent/workspace. The packages/shared/src/utils/workspace.ts module determines this path, and all session directories are constructed as children of workspaceRoot/sessions/<sanitized-session-id>/, ensuring absolute containment within the workspace.

Why does Craft-Agents use the wx flag for file writes?

The wx flag opens a file for exclusive creation, causing the operation to fail if the file already exists. This prevents Time-of-Check-to-Time-of-Use (TOCTOU) race conditions where an attacker might substitute a malicious file between the existence check and the write operation. If a collision occurs, Craft-Agents iterates to a unique filename (e.g., file-1.ext) before writing.

Are filenames sanitized before or after path construction?

Filenames are sanitized before path construction. The sanitizeFilename function in packages/shared/src/utils/binary-detection.ts processes the raw filename to remove illegal characters, collapse whitespace, and enforce length limits. Only after this sanitization step does the code use path.join to combine the workspace root, session directory, and sanitized filename into the final absolute path.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →