T09 · Insecure Skill Coding Practices
- Location
tiktok_bot.py:445- Finding
Android Shell Command Injection Through the Video URL
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This TikTok automation skill mostly matches its stated purpose, but its publish and input-handling behavior can delete phone videos or execute unintended commands on a connected Android device.
Install only after reviewing the code and preferably after removing the publish-mode camera cleanup, adding strict input validation for ADB shell commands, and replacing executable config.py/plaintext .env secret handling. Do not run publish mode on a personal phone or logged-in account containing media you care about unless the deletion behavior has been fixed and you have backups.
tiktok_bot.py:445Android Shell Command Injection Through the Video URL
src/bot/android/tiktok_navigation.py:64Android Shell Command Injection Through Search Topics
setup.py:224Arbitrary Host-Side Python Execution Through Generated Configuration
tiktok_bot.py:427Publish Mode Deletes Unrelated Camera Videos
setup.py:255AI API Keys Are Written to a Plaintext File Without Enforced Restrictive Permissions
requirements.txt:1Dependencies Are Installed Without Upper Bounds, Locking, or Integrity Hashes
The documented command rm -f /sdcard/DCIM/Camera/*.mp4 performs broad deletion of all MP4 files in a common camera directory on the device. In the context of an ADB automation skill operating on a real user's phone, this is dangerous because it can irreversibly remove unrelated personal videos, and the README presents it as part of the normal workflow without strong safeguards or scoping.
### 发布流程
1. **清理相册** → `rm -f /sdcard/DCIM/Camera/*.mp4`
2. **下载/上传** → curl 或 adb push
3. **媒体扫描** → `MEDIA_SCANNER_SCAN_FILE` 广播
4. **选择视频** → 点击第一个帧
This finding includes a materially dangerous, undeclared destructive behavior: publish mode reportedly deletes existing .mp4/.3gp files under /sdcard/DCIM/Camera. In the context of an Android automation skill, undocumented destructive file cleanup can cause irreversible loss of user media and is especially risky because the skill already normalizes shell/ADB access and automated actions.
This finding includes a materially dangerous, undeclared destructive behavior: publish mode reportedly deletes existing .mp4/.3gp files under /sdcard/DCIM/Camera. In the context of an Android automation skill, undocumented destructive file cleanup can cause irreversible loss of user media and is especially risky because the skill already normalizes shell/ADB access and automated actions.
This finding includes a materially dangerous, undeclared destructive behavior: publish mode reportedly deletes existing .mp4/.3gp files under /sdcard/DCIM/Camera. In the context of an Android automation skill, undocumented destructive file cleanup can cause irreversible loss of user media and is especially risky because the skill already normalizes shell/ADB access and automated actions.
This finding includes a materially dangerous, undeclared destructive behavior: publish mode reportedly deletes existing .mp4/.3gp files under /sdcard/DCIM/Camera. In the context of an Android automation skill, undocumented destructive file cleanup can cause irreversible loss of user media and is especially risky because the skill already normalizes shell/ADB access and automated actions.
This finding includes a materially dangerous, undeclared destructive behavior: publish mode reportedly deletes existing .mp4/.3gp files under /sdcard/DCIM/Camera. In the context of an Android automation skill, undocumented destructive file cleanup can cause irreversible loss of user media and is especially risky because the skill already normalizes shell/ADB access and automated actions.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Ask for API key
print(f"\n📝 Enter your {provider_name.upper()} API key")
print("(This will be stored in .env file, not committed to git)")
api_key = ask_text(f"API key")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Ask for API key
print(f"\n📝 Enter your {provider_name.upper()} API key")
print("(This will be stored in .env file, not committed to git)")
api_key = ask_text(f"API key")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Save AI API key to .env if AI mode
if comment_style == "ai" and ai_config:
env_content = ""
if os.path.exists(".env"):
with open(".env", 'r') as f:
env_content = f.read()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Save AI API key to .env if AI mode
if comment_style == "ai" and ai_config:
env_content = ""
if os.path.exists(".env"):
with open(".env", 'r') as f:
env_content = f.read()
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Save AI API key to .env if AI mode
if comment_style == "ai" and ai_config:
env_content = ""
if os.path.exists(".env"):
with open(".env", 'r') as f:
env_content = f.read()
This line writes the user-supplied API key into a plaintext .env file, which can expose credentials to other local users, backups, shell tooling, or accidental inclusion in project artifacts if file hygiene is poor. In the context of a bot that integrates paid AI providers, compromise of the API key can lead to unauthorized API usage, billing abuse, and indirect access to prompts or generated content.
lines = [l for l in env_content.split('\n') if not l.startswith(f"{key_name}=")]
lines.append(f"{key_name}={ai_config['api_key']}")
with open(".env", 'w') as f:
f.write('\n'.join(lines))
print(f"✓ Saved API key to .env")
publish_video claims to publish a caller-specified local file but ignores video_path and instead selects the first album item on the device. That mismatch can cause unintended or sensitive media to be posted, which is especially dangerous in a social-media automation skill where publication is externally visible and difficult to undo.
The docstring states that video_path determines the video to publish, but the implementation never uses it. This deceptive interface can mislead upstream automation into believing a safe asset is being posted when the bot may actually publish unrelated gallery content, creating a serious integrity and privacy risk.
The script executes python3 setup.py via os.system, which invokes an external file from the current working directory without validating its path or integrity. If a user runs this tool from an untrusted directory or the setup script is replaced, arbitrary local code will execute under the user's privileges.
response = input("Run interactive setup now? [Y/n]: ").strip().lower()
if response in ['', 'y', 'yes']:
print("\nStarting setup wizard...\n")
os.system("python3 setup.py")
if not os.path.exists("config.py"):
print("\n❌ Setup was not completed. Exiting.")
sys.exit(1)
The skill claims TikTok interaction and publishing, but publish mode silently performs bulk deletion of device camera videos beforehand. That undisclosed destructive capability materially increases risk because operators may not realize the tool can erase unrelated user media on the connected phone.
Publish mode deletes all videos in the device camera folder without any confirmation prompt. Because this is a destructive operation on user-owned media, lack of explicit confirmation materially increases the chance of accidental irreversible loss.
Using rm -f /sdcard/DCIM/Camera/*.mp4 through ADB is tool abuse because it applies a broad wildcard deletion to a sensitive user media directory. In context, a TikTok publishing workflow does not need blanket deletion of all camera videos, so this creates disproportionate destructive capability.
print(f"\n🧹 Cleaning up all videos in DCIM/Camera...")
# Delete all video files except .thumbnails folder
subprocess.run(
["adb", "-s", device_id, "shell", "rm -f /sdcard/DCIM/Camera/*.mp4"],
capture_output=True,
timeout=10
)
This repeats the same destructive pattern for .3gp files, enabling mass deletion of user recordings. The skill context makes it more dangerous because users may connect a personal phone expecting TikTok automation, not media wiping.
timeout=10
)
subprocess.run(
["adb", "-s", device_id, "shell", "rm -f /sdcard/DCIM/Camera/*.3gp"],
capture_output=True,
timeout=10
)
The README documents destructive behavior in publish mode, including clearing album content and deleting media after posting, without prominent warnings about data loss scope. In a skill that automates ADB commands on a real device, insufficient disclosure can cause users to unintentionally erase local media or publish/delete the wrong files.
L483-L488 的完整示例展示了 interact --videos 3 --like --favorite --comment --topics travel,并声称会先搜索 travel 再互动。可是在 L210-L213 与 L215-L221,README 明确说明互动模式从推荐页开始,且 --topics 仅适用于 search_mode,这构成了文档内部对代码/行为意图的直接矛盾。
The skill documents capabilities that imply shell, environment-variable, and file access but does not declare any tool scope or permission boundaries. In an agent setting, this creates an authorization gap: consumers cannot tell what tools the skill expects, and a host may expose broader capabilities than intended.
The AI comment feature says Claude/GPT-4/OpenRouter may perform visual analysis of video content, but the document does not warn that screenshots, frames, or metadata could be transmitted to third-party AI providers. In this context, the skill handles potentially sensitive personal or copyrighted content, so lack of a privacy disclosure meaningfully increases user exposure.
The publish workflow states that it will 'clean the album' and later 'clean up' without a prominent warning about deletion impact. In a mobile-device context, destructive media deletion can remove personal content unrelated to the automation task, making this a genuine safety/security issue even if the intent is operational convenience.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
device_id = os.environ.get("ANDROID_DEVICE_ID")
if not device_id:
# Auto-detect first connected device
result = subprocess.run(["adb", "devices"], capture_output=True, text=True)
devices = [line.split()[0] for line in result.stdout.split("\n")[1:] if line.strip() and "device" in line]
if not devices:
print("❌ No Android device found. Connect device and enable USB debugging.")
No suspicious patterns detected.