T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/verification_evidence.py:39
- Finding
- Project-controlled symlink allows evidence-management writes outside the project root## Vulnerability Details **File Location**: `scripts/verification_evidence.py`, lines 39–57 and 601–620 **Vulnerability Type**: Filesystem boundary violation through unchecked symlink traversal **Risk Level**: Medium ### Vulnerable Code ```python def locations(project): root = Path(project) / "05-verification" return { "root": root, "marker": root / "EVIDENCE-MANAGEMENT.json", "registry": root / "EVIDENCE.json", "index": root / "EVIDENCE.md", "records": root / "evidence", "lock": root / ".evidence.lock", } @contextmanager def evidence_lock(project): loc = locations(project) loc["root"].mkdir(parents=True, exist_ok=True) with loc["lock"].open("a+", encoding="utf-8") as stream: fcntl.flock(stream, fcntl.LOCK_EX) try: yield finally: fcntl.flock(stream, fcntl.LOCK_UN) ``` The initialization path subsequently writes through these unchecked paths: ```python def main(argv=None): args = build_parser().parse_args(argv) project = Path(args.project).resolve() try: if args.command == "init": with evidence_lock(project): registry = load_registry(project, required=False) loc = locations(project) loc["records"].mkdir(parents=True, exist_ok=True) write_json_atomic(loc["marker"], { "model": "PROJECT_MANAGER_VERIFICATION_EVIDENCE_MANAGEMENT", "model_version": MODEL_VERSION, "enabled": True, "enabled_at": utc_timestamp(), "enabled_by": args.actor, }) if not loc["registry"].exists(): write_json_atomic(loc["registry"], registry) render_all(project, registry) print("Verification evidence management initialized") return 0 ``` ### Technical Analysis The script resolves the sup ...[truncated 2765 chars]
- Remediation
- ## Remediation Suggestions 1. Resolve and validate the evidence root before every lock or write: ```python project_root = Path(project).resolve() evidence_root = project_root / "05-verification" resolved_root = evidence_root.resolve() if not resolved_root.is_relative_to(project_root): raise EvidenceError("Evidence-management path escapes the project") ``` 2. Reject symlinks and junctions in every existing component from the project root through: - `05-verification`; - `.evidence.lock`; - `EVIDENCE.json`; - `EVIDENCE.md`; - `EVIDENCE-MANAGEMENT.json`; and - `evidence/`. 3. Reuse or adapt the `control_path()` approach from `scripts/management_common.py` so all management subsystems enforce a consistent containment policy. 4. Revalidate containment immediately before opening a lock file and immediately before each atomic replacement. This reduces the opportunity for a path to be changed between initial validation and use. 5. Where supported, use descriptor-relative operations and no-follow semantics, such as `O_NOFOLLOW`, for security-sensitive lock and registry files. 6. Add regression tests covering: - `05-verification` as an external-directory symlink; - symlinked registry, marker, index, lock, and records paths; - symlink replacement between validation and write; - normal evidence initialization within a real project directory. 7. Fail without creating or modifying any file when a symlink, junction, or resolved path outside the project is detected.
