Back to skill

Security audit

Nex Healthcheck

Security checks for vulnerabilities and agentic risk

Overview

This monitoring skill is coherent, but it needs review because saved service targets can be executed through unsafe shell commands and alert data can be sent to Telegram despite inconsistent disclosure.

Review before installing. Only use this under a low-privilege account, do not add service targets from untrusted sources, avoid enabling the systemd timer until shell command construction is fixed, and be careful with ssh_cmd because it runs commands on remote systems. Treat Telegram bot tokens as secrets and assume Telegram notifications may send service names, targets, and health details off-host.

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

T09 · Insecure Skill Coding Practices

Error
Location
lib/checkers.py:218
Finding

Arbitrary Local Command Execution Through Shell Injection in Health Check Targets

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
nex-healthcheck.py:386
Finding

Telegram Bot Token Material Disclosed by Configuration Command

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (42)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill includes command-execution-based monitoring paths that exceed simple passive health checks by constructing shell commands for Docker inspection and delegating to remote SSH execution. In a monitoring skill, this is more dangerous because operators may trust configuration inputs and deploy it with elevated privileges on production infrastructure.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is a classic tool-parameter-abuse case: untrusted container_name is embedded in a shell command and executed with shell=True. An attacker who can influence service configuration can supply shell metacharacters to run arbitrary commands, turning a health check into code execution on the host.

Content

Scanner excerpt · lib/checkers.py (reported line 224)May include surrounding context.

python
if host and ssh_user:
            output, code = _run_ssh_command(host, ssh_user, cmd)
        else:
            output = subprocess.check_output(cmd, shell=True, timeout=10, text=True).strip()
            code = 0

        if code == 0 and output in ["running", "paused"]:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This command allows tool-parameter abuse because service_name is inserted directly into a shell command executed with shell=True. A maliciously crafted service name can append additional commands or alter command semantics, leading to arbitrary command execution.

Content

Scanner excerpt · lib/checkers.py (reported line 255)May include surrounding context.

python
output, code = _run_ssh_command(host, ssh_user, cmd)
            is_active = code == 0
        else:
            output = subprocess.check_output(cmd, shell=True, timeout=5, text=True).strip()
            is_active = True

        if is_active:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

The disk check exposes shell injection through the path parameter because it is concatenated into a shell command string with a pipe and run with shell=True. Any attacker-controlled path can abuse shell metacharacters to execute arbitrary commands on the monitored host.

Content

Scanner excerpt · lib/checkers.py (reported line 370)May include surrounding context.

python
if host and ssh_user:
            output, code = _run_ssh_command(host, ssh_user, cmd)
        else:
            output = subprocess.check_output(cmd, shell=True, timeout=10, text=True).strip()
            code = 0

        if code != 0 or not output:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
100% confidence
Finding

This helper is the most severe issue in the file because it combines unrestricted parameters (user, host, command) with shell string interpolation and shell=True. An attacker can exploit quoting to execute arbitrary local commands before SSH runs, while also sending arbitrary commands to remote hosts, making compromise of both the monitoring server and managed infrastructure possible.

Content

Scanner excerpt · lib/checkers.py (reported line 487)May include surrounding context.

python
"""Helper: Run command via SSH. Returns (output, exit_code)."""
    ssh_cmd = f"ssh {user}@{host} '{command}'"
    try:
        output = subprocess.check_output(ssh_cmd, shell=True, timeout=timeout, text=True, stderr=subprocess.STDOUT)
        return output.strip(), 0
    except subprocess.CalledProcessError as e:
        return e.output or "", e.returncode

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · README.md (reported line 36)May include surrounding context.

text

This will:
1. Create `~/.nex-healthcheck/` data directory
2. Create SQLite database for checks and incidents
3. Create `~/.local/bin/nex-healthcheck` wrapper script
4. (Linux only) Install optional systemd timer for periodic checks

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises capabilities that clearly require shell, network, and environment-variable access, but it does not declare any explicit tool scope or permission boundaries. This increases the chance an agent can invoke the skill with broader-than-necessary privileges, enabling command execution, outbound connections, and secret access without transparent constraints.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases are broad and overlap with ordinary health/status questions, making unintended activation more likely. In this skill, accidental invocation is more dangerous because the documented actions include local setup, state changes, remote SSH checks, and network probing rather than read-only summarization.

Content

No source excerpt is available for this finding.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Confidence
97% confidence
Finding

The skill explicitly supports an ssh_cmd mode that runs arbitrary commands via SSH, which is effectively unrestricted remote command execution. In the context of an agent skill, this is especially dangerous because loosely scoped user input or prompt injection could be turned into execution on remote infrastructure, leading to system compromise, data theft, or destructive actions.

Content

Scanner excerpt · SKILL.md (reported line 93)May include surrounding context.

md
- `ssl_cert`: SSL certificate expiry check
- `docker`: Docker container status (requires docker CLI or SSH)
- `systemd`: Systemd service status (requires systemctl or SSH)
- `ssh_cmd`: Run arbitrary command via SSH
- `ping`: ICMP ping check
- `disk`: Disk usage check

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documentation states that no data is sent externally, yet the same skill supports Telegram notifications using externally provided bot credentials. This mismatch can mislead users and downstream agents about data egress risk, causing monitoring results or infrastructure metadata to be transmitted off-host unexpectedly.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · lib/alerter.py (reported line 20)May include surrounding context.

