Keep the File Tools Inside the Workspace
A coding agent's read_file tool takes whatever path the model asks for. Yesterday it asked for ../../.aws/credentials, because a README said the config lived there, and got it.
Implement safe_join(root, path). root is the workspace directory as a POSIX path (/repo). Return the resolved path as a string, or raise PathEscape (provided) when the request would leave the workspace. Resolve the way a filesystem would, without touching one:
- Split on
/, and treat a backslash as a separator too. - Drop empty segments and
., sosrc//./app.pyissrc/app.py. ..steps up one segment. Stepping up from the workspace root is an escape.- An absolute path (
/etc/passwd) or a drive letter (C:) is an escape, even when it starts withroot. - A leading
~is an escape. - A null byte anywhere is an escape.
The result is root followed by the surviving segments. An empty request, or ., resolves to root itself.
read_file(files, path) in the fixtures calls your function before it looks anything up.