T09 · Insecure Skill Coding Practices
- Location
scripts/verification_evidence.py:39- Finding
Project-controlled symlink allows evidence-management writes outside the project root
- Content
View full analysis
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: MediumVulnerable 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 0Technical Analysis
The script resolves the sup ...[truncated 2765 chars]
- Remediation
View remediation
Remediation Suggestions
-
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") -
Reject symlinks and junctions in every existing component from the project root through:
05-verification;.evidence.lock;EVIDENCE.json;EVIDENCE.md;EVIDENCE-MANAGEMENT.json; andevidence/.
-
Reuse or adapt the
control_path()approach fromscripts/management_common.pyso all management subsystems enforce a consistent containment policy. -
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.
-
Where supported, use descriptor-relative operations and no-follow semantics, such as
O_NOFOLLOW, for security-sensitive lock and registry files. -
Add regression tests covering:
05-verificationas 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.
-
Fail without creating or modifying any file when a symlink, junction, or resolved path outside the project is detected.
-