python
return False

    try:
        url = f"https://api.telegram.org/bot{token}/sendMessage"
        data = urllib.parse.urlencode({
            "chat_id": chat_id,
            "text": message,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This code constructs a Telegram API request and transmits the alert message plus chat identifier to an external service. Although the function docstring says it sends alerts via Telegram, there is no user-facing warning, confirmation, or explicit disclosure that health-check results and incident details will be sent off-host to Telegram.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · lib/checkers.py (reported line 96)May include surrounding context.

python
cmd.append(domain)

        try:
            output = subprocess.check_output(cmd, timeout=5, text=True).strip()
        except FileNotFoundError:
            # Fall back to nslookup
            cmd = ["nslookup", domain]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · lib/checkers.py (reported line 102)May include surrounding context.

python
cmd.append(domain)

        try:
            output = subprocess.check_output(cmd, timeout=5, text=True).strip()
        except FileNotFoundError:
            # Fall back to nslookup
            cmd = ["nslookup", domain]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

The Docker check constructs docker inspect ... {container_name} as a string and executes it locally with shell=True. If container_name is user-controlled, shell metacharacters can inject arbitrary commands on the local host; when used remotely, the same unsanitized string is sent through the vulnerable SSH helper, compounding the risk.

Content

Scanner excerpt · lib/checkers.py (reported line 224)May include surrounding context.

python
if host and ssh_user:
            output, code = _run_ssh_command(host, ssh_user, cmd)
        else:
            output = subprocess.check_output(cmd, shell=True, timeout=10, text=True).strip()
            code = 0

        if code == 0 and output in ["running", "paused"]:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

The systemd check builds systemctl is-active {service_name} and runs it with shell=True, allowing a crafted service name to inject arbitrary shell syntax locally. If executed through the SSH helper, the unsanitized command also becomes a remote-command injection path with possible local shell injection in the SSH wrapper itself.

Content

Scanner excerpt · lib/checkers.py (reported line 255)May include surrounding context.

python
output, code = _run_ssh_command(host, ssh_user, cmd)
            is_active = code == 0
        else:
            output = subprocess.check_output(cmd, shell=True, timeout=5, text=True).strip()
            is_active = True

        if is_active:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code exposes a function that runs an arbitrary command via SSH and returns its result, but the only inline description is a terse docstring and there is no confirmation prompt, visible warning, or user-facing disclosure about remote command execution. Because this operation can change remote system state or expose remote output, it meets the code-file criteria for a safety-critical subprocess/shell operation lacking warning.

Content

No source excerpt is available for this finding.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Confidence
95% confidence
Finding

check_ssh_command is explicitly designed to run arbitrary commands supplied through configuration on remote systems. In the context of an agent skill for monitoring, unrestricted tool access is highly dangerous because it can be abused to execute destructive commands on infrastructure and, due to the unsafe SSH wrapper, potentially also trigger local command injection on the monitoring host.

Content

Scanner excerpt · lib/checkers.py (reported line 277)May include surrounding context.

python
def check_ssh_command(host, user, command, expected_output=None):
    """Run arbitrary command via SSH."""
    try:
        start = time.time()
        output, code = _run_ssh_command(host, user, command)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · lib/checkers.py (reported line 316)May include surrounding context.

python
try:
        start = time.time()
        cmd = ["ping", "-c", str(count), "-W", "5", host]
        output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, timeout=20, text=True)
        response_time_ms = int((time.time() - start) * 1000)

        # Parse packet loss

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest describes website/API/SSL/DNS/Docker/systemd/network/SSH health checks, but it does not mention disk-capacity or filesystem-usage monitoring. This file adds a separate check_disk capability and wires it into the dispatcher, extending the skill beyond the stated health-check scope presented to users.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

The disk check interpolates path into df {path} | tail -1 and executes it with shell=True. A malicious path containing shell metacharacters can execute arbitrary local commands; the pipeline also forces shell usage unnecessarily, increasing the attack surface for a monitoring function.

Content

Scanner excerpt · lib/checkers.py (reported line 370)May include surrounding context.

python
if host and ssh_user:
            output, code = _run_ssh_command(host, ssh_user, cmd)
        else:
            output = subprocess.check_output(cmd, shell=True, timeout=10, text=True).strip()
            code = 0

        if code != 0 or not output:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

The SSH helper builds a shell command string with user, host, and command interpolated directly into ssh {user}@{host} '{command}' and executes it with shell=True. Any attacker-controlled value in user, host, or especially command can break quoting and inject arbitrary local shell commands on the machine running the health checker, in addition to executing arbitrary remote commands.

Content

Scanner excerpt · lib/checkers.py (reported line 487)May include surrounding context.

python
"""Helper: Run command via SSH. Returns (output, exit_code)."""
    ssh_cmd = f"ssh {user}@{host} '{command}'"
    try:
        output = subprocess.check_output(ssh_cmd, shell=True, timeout=timeout, text=True, stderr=subprocess.STDOUT)
        return output.strip(), 0
    except subprocess.CalledProcessError as e:
        return e.output or "", e.returncode

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

save_service() uses INSERT OR REPLACE on a row identified by the unique name. In SQLite, REPLACE deletes the existing row and inserts a new one, which can change the service id and break or cascade-delete related check_results, incidents, and check_history, causing silent loss of historical monitoring data. In a healthcheck dashboard, that undermines integrity of uptime/SLA records and incident history.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The delete_service function permanently deletes a service record, and by cascade may remove associated dependent data, but the implementation provides no confirmation prompt, logging, print statement, or inline warning beyond a terse docstring. For a code file, this qualifies as a destructive operation lacking visible user disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The remove command deletes a configured service immediately via delete_service(service["id"]) and only prints a success message afterward. In this file there is no confirmation prompt, pre-action warning, or explanatory disclosure that the operation is destructive.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.