Back to skill

Security audit

Stremio Unwatched

Security checks for vulnerabilities and agentic risk

Overview

Review recommended: the skill is mostly coherent for Stremio episode management, but the default download command can start Stremio or torrent downloads without the advertised dry-run or confirmation.

Install only if you are comfortable granting the skill access to your Stremio account, local credential cache, installed Stremio addons, local Stremio/torrent clients, and optional Google Calendar mutation. Run download commands with --dry-run or --magnets first, avoid no-argument stremio_download.sh until the default is fixed, and treat printed auth keys as secrets.

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 (4)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/stremio_download.sh:24
Finding
Download command performs immediate side effects despite documented dry-run and interactive behavior<![CDATA[ ## Vulnerability Details **File Location**: `README.md:11`, `README.md:80-82`, `SKILL.md:103`, `scripts/stremio_download.sh:24-26`, `scripts/stremio_download.sh:230-263` **Vulnerability Type**: Unsafe default behavior and misleading security documentation **Risk Level**: Medium ### Vulnerable Code The documentation claims that downloads are previewed or interactive: ```markdown - **Dry-run by default** — always preview before downloading ``` ```markdown scripts/stremio_download.sh # download all unwatched scripts/stremio_download.sh --dry-run # preview only ``` The Skill documentation similarly describes the default command as interactive: ```markdown scripts/stremio_download.sh # All unwatched (interactive) ``` The implementation instead disables dry-run by default: ```bash QUALITY="any" CLIENT="" DRY_RUN=false MAGNETS_ONLY=false ``` It subsequently queues downloads without any confirmation: ```bash if $DRY_RUN; then if [[ -n "$best_hash" ]]; then echo " [dry-run] Would download: magnet:?xt=urn:btih:${best_hash}" >&2 else echo " [dry-run] Would download: ${best_url}" >&2 fi ((downloaded++)) || true continue fi if $MAGNETS_ONLY; then if [[ -n "$best_hash" ]]; then echo "magnet:?xt=urn:btih:${best_hash}&dn=$(echo "$label" | sed 's/ /%20/g')" else echo "$best_url" fi ((downloaded++)) || true continue fi # Download if [[ -n "$best_hash" ]]; then if $use_stremio; then download_via_stremio "$best_hash" "${file_idx:-0}" && ((downloaded++)) || ((failed++)) elif [[ -n "$torrent_client" ]]; then magnet="magnet:?xt=urn:btih:${best_hash}&dn=$(echo "$label" | sed 's/ /%20/g')" download_via_client "$torrent_client" "$magnet" && ((downloaded++)) || ((failed++)) fi elif [[ -n "$best_url" ]]; then echo " Direct URL: ${best_url}" >&2 echo " (Direct URL downloads not yet supported, use --magnets)" >&2 ((failed++)) || true fi ``` ### Tech ...[truncated 1842 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Initialize `DRY_RUN=true` and require an explicit side-effect option such as `--download` or `--execute`. 2. For interactive terminals, display the number of episodes, selected client, destination directory, and torrent identifiers before asking for confirmation. 3. For non-interactive execution, require an explicit `--yes` flag rather than assuming consent. 4. Apply a conservative default limit to prevent accidental bulk downloads. 5. Update `README.md` and `SKILL.md` so their descriptions exactly match enforced behavior. 6. Add automated tests confirming that the no-argument invocation cannot call any downloader. 7. Consider requiring explicit addon or stream selection instead of automatically accepting the first matching stream. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/stremio_download.sh:136
Finding
Unvalidated Stremio addon transport URLs enable server-side request forgery from the Agent host<![CDATA[ ## Vulnerability Details **File Location**: `scripts/stremio_download.sh:136-145`, `scripts/stremio_download.sh:185-191` **Vulnerability Type**: Server-side request forgery through untrusted addon URLs **Risk Level**: Medium ### Vulnerable Code The script extracts addon-provided transport URLs without validating their schemes or destinations: ```bash stream_addons=$(echo "$addons_resp" | jq -r ' [(.result.addons // .result // [])[] | select( (.manifest.resources // [] | map(if type == "string" then . else .name end) | index("stream")) and (.manifest.types // [] | index("series")) ) | .transportUrl ] | .[]' 2>/dev/null) if [[ -z "$stream_addons" ]]; then echo "warning: no stream addons found, using default sources" >&2 fi ``` Each extracted value is then passed to `curl`, with redirects enabled: ```bash for addon_url in $stream_addons; do # Strip manifest.json to get base URL addon_base="${addon_url%/manifest.json}" addon_base="${addon_base%/}" streams=$(curl -sfL "${addon_base}/stream/series/${video_id}.json" 2>/dev/null) || continue stream_count=$(echo "$streams" | jq '[.streams // [] | .[]] | length') [[ "$stream_count" -eq 0 ]] && continue ``` ### Technical Analysis `transportUrl` originates from the user’s installed addon collection. An addon may be malicious, compromised, or configured with an attacker-controlled URL. The code makes requests to that value without enforcing HTTPS, validating the hostname, checking the resolved IP address, or validating redirect targets. Because `curl -L` follows redirects, an initially acceptable-looking public URL can redirect to a loopback, private-network, link-local, or cloud metadata address. Depending on the local curl build and URL format, non-HTTP protocols may also be accepted. The URL list is additionally consumed through shell word splitting: ```bash for addon_url in $stream_addons; do ``` This makes handling of whitespace or unusual URL data unreliable, ...[truncated 1672 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse addon records as discrete JSON objects rather than a whitespace-split string. 2. Permit only explicitly supported schemes, preferably HTTPS. 3. Reject URLs containing embedded credentials, ambiguous host syntax, control characters, or unexpected ports. 4. Resolve destination hostnames and reject loopback, private, link-local, multicast, reserved, and unspecified address ranges for both IPv4 and IPv6. 5. Disable redirects or validate every redirect destination before following it. 6. Maintain an allowlist of user-approved addon origins and require confirmation before contacting a new origin. 7. Apply connection, transfer, and response-size limits to all addon requests. 8. Log the destination origin clearly without exposing credentials or sensitive query parameters. 9. Consider isolating addon requests in a restricted network sandbox without access to local or private network ranges. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/stremio_download.sh:209
Finding
Addon-controlled torrent hashes are submitted to local services and clients without validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/stremio_download.sh:209-225`, `scripts/stremio_download.sh:249-263` **Vulnerability Type**: Insufficient validation across a remote-to-local trust boundary **Risk Level**: Low ### Vulnerable Code The script accepts any nonempty addon-provided `infoHash`: ```bash hash=$(echo "$selected" | jq -r '.infoHash // ""') url=$(echo "$selected" | jq -r '.url // ""') file_idx=$(echo "$selected" | jq -r '.fileIdx // 0') desc=$(echo "$selected" | jq -r '(.name // "") + " " + (.description // "")' | head -c 80) if [[ -n "$hash" && "$hash" != "null" ]]; then best_hash="$hash" best_stream="$desc" break elif [[ -n "$url" && "$url" != "null" ]]; then best_url="$url" best_stream="$desc" break fi ``` The value is subsequently embedded in a local HTTP path or magnet URI: ```bash # Download if [[ -n "$best_hash" ]]; then if $use_stremio; then download_via_stremio "$best_hash" "${file_idx:-0}" && ((downloaded++)) || ((failed++)) elif [[ -n "$torrent_client" ]]; then magnet="magnet:?xt=urn:btih:${best_hash}&dn=$(echo "$label" | sed 's/ /%20/g')" download_via_client "$torrent_client" "$magnet" && ((downloaded++)) || ((failed++)) fi elif [[ -n "$best_url" ]]; then echo " Direct URL: ${best_url}" >&2 echo " (Direct URL downloads not yet supported, use --magnets)" >&2 ((failed++)) || true fi ``` The local Stremio request is constructed as follows: ```bash download_via_stremio() { local info_hash="$1" file_idx="${2:-0}" curl -sf -X POST "${STREMIO_SERVER}/${info_hash}/create" \ -H "Content-Type: application/json" \ -d '{}' &>/dev/null echo " Queued in Stremio (${info_hash:0:12}...)" >&2 } ``` ### Technical Analysis The stream addon is a remote trust boundary, but its `infoHash` value is only checked for being nonempty. A canonical BitTorrent v1 hash normally has a constrained representation, such as 40 hexadecimal characters. The implementation does not enforce a sup ...[truncated 1849 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `infoHash` against explicitly supported canonical formats before use. For BitTorrent v1 hexadecimal hashes, enforce `^[A-Fa-f0-9]{40}$`. 2. If base32 or BitTorrent v2 hashes are required, define and validate each format separately rather than accepting arbitrary strings. 3. Validate `fileIdx` as a nonnegative integer within a reasonable range. 4. URL-encode all values used as HTTP path components. 5. Construct magnet URIs with a standards-compliant URI encoder. 6. Reject stream records containing conflicting or unexpected source fields. 7. Require confirmation before submitting a torrent obtained from an untrusted or newly configured addon. 8. Add negative tests covering slashes, query delimiters, control characters, oversized values, and command-line option-like strings. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/bitfield_decode.mjs:21
Finding
Watched bitfield decoder performs unbounded synchronous decompression<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bitfield_decode.mjs:21-28`, invoked from `scripts/stremio_unwatched.sh:104-113` **Vulnerability Type**: Compression bomb and local resource exhaustion **Risk Level**: Low ### Vulnerable Code The decoder inflates the complete input synchronously without an output limit: ```javascript const base64Data = parts.pop(); const anchorLength = parseInt(parts.pop(), 10); const anchorVideoId = parts.join(":"); let rawBytes; try { rawBytes = inflateSync(Buffer.from(base64Data, "base64")); } catch { return Object.fromEntries(videoIds.map((id) => [id, false])); } ``` Account-derived data is passed directly to the decoder: ```bash watched="{}" if [[ -n "$bitfield" && "$bitfield" != "null" ]]; then # For bitfield decoding, we need ALL video IDs (not just filtered season) # since bitfield positions are based on the full sorted list all_videos=$(echo "$meta" | jq ' [.meta.videos // [] | .[] | select(.season and .episode and (.season | type) == "number" and (.episode | type) == "number") ] | sort_by(.season, .episode, .released) | [.[].id]') watched=$("$NODE_CMD" "${SCRIPT_DIR}/bitfield_decode.mjs" "$bitfield" "$all_videos" 2>/dev/null) || watched="{}" fi ``` ### Technical Analysis The watched bitfield is retrieved from Stremio account data and contains base64-encoded zlib-compressed bytes. `inflateSync` blocks the Node.js process until decompression completes and does not receive an application-level output limit in this implementation. A small, highly compressible payload can expand into a much larger buffer. The code also does not reject an oversized encoded input before allocating the base64 buffer. Although decompression errors are caught, memory and CPU exhaustion may occur before an exception can be handled. The expected output size is naturally bounded by the number of episode identifiers, making unrestricted decompression unnecessary. ### Attack Path 1. An attacker gains t ...[truncated 1189 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Reject serialized and base64 inputs above a conservative maximum size before decoding. 2. Configure a maximum decompressed output length where supported by the Node.js runtime. 3. Derive the maximum legitimate bitfield size from `videoIds.length`, allowing only a small protocol overhead. 4. Validate that `videoIds` is an array of strings and impose a maximum item count and identifier length. 5. Validate that `anchorLength` is a finite, nonnegative integer within an expected range. 6. Prefer bounded asynchronous or streaming decompression if larger payloads must be supported. 7. Run the decoder with explicit process memory and execution-time limits when invoked by the shell. 8. Add tests using malformed zlib data, oversized base64 strings, and highly compressible payloads. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (47)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The description presents a feature-rich Stremio episode management and download/sync skill, but the supplied code chunk is narrowly scoped to authentication. It interacts only with Stremio login/getUser/logout endpoints, caches auth credentials to disk, and exposes the authKey. None of the headline capabilities in the description—library inspection, episode calendar retrieval, download orchestration, torrent client integration, or Google Calendar sync—are implemented in this code. While authentication could be a supporting component of such a skill, this chunk by itself materially differs from the declared purpose and includes credential storage/handling behavior not mentioned in the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code chunk is narrowly focused on upcoming-episode calendar retrieval and Google Calendar synchronization. It uses Cinemeta and a Stremio library helper to list series and show future air dates, and it can create or clear events in a dedicated Google Calendar. However, there is no logic for identifying unwatched episodes, no interaction with playback/watch-state data, no download initiation, and no integration with any torrent client or Stremio server download mechanism. The description therefore overstates the skill's implemented capabilities relative to this code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description promises a broader workflow: identifying unwatched episodes, viewing upcoming air dates, downloading pending episodes, and syncing calendars. This code chunk only retrieves Stremio library items from the central API and returns selected metadata such as watched bitfield and last watched state. While that metadata could support a higher-level unwatched-episode feature elsewhere, this script itself does not implement episode detection, release calendar access, downloading, or Google Calendar integration. Therefore the description materially overstates what this supplied code chunk actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description promises a broad Stremio episode-management tool covering library analysis, release calendar viewing, episode downloading, and Google Calendar sync, with integration to Stremio central API, Cinemeta, and multiple torrent clients. The actual code only checks the status of downloads from a local Stremio streaming server and optionally shows basic fallback information for Transmission or aria2 when the server is unavailable. Its primary purpose is materially narrower and different: monitoring download progress, not discovering unwatched episodes, showing release schedules, initiating downloads, or syncing calendars. This is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code chunk is narrowly focused on identifying unwatched episodes in a Stremio library. It invokes a library-fetching helper, calls the Cinemeta API for series metadata, decodes the watched bitfield, filters for aired and unwatched episodes, and prints results. This aligns with only part (1) of the description. There is no code here for showing upcoming air dates as a calendar view, no handling of future episodes beyond excluding unaired entries, no download logic, no communication with Stremio server download endpoints or external torrent clients, and no Google Calendar synchronization. Because the declared description presents a broader multi-capability skill than what this code chunk actually implements, the description materially overstates the behavior.

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_calendar.sh # Next 30 days
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
scripts/stremio_download.sh # All unwatched (interactive)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises shell-based scripts and installation/usage commands but does not declare an explicit tool scope such as permissions or allowed-tools. In an agent environment, undeclared shell capability increases the chance that the skill can invoke local commands, network utilities, and external binaries without the platform applying least-privilege controls.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The documentation exposes commands that can trigger downloads through Stremio or multiple torrent clients, but it does not clearly warn that running them may start network transfers, consume bandwidth/storage, or interact with existing local download daemons. In an agent-assisted setting, this can lead to unintended side effects on the host and may cause legal, operational, or privacy issues depending on what content is fetched.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This markdown file documents login with email/password and repeated use of `authKey`, which are sensitive credentials, but it provides no warning about protecting tokens, avoiding shell history leakage, or treating these requests as sensitive. Under the markdown-file criteria, the description should warn about privacy and account-impacting behavior when demonstrating credentialed API usage.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Login
curl -X POST https://api.strem.io/api/login \
  -H "Content-Type: application/json" \
  -d '{"type":"Login","email":"...","password":"...","facebook":false}'
# Response: {"result":{"authKey":"...","user":{...}}}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Get installed addons
curl -X POST https://api.strem.io/api/addonCollectionGet \
  -H "Content-Type: application/json" \
  -d '{"authKey":"..."}'
```
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script explicitly provides a --key mode and default/login paths that echo the authKey to stdout, making accidental secret disclosure likely through terminal scrollback, command substitution, logs, or pipeline capture by other tools. In an agent/automation skill context, stdout is commonly collected or forwarded, which increases the chance that long-lived authentication material is exposed outside the user's intent.

Static analysis

No suspicious patterns detected.