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

Content
View full analysis

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
View 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
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).

Content

Scanner excerpt · src/commands/doctor.ts (reported line 76)May include surrounding context.

ts
// 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' };
      }

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Unrestricted Tool Access

Medium
Category
Excessive Agency
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.

Content

Scanner excerpt · src/commands/run.ts (reported line 1)May include surrounding context.

ts
// `ssh-lab run <host> <cmd>` — execute arbitrary command on remote host(s)

import { execSsh } from '../ssh/exec.js';
import { withPool } from '../ssh/pool.js';

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 40)May include surrounding context.

json
"README.md"
  ],
  "devDependencies": {
    "@types/node": "^25.4.0",
    "typescript": "^5.9.3"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 41)May include surrounding context.

json
],
  "devDependencies": {
    "@types/node": "^25.4.0",
    "typescript": "^5.9.3"
  }
}

Intent-Code Divergence

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.