馃悰 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.txt
instead 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
main at 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.txt is created outside workspace_path.
Expected Behavior
inject_files should never create, overwrite, or delete files outside workspace_path.
For a key such as:
the 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.py
should 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 / k
It 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_KEY delete operations;
- preservation of
file_dict behavior for accepted paths.
Targeted issue and pull-request searches found no existing report for this FBWorkspace.inject_files workspace-escape root.
馃悰 Bug Description
FBWorkspace.inject_filesjoins caller-provided file keys directly withworkspace_pathand 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:
This creates:
instead 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:
mainat commit:python -m pip install -e . python -m pip install pytesttest/test_fbworkspace_inject_files_path_traversal.py:escape.txtis created outsideworkspace_path.Expected Behavior
inject_filesshould never create, overwrite, or delete files outsideworkspace_path.For a key such as:
the 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:
should continue to work.
Screenshot
Not applicable. This is a deterministic filesystem-level reproduction.
Environment
Note: Users can run
rdagent collect_infoto get system information and paste it directly here.main@2c878f9d2453dced35061165786d1f31bbff0ab6Additional Notes
The current implementation builds the target path directly from the untrusted key:
It then writes or deletes that path:
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:
Regression coverage should include:
file.py;subdir/file.py;../escape.txt;DEL_KEYdelete operations;file_dictbehavior for accepted paths.Targeted issue and pull-request searches found no existing report for this
FBWorkspace.inject_filesworkspace-escape root.