Back to skill

Security audit

teleskopiq

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Teleskopiq automation client, with disclosed OAuth/API-key access and remote content changes that match its stated purpose.

Install this only if you want an agent to read and modify Teleskopiq channel content. Prefer OAuth with the narrowest scope needed, use read-only scopes for review tasks, keep credential files private, and treat delete-script and direct graphql mutations as permanent remote actions requiring clear user intent.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (14)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Set `SKILL_DIR` to the absolute directory containing this `SKILL.md`. Use the actual installed location;

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/oauth.md (reported line 15)May include surrounding context.

md
The CLI keeps waiting for up to one hour. Access is issued only after setup and explicit approval.
`--scope 'scripts:read channel:read'` requests read-only access when that is sufficient for their task.

The CLI uses the device authorization grant. Access tokens expire after 15 minutes; refresh tokens rotate
and expire after 30 days without renewal. Connections require fresh approval after 90 days.
The private token file is `~/.config/teleskopiq/oauth.json`; `TELESKOPIQ_OAUTH_FILE` overrides it.
Tokens are bound to the exact GraphQL endpoint and selected channel. Local refresh operations are locked

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/oauth.md (reported line 28)May include surrounding context.

md
Once the server is deployed, add **https://teleskopiq.com/api/mcp** as a remote Streamable HTTP MCP server
in the user's AI app and choose OAuth authentication. The app discovers Teleskopiq's authorization server,
registers a public client, and uses authorization code with S256 PKCE. The user signs in and approves
a channel and permissions in their browser. Do not paste a GraphQL access token into MCP: the audiences differ.

Tools cover channel/style reads, script listing/search/creation/editing/deletion, comments, history/version
restoration, scheduling, metadata, thumbnail generation/references, topic ideas, and resumable AI conversations.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill requests and documents broad operational capabilities including environment access, file reads/writes, network access, and shell execution, but it does not declare any explicit tool scope or permission boundaries. Even though the described functionality legitimately needs some of these capabilities, the lack of a machine-readable least-privilege policy increases the risk that an agent could overuse available tools, access unrelated local files or secrets, or perform unintended network/shell actions during execution.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/oauth_login.py (reported line 22)May include surrounding context.

python
CLIENT_ID = 'teleskopiq-skill'
DEVICE_GRANT = 'urn:ietf:params:oauth:grant-type:device_code'
DEFAULT_SCOPES = 'scripts:read scripts:write thumbnails:write channel:read'


class OAuthError(RuntimeError):

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/teleskopiq.py (reported line 86)May include surrounding context.

python
requestKind: $kind, pacingTarget: $pacing, selection: $selection, selectionContext: $context
    ) { """ + EVENT_FIELDS + " } }",
}
DEFAULT_PROMPT = "Write a complete YouTube script for this video's title using the channel style and pacing. Use Markdown sections and relevant production directions."
STATUSES = ("Draft", "InProgress", "ReadyToShoot", "Recorded", "Editing", "Ready", "Published", "OnIce")

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/teleskopiq.py (reported line 147)May include surrounding context.

python
class Client:
    def __init__(self, key, endpoint="https://teleskopiq.com/api/graphql", timeout=300, oauth=None):
        self.oauth = oauth
        if oauth:
            if oauth.endpoint != endpoint:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The delete-script command issues a destructive remote delete immediately with no confirmation, dry-run, or force flag. In an agent skill context, where commands may be composed or triggered automatically, this increases the chance of accidental irreversible deletion of user content through prompt misunderstanding or operator error.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/teleskopiq.py (reported line 426)May include surrounding context.

python
if command == "full-flow":
        client.op("style")
        if not sid:
            sid = client.create(args.title, None if args.ai_write else content_arg(args))["id"]
        if args.ai_write:
            result = client.chat(sid, args.prompt or DEFAULT_PROMPT, args)
            if not (result["script"].get("content") or "").strip():

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
70% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · tests/test_oauth.py (reported line 54)May include surrounding context.

python
with self.assertRaises(OAuthError):
            self.save({**token(), 'resource': 'https://other.test/api/graphql'})

    def test_rejects_world_readable_credentials_and_symlinks(self):
        self.save()
        self.oauth.path.chmod(0o644)
        with self.assertRaises(OAuthError):

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The dependency specification allows any requests release from 2.32 up to but not including 3, which does not guarantee installation of a version free from known advisories. In a skill that performs authenticated API calls and browser OAuth, using an unpinned HTTP client increases the chance of pulling a vulnerable version that could expose credentials, mishandle TLS or redirects, or otherwise affect sensitive network interactions.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/teleskopiq.py (reported line 369)May include surrounding context.

python
raise APIError("--input-file must contain a JSON object")
        value["id"] = sid
        for field in ("title", "status"):
            if getattr(args, field, None) is not None:
                value[field] = getattr(args, field)
        content = content_arg(args)
        if content is not None:

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/teleskopiq.py (reported line 370)May include surrounding context.

python
value["id"] = sid
        for field in ("title", "status"):
            if getattr(args, field, None) is not None:
                value[field] = getattr(args, field)
        content = content_arg(args)
        if content is not None:
            value["content"] = content

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/teleskopiq.py (reported line 541)May include surrounding context.

python
raise APIError("--limit must be positive")
    # Read local inputs before creating any remote records.
    for field in ("input_file", "comments_file", "pacing_file", "files_json", "selection_file", "variables_file"):
        path = getattr(args, field, None)
        if path:
            value = json_file(path)
            expected = list if field in {"comments_file", "files_json"} else dict

Static analysis

Detected: suspicious.dynamic_code_execution, suspicious.potential_exfiltration

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_client.py:16

Python code base64-encodes a local file and sends it over the network.

Critical
Code
suspicious.potential_exfiltration
Location
scripts/teleskopiq.py:127