T09 · Insecure Skill Coding Practices
Error
- Location
- api/cli.py:70
- Finding
- Path Traversal in Tracking File Operations## Vulnerability Details **File Location**: `api/cli.py:70-98`, with affected command handlers at `api/cli.py:352-366`, `api/cli.py:388-409`, and `api/cli.py:430-481` **Vulnerability Type**: Path traversal leading to unauthorized JSON file read and modification **Risk Level**: High ### Vulnerable Code ```python def tracking_path(period): """Return absolute path for a tracking file given period like 2026_02.""" return os.path.join(DATA_DIR, f"{period}.json") def read_tracking(period): """Read a tracking file, return None if it doesn't exist.""" path = tracking_path(period) if not os.path.exists(path): return None with open(path, "r", encoding="utf-8") as f: return json.load(f) def write_tracking(period, data): """Atomically write a tracking file.""" os.makedirs(DATA_DIR, exist_ok=True) path = tracking_path(period) fd, tmp_path = tempfile.mkstemp(dir=DATA_DIR, suffix=".tmp") try: with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) f.write("\n") os.replace(tmp_path, path) except Exception: if os.path.exists(tmp_path): os.unlink(tmp_path) raise ``` Representative affected handlers read the supplied period before validating it: ```python def cmd_tracking_get(args): existing = read_tracking(args.period) if existing is not None: output(True, data=existing) # Auto-generate data = generate_tracking_data(args.period) write_tracking(args.period, data) output(True, data=data) def cmd_tracking_use(args): existing = read_tracking(args.period) if existing is None: # Auto-generate first existing = generate_tracking_data(args.period) write_tracking(args.period, existing) ``` Additional affected handlers follow the same pattern: ```p ...[truncated 5091 chars]
- Remediation
- ## Remediation Suggestions 1. Validate `args.period` before every read or write: ```python def require_valid_period(period): if parse_period(period) is None: output(False, error=f"Invalid period format: {period}. Use YYYY_MM.") return period ``` 2. Apply validation at the beginning of every tracking handler, including `get`, `use`, `unuse`, `generate`, `add-entry`, and `remove-entry`. 3. Enforce path confinement after canonicalization: ```python from pathlib import Path DATA_ROOT = Path(DATA_DIR).resolve() def tracking_path(period): if parse_period(period) is None: raise ValueError("Period must use YYYY_MM format") candidate = (DATA_ROOT / f"{period}.json").resolve() if candidate.parent != DATA_ROOT: raise ValueError("Tracking path escapes the data directory") return str(candidate) ``` 4. Reject path separators, traversal components, absolute paths, and any period not matching exactly four digits, an underscore, and a valid two-digit month. 5. Keep validation inside `tracking_path()` as a defense-in-depth measure so future command handlers cannot accidentally bypass it. 6. Add regression tests for all tracking actions using inputs such as `../target`, `../../target`, absolute paths, invalid months, encoded separators where relevant, and valid values such as `2026_02`. 7. Run the CLI with least-privilege filesystem permissions so the process cannot read or replace unrelated application files.
