T09 · Insecure Skill Coding Practices
- Location
scripts/memory_review.py:352- Finding
Unvalidated scan state path permits writes outside the workspace
- Content
View full analysis
int: root = args.root.resolve() scan = json.loads(Path(args.scan).read_text()) state_after = scan.get("state_after") if not isinstance(state_after, dict) or not isinstance(state_after.get("sources"), dict): raise SystemExit("scan plan does not contain state_after.sources") for rel, expected in state_after["sources"].items(): path = root / rel if not path.is_file() or sha256(path) != expected: raise SystemExit(f"stale scan plan; source changed or disappeared: {rel}") state_path = root / str(scan.get("state_path", args.state)) atomic_json(state_path, state_after) print(state_path) return 0 ``` The destination is subsequently created and replaced without a containment check: ```python def atomic_json(path: Path, value: Any) -> None: path.parent.mkdir(parents=True, exist_ok=True) temporary = path.with_name(f".{path.name}.tmp") temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n") temporary.replace(path) ``` ### Technical Analysis `cmd_commit_state` treats the JSON supplied through `--scan` as trusted. Although it performs limited structural validation on `state_after`, it does not validate `scan["state_path"]` with `safe_relative_path()`, resolve it against the workspace boundary, or require it to match the expected state-file location. A value containing traversal components, such as `../../target.json`, can escape the configured root. An absolute path is also accepted because joining a `pathlib.Path` root with an absolute path discards the root. The validation of `state_after.sources` does not prevent exploitation ...[truncated 2266 chars]- Remediation
View remediation
