Bug: FBWorkspace.inject_files can write files outside workspace_path
Bug Description
FBWorkspace.inject_files joins caller-provided file keys directly with workspace_path and then writes the result without checking that the resolved path remains inside the workspace.
A file key containing parent-directory components can therefore write outside the workspace folder:
workspace.inject_files(
**{
"../escape.txt": "escaped",
}
)This creates:
<workspace_path>/../escape.txtinstead of rejecting the path or otherwise preventing the write.
The method's docstring describes the operation as injecting code "into the folder", and normal callers pass dynamic filename-to-code dictionaries through this API. A filename key should not be able to escape workspace_path.
To Reproduce
Steps to reproduce the behavior:
- Check out RD-Agent
mainat commit:
2c878f9d2453dced35061165786d1f31bbff0ab6- Install RD-Agent from source and install pytest:
python -m pip install -e .
python -m pip install pytest- Create
test/test_fbworkspace_inject_files_path_traversal.py:
from rdagent.core.experiment import FBWorkspace
def test_inject_files_does_not_write_outside_workspace(tmp_path):
workspace = FBWorkspace()
workspace.workspace_path = tmp_path / "workspace"
workspace.file_dict = {}
workspace.inject_files(
**{
"inside.txt": "inside",
}
)
assert (
workspace.workspace_path / "inside.txt"
).read_text() == "inside"
outside = tmp_path / "escape.txt"
try:
workspace.inject_files(
**{
"../escape.txt": "escaped",
}
)
except ValueError:
pass
assert not outside.exists()- Run:
python -m pytest \
test/test_fbworkspace_inject_files_path_traversal.py \
-q- Observe that the test fails because
escape.txtis created outsideworkspace_path.
Expected Behavior
inject_files should never create, overwrite, or delete files outside workspace_path.
For a key such as:
../escape.txtthe method should reject the input, for example by raising ValueError, or otherwise guarantee that no filesystem operation occurs outside the workspace.
Normal relative paths inside the workspace, such as:
inside.txt
subdir/file.pyshould continue to work.
Screenshot
Not applicable. This is a deterministic filesystem-level reproduction.
Environment
Note: Users can run rdagent collect_info to get system information and paste it directly here.
- Name of current operating system: macOS
- Processor architecture: arm64
- System, version, and hardware information: macOS 15.7.3, arm64
- Version number of the system: 15.7.3
- Python version: 3.13.2
- Container ID: Not applicable
- Container Name: Not applicable
- Container Status: Not applicable
- Image ID used by the container: Not applicable
- Image tag used by the container: Not applicable
- Container port mapping: Not applicable
- Container Label: Not applicable
- Startup Commands: Not applicable
- RD-Agent version:
main@2c878f9d2453dced35061165786d1f31bbff0ab6 - Package version: Source checkout
Additional Notes
The current implementation builds the target path directly from the untrusted key:
target_file_path = self.workspace_path / kIt then writes or deletes that path:
if v == self.DEL_KEY:
if target_file_path.exists():
target_file_path.unlink()
self.file_dict.pop(k, None)
else:
self.file_dict[k] = v
target_file_path.parent.mkdir(parents=True, exist_ok=True)
target_file_path.write_text(v)Because Path / "../escape.txt" is still allowed by the filesystem, this escapes the workspace once resolved.
A possible fix is to resolve the candidate path and verify that it is contained by the resolved workspace root before either the write or delete branch performs filesystem operations:
workspace_root = self.workspace_path.resolve()
target_file_path = (workspace_root / k).resolve()
if not target_file_path.is_relative_to(workspace_root):
raise ValueError(
f"File path escapes workspace: {k}"
)Regression coverage should include:
- ordinary files such as
file.py; - nested relative files such as
subdir/file.py; - parent-directory paths such as
../escape.txt; - absolute paths;
- both write and
DEL_KEYdelete operations; - preservation of
file_dictbehavior for accepted paths.
Targeted issue and pull-request searches found no existing report for this FBWorkspace.inject_files workspace-escape root.
Source: microsoft/RD-Agent