Back to skill

Security audit

IATerm WebSocket Agent

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-built to control IATerm terminals, but it gives agents high-impact terminal access with approval bypass and persistent authorization paths.

Install only if you intentionally want an agent to control IATerm terminal sessions, including remote SSH or serial sessions. Avoid --auto-approve and the always-approve option unless you fully trust every caller and workflow using the skill; clear ~/.cache/iaterm-ws-client if approval state should be reset. Prefer a pinned, reviewed websockets dependency and treat interactive raw JSON mode as unsafe for sensitive terminal operations until it enforces the same approval checks as the normal commands.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ws_client.py:317
Finding
Interactive Mode Bypasses Approval Controls for Sensitive Terminal Operations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ws_client.py:317-368` **Vulnerability Type**: Missing authorization enforcement in an alternate request path **Risk Level**: High ### Vulnerable Code ```python async def cmd_interactive(client_type="ws-client", client_id=None): """Interactive mode.""" session_id = _get_session_id() ws, token = await _connect_and_identify(session_id, client_type, client_id) try: print("Connected. Type JSON requests or use shortcuts:", file=sys.stderr) print(" lw = list_workspaces | lp = list_panels | lc = list_connections", file=sys.stderr) print(" q = quit", file=sys.stderr) shortcuts = { "lw": ("list_workspaces", {}), "lp": ("list_panels", {}), "lc": ("list_connections", {}), } async def reader(): try: while True: raw = await ws.recv() data = json.loads(raw) if "event" in data: print(f"\n[EVENT] {json.dumps(data, ensure_ascii=False)}") else: print(f"\n[RESP] {json.dumps(data, ensure_ascii=False, indent=2)}") print("> ", end="", flush=True) except websockets.exceptions.ConnectionClosed: pass reader_task = asyncio.create_task(reader()) loop = asyncio.get_event_loop() try: while True: print("> ", end="", flush=True) line = await loop.run_in_executor(None, sys.stdin.readline) line = line.strip() if not line: continue if line == "q": break if line in shortcuts: method, params = shortcuts[line] req_id = str(uuid.uuid4())[:8] msg = {"id": req_id, "method": method, "params": params, "toke ...[truncated 3630 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse every interactive request into a validated method and parameter structure before transmission. 2. Apply `_check_approval(method, params)` to all interactive requests, particularly `send_input` and `subscribe_output`. 3. Define an explicit allowlist of methods that interactive mode may invoke. Reject unknown or undocumented methods by default. 4. Prevent callers from supplying or replacing authentication tokens. Remove any user-provided `token` field and inject the authenticated connection token internally. 5. Validate that `method` is a string and `params` is an object before evaluating authorization. 6. Centralize request authorization in a single function used by `cmd_execute()`, `cmd_subscribe()`, and `cmd_interactive()` so alternate request paths cannot bypass the control. 7. Add regression tests proving that interactive `send_input` and `subscribe_output` requests are rejected unless explicitly approved. 8. Consider removing arbitrary raw JSON support if it is not essential. A fixed set of interactive commands provides a smaller and more auditable attack surface. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:22
Finding
Unpinned Runtime Installation of the WebSockets Dependency<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:22-26` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```bash ### Prerequisites ```bash pip install websockets 2>/dev/null || pip3 install websockets 2>/dev/null ``` ``` ### Technical Analysis The installation instruction requests the package named `websockets` without specifying a reviewed version, lock file, package hash, index, or trusted-source policy. The effective dependency can therefore change after the Skill has been audited. Installing a mutable latest release at runtime creates both supply-chain and reliability risks. A future compromised release, compromised package repository, maliciously configured Python package index, or unexpected dependency resolution result could introduce code that was not present during this audit. Python packages may execute code during installation and are subsequently imported at client startup: ```python import websockets ``` The suppression of standard error with `2>/dev/null` also reduces visibility into resolver warnings, repository errors, and other diagnostic information that could reveal an unsafe installation source. No evidence was found that the currently named `websockets` project is malicious. The finding concerns the unsafe dependency acquisition practice and the resulting inability to reproduce the audited dependency set. ### Attack Path 1. An operator follows the prerequisite command from `SKILL.md`. 2. `pip` resolves `websockets` using the active Python and package-index configuration. 3. Because no version or hash is specified, the resolver selects whatever release and artifact are currently available and compatible. 4. An attacker who has compromised the selected release, package repository, index configuration, or network/package-distribution path supplies a malicious artifact. 5. The artifact executes code during installation or when `ws_client.py` imports `webso ...[truncated 834 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin `websockets` to a specifically reviewed version rather than installing the latest available release. 2. Maintain a dependency lock file containing cryptographic hashes for all resolved artifacts. 3. Install with hash enforcement, for example through a reviewed requirements file and `pip install --require-hashes -r requirements.txt`. 4. Use an isolated virtual environment rather than modifying the user's global Python environment. 5. Configure an explicit trusted package index and avoid inheriting unreviewed `PIP_INDEX_URL`, `PIP_EXTRA_INDEX_URL`, or related configuration. 6. Preserve installation diagnostics instead of redirecting all standard error to `/dev/null`. 7. Scan and periodically update the pinned dependency through a controlled review process. 8. Document the supported Python and `websockets` versions to ensure reproducible installations. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (32)

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
### List active connections

**IMPORTANT:** `list_connections` only returns **remote** connections (SSH, Serial, JumpServer). Local terminal connections are automatically filtered out by the backend. If this command returns connections, they are ALL remote — do NOT judge by the `name` field. Always check the `connection_type` field to determine the actual type:
- `ssh` — SSH remote connection
- `serial` — Serial port connection
Confidence
85% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill exposes powerful capabilities—environment-variable access, file reads/writes, and network interaction with a localhost terminal-control WebSocket API—but declares no explicit tool scope or permission boundaries. In an agent setting, this increases the chance that the skill can be invoked with broader authority than intended, enabling terminal interaction and local state modification without a machine-readable restriction layer.

Session Persistence

Medium
Category
Rogue Agent
Content
## CRITICAL: Always use the ws_client.py script

**NEVER write inline WebSocket code.** Always use the provided CLI script at `scripts/ws_client.py` (relative to this skill file).

Find the script path:
```bash
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.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
`IATERM_SESSION_ID` environment variable must be set by the host application (e.g. IATerm) before invoking the client. **Do NOT generate this value yourself** — if the variable is missing, the client will exit with an error, indicating the host application has not properly initialized the session.

The first command triggers user approval in the IATerm UI. Once approved, the WS token is cached at `~/.cache/iaterm-ws-client/ws_token.json` (permissions 0600). Subsequent commands reuse the cached token and skip approval.

If the token expires, the client automatically clears the cache and re-prompts for approval.
Confidence
93% confidence
Finding
The skill describes caching a ws_token so subsequent connections can skip fresh user approval. In a terminal-control skill, persistent authorization materially increases risk because later invocations may gain command and output access without renewed user intent, especially if the invoking agent or local account is compromised.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- **n** — reject (command exits with error)
- **a** — always approve for this specific target (saved to `~/.cache/iaterm-ws-client/approval.json`)

Use `--auto-approve` to skip all confirmation prompts (for automated pipelines):

```bash
python3 "$WS_CLIENT" --auto-approve send_input --connection-id <id> --data "ls\n"
Confidence
98% confidence
Finding
This finding is substantively the same risky behavior as the earlier auto-approve reference: it advertises a flag that suppresses all prompts before sending terminal input. That creates a straightforward path for automated command execution without human review.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- **n** — reject (command exits with error)
- **a** — always approve for this specific target (saved to `~/.cache/iaterm-ws-client/approval.json`)

Use `--auto-approve` to skip all confirmation prompts (for automated pipelines):

```bash
python3 "$WS_CLIENT" --auto-approve send_input --connection-id <id> --data "ls\n"
Confidence
98% confidence
Finding
This finding is substantively the same risky behavior as the earlier auto-approve reference: it advertises a flag that suppresses all prompts before sending terminal input. That creates a straightforward path for automated command execution without human review.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Use `--auto-approve` to skip all confirmation prompts (for automated pipelines):

```bash
python3 "$WS_CLIENT" --auto-approve send_input --connection-id <id> --data "ls\n"
```

## Commands
Confidence
98% confidence
Finding
This duplicate occurrence again documents full prompt suppression for automated pipelines, which weakens the intended safeguard model. In a terminal-control skill, repeated encouragement of such bypass materially increases the chance of unsafe integration patterns.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Use `--auto-approve` to skip all confirmation prompts (for automated pipelines):

```bash
python3 "$WS_CLIENT" --auto-approve send_input --connection-id <id> --data "ls\n"
```

## Commands
Confidence
98% confidence
Finding
This duplicate occurrence again documents full prompt suppression for automated pipelines, which weakens the intended safeguard model. In a terminal-control skill, repeated encouragement of such bypass materially increases the chance of unsafe integration patterns.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
2. **First connection** — sends `identify(session_id)` → user approves in IATerm UI (up to 60s) → receives `ws_token` → cached to disk.
3. **Subsequent connections** — sends `identify(session_id + cached token)` → server recognizes the token → skips approval → executes immediately.
4. **Token expiry** — if the server rejects a cached token (error -15 or `connection_rejected`), the client clears the cache and retries with a fresh approval flow.
5. **Approval gate** — `send_input` and `subscribe_output` prompt for interactive confirmation (y/n/a) unless `--auto-approve` is set.

## Configuration
Confidence
97% confidence
Finding
This repeated mention confirms immediate execution after cached auth plus optional prompt bypass, reducing friction for remote terminal manipulation. The context makes it more dangerous than generic automation because the target surface includes live remote connections and terminal output streams.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
2. **First connection** — sends `identify(session_id)` → user approves in IATerm UI (up to 60s) → receives `ws_token` → cached to disk.
3. **Subsequent connections** — sends `identify(session_id + cached token)` → server recognizes the token → skips approval → executes immediately.
4. **Token expiry** — if the server rejects a cached token (error -15 or `connection_rejected`), the client clears the cache and retries with a fresh approval flow.
5. **Approval gate** — `send_input` and `subscribe_output` prompt for interactive confirmation (y/n/a) unless `--auto-approve` is set.

## Configuration
Confidence
97% confidence
Finding
This repeated mention confirms immediate execution after cached auth plus optional prompt bypass, reducing friction for remote terminal manipulation. The context makes it more dangerous than generic automation because the target surface includes live remote connections and terminal output streams.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**操作审批**
- `send_input` / `subscribe_output` 执行前交互式确认:y(本次通过)/ n(拒绝)/ a(始终通过)
- 选 `a` 记入 `~/.cache/iaterm-ws-client/approval.json`,后续同目标操作自动通过
- `--auto-approve` CLI 参数跳过所有确认(用于自动化流水线)

**文档补充**
- `list_connections` 明确只返回远程连接(SSH/Serial/JumpServer),本地终端被后端过滤
Confidence
97% confidence
Finding
The repeated changelog entry documents persistent approvals and approval bypass as product features, not incidental behavior. That normalizes insecure operation for a capability that can control remote terminals, increasing the impact of misuse.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**操作审批**
- `send_input` / `subscribe_output` 执行前交互式确认:y(本次通过)/ n(拒绝)/ a(始终通过)
- 选 `a` 记入 `~/.cache/iaterm-ws-client/approval.json`,后续同目标操作自动通过
- `--auto-approve` CLI 参数跳过所有确认(用于自动化流水线)

**文档补充**
- `list_connections` 明确只返回远程连接(SSH/Serial/JumpServer),本地终端被后端过滤
Confidence
97% confidence
Finding
The repeated changelog entry documents persistent approvals and approval bypass as product features, not incidental behavior. That normalizes insecure operation for a capability that can control remote terminals, increasing the impact of misuse.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Connection flow:
  - Session ID from IATERM_SESSION_ID env var (required)
  - First connection: identify(session_id) → IATerm UI approval → ws_token → cached
  - Subsequent connections: identify(session_id + cached token) → server recognizes → skip approval
  - Token invalidated: auto-clear cache, re-prompt approval

Usage:
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
json.dump(approvals, f, indent=2)


def _check_approval(method, params, auto_approve=False):
    """Check if an operation needs approval. Returns True if approved, False if rejected."""
    # Only gate sensitive operations
    if method not in ("send_input", "subscribe_output"):
Confidence
94% confidence
Finding
The approval function is designed to permit sensitive terminal actions without fresh human confirmation when auto_approve is enabled or persistent approvals exist. In a skill that can send terminal input and subscribe to output, this weakens the human-in-the-loop control boundary and can allow an external agent to act autonomously on a local terminal session.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
if method not in ("send_input", "subscribe_output"):
        return True

    if auto_approve:
        return True

    # Check persistent approvals
Confidence
96% confidence
Finding
This branch causes all approval checks for sensitive operations to be skipped whenever --auto-approve is set. Because the skill can inject input into active terminal connections and read terminal output, an agent or caller that can enable this flag gains unattended command execution and surveillance capability over local terminal sessions.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
print(json.dumps(data.get("result", data), ensure_ascii=False, indent=2))


async def cmd_execute(method, params=None, auto_approve=False, client_type="ws-client", client_id=None):
    """Execute a single command: connect, request, disconnect."""
    session_id = _get_session_id()
    params = params or {}
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Static analysis

No suspicious patterns detected.