T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/cat_therapy.py:70
- Finding
- Path Traversal in Language File Selection## Vulnerability Details **File Location**: `scripts/cat_therapy.py`, lines 70–77; attacker-controlled input originates at line 106 **Vulnerability Type**: Path traversal and arbitrary local JSON file read **Risk Level**: Medium **Vulnerable Code**: ```python def get_quote(language="zh"): """Get a random healing quote in specified language.""" i18n_dir = os.path.join(os.path.dirname(__file__), "..", "i18n") lang_file = os.path.join(i18n_dir, f"{language}.json") if os.path.exists(lang_file): with open(lang_file, 'r', encoding='utf-8') as f: data = json.load(f) quotes = data.get("quotes", []) ``` The untrusted value is obtained from a command-line argument: ```python language = sys.argv[1] if len(sys.argv) > 1 else "zh" quote = get_quote(language) ``` ### Technical Analysis The `language` value is incorporated into a filesystem path without an allowlist, canonicalization, or containment check. An attacker who can control the command-line argument can supply traversal sequences such as `../../directory/file` or an absolute path. Because `.json` is appended, the target must be a process-readable JSON file whose name ends in `.json`. If the selected file contains a `quotes` array, one of its entries is returned in the program output. If the file is malformed JSON or has an unexpected top-level type, the uncaught parsing or attribute error can terminate the process. ### Attack Path 1. The attacker gains control over the language argument passed to `scripts/cat_therapy.py`. 2. The attacker supplies a value such as `../../target`, causing the constructed path to resolve to `../../target.json` outside the intended `i18n` directory. 3. The script confirms that the path exists and opens it with the privileges of the Skill process. 4. If the file is valid JSON with a `quotes` array, a randomly selected entry is exposed in the generated response. 5. Alternat ...[truncated 577 chars]
- Remediation
- ## Remediation Suggestions - Restrict the language value to an explicit allowlist such as `{"zh", "en"}` before constructing a path. - Reject absolute paths, path separators, traversal components, and unsupported locale identifiers. - Resolve both the intended localization directory and candidate file with `pathlib.Path.resolve()`, then verify that the candidate remains beneath the localization directory. - Avoid using raw command-line input as a filename; map accepted locale identifiers to fixed filenames. - Catch `OSError`, `json.JSONDecodeError`, and schema/type errors and safely fall back to the default localization file. - Validate that parsed content is an object and that `quotes` is a list of strings. Example hardening approach: ```python LANGUAGE_FILES = { "zh": "zh.json", "en": "en.json", } def get_quote(language="zh"): filename = LANGUAGE_FILES.get(language, LANGUAGE_FILES["en"]) lang_file = os.path.join(i18n_dir, filename) ```
