Nous.Tools.PathGuard (nous v0.16.0)

View Source

Path-traversal & symlink-escape protection for filesystem tools.

LLMs control the path argument to file tools. Without a guard, a single prompt-injected document can read ~/.aws/credentials, write to ~/.ssh/authorized_keys, or globsweep /etc/. This module enforces that every path resolves inside a configured workspace root.

Configuring the workspace root

Pass it via the agent's ctx.deps:

Agent.new("openai:gpt-4",
  tools: [Nous.Tools.FileRead, Nous.Tools.FileWrite],
  deps: %{workspace_root: "/srv/agent_workspace/#{user_id}"}
)

When workspace_root is unset, the guard defaults to the current working directory (File.cwd!/0). For multi-tenant deployments you almost certainly want to set it explicitly per session.

What's blocked

  • Paths that, after Path.expand/1, escape the configured root
  • Symlinks whose target escapes the root
  • Any path containing a NUL byte (defense-in-depth)

Summary

Functions

Resolve path against the configured workspace root and return either {:ok, absolute_path} or {:error, reason} where reason is a human-readable string suitable to surface back to the LLM.

Functions

validate(path, ctx \\ nil)

@spec validate(String.t(), Nous.RunContext.t() | map() | nil) ::
  {:ok, String.t()} | {:error, String.t()}

Resolve path against the configured workspace root and return either {:ok, absolute_path} or {:error, reason} where reason is a human-readable string suitable to surface back to the LLM.