Ae1
- Category
- analysis-evasion
- Confidence
- 100% confidence
- Finding
Referenced artifact was not completely inspected
- Content
md Set `SKILL_DIR` to the absolute directory containing this `SKILL.md`. Use the actual installed location;
Security audit
Security checks for vulnerabilities and agentic risk
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.
Referenced artifact was not completely inspected
Set `SKILL_DIR` to the absolute directory containing this `SKILL.md`. Use the actual installed location;
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
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
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
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.
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.
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.
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):
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.
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")
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.
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:
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.
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.
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():
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.
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):
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.
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.
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 getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.
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 getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.
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
Detected: suspicious.dynamic_code_execution, suspicious.potential_exfiltration