Back to skill

Security audit

Tiktok Android

Security checks for vulnerabilities and agentic risk

Overview

This TikTok automation skill is mostly coherent, but it needs Review because it can post public comments from a logged-in account, upload screenshots to AI providers, store API keys locally, and contains unsafe input-handling flaws.

Install only if you are comfortable with a bot controlling a logged-in TikTok account and posting comments publicly. Review the code first, avoid scheduled live runs until you add explicit confirmation or dry-run controls, protect or avoid plaintext .env API keys, and understand that AI mode can send captured TikTok screenshots to external providers.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
src/bot/android/tiktok_android_bot.py:99
Finding

Android Shell Command Injection Through Unsanitized Newline Characters

Content
View full analysis

Vulnerability Details

File Location: src/bot/android/tiktok_android_bot.py:99-119
Vulnerability Type: Android shell command injection
Risk Level: High

Vulnerable Code

python
def _type_text(self, text: str):
    """Type text using ADB input.

    Args:
        text: Text to type (will escape special characters)
    """
    # Remove emojis and special characters that cause issues
    # Keep only alphanumeric, spaces, and basic punctuation
    import re
    # Remove emojis
    text_clean = re.sub(r'[^\w\s\.,!?\'-]', '', text, flags=re.UNICODE)

    # Escape special characters for shell
    escaped = text_clean.replace(" ", "%s").replace("'", "").replace('"', '')

    if not escaped:
        log.warning(f"[Android Bot] Text became empty after cleaning: {text}")
        return

    self._adb_shell(f"input text {escaped}")

The resulting command is executed through the following Android shell sink at src/bot/android/tiktok_android_bot.py:46-63:

python
def _adb_shell(self, command: str, timeout: int = 10) -> str:
    """Execute ADB shell command."""
    try:
        result = subprocess.run(
            ["adb", "-s", self.device_id, "shell", command],
            capture_output=True,
            text=True,
            timeout=timeout
        )
        return result.stdout.strip()

Technical Analysis

The regular expression permits every character matched by \s, including carriage returns and newline characters. The subsequent escaping logic replaces only literal spaces and removes quotes; it does not reject control characters.

The sanitized value is concatenated into a command string passed to adb shell. A newline is interpreted by the Android shell as a command separator. Consequently, text originating from a search topic, a configured static comment, or an AI-generated comment can terminate the intended input text command and introduce another Android command.

