T09 · Insecure Skill Coding Practices
- Location
smart-update.py:74- Finding
Shell Command Injection and Path Traversal Through Unvalidated Skill Slugs
- Content
View full analysis
Vulnerability Details
File Location:
smart-update.py:74-83,smart-update.py:102-105,smart-update.py:140-152
Vulnerability Type: Shell command injection and unsafe path construction
Risk Level: HighVulnerable Code
python def run_command(self, cmd: str) -> Tuple[bool, str]: """Run shell command and return success status and output.""" try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=60 ) return result.returncode == 0, result.stdout + result.stderr except Exception as e: return False, str(e)python def get_latest_version(self, slug: str) -> Optional[str]: """Get latest version of a skill from ClawHub.""" success, output = self.run_command(f"clawhub inspect {slug}") if not success: return Nonepython def download_new_version(self, slug: str) -> Optional[str]: """Download new version of skill to temp directory.""" temp_slug_dir = os.path.join(self.temp_dir, f"{slug}-new") if os.path.exists(temp_slug_dir): shutil.rmtree(temp_slug_dir) os.makedirs(temp_slug_dir) success, output = self.run_command( f"clawhub inspect {slug} --files --output \"{temp_slug_dir}\"" )Technical Analysis
Skill slugs parsed from
clawhub listare inserted directly into command strings passed tosubprocess.runwithshell=True. No strict character validation, escaping, or argument-array invocation is used.If an attacker can cause a crafted slug containing shell metacharacters to appear in the installed-skill listing, the shell interprets those characters as command syntax rather than as part of a literal skill identifier. Quoting only the output path does not protect the unquoted slug in
clawhub inspect {slug}.The same slug is also passed to `os.pa ...[truncated 1843 chars]
- Remediation
View remediation
Remediation Suggestions
-
Remove
shell=Trueand pass commands as argument arrays:python subprocess.run( ["clawhub", "inspect", slug], shell=False, capture_output=True, text=True, timeout=60, check=False, ) -
Apply a strict allowlist to every slug before using it in commands or paths:
python import re if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._-]*", slug): raise ValueError("Invalid skill slug") -
Resolve every generated path and verify that it remains beneath its intended parent directory using
Path.resolve()andPath.relative_to(). -
Reject absolute paths,
..components, path separators, control characters, and shell metacharacters. -
Avoid accepting security-sensitive identifiers from loosely parsed human-readable CLI output. Prefer a structured JSON output mode if the ClawHub CLI supports one.
-
Run scheduled updates under a dedicated, minimally privileged account with access limited to the required workspace.
-
