Back to skill

Security audit

YouTube Uploader

Security checks for vulnerabilities and agentic risk

Overview

This YouTube uploader is broadly purpose-aligned, but it warrants Review because it stores broad long-lived YouTube OAuth credentials and auto-installs unpinned dependencies at runtime.

Install only if you are comfortable granting this skill durable access to a YouTube account and allowing it to install Python packages at runtime. Prefer a version that pins dependencies, documents required domains and OAuth scopes, uses narrower YouTube scopes where possible, and provides a clear way to delete or revoke stored credentials.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T08 · Insecure Dependencies

Warning
Location
scripts/youtube-upload.py:22
Finding
Automatic Installation of Unpinned Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/youtube-upload.py:22-62` **Vulnerability Type**: Supply-chain exposure through unpinned runtime dependency installation **Risk Level**: Medium ### Vulnerable Code ```python REQUIRED_PACKAGES = [ ("googleapiclient", "google-api-python-client"), ("google_auth_oauthlib", "google-auth-oauthlib"), ("google.auth.transport.requests", "google-auth-httplib2"), ] VENV_DIR = Path.home() / ".openclaw" / "youtube" / ".venv" def _in_venv() -> bool: """Check if running inside our dedicated venv.""" return any(str(VENV_DIR) in p for p in sys.path) def ensure_dependencies(): """Install missing Google API packages into a dedicated venv.""" venv_python = VENV_DIR / "bin" / "python3" # If running outside the venv and it exists, re-exec into it directly if venv_python.exists() and not _in_venv(): os.execv(str(venv_python), [str(venv_python), *sys.argv]) # Check if imports are available missing = [] for module_name, pip_name in REQUIRED_PACKAGES: try: __import__(module_name) except ImportError: missing.append(pip_name) if not missing: return # Create venv if needed (first run) if not venv_python.exists(): print("Creating virtual environment for YouTube skill...", file=sys.stderr) subprocess.check_call([sys.executable, "-m", "venv", str(VENV_DIR)]) print(f"Installing dependencies: {', '.join(missing)}", file=sys.stderr) subprocess.check_call([str(venv_python), "-m", "pip", "install", "--quiet", *missing]) # Re-exec inside the venv so imports resolve os.execv(str(venv_python), [str(venv_python), *sys.argv]) ``` ### Technical Analysis The script automatically creates a virtual environment and installs packages from the configured Python package index whenever required imports are unavailable. Package names are not constrained by exact versions, cryptographic hashes ...[truncated 1876 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic dependency installation from normal script execution. 2. Declare dependencies through a reviewed package manifest and lock file. 3. Pin every direct and transitive dependency to an approved version. 4. Record cryptographic hashes and install with `pip --require-hashes`. 5. Install dependencies during an explicit setup or deployment phase requiring user consent. 6. Use a controlled package repository or verified internal mirror where appropriate. 7. Document all dependencies and installation behavior in `SKILL.md` and Skill metadata. 8. Regularly audit pinned dependencies for known vulnerabilities and update them through a controlled review process. 9. Apply restrictive permissions to the virtual environment and prevent less-trusted users from modifying it. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/youtube-upload.py:76
Finding
OAuth Scopes Exceed the Skill's Minimum Functional Requirements<![CDATA[ ## Vulnerability Details **File Location**: `scripts/youtube-upload.py:76-80` **Vulnerability Type**: Excessive OAuth authorization scope **Risk Level**: Medium ### Vulnerable Code ```python SCOPES = [ "https://www.googleapis.com/auth/youtube", "https://www.googleapis.com/auth/youtube.upload", "https://www.googleapis.com/auth/youtube.force-ssl", ] ``` These scopes are used when initiating authorization: ```python flow = InstalledAppFlow.from_client_secrets_file( str(client_secret_path), scopes=SCOPES, redirect_uri=redirect_uri, ) ``` The resulting long-lived credentials are subsequently persisted: ```python channels[channel_id] = { "title": channel_title, "token": creds.token, "refresh_token": creds.refresh_token, "token_uri": creds.token_uri, "client_id": creds.client_id, "client_secret": creds.client_secret, "expiry": creds.expiry.isoformat() if creds.expiry else None, "authenticated_at": datetime.now(timezone.utc).isoformat(), } save_channels(channels) ``` ### Technical Analysis The Skill declares video upload, thumbnail upload, channel identification, and credential refresh functionality. It requests three overlapping YouTube scopes, including the broad `youtube` and `youtube.force-ssl` scopes. These authorize substantially more account-management capability than a narrowly scoped upload workflow requires. The access is requested with `access_type="offline"` and `prompt="consent"`, resulting in a refresh token that can retain the granted permissions across sessions. Although `channels.json` is changed to mode `0600`, any process executing as the same user can potentially read it. The automatic third-party dependency installation identified separately also increases the importance of minimizing these token privileges. The OAuth authorization-code exchange at line 245 is legitimate and necessary: `flow.fetch_token(code=OAuthCallbackHandler.auth_code)` sends the one-time code to Google' ...[truncated 1673 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Determine the exact OAuth scopes required for each implemented API method using current Google API documentation. 2. Remove redundant broad scopes such as `youtube` and `youtube.force-ssl` when narrower scopes provide the required functionality. 3. Prefer the narrow upload scope for video and thumbnail operations and a read-only scope only if channel identification requires it. 4. Separate workflows into independently authorized scope sets if some optional operations require broader access. 5. Clearly disclose every requested scope and its purpose before opening the OAuth consent flow. 6. Preserve restrictive `0600` permissions and also create the storage directory with owner-only permissions. 7. Support credential revocation and deletion so users can remove stored channel authorization. 8. Avoid retaining the OAuth client secret in each channel entry when it can be securely referenced from one protected configuration file. 9. Re-authenticate existing channels after reducing scopes so previously issued broad refresh tokens are revoked and replaced. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (14)

Credential Access

High
Category
Privilege Escalation
Content
## Setup (one-time)

The user needs a Google Cloud project with the YouTube Data API v3 enabled and an OAuth2 client ID (type "Desktop app"). Download the `client_secret.json` file.

### Authenticate
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Setup (one-time)

The user needs a Google Cloud project with the YouTube Data API v3 enabled and an OAuth2 client ID (type "Desktop app"). Download the `client_secret.json` file.

### Authenticate
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Setup (one-time)

The user needs a Google Cloud project with the YouTube Data API v3 enabled and an OAuth2 client ID (type "Desktop app"). Download the `client_secret.json` file.

### Authenticate
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Setup (one-time)

The user needs a Google Cloud project with the YouTube Data API v3 enabled and an OAuth2 client ID (type "Desktop app"). Download the `client_secret.json` file.

### Authenticate
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Setup (one-time)

The user needs a Google Cloud project with the YouTube Data API v3 enabled and an OAuth2 client ID (type "Desktop app"). Download the `client_secret.json` file.

### Authenticate
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
# If running outside the venv and it exists, re-exec into it directly
    if venv_python.exists() and not _in_venv():
        os.execv(str(venv_python), [str(venv_python), *sys.argv])

    # Check if imports are available
    missing = []
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
# If running outside the venv and it exists, re-exec into it directly
    if venv_python.exists() and not _in_venv():
        os.execv(str(venv_python), [str(venv_python), *sys.argv])

    # Check if imports are available
    missing = []
Confidence
85% confidence
Finding
os.system() and os exec-family calls run shell commands with the process's full privileges, enabling arbitrary command execution.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill invokes a Python script that performs shell execution, network access to Google/YouTube, and writes credential files, but the manifest does not declare any explicit tool scope or permissions. This creates an authorization and review gap: operators and users cannot clearly see or constrain the capabilities the skill requires before it runs.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill performs self-bootstrapping by creating a virtualenv, installing packages from the network, and re-executing itself at runtime. In an agent-skill context this expands the trust boundary significantly: executing network-fetched code during normal skill use can introduce supply-chain risk and unexpected host modification beyond the stated upload/auth purpose.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
missing = []
    for module_name, pip_name in REQUIRED_PACKAGES:
        try:
            __import__(module_name)
        except ImportError:
            missing.append(pip_name)
    if not missing:
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Create venv if needed (first run)
    if not venv_python.exists():
        print("Creating virtual environment for YouTube skill...", file=sys.stderr)
        subprocess.check_call([sys.executable, "-m", "venv", str(VENV_DIR)])

    print(f"Installing dependencies: {', '.join(missing)}", file=sys.stderr)
    subprocess.check_call([str(venv_python), "-m", "pip", "install", "--quiet", *missing])
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
subprocess.check_call([sys.executable, "-m", "venv", str(VENV_DIR)])

    print(f"Installing dependencies: {', '.join(missing)}", file=sys.stderr)
    subprocess.check_call([str(venv_python), "-m", "pip", "install", "--quiet", *missing])

    # Re-exec inside the venv so imports resolve
    os.execv(str(venv_python), [str(venv_python), *sys.argv])
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script copies the OAuth client secret into persistent local storage for later use without explicit user-facing disclosure or consent. While file permissions are restricted, persistent duplication of sensitive material increases exposure if the account directory is backed up, synced, or later accessed by other local processes.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The script stores access tokens, refresh tokens, client ID, and client secret in a JSON file on disk. Even with chmod 600, this creates a durable local credential cache that could allow unauthorized YouTube account access if the user account, home directory backups, or other local software are compromised.

Static analysis

No suspicious patterns detected.