T09 · Insecure Skill Coding Practices
- Location
scripts/git-sync.py:1373- Finding
Shell Command Injection Through ClawHub Package Metadata
- Content
View full analysis
Vulnerability Details
File Location:
scripts/git-sync.py:1373-1381
Vulnerability Type: Shell command injection
Risk Level: HighComplete Code Snippet
python def step_clawhub_publish(name: str, version: str): # v2.37.0 多仓库:skill 仓库根下直接是技能目录 sd = get_work_repo("skill") / name if not sd.is_dir(): print(" ❌ 技能目录不存在"); return meta = json.loads((sd/"_meta.json").read_text(encoding="utf-8")) slug = meta.get("slug",name) cmd = f'npx clawhub publish "{sd}" --slug "{slug}" --name "{meta.get("displayName",name)}" --version "{version}" --changelog "v{version}"' if meta.get("tags"): cmd += ' --tags "' + ",".join(meta["tags"]) + '"' r = subprocess.run(cmd, capture_output=True, text=True, shell=True) if r.returncode==0 or "ok" in r.stdout.lower(): print(f" ✅ ClawHub: {slug}") else: print(f" ⚠️ ClawHub: {(r.stdout+r.stderr)[-300:]}")Technical Analysis
The publishing function reads
slug,displayName, andtagsfrom the target skill's_meta.json. These fields are interpolated directly into a single command string. The resulting string is then passed tosubprocess.run()withshell=True.Double-quoting the fields does not prevent command injection. A metadata value can contain an embedded quote, shell substitution, command separator, or another shell metacharacter that terminates or alters the intended argument. No character allowlist, escaping routine, or type validation is applied before shell interpretation.
The project contains a safer implementation in
scripts/clawhub_publish.py, which constructs an argument list and does not enableshell=True; however, the main workflow calls the vulnerablestep_clawhub_publish()implementation inscripts/git-sync.py.The vulnerable function is reachable through ordinary skill publishing. It is invoked in
--market-onlymode and after a normal synchronization unless--skip-marketis specified.Attack Path
- A third-party ...[truncated 1803 chars]
- Remediation
View remediation
Remediation Suggestions
- Remove
shell=Trueand construct the command as an argument list:
python cmd = [ "npx", "clawhub", "publish", str(sd), "--slug", slug, "--name", display_name, "--version", version, "--changelog", f"v{version}", ] if tags: cmd.extend(["--tags", ",".join(tags)]) subprocess.run(cmd, capture_output=True, text=True, check=False)- Reuse the argument-list implementation already present in
scripts/clawhub_publish.pyrather than maintaining a separate shell-based implementation. - Validate metadata before use:
- Require
slug,displayName, andversionto be strings. - Require
tagsto be a list of strings. - Apply a restrictive allowlist to fields such as
slugandversion. - Reject control characters and malformed metadata.
- Require
- Keep shell execution disabled even after validation. Validation should be defense in depth, not the primary command-injection control.
- Add regression tests using embedded quotes, command separators, command substitutions, newlines, and platform-specific shell metacharacters.
- Remove
