Back to skill

Security audit

YouTube Shorts Automation

Security checks for vulnerabilities and agentic risk

Overview

This skill matches its YouTube Shorts automation purpose, but it has a real privacy-control flaw that can publish uploads publicly even when users request private or unlisted visibility.

Review before installing. Use this only with a YouTube account and content you are willing to publish, fix or remove the broken --privacy option before relying on private/unlisted uploads, keep OAuth files out of shared folders or source control, and do not enable scheduled runs until publication visibility and target channel confirmation are explicit.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/youtube_upload.py:53
Finding

The privacy option is ignored and all videos are uploaded publicly

Content
View full analysis

Vulnerability Details

File Location: scripts/youtube_upload.py:53-65, scripts/youtube_upload.py:88, and scripts/youtube_upload.py:98
Vulnerability Type: Privacy-control failure caused by an ignored security parameter
Risk Level: High

Vulnerable Code

python
def upload_video(youtube, file_path, title, description, tags=None, category="22"):
    body = {
        "snippet": {
            "title": title,
            "description": description,
            "tags": tags or [],
            "categoryId": category,
        },
        "status": {
            "privacyStatus": "public",
            "selfDeclaredMadeForKids": False,
        },
    }
python
parser.add_argument(
    "--privacy",
    default="public",
    choices=["public", "private", "unlisted"]
)
python
video_id = upload_video(
    youtube, args.file, args.title, args.description, tags
)

Technical Analysis

The command-line interface accepts public, private, and unlisted as valid privacy settings. However, args.privacy is never passed to upload_video(). Instead, the YouTube API request unconditionally assigns the literal value public to status.privacyStatus.

This creates a fail-open privacy control. The script appears to provide a confidentiality option, and the documentation states that --privacy changes upload visibility, but the effective behavior is always public. No warning or error informs the operator that the requested privacy setting was discarded.

