T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/ari.py:1474
- Finding
- Arbitrary Local File Overwrite Through the Export Output Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ari.py:1474-1475`, `scripts/ari.py:1614-1627`, and `scripts/ari.py:1929` **Vulnerability Type**: Unrestricted file write and destructive overwrite **Risk Level**: Medium ### Vulnerable Code ```python def request_download(path, params, dest): """Download a non-JSON response to a local file.""" query = {"method": "GET", "path": path, "params": {k: v for k, v in (params or {}).items() if v not in (None, "")}, "payload": None} url = base_url() + path if query["params"]: url += "?" + urllib.parse.urlencode(query["params"], doseq=True) headers = {"Authorization": "Bearer " + require_key(), "User-Agent": user_agent()} try: req = urllib.request.Request(url, headers=headers, method="GET") with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as resp: note_release(resp.headers) ctype = resp.headers.get("Content-Type", "") body = resp.read() if "application/json" in ctype: out = json.loads(body.decode("utf-8", errors="replace")) if isinstance(out, dict): out["_query"] = query return out tail = body[-300:].decode("utf-8", errors="replace") if "# export error:" in tail: return error_obj("ARI_EXPORT_ERROR", 200, tail.split("# export error:", 1)[1].strip(), "Export failed before completion; the incomplete file was not saved.", query) with open(dest, "wb") as fh: fh.write(body) return {"success": True, "data": {"savedTo": os.path.abspath(dest), "bytes": len(body), "contentType": ctype.split(";")[0].strip()}, "_query": query, "links": links()} ``` ```python def cmd_export(args): ...[truncated 3672 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Use a dedicated export directory by default** - Create a directory such as `~/.ari/exports`. - Resolve the canonical destination and ensure it remains inside that directory. 2. **Prevent silent replacement** - Open new files with exclusive creation mode, such as `open(path, "xb")`. - Return an error when the destination already exists. - Add an explicit `--force` option for intentional replacement. 3. **Defend against symbolic-link attacks** - Reject destinations that are symbolic links. - On supported systems, use `os.open()` with `O_NOFOLLOW`. - Validate both the resolved parent directory and final destination. 4. **Require explicit approval for destinations outside the export directory** - Display the canonical absolute path. - Require a dedicated flag or direct user confirmation before writing there. - Agents should not infer this approval from unrelated export requests. 5. **Use atomic writes** - Download into a securely created temporary file in the destination directory. - Validate the response type and completion status. - Flush and synchronize the file if required. - Atomically rename it to the final destination only after all validation succeeds. 6. **Apply restrictive permissions** - Create exported files with conservative permissions, such as `0600`, unless sharing is explicitly requested. ]]>
