T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:229
- Finding
- Unrestricted Proposal Path Allows Arbitrary JSON File Processing and Deletion<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:229-239` **Vulnerability Type**: Path traversal and arbitrary file deletion **Risk Level**: High ### Vulnerable Code ```python def confirm_proposal(proposal_file: str, confirmed_by: str): """Move a proposal from pending_review into the live knowledge base.""" kb = pathlib.Path("aps_knowledge_base") proposal_path = pathlib.Path(proposal_file) proposal = json.loads(proposal_path.read_text()) if proposal.get("type") == "client_memory_update": _apply_memory_update(proposal, confirmed_by) else: _apply_rule(proposal, confirmed_by) # Remove from pending proposal_path.unlink() ``` ### Technical Analysis `proposal_file` is converted directly into a `Path` without verifying that it belongs to `aps_knowledge_base/pending_review`. The path can therefore be absolute or contain `..` traversal components. The selected file is parsed and processed as a proposal, after which `unlink()` deletes it. A symlink placed in the proposal directory could create a similar boundary violation unless symlink handling is explicitly restricted. Schema and proposal-state validation are also absent, so an arbitrary JSON document with compatible fields can be applied to the live knowledge base before deletion. ### Attack Path 1. An attacker supplies or influences a `proposal_file` value referencing an arbitrary readable JSON file, such as an absolute path or a path containing `../`. 2. The attacker induces the user to provide nominal approval for the proposal, or the caller otherwise invokes `confirm_proposal()`. 3. The function reads the external file and interprets its contents as a memory update or rule. 4. The parsed content may be written into the live knowledge base. 5. `proposal_path.unlink()` deletes the externally selected JSON file. ### Impact Assessment An attacker can delete any JSON file writable by the Agent process. Depending on the selected content, the attack ...[truncated 215 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Resolve both the trusted proposal directory and supplied path with `Path.resolve(strict=True)`. - Require the resolved proposal path to be a direct child of the resolved `pending_review` directory. - Reject absolute input paths, `..` components, unexpected filename formats, and symbolic links. - Validate the parsed document against a strict proposal schema before applying it. - Verify that the proposal has `status: "proposed"` and has not already been processed. - Delete only the validated in-directory proposal after all updates complete successfully. - Use transactional or failure-safe update handling so partial processing does not corrupt state. Example containment check: ```python pending = (kb / "pending_review").resolve(strict=True) proposal_path = pathlib.Path(proposal_file).resolve(strict=True) if proposal_path.parent != pending or proposal_path.is_symlink(): raise ValueError("Invalid proposal path") ``` ]]>
