T09 · Insecure Skill Coding Practices
- Location
tv.js:209- Finding
OS Command Injection Through ADB Device and Application Parameters
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill mostly does what it says, but its Android TV ADB path can turn user-controlled input into host shell commands and its examples send sensitive tokens over plaintext HTTP.
Review before installing. Use HTTPS or a trusted encrypted tunnel for Jellyfin and Home Assistant, use least-privileged dedicated API keys/tokens, avoid admin Jellyfin credentials unless you need history or scan, and do not use the direct ADB backend until ADB_DEVICE and app IDs are validated or ADB execution is changed to argument-based process calls.
tv.js:209OS Command Injection Through ADB Device and Application Parameters
jellyfin.js:23Sensitive Jellyfin and Home Assistant Credentials Permitted Over Plaintext HTTP
The description emphasizes playback and TV control, but the document also advertises history retrieval, library statistics, and library scan operations. Undeclared or under-declared administrative behaviors are dangerous because users may authorize the skill for simple media control without realizing it can access activity data or trigger state-changing server operations.
The description emphasizes playback and TV control, but the document also advertises history retrieval, library statistics, and library scan operations. Undeclared or under-declared administrative behaviors are dangerous because users may authorize the skill for simple media control without realizing it can access activity data or trigger state-changing server operations.
The description emphasizes playback and TV control, but the document also advertises history retrieval, library statistics, and library scan operations. Undeclared or under-declared administrative behaviors are dangerous because users may authorize the skill for simple media control without realizing it can access activity data or trigger state-changing server operations.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
| `TV_BACKEND` | All | Force backend: `homeassistant`, `webos`, `androidtv`, or `auto` |
| `TV_PLATFORM` | HA | Force platform: `webos` or `androidtv` (auto-detected from entity) |
| `HA_URL` | HA | Home Assistant URL, e.g. `http://192.168.1.138:8123` |
| `HA_TOKEN` | HA | HA long-lived access token (Profile → Long-Lived Access Tokens) |
| `HA_TV_ENTITY` | HA | Entity ID of your TV, e.g. `media_player.lg_webos_tv_oled48c34la` |
| `TV_IP` | WebOS | LG TV IP address for direct WebOS SSAP connection |
| `TV_CLIENT_KEY` | WebOS | Pairing key (printed on first connection — save it!) |
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
| `TV_BACKEND` | All | Force backend: `homeassistant`, `webos`, `androidtv`, or `auto` |
| `TV_PLATFORM` | HA | Force platform: `webos` or `androidtv` (auto-detected from entity) |
| `HA_URL` | HA | Home Assistant URL, e.g. `http://192.168.1.138:8123` |
| `HA_TOKEN` | HA | HA long-lived access token (Profile → Long-Lived Access Tokens) |
| `HA_TV_ENTITY` | HA | Entity ID of your TV, e.g. `media_player.lg_webos_tv_oled48c34la` |
| `TV_IP` | WebOS | LG TV IP address for direct WebOS SSAP connection |
| `TV_CLIENT_KEY` | WebOS | Pairing key (printed on first connection — save it!) |
Referenced artifact was not completely inspected
- `cli.js` — User-friendly CLI with all commands
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
npm install ws
sudo apt install adb # Debian/Ubuntu
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
npm install ws
sudo apt install adb # Debian/Ubuntu
The skill declares required environment variables, including sensitive API keys and tokens, but does not declare an explicit tool scope or permission boundary. That omission makes the skill's access expectations less transparent and weakens reviewability, increasing the chance that a caller grants broader capabilities than intended.
The skill metadata describes playback/session/TV control, but the CLI also exposes a library administration action via scan. Hidden or undocumented administrative capabilities expand the effective permission surface and can surprise users or orchestrators into invoking a more impactful action than expected. In this context, triggering a library scan is not destructive in the classic sense, but it can consume server resources and perform an administrative operation outside the declared scope.
The skill exposes admin-only capabilities to read activity history and trigger a library refresh, which exceed the stated playback/control scope. In an agent context, unnecessary privileged actions increase attack surface and can enable privacy-invasive access to viewing history or unauthorized administrative operations if the skill is invoked with elevated Jellyfin credentials.
This file includes server-administration behavior not justified by a media playback controller, namely reading system activity logs and initiating library scans. Even though Jellyfin enforces privileges server-side, embedding these actions in a broadly usable playback skill creates scope creep and raises the risk of misuse when an admin API key or admin password is configured.
The manifest describes media-server and TV control, but does not indicate that the skill will execute local system binaries via child_process. Running adb through execSync gives the skill host-level command execution dependencies that are not apparent from the stated purpose.
This method invokes Home Assistant's media_player.turn_off, which can immediately power off the user's TV. The code emits only a success log after execution and does not present any warning or confirmation before performing the irreversible/disruptive action.
The manifest describes controlling Jellyfin playback flow and TV power/apps, but this file also implements listing installed applications/packages on the TV through Home Assistant. Enumerating installed apps is a broader device-inspection capability than the stated one-command Jellyfin control workflow and is not mentioned in the manifest description.
The manifest frames the skill as controlling Jellyfin media server and TV, but the implementation provides a general-purpose ADB command executor used for package listing, connection management, key events, and app launching. This introduces a broader device-control surface than the manifest explicitly claims, even though some uses support TV control.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
throw new Error(
'adb is not installed or not in PATH.\n' +
'Install it:\n' +
' • Debian/Ubuntu: sudo apt install adb\n' +
' • macOS: brew install android-platform-tools\n' +
' • Or use TV_BACKEND=homeassistant instead'
);
The Android TV turnOff method uses ADB to send KEYCODE_SLEEP, changing the state of the target device in a potentially disruptive way. Although the code logs success afterward, it does not provide any prior user disclosure or confirmation that the operation will immediately put the TV to sleep.
The turnOff method for the WebOS backend sends system/turnOff, which is a destructive/disruptive operation affecting device state. Unlike pairing or wake operations, there is no user-facing warning, confirmation prompt, or cautionary comment around this shutdown behavior in the code shown.
This markdown file describes commands that power on/off a TV, launch apps, and send playback/volume controls, which can affect system state and connected devices. While the usage is explicit, there is no direct warning in the usage section that these commands will immediately control the user's TV and alter playback/device state.
The manifest does not disclose that the skill can inspect user activity history, yet the CLI provides a history command that reveals viewing activity. This is a scope/consent issue: even if the underlying API legitimately supports it, exposing user-behavior data without clear declaration can lead to privacy surprises and misuse in multi-user environments.
The CLI exposes a stats command for library analytics, but this capability is not mentioned in the manifest. While lower risk than direct control or admin actions, it still expands data exposure beyond the documented behavior and may leak information about the media library's size and composition.
The scan command immediately triggers a library refresh without warning, confirmation, or friction. Because library scans can be resource-intensive and may affect server responsiveness, executing them as a one-step command increases the chance of accidental or repeated administrative impact, especially when invoked through automation or an agent.
This code reads a password from the JF_PASS environment variable and sends it to /Users/AuthenticateByName, but the only user-facing log messages about login are commented out. For code files, credential use and network transmission of sensitive data should have some visible disclosure unless clearly surfaced elsewhere; in this file, that disclosure is absent in the active execution path.
The axios dependency is specified with a caret range, which allows newer minor/patch versions to be installed over time. This reduces build reproducibility and can expose the skill to accidental supply-chain risk or unexpected vulnerable releases, though by itself it is a hardening issue rather than an immediate exploitable flaw in this manifest.
"author": "Titunito",
"license": "ISC",
"dependencies": {
"axios": "^1.13.5",
"fuse.js": "^7.1.0",
"yargs": "^18.0.0"
},
Detected: suspicious.dangerous_exec, suspicious.env_credential_access