Back to skill

Security audit

Youtube Playlist Handler

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does YouTube playlist management, but it also has undocumented private account-data commands and stores OAuth tokens in an unsafe local pickle file.

Review this skill before installing. It can change your YouTube account through OAuth, cache a reusable token locally, remove playlist items, and expose liked videos and subscriptions even though those commands are not documented in the skill instructions. Use only in a private, trusted workspace, and prefer a version that removes undocumented account-data commands, narrows OAuth permissions, and stores tokens safely without pickle.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/yt_playlist.py:30
Finding

Unsafe Deserialization of Cached OAuth Credentials

Content
View full analysis

Vulnerability Details

File Location: scripts/yt_playlist.py:30-32; duplicated in yt_playlist.py:30-32
Vulnerability Type: Unsafe Python pickle deserialization
Risk Level: High

Vulnerable Code:

python
if TOKEN_FILE.exists():
    with open(TOKEN_FILE, 'rb') as token:
        creds = pickle.load(token)

Technical Analysis

The application loads token.pickle with pickle.load() without establishing its integrity or provenance. Python pickle data can contain reducer instructions that invoke arbitrary callables during deserialization. Consequently, loading a maliciously modified token file can execute code before the resulting object is used as an OAuth credential.

The token has a predictable location in the project root:

python
SKILL_DIR = Path(__file__).parent.parent
TOKEN_FILE = SKILL_DIR / "token.pickle"

The same vulnerable implementation exists in both Python files. Exploitation requires an attacker or compromised process to have permission to create or replace the token file. This does not create a new privilege boundary by itself, but it converts file-write access into code execution in the context of the user running the Skill.

Attack Path

  1. The attacker obtains write access to the project root or otherwise replaces token.pickle.
  2. The attacker creates a crafted pickle whose deserialization reducer invokes an arbitrary command or Python callable.
  3. The user or Agent invokes any supported command that calls get_authenticated_service().
  4. The program opens the attacker-controlled file and executes pickle.load(token).
  5. The embedded reducer executes with the local privileges and environment of the Skill process.

Impact Assessment

Successful exploitation permits arbitrary code execution with the privileges of the user running the Skill. The attacker could read or modify files accessible to that account, access process environment data, steal OAuth m ...[truncated 179 chars]

Remediation
View remediation

Remediation Suggestions

  • Eliminate pickle for credential persistence.
  • Serialize credentials as non-executable JSON and reconstruct them through supported Google authentication APIs, such as google.oauth2.credentials.Credentials.from_authorized_user_file().
  • Store OAuth material in a dedicated user-private configuration directory rather than the project root.
  • Create credential files with restrictive permissions, such as mode 0600 on POSIX systems.
  • Reject token files with unsafe ownership, unexpected permissions, symbolic links, or an unexpected file type.
  • If migration from existing pickle files is necessary, do not automatically deserialize them. Require users to authenticate again and delete the legacy files.
  • Apply the same correction to both duplicated implementations to prevent users from invoking an unfixed copy.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/yt_playlist.py:24
Finding

Overbroad OAuth Authorization and Undocumented Access to Private Account Data

Content
View full analysis

Vulnerability Details

File Location: scripts/yt_playlist.py:24,199-242,293-297; duplicated in yt_playlist.py:24,199-242,293-297
Vulnerability Type: Excessive authorization scope and undocumented data-access capabilities
Risk Level: Medium

Relevant Code:

python
SCOPES = ['https://www.googleapis.com/auth/youtube']
python
def get_watch_history():
    """Get user's watch history (liked videos as proxy)."""
    service = get_authenticated_service()

    # Get liked videos
    request = service.videos().list(
        part="snippet,contentDetails",
        myRating="like",
        maxResults=50
    )
    response = request.execute()

    print("Your liked videos:\n")
    for item in response.get('items', []):
        title = item['snippet']['title']
        video_id = item['id']
        channel = item['snippet']['channelTitle']
        print(f"  • {title}")
        print(f"    ID: {video_id} | Channel: {channel}\n")

    return response.get('items', [])

def get_subscriptions(max_results: int = 50):
    """Get user's subscriptions."""
    service = get_authenticated_service()

    request = service.subscriptions().list(
        part="snippet",
        mine=True,
        maxResults=max_results
    )
    response = request.execute()

    print("Your subscriptions:\n")
    for item in response.get('items', []):
        title = item['snippet']['title']
        channel_id = item['snippet']['resourceId']['channelId']
        print(f"  • {title}")
        print(f"    Channel ID: {channel_id}\n")

    return response.get('items', [])
python
elif command == "liked":
    get_watch_history()

elif command == "subscriptions":
    get_subscriptions()

Technical Analysis