Using a subprocess.run() argument list protects the ...[truncated 1407 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject all control characters, especially \r, \n, tabs, null bytes, and other Unicode separators.

  2. Replace \s with an explicit literal-space character in the allowlist:

    python
    text_clean = re.sub(r"[^A-Za-z0-9 .,!?_-]", "", text)
    
  3. Enforce a reasonable maximum text length before passing it to ADB.

  4. Prefer an Android input mechanism that does not construct shell command strings, such as a dedicated ADB keyboard or a UI automation API.

  5. If adb shell input text must be used, validate the final encoded value against a strict pattern before execution:

    python
    if not re.fullmatch(r"[A-Za-z0-9%._,!?-]+", escaped):
        raise ValueError("Unsafe text for ADB input")
    
  6. Treat AI-generated text as untrusted input and apply the same validation regardless of provider.

  7. Add regression tests covering newlines, carriage returns, tabs, shell metacharacters, and Unicode whitespace.

T09 · Insecure Skill Coding Practices

Error
Location
setup.py:122
Finding

Arbitrary Python Code Execution Through Generated Configuration

Content
View full analysis

Vulnerability Details

File Location: setup.py:122-145, 220-248
Vulnerability Type: Python source-code injection
Risk Level: High

Vulnerable Code

The setup wizard accepts an unrestricted model name:

python
# 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")

# Model selection
models = {
    "anthropic": ["claude-3-5-sonnet-20241022", "claude-3-opus-20240229"],
    "openai": ["gpt-4o", "gpt-4-turbo"],
    "openrouter": ["anthropic/claude-3.5-sonnet", "openai/gpt-4o"]
}

print(f"\n🎯 Recommended models for {provider_name}:")
for m in models[provider_name]:
    print(f"  • {m}")

model = ask_text(f"Model name", default=models[provider_name][0])

return {
    "provider": provider_name,
    "api_key": api_key,
    "model": model
}

The value is then inserted directly into executable Python source:

python
else:  # AI
    config_content += f'''
# AI Configuration
AI_PROVIDER = "{ai_config['provider']}"
AI_MODEL = "{ai_config['model']}"

# Comment generation prompt
AI_COMMENT_PROMPT = """
Analyze this video screenshot and generate a natural, engaging comment.
The video is related to: {{topic}}

Guidelines:
- 10-25 words
- Ask a question or make a specific observation
- Sound like a genuine enthusiast
- No emojis
- Be encouraging and positive
"""

# Generic comments as fallback (if AI fails)
GENERIC_COMMENTS = [
    "This is great content! Keep it up!",
    "Really enjoyed this! More please!",
    "Awesome work! Very informative!",
]
'''

The generated file is later imported by tiktok_bot.py, causing its contents to execute as Python code.

Technical Analysis

The model name is neither validated nor safely serialized before being inserted between quotation marks in config.py. An input containing a quote and newline can terminate the intended string assignment and add arbitrary Python statements.

This i ...[truncated 1593 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not generate executable Python configuration from raw user input. Store configuration in JSON, TOML, or another data-only format.

  2. If Python generation must be retained, serialize every inserted value safely:

    python
    config_content += f"""
    AI_PROVIDER = {ai_config['provider']!r}
    AI_MODEL = {ai_config['model']!r}
    """
    
  3. Validate model identifiers against a strict allowlist or pattern, for example:

    python
    if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._/-]{0,127}", model):
        raise ValueError("Invalid model identifier")
    
  4. Reject carriage returns, newlines, quotes, backslashes, and control characters.

  5. Parse the generated file with ast.parse() before saving it and verify that it contains only expected assignments with literal values.

  6. Write the configuration atomically so an interrupted operation cannot leave partially executable Python.

  7. Add tests using quote, newline, triple-quote, backslash, and comment-character payloads.

T09 · Insecure Skill Coding Practices

Warning
Location
setup.py:255
Finding

API Credentials Stored in a Predictable Plaintext File Without Explicit Permission Controls

Content
View full analysis

Vulnerability Details

File Location: setup.py:255-275
Vulnerability Type: Insecure secret storage and unsafe file handling
Risk Level: Medium

Vulnerable Code

python
# 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()

    key_name = {
        "anthropic": "ANTHROPIC_API_KEY",
        "openai": "OPENAI_API_KEY",
        "openrouter": "OPENROUTER_API_KEY"
    }[ai_config['provider']]

    # Remove existing key if present
    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")

Technical Analysis

The wizard stores provider API keys in plaintext at a predictable relative path. The file is opened with the process's ambient umask rather than an explicitly restrictive mode. On systems with permissive defaults, the resulting file may be readable by other local users or processes.

The code also follows symbolic links and performs a non-atomic read-modify-write operation. If an attacker can place or replace .env in the working directory, the program can be induced to read from or overwrite a symlink target accessible to the victim account.

Documentation stating that the file is excluded from Git does not protect it from local disclosure, backups, accidental archive inclusion, malicious symlinks, or permissive filesystem permissions.

Attack Path

A local exploitation path is:

  1. The attacker has write access to the project directory or can influence the bot's working directory.
  2. The attacker creates .env as a symbolic link to another victim-writable file, or waits for the wizard to create a broadly readable file.
  3. The user enters an Anthropic, OpenAI, or OpenRouter API key.
  4. `setup.py ...[truncated 786 chars]
Remediation
View remediation

Remediation Suggestions

  1. Prefer an operating-system credential manager or dedicated secret-management service.

  2. If a local file is necessary, create it with mode 0600 using low-level, no-follow, atomic file operations.

  3. Verify that an existing path is a regular file owned by the current user and not a symbolic link.

  4. Write updates to a securely created temporary file in the same directory, apply mode 0600, and atomically replace the destination.

  5. Explicitly enforce permissions after creation:

    python
    os.chmod(".env", 0o600)
    
  6. Avoid retaining the API key in configuration dictionaries longer than necessary.

  7. Warn users that the file contains plaintext credentials and must not be shared, archived, logged, or committed.

  8. Provide key rotation guidance in case the file is exposed.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Dependencies Are Unbounded and Installed Without Integrity Pinning

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-3
Vulnerability Type: Uncontrolled dependency resolution
Risk Level: Low

Vulnerable Code

text
loguru>=0.7.0
anthropic>=0.18.0
openai>=1.12.0

Technical Analysis

Each dependency is specified only with a minimum version. There is no upper bound, exact version lock, or package hash. A future installation can therefore resolve to versions that were not present or reviewed when this project was audited.

The packages use legitimate names and no evidence of typosquatting, dependency confusion, or a currently malicious dependency was identified. The risk arises from mutable dependency resolution and absent artifact verification rather than a known compromised package.

Attack Path

  1. A user installs the dependencies at a later date.
  2. The package resolver selects the newest versions satisfying the open-ended constraints.
  3. A compromised, malicious, or unexpectedly incompatible future release is downloaded.
  4. Package installation or subsequent import executes code from that unreviewed release.
  5. The dependency receives the privileges of the Python environment running the bot.

Impact Assessment

A compromised dependency could execute code with the current user's privileges and potentially access:

  • Provider API keys in the process environment.
  • TikTok screenshots and local project files.
  • The connected Android device through ADB.
  • Network resources available to the host.
  • Any other files available to the Python process.

There is no evidence that this attack has occurred; this finding concerns preventable supply-chain exposure.

Remediation
View remediation

Remediation Suggestions

  1. Pin every dependency to an exact, reviewed version.
  2. Generate and maintain a lock file using a tool such as pip-tools, Poetry, or uv.
  3. Require package hashes during installation, for example with pip install --require-hashes.
  4. Perform dependency vulnerability scanning in continuous integration.
  5. Review and deliberately approve dependency upgrades rather than resolving them implicitly.
  6. Use an isolated virtual environment and avoid installing the bot as an administrator or root user.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (41)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The description claims a substantial set of capabilities centered on TikTok automation via ADB, including search, commenting, and setup flows. The provided code chunk does not implement any of these behaviors; it only defines a module with a descriptive docstring. Because the actual code lacks the declared primary functionality, the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code broadly matches the high-level theme of Android ADB TikTok automation and comment posting, but the declared description overstates several key capabilities. The supplied code does not search topics, does not generate comments with AI or templates, and does not include any setup wizard. Instead, it automates app control on a device, navigation through the feed, and posting a supplied comment on the current video. It also includes screenshot capture, which is undeclared, though that appears secondary. Because important advertised features are absent and the implemented behavior is narrower/different than described, this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.py (reported line 124)May include surrounding context.

python
# 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")

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.py (reported line 255)May include surrounding context.

python
# 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")

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.py (reported line 258)May include surrounding context.

python
# 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()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.py (reported line 259)May include surrounding context.

python
# 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()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · setup.py (reported line 275)May include surrounding context.

python
# 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()

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

This line writes the API key in plaintext to a local .env file, which can expose the credential if the working directory is shared, backed up insecurely, accidentally committed, or readable by other local users/processes. In a social-media automation skill that encourages operational deployment and scheduling, persistent plaintext secret storage increases the chance of long-term credential leakage.

Content

Scanner excerpt · setup.py (reported line 272)May include surrounding context.

python
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")

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Confidence
94% confidence
Finding

The script executes another program via os.system("python3 setup.py"), which delegates command resolution to the shell and trusts the current working directory. If an attacker can place or modify setup.py in that directory, or influence execution context, arbitrary code will run when the user accepts setup.

Content

Scanner excerpt · tiktok_bot.py (reported line 51)May include surrounding context.

python
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)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README prominently markets bot-evasion claims such as 'No bot detection' and scheduled automated commenting, while delaying meaningful warnings about account enforcement, moderation risk, and ToS violations until much later. In this skill context, that omission is more dangerous because the tool is explicitly designed for mass social-platform automation and could mislead users into unsafe operational use, account loss, or platform abuse.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The AI-generated commenting section instructs users to analyze video screenshots with third-party providers but does not clearly warn that screenshots, prompts, topics, and possibly other metadata may be transmitted off-device to external AI services. In this context, the omission is more dangerous because the skill automates collection and processing of user-generated content at scale, creating privacy, compliance, and data-handling risks for operators and potentially for content subjects.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The README states the skill 'uses Claude Vision or GPT-4 Vision to analyze videos and generate contextual comments' at L153, presenting AI-based video analysis as a current capability. Later, the same README says 'No video analysis yet: Comments use topic templates' at L489 and lists 'Claude Vision integration' as a future enhancement at L495, which directly contradicts the earlier claim about implemented behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill documentation advertises capabilities that require sensitive operations like shell execution, filesystem access, and environment-variable handling, but it does not declare any tool scope or permissions. That creates an authorization and transparency gap: users and reviewers cannot easily assess what the skill is allowed to do before execution, increasing the risk of unexpected command execution or secret access.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill automates interactions on a logged-in TikTok account, including posting comments and navigating the feed, but the description does not clearly warn that it will change account state and publish user-visible content. This can mislead users into authorizing actions with reputational, moderation, or account-enforcement consequences they did not fully anticipate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The AI-commenting mode states that Claude or GPT Vision analyzes video screenshots, but it does not clearly disclose that screenshots and video-derived content are transmitted to third-party AI providers. This creates a privacy and data-governance risk because users may unknowingly send potentially sensitive or copyrighted content off-device to external services.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · run_complete_session.py (reported line 27)May include surrounding context.

python
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.")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · run_full_campaign.py (reported line 248)May include surrounding context.

python
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.")

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tiktok_bot.py (reported line 88)May include surrounding context.

python
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.")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script captures screenshots of TikTok content and stores them locally without any user notice, consent flow, retention policy, or protection for potentially sensitive content. Because screenshots may contain usernames, messages, profile details, or other personal data, silent local persistence increases privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script automatically posts comments on TikTok on the user's behalf without a per-action confirmation, consent gate, or clear runtime warning. In the context of a social-media automation skill, this creates a real risk of unauthorized or unintended account actions, reputation damage, spam/policy violations, and misuse if the skill is triggered in the wrong environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script automates TikTok commenting at scale across multiple topics with no per-action confirmation, rate-limiting safeguards, or abuse-prevention controls. In the context of a TikTok automation bot, this materially increases the risk of spam, account sanctions, platform-policy violations, and unintended posting from a connected account.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code persists the user's API key into a local .env file. Although the setup mentions that the key will be stored and not committed to git, it does not warn about the sensitivity of the credential or advise the user to protect the file, which is a user-facing warning gap for a credential-handling operation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This function reads a local screenshot, base64-encodes it, and sends the full image to Anthropic for vision-based comment generation. Screenshots from an Android TikTok automation workflow can easily contain sensitive or personal data, and this file provides no consent, minimization, redaction, or disclosure controls before exfiltrating that data to a third party.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The OpenAI path transmits a screenshot as a data URL to an external API without any visible warning, consent gate, or content filtering in this file. Because the skill automates social-media interaction on Android, screenshots may include usernames, DMs, notifications, or other sensitive UI content, making silent third-party transmission a real privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This function sends screenshot contents to OpenRouter, which is an aggregator layer and can increase uncertainty around downstream model routing and data handling. In the context of TikTok Android automation, that makes undisclosed screenshot upload more dangerous because potentially sensitive mobile UI content is being exported outside the local environment without safeguards shown here.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.