Back to skill

Security audit

Jellyseerr

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with Jellyseerr media requests, but its notification setup installs persistent services and exposes an unauthenticated webhook listener that needs review before use.

Install only if you are comfortable running a persistent Jellyseerr integration on this host. Prefer binding the webhook to localhost or a trusted interface behind HTTPS/authentication, restrict firewall access to the Jellyseerr server, inspect any generated systemd unit before enabling it, avoid the /tmp crontab method, and leave auto_approve disabled unless you intentionally want automatic media approvals.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/webhook_server.py:64
Finding
Unauthenticated and Unbounded Network Webhook Receiver<![CDATA[ ## Vulnerability Details **File Location**: `scripts/webhook_server.py`, lines 64-71 and 109-110 **Vulnerability Type**: Unauthenticated network service with unbounded request processing **Risk Level**: High ### Vulnerable Code ```python def do_POST(self): """Handle POST request from Jellyseerr.""" content_length = int(self.headers.get('Content-Length', 0)) body = self.rfile.read(content_length) try: data = json.loads(body) logger.info(f"Received webhook: {json.dumps(data, indent=2)}") ``` ```python def run_server(port=8384): """Run the webhook server.""" server = HTTPServer(('0.0.0.0', port), WebhookHandler) ``` ### Technical Analysis The webhook server binds to `0.0.0.0`, making it reachable through every available network interface. It does not authenticate webhook requests, verify a shared secret, validate the source address, or check a cryptographic signature. The supplied `Content-Length` is converted to an integer and used directly as the number of bytes to read. There is no maximum body size and no socket timeout. A client can therefore advertise a very large request or send the body extremely slowly, consuming memory or blocking the single-threaded `HTTPServer`. Attacker-controlled webhook content is also written in full to the systemd journal. For a `MEDIA_AVAILABLE` request, the attacker-controlled `subject` is placed into the notification queue and subsequently emitted by `scripts/send_notifications.py` as a `SEND_MESSAGE:` record. This permits forged availability notifications and can expose or propagate untrusted content into downstream notification processing. The queue file is repeatedly loaded, appended to, and rewritten without a queue-size limit. Repeated forged requests can consequently cause persistent cache and journal growth. ### Attack Path 1. The operator installs and starts the webhook service on TCP port 8384. 2. The service listens on all interfaces, and the port becomes ...[truncated 1316 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require a high-entropy webhook secret, preferably through an HMAC signature header verified with constant-time comparison. - Alternatively, place the receiver behind an authenticated reverse proxy and restrict direct access to the application port. - Bind to a specific trusted interface or `127.0.0.1` when remote network access is unnecessary. - Reject missing, negative, malformed, or excessive `Content-Length` values before reading the body. - Enforce a small request-body limit appropriate for Jellyseerr notifications, such as 16–64 KiB. - Configure socket read timeouts and reverse-proxy request timeouts. - Apply source-network firewall restrictions so only the Jellyseerr host can connect. - Validate the JSON object against a strict schema, including field types and maximum string lengths. - Rate-limit requests and cap the number and total size of queued notifications. - Avoid logging complete untrusted request bodies; log only validated event metadata. - Use atomic file replacement and restrictive permissions for the notification queue. - Consider `ThreadingHTTPServer` or a production webhook framework, while still enforcing concurrency and resource limits. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/setup_webhook.sh:7
Finding
Root-Owned systemd Unit Injection Through Unvalidated Setup Inputs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup_webhook.sh`, lines 7-31 **Vulnerability Type**: Privileged service configuration injection **Risk Level**: High ### Vulnerable Code ```bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SERVICE_NAME="jellyseerr-webhook" PORT="${1:-8384}" # Create systemd service file sudo tee /etc/systemd/system/${SERVICE_NAME}.service > /dev/null << EOF [Unit] Description=Jellyseerr Webhook Receiver After=network.target [Service] Type=simple User=$USER WorkingDirectory=$SCRIPT_DIR ExecStart=/usr/bin/python3 $SCRIPT_DIR/webhook_server.py $PORT Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF # Reload systemd and enable service sudo systemctl daemon-reload sudo systemctl enable ${SERVICE_NAME} sudo systemctl start ${SERVICE_NAME} ``` ### Technical Analysis The script writes a root-owned unit under `/etc/systemd/system` and immediately enables and starts it. The command-line `PORT` value, the environment-derived `USER` value, and the resolved script path are interpolated directly into the unit without validation or systemd escaping. Shell command substitution is not performed on text introduced by variable expansion inside the heredoc, so ordinary shell metacharacters in `PORT` do not directly execute commands during file creation. However, newline characters remain significant to the systemd unit parser. A newline-bearing port argument can inject additional service directives after the `ExecStart` line. The `User` directive is derived from the mutable `$USER` environment variable instead of a fixed, dedicated service account. A caller can therefore influence which account systemd uses. A crafted argument can also inject a later `User=root` directive. If the installed Skill directory is writable by the invoking user, a root service that executes `webhook_server.py` from that directory creates a privilege-escalation and persistent-execution path. Creating a system servi ...[truncated 1933 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate that the port consists only of decimal digits and is within `1` through `65535`. - Explicitly reject newline, carriage-return, NUL, and other control characters in every value written into the unit. - Do not derive the service account from `$USER`. Create or require a fixed, dedicated, unprivileged account such as `jellyseerr-webhook`. - Install executable code into a root-owned, non-user-writable directory before running it as a system service. - Generate the unit from a static template rather than interpolating untrusted arguments. - Use appropriate systemd escaping utilities when dynamic values are unavoidable. - Validate and canonicalize `SCRIPT_DIR`; reject unexpected paths. - Add systemd hardening directives, for example: ```ini NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ProtectKernelTunables=true ProtectControlGroups=true RestrictSUIDSGID=true ``` - Grant write access only to the specific cache directory required by the service, such as through `ReadWritePaths`. - Separate installation from activation and display the generated unit for explicit review before enabling it. - Provide a user-level systemd service where feasible, avoiding root-owned system persistence entirely. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:76
Finding
Predictable Temporary File Used to Replace the User Crontab<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 76-78 **Vulnerability Type**: Unsafe predictable temporary file and crontab replacement **Risk Level**: Medium ### Vulnerable Code ```bash crontab -l > /tmp/cron_backup.txt echo "* * * * * $(pwd)/scripts/auto_monitor.sh" >> /tmp/cron_backup.txt crontab /tmp/cron_backup.txt ``` ### Technical Analysis The documented polling setup uses the fixed, globally predictable path `/tmp/cron_backup.txt`. Files in `/tmp` are exposed to other local users and processes, and normal shell redirection follows symbolic links. On systems where protected-symlink controls do not prevent the operation, another local user can prepare the path as a symbolic link to a file writable by the victim. The first redirection then truncates and overwrites that target. There is also a time-of-check/time-of-use interval between writing the backup, appending the new entry, and importing the file with `crontab`. A local attacker able to replace or alter the temporary path during that interval can influence the crontab content that is installed. The instructions do not use `mktemp`, do not verify ownership or file type, do not set restrictive permissions, and do not remove the temporary file afterward. The fixed filename can also retain sensitive crontab contents in `/tmp`. The cron mechanism is explicitly documented and is functionally relevant to polling. It is not hidden persistence, but this implementation introduces unnecessary local-file and scheduled-command risks. ### Attack Path One possible exploitation sequence is: 1. A local attacker predicts that the victim will follow the documented polling setup. 2. The attacker creates `/tmp/cron_backup.txt` as a symlink to a file the victim can modify, on a system without effective protected-symlink enforcement. 3. The victim executes the first command, causing shell redirection to truncate and overwrite the symlink target with the victim's current crontab. 4. The subse ...[truncated 1072 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Avoid a temporary file and update the crontab through a pipeline, while preventing duplicate entries: ```bash SCRIPT_PATH="$(pwd)/scripts/auto_monitor.sh" ( crontab -l 2>/dev/null | grep -Fv -- "$SCRIPT_PATH" printf '%s\n' "* * * * * $SCRIPT_PATH" ) | crontab - ``` Additional hardening should include: - Resolve and safely quote the absolute script path. - Validate that the script is owned by the expected user and is not writable by untrusted users. - Prefer a user-level systemd timer with explicit installation and uninstall instructions. - If a temporary file is unavoidable, create it with `mktemp`, apply mode `600`, verify ownership and file type, and remove it through an `EXIT` trap. - Do not retain copies of the user's crontab under a globally shared temporary directory. - Document how to remove only the exact installed entry instead of broadly filtering unrelated cron jobs. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (64)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The documented capability extends beyond Jellyseerr request/search operations to running an HTTP receiver, processing inbound events, and preparing outbound notifications using additional configuration data. This broader integration increases data-flow and exposure risks and is insufficiently disclosed by the current description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented capability extends beyond Jellyseerr request/search operations to running an HTTP receiver, processing inbound events, and preparing outbound notifications using additional configuration data. This broader integration increases data-flow and exposure risks and is insufficiently disclosed by the current description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented capability extends beyond Jellyseerr request/search operations to running an HTTP receiver, processing inbound events, and preparing outbound notifications using additional configuration data. This broader integration increases data-flow and exposure risks and is insufficiently disclosed by the current description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented capability extends beyond Jellyseerr request/search operations to running an HTTP receiver, processing inbound events, and preparing outbound notifications using additional configuration data. This broader integration increases data-flow and exposure risks and is insufficiently disclosed by the current description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented capability extends beyond Jellyseerr request/search operations to running an HTTP receiver, processing inbound events, and preparing outbound notifications using additional configuration data. This broader integration increases data-flow and exposure risks and is insufficiently disclosed by the current description.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```bash
sudo systemctl stop jellyseerr-webhook
sudo systemctl disable jellyseerr-webhook
sudo rm /etc/systemd/system/jellyseerr-webhook.service
sudo systemctl daemon-reload
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
#!/bin/bash
# Setup webhook server as a systemd service

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVICE_NAME="jellyseerr-webhook"
PORT="${1:-8384}"

# Create systemd service file
sudo tee /etc/systemd/system/${SERVICE_NAME}.service > /dev/null << EOF
[Unit]
Description=Jellyseerr Webhook Receiver
After=network.target

[Service]
Type=simple
User=$USER
WorkingDirectory=$SCRIPT_DIR
ExecStart=/usr/bin/python3 $SCRIPT_DIR/webhook_server.py $PORT
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

# Reload systemd and enable service
sudo systemctl daemon-reload
sudo systemctl enable ${SERVICE_NAME}
sudo systemctl start ${SERVICE_NAME}

echo "✓ Webhook server installed and started on port $PORT"
echo ""
echo "Next steps:"
echo "1. Get your server's IP address: hostname -I"
echo "2. In Jellyseerr, go to Settings → Notifications �
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Obfuscated Code

High
Category
Supply Chain
Content
echo "4. Set Webhook URL to: http://YOUR_IP:$PORT/"
echo "5. In JSON Payload, paste the following (base64 encoded):"
echo ""
echo 'eyJub3RpZmljYXRpb25fdHlwZSI6Int7bm90aWZpY2F0aW9uX3R5cGV9fSIsInN1YmplY3QiOiJ7e3N1YmplY3R9fSIsIm1lc3NhZ2UiOiJ7e21lc3NhZ2V9fSIsIm1lZGlhX3R5cGUiOiJ7e21lZGlhX3R5cGV9fSIsIm1lZGlhX3RtZGJpZCI6Int7bWVkaWFfdG1kYmlkfX0ifQ=='
echo ""
echo "6. Enable notification type: Media Available"
echo "7. Test the webhook and Save Changes"
Confidence
50% confidence
Finding
Code contains obfuscation (base64, hex encoding with execution). This is often used to hide malicious functionality.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises executable scripts and host-modifying setup steps but declares no explicit tool scope or permission boundaries. In an agent ecosystem, this creates hidden capabilities: shell, network, environment access, and file writes may be invoked without the skill manifest clearly warning reviewers or policy engines.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The webhook setup tells the user to run an installation script with sudo without clearly describing the privileged changes being made. Running undocumented root-level installation increases the risk of unintended system modification and reduces informed consent.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
Quick setup:
```bash
scripts/install_service.sh  # Run with sudo
```

Then configure Jellyseerr to send webhooks to `http://YOUR_IP:8384/`
Confidence
93% confidence
Finding
Instructing users to run a script with sudo introduces root-level execution, which is inherently high trust and can affect the entire host. Without transparent disclosure of script contents and effects, this creates an avoidable security risk.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The polling setup appends a cron job into the user's crontab, creating persistence, but the instructions do not clearly warn that this is a lasting system change. Users may believe they are running a one-time command when they are actually scheduling recurring execution every minute.

Session Persistence

Medium
Category
Rogue Agent
Content
For environments where webhooks aren't available, use cron-based polling:

```bash
crontab -l > /tmp/cron_backup.txt
echo "* * * * * $(pwd)/scripts/auto_monitor.sh" >> /tmp/cron_backup.txt
crontab /tmp/cron_backup.txt
```
Confidence
95% confidence
Finding
The cron instructions establish recurring execution, creating session persistence beyond the immediate user action. Persistence is not inherently malicious, but when introduced without strong disclosure and lifecycle management it can lead to unnoticed background activity and long-term exposure.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
{
  "server_url": "https://jellyseerr.yourdomain.com",
  "api_key": "your-api-key",
  "auto_approve": true
}
```
Confidence
85% confidence
Finding
The example configuration enables auto_approve, which can cause requests to be approved automatically without a human review step. In a media-request workflow this may allow unauthorized or unintended content acquisition if the account or agent is misused.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The guide instructs users to configure a webhook endpoint over plain HTTP using the server IP, which exposes webhook payloads to interception or modification by any attacker on the local network path. Even if the payload is not highly sensitive, unauthenticated and unencrypted transport can enable spoofed notifications, event tampering, and disclosure of media-request metadata.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Check Service Status

```bash
sudo systemctl status jellyseerr-webhook
```

### View Live Logs
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.