The Skill documentation presents playlist creation and management as its purpose, but the implementation also supports commands that enumerate the authenticated use ...[truncated 1636 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove the liked and subscriptions commands if they are not required for playlist management.
  • If these capabilities are intentional, document them clearly and obtain explicit user confirmation immediately before accessing private preference data.
  • Select the narrowest Google OAuth scope that supports the intended playlist operations.
  • Separate read-only account-inspection features from playlist-management authorization where practical, using distinct consent flows and tokens.
  • Avoid printing private account information by default; provide a deliberate output option and warn that output may be retained in logs or Agent context.
  • Revoke existing broadly scoped tokens and require reauthorization after reducing the requested scopes.
  • Keep the root and scripts/ implementations synchronized or remove the duplicate implementation to prevent security fixes from diverging.
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

A playlist utility should not need access to watch-history-like or subscription data unless that behavior is clearly disclosed and justified. Hidden or unnecessary access to broader YouTube account data increases privacy risk and can enable profiling of the user beyond the requested operation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

A playlist utility should not need access to watch-history-like or subscription data unless that behavior is clearly disclosed and justified. Hidden or unnecessary access to broader YouTube account data increases privacy risk and can enable profiling of the user beyond the requested operation.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/yt_playlist.py (reported line 24)May include surrounding context.

python
from googleapiclient.discovery import build

SKILL_DIR = Path(__file__).parent.parent
CREDENTIALS_FILE = SKILL_DIR / "credentials.json"
TOKEN_FILE = SKILL_DIR / "token.pickle"

SCOPES = ['https://www.googleapis.com/auth/youtube']

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · scripts/yt_playlist.py (reported line 42)May include surrounding context.

python
from googleapiclient.discovery import build

SKILL_DIR = Path(__file__).parent.parent
CREDENTIALS_FILE = SKILL_DIR / "credentials.json"
TOKEN_FILE = SKILL_DIR / "token.pickle"

SCOPES = ['https://www.googleapis.com/auth/youtube']

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · yt_playlist.py (reported line 24)May include surrounding context.

python
from googleapiclient.discovery import build

SKILL_DIR = Path(__file__).parent.parent
CREDENTIALS_FILE = SKILL_DIR / "credentials.json"
TOKEN_FILE = SKILL_DIR / "token.pickle"

SCOPES = ['https://www.googleapis.com/auth/youtube']

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · yt_playlist.py (reported line 42)May include surrounding context.

python
from googleapiclient.discovery import build

SKILL_DIR = Path(__file__).parent.parent
CREDENTIALS_FILE = SKILL_DIR / "credentials.json"
TOKEN_FILE = SKILL_DIR / "token.pickle"

SCOPES = ['https://www.googleapis.com/auth/youtube']

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill is presented as a playlist-management tool, but it also exposes commands to retrieve liked videos and subscriptions, which are broader categories of private account data. This scope expansion violates least privilege and increases the chance that users authorize or invoke access they did not expect.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Fetching liked videos and subscriptions is not necessary to create, list, add, or remove playlist items. Accessing unrelated account data broadens the attack surface and creates unnecessary privacy exposure if the skill is abused or misused.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill invokes a Python script that authenticates to YouTube and performs networked API actions, but the manifest does not declare any explicit tool scope or allowed tools. Missing capability declarations weaken reviewability and consent boundaries, making it easier for a skill to perform external actions without clear policy visibility.

Content

No source excerpt is available for this finding.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

The code deserializes OAuth credentials from token.pickle using pickle.load(), which can execute arbitrary code if that file is replaced or tampered with. In an agent/skill environment where files may be writable by other processes or users, this turns local file manipulation into code execution under the skill's privileges.

Content

Scanner excerpt · scripts/yt_playlist.py (reported line 35)May include surrounding context.

python
if TOKEN_FILE.exists():
        with open(TOKEN_FILE, 'rb') as token:
            creds = pickle.load(token)
    
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script persists OAuth tokens locally without informing the user that long-lived credentials are being written to disk. In shared or poorly secured environments, this can expose account access to other local users or processes and reduces informed consent around credential handling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The remove operation deletes a playlist item via the YouTube API immediately after lookup, which is a destructive action affecting user data. The code provides only a success message after deletion and does not prompt for confirmation or otherwise warn the user beforehand.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill metadata says it manages playlists, but the code also accesses liked videos and channel subscriptions, expanding data access beyond the declared purpose. This violates least privilege and increases privacy risk because a user invoking a playlist tool would not reasonably expect collection of broader account activity data.

Content

No source excerpt is available for this finding.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The code deserializes token.pickle with pickle.load, which can execute arbitrary code if that file is replaced or tampered with. In an agent/skill environment where local files may be writable or synced from untrusted sources, this turns token loading into a code-execution primitive in addition to exposing OAuth credentials.

Content

Scanner excerpt · yt_playlist.py (reported line 35)May include surrounding context.

python
if TOKEN_FILE.exists():
        with open(TOKEN_FILE, 'rb') as token:
            creds = pickle.load(token)
    
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The OAuth token is persisted to disk automatically without any explicit disclosure about storage location, lifetime, or sensitivity. That increases the chance of accidental credential exposure on shared systems or in copied skill directories, especially because the token grants access to the user's YouTube account.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

get_watch_history is misleadingly named and documented as retrieving watch history, but it actually fetches liked videos. Mislabeling account-data collection undermines informed consent and can cause operators or users to misunderstand what private data is being accessed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The commands for liked videos and subscriptions read private account data without clear user-facing disclosure in the advertised skill description. In a skill context, hidden or under-disclosed data access is dangerous because users may invoke what they think is playlist management while exposing broader personal account information.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The function get_watch_history() is documented as retrieving the user's watch history, and the docstring even labels liked videos as only a proxy. In reality, the API call uses myRating="like", which fetches liked videos, not watch history. This is an active intent/documentation mismatch that could mislead reviewers or users about what account data is accessed.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.