T09 · Insecure Skill Coding Practices
- Location
api/cli.py:70- Finding
Path Traversal in Tracking File Operations
- Content
View full analysis
Vulnerability Details
File Location:
api/cli.py:70-98, with affected command handlers atapi/cli.py:352-366,api/cli.py:388-409, andapi/cli.py:430-481
Vulnerability Type: Path traversal leading to unauthorized JSON file read and modification
Risk Level: HighVulnerable 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) raiseRepresentative 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
View remediation
Remediation Suggestions
-
Validate
args.periodbefore 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 -
Apply validation at the beginning of every tracking handler, including
get,use,unuse,generate,add-entry, andremove-entry. -
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) -
Reject path separators, traversal components, absolute paths, and any period not matching exactly four digits, an underscore, and a valid two-digit month.
-
Keep validation inside
tracking_path()as a defense-in-depth measure so future command handlers cannot accidentally bypass it. -
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 as2026_02. -
Run the CLI with least-privilege filesystem permissions so the process cannot read or replace unrelated application files.
-
