T05 · Unauthorized Access and Privilege Escalation
Warning
- Location
- fluid_network/skills/inputs.py:153
- Finding
- Agent-Callable Analysis Tools Permit Unrestricted Local File Reads## Vulnerability Details **File Location**: `fluid_network/skills/inputs.py:153-156`; `fluid_network/parser.py:133-137` **Vulnerability Type**: Missing filesystem sandbox and path authorization **Risk Level**: Medium ### Vulnerable Code `fluid_network/skills/inputs.py:153-156`: ```python if looks_like_path(text): path = Path(text) if path.is_file(): network, scenarios = parser.load(path) return ResolvedNetwork(network, tuple(scenarios), "path", str(path)) ``` `fluid_network/parser.py:133-137`: ```python path = Path(path) if not path.exists(): raise ConfigError(f"配置文件不存在:{path}") try: raw_bytes = path.read_bytes() ``` ### Technical Analysis The `parse_network`, `analyze_scenario`, and `analyze_all_scenarios` tools accept `network_source` as a string. When the string resembles a path and points to an existing file, `resolve_network_source()` passes it directly to `FluidConfigParser.load()`, which reads the entire file with `Path.read_bytes()`. This path lacks all of the controls claimed by the documented security boundary: - No approved-root or directory-containment check - No canonical path validation - No rejection of traversal paths - No protection against symlinks escaping an approved directory - No `.toml` extension restriction - No file-size limit - No default-deny behavior when a sandbox is not configured Consequently, any file readable by the Skill process can be supplied as `network_source`. The parser may then expose information derived from that file through parsed network summaries or detailed syntax and validation diagnostics. The issue also creates a denial-of-service risk because `read_bytes()` loads the complete target into memory without first enforcing the documented 256 KB limit. The SSH-key reference at `SKILL.md:484` is only a deny-list example and does not itself read or write SSH keys. However, the unrestricted path handling means the implementation does not enforce that documented prohibition. # ...[truncated 1487 chars]
- Remediation
- ## Remediation Suggestions 1. Introduce one centralized file-access policy and require every file-reading entry point to use it. 2. Resolve the requested path with strict canonicalization before reading: ```python requested = Path(value).expanduser().resolve(strict=True) ``` 3. Resolve configured allowed roots and verify containment using `Path.is_relative_to()` rather than string-prefix comparisons: ```python if not any(requested.is_relative_to(root) for root in allowed_roots): raise PathNotAllowed(...) ``` 4. Default to denying all path-based reads when no allowed roots are configured. 5. Permit only the required `.toml` extension for `network_source`. 6. Reject symlinks or ensure that the resolved target remains inside an allowed root. 7. Inspect file metadata before reading and reject files over 256 KB. Also use bounded reads to reduce time-of-check/time-of-use risk. 8. Avoid including file contents in parse errors. Return only sanitized locations and diagnostics. 9. Apply the same controls to CLI JSON loading in `skill_runner.py` if the CLI may process attacker-controlled paths. 10. Add tests covering absolute paths, `../` traversal, symlink escapes, sibling directories with matching prefixes, SSH-key paths, unsupported extensions, oversized files, and operation without configured roots. 11. Update the tool documentation to match the actual registered tool set, or implement the documented sandboxed `read_file` tool without allowing analysis tools to bypass its policy.
