T09 · Insecure Skill Coding Practices
- Location
scripts/fetch-hot-search.py:126- Finding
Shell Command Injection Through a Remotely Supplied Topic URL
- Content
View full analysis
Vulnerability Details
File Location:
scripts/fetch-hot-search.py, lines 16-19 and 126-129
Vulnerability Type: OS command injection
Risk Level: HighVulnerable Code
python def run_command(cmd, timeout=30): try: result = subprocess.run( cmd, shell=True, capture_output=True, text=True, timeout=timeout ) return result.returncode == 0, result.stdout, result.stderr except Exception as e: return False, "", str(e)python success, _, _ = run_command( f"openclaw browser open --profile openclaw '{url}'", timeout=15 )Technical Analysis
The
run_command()function passes a string tosubprocess.run()withshell=True. Infetch_topic_content(), the topic URL is interpolated directly into that shell command.The URL originates in a browser snapshot of remote Weibo content. The parser only verifies that the extracted value contains the substring
s.weibo.com; it does not establish that the entire value is a valid URL with an exact approved hostname, nor does it prevent shell metacharacters such as apostrophes, semicolons, command substitutions, or comment characters.Although the URL is enclosed in single quotes, an attacker-controlled apostrophe can terminate that quoted argument. Subsequent shell syntax is then interpreted by the operating system shell. A malicious snapshot value conceptually shaped like the following would pass the substring check while breaking out of the command argument:
text https://s.weibo.com/topic';malicious_command;#The vulnerability is reached when detailed topic collection is enabled through
--with-content.Attack Path
- An attacker causes maliciously structured link data to appear in content represented by the OpenClaw browser snapshot.
parse_hot_search()extracts the attacker-controlled/urlv ...[truncated 959 chars]
- Remediation
View remediation
Remediation Suggestions
Remove shell interpretation entirely. Change the command helper to accept an argument list and invoke
subprocess.run()with its defaultshell=Falsebehavior:python def run_command(args, timeout=30): try: result = subprocess.run( args, shell=False, capture_output=True, text=True, timeout=timeout, check=False, ) return result.returncode == 0, result.stdout, result.stderr except Exception as e: return False, "", str(e) success, _, _ = run_command( [ "openclaw", "browser", "open", "--profile", "openclaw", url, ], timeout=15, )Validate the URL before invoking OpenClaw:
- Parse it with
urllib.parse.urlparse(). - Permit only
https. - Require an exact approved hostname, such as
s.weibo.com; do not use substring matching. - Reject embedded credentials, control characters, malformed ports, and unexpected hostname suffixes.
- Normalize protocol-relative URLs before validation.
- Apply the same non-shell execution pattern to all other OpenClaw commands, even where their current arguments are constants.
- Add regression tests containing apostrophes, semicolons, command substitutions, newlines, and malformed hostnames.
- Parse it with
