T09 · Insecure Skill Coding Practices
Error
- Location
- bankcardcognition.py:35
- Finding
- Symlink Bypass Enables Unauthorized Local File Disclosure to a Third-Party API## Vulnerability Details **File Location**: `bankcardcognition.py`, lines 35–44, 71–87, and 92–98 **Vulnerability Type**: Symlink-based path containment bypass and sensitive-file disclosure **Risk Level**: High ### Vulnerable Code ```python norm = os.path.normpath(user_path) if norm.startswith("..") or norm == "..": return { "error": "invalid_path", "message": f"Path traversal is not allowed for '{field}'", } base = os.getcwd() full = os.path.join(base, norm) return {"error": None, "path": full, "relative": norm} ``` ```python path = safe["path"] if not os.path.isfile(path): return {"pic": None, "error": f"File not found: {safe['relative']}"} try: with open(path, "rb") as f: raw = f.read() except Exception as e: return {"pic": None, "error": f"Failed to read file: {e}"} try: encoded = base64.b64encode(raw).decode("utf-8") except Exception as e: return {"pic": None, "error": f"Failed to base64-encode file: {e}"} ``` ```python params = {"appkey": appkey} data = {"pic": pic_base64} try: resp = requests.post(BANKCARD_URL, params=params, data=data, timeout=20) ``` ### Technical Analysis The path validator prevents absolute paths and lexical traversal through `..`, but it does not resolve symbolic links before determining whether the requested file remains inside the current working directory. Both `os.path.isfile()` and `open()` follow symbolic links. A relative path that appears to be contained within the working directory can therefore reference a symlink whose target is an arbitrary readable file outside that directory. The target's contents are read in full, base64-encoded, and submitted as the `pic` field to the configured JisuAPI endpoint. Encoding the selected bank-card image and sending it to the documented OCR provider are necessary for the Skill's declared cloud OCR functionality. The vulnerability is not the use ...[truncated 1752 chars]
- Remediation
- ## Remediation Suggestions 1. Resolve the canonical working directory and candidate path with `os.path.realpath()` or `pathlib.Path.resolve()`. 2. Verify canonical containment using `os.path.commonpath()` rather than string-prefix checks: ```python base = os.path.realpath(os.getcwd()) candidate = os.path.realpath(os.path.join(base, norm)) try: if os.path.commonpath([base, candidate]) != base: return { "error": "invalid_path", "message": f"Resolved path escapes the working directory for '{field}'", } except ValueError: return { "error": "invalid_path", "message": f"Invalid path for '{field}'", } ``` 3. Reject symbolic links explicitly with `os.path.islink()` when symlink support is unnecessary. For stronger protection against check-to-use races on supported systems, open the file with `os.open()` and `O_NOFOLLOW`, then read it through the returned descriptor. 4. Validate the opened object with `fstat()` and ensure that it is a regular file. 5. Enforce the documented file-size limit before reading the complete file into memory. 6. Validate the file's actual image signature and permit only expected image formats before upload; do not rely solely on its extension. 7. Recheck containment and file identity at open time to minimize time-of-check/time-of-use race conditions. 8. Run the Skill under a dedicated low-privilege account with access only to the intended upload directory. 9. Clearly inform users that selected images are transferred to a third-party OCR provider and avoid retaining or logging full card numbers or image contents.
