Back to skill

Security audit

ssh-lab

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real SSH server-management tool, but it needs Review because a confirmed host-alias bug can run local shell commands and the high-impact SSH actions are broadly scoped.

Install only after reviewing the command-injection issue in src/ssh/config.ts. Use this skill only with trusted host names and prompts, pin the package version instead of unpinned npx, prefer dry-run for sync, and require explicit approval before run, sync, watch, tail, or all-host operations.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/ssh/config.ts:13
Finding
Local Command Injection Through Unsanitized SSH Host Alias## Vulnerability Details **File Location**: `src/ssh/config.ts:13-20` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```ts /** Use `ssh -G <host>` to resolve effective SSH config for a host */ function resolveWithSshG(alias: string): Partial<HostConfig> | null { try { const output = execSync(`ssh -G ${alias} 2>/dev/null`, { encoding: 'utf-8', timeout: 5000, }); ``` Relevant unsanitized input flow from `src/cli.ts:195-208`: ```ts case 'run': { if (positional.length < 2) { process.stderr.write('Usage: ssh-lab run <host|all> <command...>\n'); process.exit(1); } const host = positional[0]; const cmd = positional.slice(1).join(' '); const result = await runCommand(host, cmd, timeoutFor('standard'), { concurrency }); render(result, mode); process.exit(exitCode(result)); break; } ``` ### Technical Analysis The `alias` value is incorporated directly into a command string passed to Node.js `execSync`. Unlike `execFileSync` with a discrete argument array, `execSync` runs the string through a shell. Consequently, shell metacharacters in an attacker-controlled alias—such as semicolons, command substitutions, backticks, pipes, redirections, or newlines—are interpreted as local shell syntax. Host aliases originate from CLI positional arguments and are not validated before reaching `resolveHost()` and then `resolveWithSshG()`. The same vulnerable resolution path can be reached through multiple host-taking commands, including `run`, `doctor`, `tail`, `ls`, `df`, `watch`, `sync`, `status`, and `compare`. The command is executed locally, not on the intended remote SSH server. Redirecting standard error does not neutralize the injected syntax. ### Attack Path 1. An attacker persuades a user or AI Agent to invoke a host-taking command with a crafted alias. 2. The CLI stores the supplied al ...[truncated 1446 chars]
Remediation
## Remediation Suggestions Replace shell-string execution with an API that passes arguments directly to the executable: ```ts import { execFileSync } from 'node:child_process'; function resolveWithSshG(alias: string): Partial<HostConfig> | null { try { const output = execFileSync('ssh', ['-G', '--', alias], { encoding: 'utf-8', timeout: 5000, stdio: ['ignore', 'pipe', 'ignore'], }); // Parse the output as before. } catch { return null; } } ``` Apply defense in depth: 1. Validate aliases against a conservative allowlist appropriate for SSH host aliases. 2. Reject empty aliases, control characters, whitespace, shell metacharacters, and values beginning with `-`. 3. Use `spawn`, `execFile`, or `execFileSync` with argument arrays for every local program invocation. 4. Never attempt to fix this solely by manually adding shell quotes; eliminating the shell is safer. 5. Add regression tests covering aliases containing `;`, `|`, `&`, backticks, `$()`, redirections, quotes, newlines, and option-like prefixes. 6. Review every host-taking command to ensure all paths use the same validated resolution function. 7. Consider validating custom hostnames, usernames, identity paths, ports, rsync paths, and exclusions separately according to their destination parser.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (16)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
// 3. Disk writable
    checks.push(await runCheck('Disk writable', async () => {
      const r = await execSsh(host, 'touch /tmp/.ssh-lab-probe && rm /tmp/.ssh-lab-probe && echo writable', { timeoutMs });
      if (r.exitCode === 0 && r.stdout.includes('writable')) {
        return { status: 'pass', message: '/tmp is writable' };
      }
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).

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The markdown documents `run`, `tail`, `ls`, `df`, and especially `sync` as available commands, but it does not include any caution that `run` can execute arbitrary commands on remote hosts or that `sync` can transfer files and affect remote data. For a skill intended for AI agents, omitting a user-facing warning about these potentially impactful operations increases the risk of unintended changes to remote systems.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The skill advertises very broad trigger conditions such as any mention of remote servers, GPU status, `nvidia-smi`, or running commands on remote servers. In an agent environment, this can cause the skill to be invoked in situations the user did not clearly intend, increasing the chance of unsolicited remote actions or exposure of sensitive server state.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The documentation recommends invoking the tool via `npx ssh-lab` without pinning a specific version. That creates a supply-chain risk because the executed package can change over time or be substituted if the registry/package is compromised, leading to unreviewed code execution on the operator's machine.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The documentation recommends invoking the tool via `npx ssh-lab` without pinning a specific version. That creates a supply-chain risk because the executed package can change over time or be substituted if the registry/package is compromised, leading to unreviewed code execution on the operator's machine.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill exposes remote command execution, log access, directory listing, disk inspection, and rsync-based file transfer, but does not prominently require user confirmation or warn about the operational impact. In practice, these capabilities can alter remote systems, exfiltrate data, or disclose sensitive information if an agent invokes them too freely or with attacker-influenced inputs.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger list includes broad phrases such as "GPU", "remote server", "server status", and "compare servers" that can match routine user requests unrelated to an intentional invocation of this skill. Because this skill enables remote host inspection and command execution, accidental or overly eager activation could expose sensitive infrastructure data or initiate actions against remote systems in response to ambiguous prompts.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The doctor command performs a state-changing operation on the remote host by creating and deleting a file in /tmp without any explicit user warning or opt-in. Even though /tmp is an expected scratch location, diagnostic commands are generally expected to be low-impact, and silent remote writes can violate user expectations, trigger monitoring, or have unintended side effects on sensitive or tightly controlled systems.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
// `ssh-lab run <host> <cmd>` — execute arbitrary command on remote host(s)

import { execSsh } from '../ssh/exec.js';
import { withPool } from '../ssh/pool.js';
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The function constructs and executes a remote shell command over SSH, which is a safety-relevant operation under the code-file criteria. In this file there is no confirmation prompt, user-facing log/print, or explanatory warning comment/docstring disclosing that the skill will execute a command on a remote host.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This command retrieves the contents of an arbitrary remote file over SSH and includes that content directly in the returned summary/raw output. In an agent setting, that creates a real data-exposure risk because sensitive files can be read and then propagated into logs, model context, downstream tools, or user-visible output without any warning, confirmation, or redaction.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The probe goes beyond collecting GPU utilization data and reads full process command lines from /proc for GPU-using processes. Command lines frequently contain sensitive material such as file paths, dataset names, internal hostnames, access tokens, API keys, or user arguments, so this creates unnecessary information disclosure relative to the stated telemetry purpose.

Context-Inappropriate Capability

Medium
Confidence
99% confidence
Finding
The code builds a shell command with untrusted input using string interpolation: `execSync(`ssh -G ${alias} 2>/dev/null`)`. Because `execSync` invokes a shell here, a crafted host alias containing shell metacharacters like `;`, `$()`, or backticks can execute arbitrary local commands, not just query SSH configuration. In this skill context, aliases can come from user-supplied target strings or custom config entries, which makes the issue directly reachable and more dangerous than a purely internal helper.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"README.md"
  ],
  "devDependencies": {
    "@types/node": "^25.4.0",
    "typescript": "^5.9.3"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
],
  "devDependencies": {
    "@types/node": "^25.4.0",
    "typescript": "^5.9.3"
  }
}
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Intent-Code Divergence

Low
Confidence
81% confidence
Finding
The top-level comment says the probe parses `nvidia-smi` CSV output and a compute process list, which suggests GPU telemetry and basic process listing. The implementation goes further by invoking `/proc/<pid>/cmdline` or `ps` to recover full command lines, so the documentation understates the nature of the collected process data.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/commands/sync.ts:77

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/ssh/config.ts:16

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/ssh/exec.ts:210