T09 · Insecure Skill Coding Practices
- Location
scripts/base/mimo-tts.sh:30- Finding
Arbitrary Python Code Execution Through Unsafe Heredoc Interpolation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is mostly a disclosed Xiaomi text-to-speech integration, but it needs Review because bundled scripts contain a local code-execution flaw and the docs recommend unpinned package execution.
Install only if you are comfortable sending the text you synthesize to Xiaomi's MiMo API using your API key. Avoid the documented unpinned `npx` and unnecessary `pip install openai` examples unless you pin and trust the exact versions. Do not pass untrusted text through the shell `--dry-run` path until the heredoc interpolation bug is fixed, and prefer constraining outputs to a dedicated audio directory.
scripts/base/mimo-tts.sh:30Arbitrary Python Code Execution Through Unsafe Heredoc Interpolation
README.md:182Unpinned Third-Party Package Installation and Execution Instructions
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}
)
try:
with urllib.request.urlopen(req, timeout=60) as resp:
status = getattr(resp, 'status', None)
resp_text = resp.read().decode('utf-8')
except Exception as e:
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Accepting an undocumented credential source and dynamically selecting/executing local NodeJS, Python, or Shell implementations introduces hidden trust boundaries. In an agent context, dynamic interpreter selection and undeclared credential handling can lead to unpredictable execution, broader attack surface, and accidental use of the wrong secret source.
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
# Reminder for agents/scripts: generated audio files are temporary artifacts.
# Agents/users should remove files in $SKILL_OUT when finished. Example:
# rm -f "$SKILL_OUT"/*.ogg
# Helper: resolve a script path relative to SKILL_HOME
skill_path() {
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.
| jq -r '.choices[0].message.audio.data' | base64 -d > "$OUTPUT.wav" || true
if command -v ffmpeg >/dev/null 2>&1; then
ffmpeg -y -i "$OUTPUT.wav" -acodec libopus -b:a 128k "$OUTPUT" >/dev/null 2>&1 && rm -f "$OUTPUT.wav"
else
echo "ffmpeg not found; leaving wav at $OUTPUT.wav"
mv "$OUTPUT.wav" "$OUTPUT"
This mock-mode ffmpeg call has the same argument-injection-by-filename issue: an attacker-controlled output path beginning with '-' can be parsed as an ffmpeg option. In automation or multi-tenant agent environments, that can be abused to alter ffmpeg behavior or write to unexpected locations.
if MOCK:
# generate mock silent ogg using ffmpeg if available
try:
subprocess.run(['ffmpeg','-f','lavfi','-i','anullsrc=r=16000:cl=mono','-t','0.5','-q:a','9','-acodec','libopus',output,'-y'],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
except Exception:
open(output,'wb').close()
print(output)
The output filename is user-controlled and passed directly to ffmpeg. Even without shell=True, many CLI tools treat leading-dash arguments as options, so a crafted output value could be interpreted by ffmpeg as an option rather than a filename, potentially causing unintended file writes, overwrites, or other unsafe behavior within the privileges of the running process.
# convert wav to ogg if ffmpeg exists
try:
subprocess.run(['ffmpeg','-y','-i',wav_path,'-acodec','libopus','-b:a','128k',output], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
os.remove(wav_path)
except Exception as e:
print('ffmpeg conversion failed:', e)
The shell branch claims to use a shell implementation but actually re-invokes the same wrapper script, creating unbounded recursion whenever NodeJS and Python are unavailable. This can lead to denial of service through repeated process spawning, CPU consumption, and eventual resource exhaustion, and it also prevents the intended TTS function from completing.
The natural-language instructions, usage guidance, and warnings in this file are presented only in Chinese, which effectively forces a specific language for users reading the skill documentation. The file does not offer an alternative language, opt-in, or a documented reason that the skill must be Chinese-only.
The README instructs users to run npx @openclaw/skill-runner without pinning a specific version, which means execution will fetch whatever package version is current at the time. If the upstream package is compromised, replaced, or updated with breaking or malicious behavior, users may execute unreviewed code directly from the registry.
The skill documentation describes capabilities that imply environment variable access, network calls, shell execution, and local file writes, but it does not declare any tool scope or permissions. In an agent setting, undeclared execution and I/O surfaces reduce transparency and can lead to over-privileged use or unsafe invocation of external tools and remote services.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
fi
# fallback: simple curl-based request
curl -s -X POST "https://api.xiaomimimo.com/v1/chat/completions" \
-H "Authorization: Bearer ${XIAOMI_API_KEY}" \
-H "Content-Type: application/json" \
-d "$(printf '%s' "{\"model\":\"mimo-v2-tts\",\"messages\":[{\"role\":\"user\",\"content\":\"请朗读\"},{\"role\":\"assistant\",\"content\":\"$TEXT\"}],\"audio\":{\"format\":\"wav\",\"voice\":\"$VOICE\"}}")" \
The script sends the provided text to an external API via curl and includes an Authorization header derived from XIAOMI_API_KEY. While there is a dry-run mode, the normal execution path has no visible confirmation prompt, user-facing notice, or explanatory comment warning that user text will be transmitted off-system using credentials.
The script usage example and API prompt include Chinese text ("文本" and "请朗读") and do not indicate that the skill is intentionally limited to Chinese or allow the user to select language/locale behavior. Under SQP-3, forcing a specific language without opt-in can be a policy issue.
The script sends user-provided text to a remote Xiaomi API, which can expose sensitive or proprietary content if users assume processing is local. In a skill context, this is materially relevant because prompts or private text may be forwarded off-host without explicit disclosure or consent flow.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if MOCK:
# generate mock silent ogg using ffmpeg if available
try:
subprocess.run(['ffmpeg','-f','lavfi','-i','anullsrc=r=16000:cl=mono','-t','0.5','-q:a','9','-acodec','libopus',output,'-y'],check=True,stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL)
except Exception:
open(output,'wb').close()
print(output)
The script sends arbitrary input text to a third-party remote TTS service over the network, but it provides no explicit warning or consent checkpoint to users at the point of transmission. In agent/automation contexts, this can cause accidental disclosure of secrets, personal data, or sensitive internal content when users may assume processing is local.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# convert wav to ogg if ffmpeg exists
try:
subprocess.run(['ffmpeg','-y','-i',wav_path,'-acodec','libopus','-b:a','128k',output], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
os.remove(wav_path)
except Exception as e:
print('ffmpeg conversion failed:', e)
This shell script presents all user-facing text, errors, and example invocations in Chinese, which effectively forces a specific language for users interacting with the skill. Under the policy, language constraints should be optional or clearly justified as region-specific; neither is present in this file.
This shell script presents its title, usage guidance, status messages, and errors entirely in Chinese. That imposes a specific language on all users without opt-in, which matches the policy category for language/locale violations.
Detected: suspicious.dangerous_exec