Attack Path

  1. An operator prepares confidential, embargoed, internal, or pre-release video content.
  2. The operator invokes the script with --privacy private or --privacy unlisted.
  3. The argument parser accepts the requested value, making the invocation appear valid.
  4. The main function does not pass args.privacy to upload_video().
  5. The upload request uses the hard-coded value `"privacySt ...[truncated 880 chars]
Remediation
View remediation

Remediation Suggestions

Add an explicit privacy parameter to upload_video() and pass the validated command-line value into the API request:

python
def upload_video(
    youtube,
    file_path,
    title,
    description,
    tags=None,
    category="22",
    privacy="private",
):
    allowed_privacy = {"public", "private", "unlisted"}
    if privacy not in allowed_privacy:
        raise ValueError(f"Unsupported privacy setting: {privacy}")

    body = {
        "snippet": {
            "title": title,
            "description": description,
            "tags": tags or [],
            "categoryId": category,
        },
        "status": {
            "privacyStatus": privacy,
            "selfDeclaredMadeForKids": False,
        },
    }

Pass the value from main():

python
video_id = upload_video(
    youtube,
    args.file,
    args.title,
    args.description,
    tags,
    privacy=args.privacy,
)

Use private as the default if a fail-safe publication policy is appropriate. Add automated tests that inspect the generated API body for all three supported values. The script should also print the selected visibility before initiating the upload and require explicit confirmation for public publication in interactive use.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/youtube_upload.py:48
Finding

YouTube OAuth credentials are written without explicitly restrictive file permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/youtube_upload.py:26 and scripts/youtube_upload.py:48
Vulnerability Type: Insecure local storage of OAuth credentials
Risk Level: Medium

Vulnerable Code

python
TOKEN_FILE = SCRIPT_DIR / "token.json"
python
if not creds or not creds.valid:
    if creds and creds.expired and creds.refresh_token:
        creds.refresh(Request())
    else:
        if not CLIENT_SECRET_FILE.exists():
            print(f"ERROR: {CLIENT_SECRET_FILE} not found!")
            print("Google Cloud Console에서 OAuth 클라이언트 ID를 생성하고")
            print(f"JSON 파일을 {CLIENT_SECRET_FILE}에 저장하세요.")
            sys.exit(1)
        flow = InstalledAppFlow.from_client_secrets_file(
            str(CLIENT_SECRET_FILE), SCOPES
        )
        creds = flow.run_local_server(port=8090, open_browser=True)

    TOKEN_FILE.write_text(creds.to_json())

Technical Analysis

Path.write_text() writes the serialized OAuth credentials using process-default creation permissions and the current umask. The code does not explicitly require an owner-only mode such as 0600, does not verify the resulting permissions, and stores the token directly inside the Skill's script directory.

On a system with a permissive umask, shared project directory, backup process, container volume, or other local users, the resulting token.json may be readable by principals other than the account that completed OAuth authorization. The file may contain an access token and a long-lived refresh token.

The configured OAuth scope is:

python
SCOPES = ["https://www.googleapis.com/auth/youtube.upload"]

Consequently, disclosure of the credential may allow another local process or user to act through the YouTube API within the granted upload scope.

Attack Path

  1. A user runs the uploader and completes Google OAuth authorization.
  2. The script serializes the credentials into ...[truncated 1370 chars]
Remediation
View remediation

Remediation Suggestions

Store OAuth credentials outside the distributable project directory, preferably in an operating-system credential store or a user-specific configuration directory with restrictive permissions.

If a file must be used, create it atomically with owner-only permissions. For example:

python
import os
import tempfile

def securely_write_token(token_file: Path, token_data: str) -> None:
    token_file.parent.mkdir(mode=0o700, parents=True, exist_ok=True)

    fd, temporary_name = tempfile.mkstemp(
        dir=token_file.parent,
        prefix=".token-",
        text=True,
    )
    try:
        os.fchmod(fd, 0o600)
        with os.fdopen(fd, "w", encoding="utf-8") as handle:
            handle.write(token_data)
            handle.flush()
            os.fsync(handle.fileno())

        os.replace(temporary_name, token_file)
        os.chmod(token_file, 0o600)
    except Exception:
        try:
            os.unlink(temporary_name)
        except FileNotFoundError:
            pass
        raise

Replace the direct write with:

python
securely_write_token(TOKEN_FILE, creds.to_json())

Also implement the following controls:

  • Place token.json and client_secret.json in a user-owned configuration directory rather than scripts/.
  • Add both files to version-control and packaging exclusion rules.
  • Verify ownership and permissions before loading an existing token.
  • Refuse to load a token that is group-readable, world-readable, or owned by another user.
  • Revoke and regenerate any token that may already have been exposed.
  • Use the minimum OAuth scope required and periodically review authorized applications.
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented behavior promises automated generation, upload, and scheduling, but the analyzed content does not implement or substantiate those controls and reportedly always uploads publicly despite exposing a privacy argument. This mismatch is dangerous because users may rely on claimed safeguards or workflow behavior that do not actually exist, leading to accidental public posting or over-trusting incomplete automation.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The documentation explicitly relies on OAuth client secrets and access tokens for a YouTube account, which are sensitive credentials capable of enabling channel actions if exposed. In a skill centered on automated uploads and scheduled runs, compromised tokens could permit unauthorized publication, account abuse, or persistent access until revoked.

Content

Scanner excerpt · SKILL.md (reported line 59)May include surrounding context.

--description "설명"
--tags "tag1,tag2"

text
- 사전 설정: OAuth `client_secret.json` + `token.json` 필요
- 상세: [references/youtube-upload.md](references/youtube-upload.md)

### 4. 크론잡 등록 (OpenClaw)

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 · references/youtube-upload.md (reported line 5)May include surrounding context.

md
## 사전 요구사항
- Google Cloud Console에서 OAuth 클라이언트 생성 (YouTube Data API v3 활성화)
- `client_secret.json` 다운로드
- 첫 실행 시 브라우저 OAuth 인증 → `token.json` 생성됨
- Python 패키지: `google-api-python-client`, `google-auth-oauthlib`

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/youtube_upload.py (reported line 25)May include surrounding context.

python
## 사전 요구사항
- Google Cloud Console에서 OAuth 클라이언트 생성 (YouTube Data API v3 활성화)
- `client_secret.json` 다운로드
- 첫 실행 시 브라우저 OAuth 인증 → `token.json` 생성됨
- Python 패키지: `google-api-python-client`, `google-auth-oauthlib`

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documents capabilities that require network access and likely file writes, but it does not declare any explicit tool scope or permission boundaries. In an agent ecosystem, missing scope declarations increases the chance the skill is invoked with broader-than-necessary privileges, enabling unintended uploads, writes, or external communications.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description says to use the skill when 'generating short-form vertical videos' or 'creating AI-generated video content,' which are broad phrases not tightly constrained to this specific YouTube Shorts automation workflow. Without clearer boundaries or negative examples, the skill could be invoked for general video-generation requests beyond its intended scope.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation encourages automatic uploads and cron-based scheduled execution without warning about privacy, account, and publishing consequences. In the context of a skill that can post to YouTube, omission of these warnings makes accidental public distribution and unattended account actions materially more likely.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The natural-language values for the channel name, title template, and description template are fixed to Korean, which can violate language/locale policy when a skill forces one locale without offering a choice. Nothing in this file indicates that Korean is optional, user-selected, or justified as a region-specific configuration.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The workflow explicitly instructs users to extract a direct public CDN URL for the generated video and download it, but provides no warning that such links may be publicly accessible or easily shared. In a content automation pipeline, this can lead to unintentional exposure of unpublished or sensitive media assets before intended release.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script's natural-language documentation and console messages are written only in Korean, which forces a specific language/locale without user opt-in. This can violate language policy expectations when a skill is intended for broader use and does not document a justified locale restriction.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The CLI advertises a --privacy option with values public/private/unlisted, but the upload request hard-codes privacyStatus to "public" and never uses args.privacy. In an automation pipeline for daily YouTube uploads, this can cause unintended public disclosure of videos a user believed would be private or unlisted, making it a real security/privacy issue rather than a usability bug.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The required field description specifies that deevid_prompt must be in English, which is a language constraint stated as a requirement rather than a user option. The file does not explain why English is required or offer alternative language handling, making it a natural-language policy concern.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill instructs users to provide OAuth client_secret.json and token.json but does not include guidance on securely storing, rotating, or limiting those credentials. While this is documentation rather than active exfiltration, it normalizes handling high-value account tokens without safeguards, increasing the risk of leakage or reuse.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This JSON manifest/config is in scope for SQP-1, and the content fields define a Korean-language channel and templates without any indication of scope, locale constraints, or alternative variants. In a reusable skill configuration, that lack of specificity can make the intended trigger/context ambiguous, especially if the skill is not clearly limited to Korean-only workflows elsewhere in the file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown file contains explicit file-download commands that save media to local disk, but the surrounding instructions do not disclose that files will be written locally or remind users to verify the destination and sensitivity of the content. Under the markdown-specific warning criteria, user-visible guidance about data-handling effects is expected when instructions affect local user data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

This code stores reusable OAuth credentials in token.json, which is a sensitive local file write involving authentication data. Although the module docstring mentions that the token is saved, there is no runtime disclosure or warning near the write operation itself, and users may not realize credentials are being persisted on disk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.