T09 · Insecure Skill Coding Practices
- Location
scripts/dsp-cli.py:419- Finding
Unvalidated UID Paths Allow Arbitrary Directory Deletion and Filesystem Access
- Content
View full analysis
Vulnerability Details
File Location:
scripts/dsp-cli.py, lines 161–175 and 419–455
Vulnerability Type: Path traversal and absolute-path injection
Risk Level: HighVulnerable Code
Path construction and entity validation at lines 161–175:
python class Store: def __init__(self, root: Path): self.root = root.resolve() self.base = self.root / DSP_DIR # ── guards ── def ensure_init(self) -> None: if not self.base.is_dir(): _fail(f"directory {self.base} not found — run 'init' first") def entity_exists(self, uid: str) -> bool: return (self.base / uid).is_dir() def require_entity(self, uid: str) -> None: if not self.entity_exists(uid): _fail(f"entity {uid} does not exist")Recursive deletion at lines 419–455:
python def remove_entity(self, uid: str) -> None: self.s.ensure_init() self.s.require_entity(uid) all_uids = self.s.all_uids() for other in all_uids: if other == uid: continue imports = self.s.read_imports(other) had = any(u == uid or v == uid for u, v in imports) if had: new_lines = [ _format_import_line(u, v) for u, v in imports if u != uid and v != uid ] _write_lines(self.s.imports_path(other), new_lines) for imp_uid, imp_via in self.s.read_imports(uid): if imp_via: _safe_unlink(self.s.exports_dir(imp_via) / imp_uid / uid) else: _safe_unlink(self.s.exports_dir(imp_uid) / uid) for other in all_uids: if other == uid: continue shared = self.s.read_shared(other) if uid in shared: _remove_line_value(self. ...[truncated 3001 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate every UID before using it in a path:
python UID_RE = re.compile(r"^(?:obj|func)-[0-9a-f]{8}$") def validate_uid(uid: str) -> str: if not UID_RE.fullmatch(uid): _fail(f"invalid entity UID: {uid}") return uid -
Apply UID validation consistently to all entity-related parameters, including:
uidownerexportershared_uidimporterimported- Parsed
viavalues - TOC root identifiers
-
Centralize safe path construction and verify containment after resolution:
python def entity_path(self, uid: str) -> Path: validate_uid(uid) candidate = (self.base / uid).resolve() try: candidate.relative_to(self.base.resolve()) except ValueError: _fail("entity path escapes the .dsp directory") return candidate -
Replace direct expressions such as
self.base / uidwith the centralized safe-path function. -
Before recursive deletion, perform a second containment check and reject:
- Absolute user-supplied path components
.and..traversal components- Directory separators inside identifiers
- Symlinks or resolved paths outside
.dsp - Attempts to delete
.dspitself or the project root
-
Validate identifiers read from existing
imports,shared, TOC, and export-index files. Treat graph files as potentially untrusted project data rather than assuming their contents are safe. -
Add regression tests covering absolute paths, traversal strings, malformed UIDs, symlink escapes, and valid entity deletion.
-
