Back to skill

Security audit

Bot Status API

Security checks for vulnerabilities and agentic risk

Overview

This is a real monitoring API, but it exposes detailed bot and system status broadly while also running shell commands and disabling TLS verification.

Install only in a trusted, private environment after adding authentication, binding detailed status to localhost or a protected interface, removing the global TLS bypass, and replacing raw shell-command collectors with fixed allowlisted commands or safer APIs. Treat the status output as sensitive because it can reveal services, ports, cron timing, email metadata, skill inventory, and host details.

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

T09 · Insecure Skill Coding Practices

Error
Location
server.js:89
Finding

Unauthenticated Status API Exposes Sensitive Operational Data

Content
View full analysis
{ // CORS res.setHeader("Access-Control-Allow-Origin", "*"); res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); if (req.method === "OPTIONS") { res.writeHead(204); res.end(); return; } if (req.url === "/status" || req.url === "/") { if (cachedStatus) { res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": `public, max-age=${Math.ceil(CACHE_TTL / 1000)}`, }); res.end(JSON.stringify(cachedStatus)); } else { res.writeHead(503, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "starting up, no data yet" })); } } else if (req.url === "/health") { res.writeHead(200, { "Content-Type": "application/json" }); res.end(JSON.stringify({ status: "ok", uptime: formatUptime(Date.now() - startTime) })); } else { res.writeHead(404); res.end("Not Found"); } }); server.listen(PORT, "0.0.0.0", () => { console.log(`[bot-status] ${config.name} Status API`); console.log(`[bot-status] Listening on http://0.0.0.0:${PORT}`); }); ``` ### Technical Analysis The service binds to `0.0.0.0`, exposes `/status` without authentication or authorization, and permits browser access from every origin through `Access-Control-Allow-Origin: *`. It also marks the sensitive response as publicly cacheable. The response aggregates data from all collectors, including: - Hostname, configured IP address, CPU, memory, and dis ...[truncated 1735 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
server.js:10
Finding

Global TLS Certificate Verification Disabled

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
collectors/services.js:48
Finding

Shell Command Injection in Configurable Service Checks

Content
View full analysis
&1`, { timeout: svc.timeout || 5000 }); const result = { status: res.ok ? "authenticated" : "error" }; if (svc.label) result.account = svc.label; return result; } async function checkFileExists(svc) { try { await access(svc.path); const res = await execAsync(`ls ${svc.path} 2>/dev/null`, { timeout: 2000 }); const ok = res.ok && (res.output.includes("token") || res.output.length > 10); const result = { status: ok ? "authenticated" : "not configured" }; if (svc.label) result.account = svc.label; return result; } catch { const result = { status: "not configured" }; if (svc.label) result.account = svc.label; return result; } } ``` ### Technical Analysis Both `svc.command` and `svc.path` originate from `config.json` and are passed to `child_process.exec`, which invokes a shell. Shell metacharacters, substitutions, pipes, redirections, and command separators are therefore interpreted. The `file-exists` check is especially unsafe because a path is interpolated into an `ls` command without quoting or argument separation. Filesystem APIs are already used to test access, so invoking a shell exceeds the minimum privileges and functionality necessary for this check. The explicit `command` service type is intended to run commands, but it still creates unrestricted arbitrary command execution under a persistent monitoring service. It should be constrained to fixed executables and validated arguments rather than accepting raw shell programs. ### Attack Path 1. An attacker gains the ability to alter `config.json`, a deployment template, or configuration supplied to the service. 2. The attacker places shell syntax in `services[].command` or in a reachable `servi ...[truncated 966 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
collectors/email.js:30
Finding

Arbitrary Shell Execution Through Email Check Configuration

Content
View full analysis
{ const res = await execAsync(acct.command, { timeout: acct.timeout || 8000, env: { ...process.env, ...acct.env }, }); let status = "error"; let unread = 0; if (res.ok) { status = "connected"; const count = parseInt(res.output) || 0; unread = count > 0 && res.output !== "No results" ? count : 0; } return { name: acct.name, data: { status, unread, address: acct.address, lastCheck: lastCheckIso }, }; }) ); ``` ### Technical Analysis Each `email[].command` value is passed directly to `child_process.exec`. The shell interprets the entire configured value, including substitutions, separators, pipes, redirections, and environment expansion. The code additionally merges arbitrary `acct.env` values into the full process environment. Although executing provider-specific email tools is documented functionality, accepting an unrestricted shell program is broader than necessary for obtaining an unread count. The recurring background collector also turns a configuration compromise into repeatedly scheduled execution. ### Attack Path 1. An attacker modifies an email account entry in `config.json`. 2. The attacker sets `command` to a shell payload or appends a payload to a legitimate email command. 3. On startup or the next cache refresh, the email collector calls `exec`. 4. The shell executes the supplied payload with the service account's privileges and inherited environment. 5. The command runs again at later refresh intervals while the malicious configuration remains present. ### Impact Assessment The attacker can execute arbitrary commands ...[truncated 367 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
collectors/skills.js:36
Finding

Installed Skill Metadata Can Trigger Shell Command Injection

Content
View full analysis
0) { const checks = await Promise.all( requiredBins.map((b) => execAsync(`which ${b}`, { timeout: 2000 })) ); binsAvailable = checks.every((c) => c.ok); } ``` ### Technical Analysis The collector treats content from every discovered `SKILL.md` as data, extracts purported binary names using a permissive regular expression, and interpolates each result into a shell command. The extracted strings are not validated as executable names. An attacker-controlled Skill can include shell syntax in the `bins` array. When the status service scans that directory, `exec("which " + b)` causes the shell to interpret the injected syntax. This creates a cross-Skill execution path: merely installing or placing crafted metadata in a configured Skill directory can lead to execution by the monitoring process. Checking binary availability does not require a shell and therefore violates least privilege. ### Attack Path 1. An attacker publishes or supplies a Skill with a crafted `SKILL.md` containing shell syntax in a `bins` entry. 2. A user installs or copies that Skill i ...[truncated 795 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
collectors/devservers.js:12
Finding

Shell Injection Through Development Server Process Filter

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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (20)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README explicitly advertises collecting email unread counts, installed skills, runtime health, and other operational details, but it does not warn that exposing this API can leak privacy-sensitive or security-relevant metadata. In a monitoring endpoint, this kind of aggregation increases reconnaissance value for an attacker and may unintentionally disclose personal or internal system information if the endpoint is exposed beyond a trusted network.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 92)May include surrounding context.

bash
systemctl --user daemon-reload
systemctl --user enable --now bot-status
loginctl enable-linger $USER

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill describes deploying a Node.js HTTP service that performs network checks, reads local files, inspects system state, and runs configured commands, yet it declares no tool scope or permissions boundaries. In an agent ecosystem, missing capability declarations can cause reviewers or orchestrators to underestimate the skill's reach, increasing the chance that sensitive environment and network access is granted implicitly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill advertises an HTTP status API exposing runtime health, service connectivity, cron jobs, skills, and system metrics, but it does not warn that these endpoints may disclose sensitive operational details. Such information can aid reconnaissance by revealing internal topology, installed components, filesystem layout, uptime patterns, and monitoring targets if the API is exposed beyond a trusted boundary.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documented command service check type explicitly allows configured shell commands to be executed, but the skill provides no warning about command-injection, arbitrary-code-execution, or privilege risks. In practice, operators may supply untrusted or loosely reviewed command strings, and the status service would become a persistent execution surface running with the bot user's privileges.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
82% confidence
Finding

The instructions enable a persistent user service and loginctl enable-linger, causing the process to survive logout and restart automatically. While commonly used for reliability, this also creates durable execution that can outlive interactive sessions, making unintended exposure or misuse harder to notice and easier to persist.

Content

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

bash
systemctl --user daemon-reload
systemctl --user enable --now bot-status
loginctl enable-linger $USER  # survive logout

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The status collector reads a sensitive authentication profile file to infer whether an API token exists. Even though it does not directly return the token, this unnecessarily expands the skill's access to credential-bearing files and creates a side channel that reveals secret presence and internal filesystem layout, which is not clearly required for a lightweight health endpoint.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The collector builds a shell pipeline using ds.processGrep from configuration and passes it to exec, which invokes a shell. Because the value is only wrapped in single quotes and not safely parameterized, an attacker who can influence configuration can inject shell metacharacters or break out of quoting, leading to arbitrary command execution under the agent's privileges.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The collector sends an HTTP request to a configured Docker API endpoint and includes the sensitive credential docker.token in the X-API-Key header. There is no confirmation prompt, logging, or inline warning indicating that credentials will be used and transmitted over the network.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The collector executes shell commands directly from configuration via child_process.exec, which allows arbitrary command execution with the privileges of the running agent. In a status API, this is especially risky because monitoring config is often edited by operators or automation, so any config injection, misconfiguration, or untrusted workspace content can become full remote code execution on the host.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code performs shell command execution as part of normal status collection with no explicit warning, consent boundary, or disclosure that configuring an email account can run host commands. That hidden execution model increases the chance that users will treat the feature as harmless monitoring while unintentionally enabling command execution pathways that can be abused or misconfigured.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
81% confidence
Finding

The collector builds requests from svc.url, svc.headers, and svc.body and sends them over the network, which can include sensitive metadata or tokens. The file contains no user-facing warning, confirmation, or explanatory comment disclosing that configured checks may send request bodies and headers to external services.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The service checker executes arbitrary shell commands from configuration via exec, which turns a monitoring feature into a general command-execution primitive. If an attacker can influence configuration, or if operators paste untrusted config, this enables remote code execution under the agent's privileges and is far beyond what a status API should need.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This line executes a configured shell command with no validation, approval, or visible warning to users, making dangerous behavior easy to trigger unintentionally. In the context of a status API, hidden command execution is especially risky because operators may assume checks are passive while they actually run arbitrary code.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The file-existence check invokes ls ${svc.path} through the shell after already using access, so an untrusted path can inject shell syntax and execute commands. It also derives authentication state from filename/output heuristics, which can leak information about sensitive filesystem contents and exceeds a simple existence check.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This is a real command-injection risk because values parsed from SKILL.md metadata are inserted directly into a shell command via execAsync(which ${b}). Since SKILL.md content may come from custom/workspace skills and is treated as data, an attacker can place shell metacharacters in a bin name and cause arbitrary command execution during status collection.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest emphasizes a lightweight, Node.js-only status API, but this collector depends on external system commands executed through child_process.exec. While collecting system metrics is consistent with the skill's purpose, invoking shell utilities is an additional capability not clearly justified by the 'Node.js only' framing and creates platform/runtime dependencies beyond pure Node functionality.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code globally sets NODE_TLS_REJECT_UNAUTHORIZED to "0", disabling certificate validation for all outbound HTTPS/TLS connections made by this process. That allows man-in-the-middle interception or spoofing of health-check targets and any other HTTPS-based collectors, causing the status API to trust forged endpoints and potentially expose misleading or attacker-controlled monitoring data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

Disabling TLS verification without any warning means operators may believe HTTPS-backed checks are authenticated when they are not. An attacker on the network path can impersonate monitored services such as Portainer or UniFi, falsify service status, and undermine the integrity of the monitoring output.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
99% confidence
Finding

This is an unsafe default because every HTTPS request in the Node.js process will skip certificate verification, not just the intended status checks for a small set of services. In a monitoring skill that connects to arbitrary configured services, that broad trust downgrade increases exposure and makes the collected status information easier to tamper with.

Content

Scanner excerpt · server.js (reported line 13)May include surrounding context.

js
import { collect as collectSkills } from "./collectors/skills.js";

// Allow self-signed certs for Portainer/UniFi
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

// --- Load Config ---
const configPath = new URL("./config.json", import.meta.url);

Static analysis

Detected: suspicious.dangerous_exec, suspicious.insecure_tls_verification

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
collectors/devservers.js:5

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
collectors/email.js:6

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
collectors/services.js:6

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
collectors/skills.js:7

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
collectors/system.js:7

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
server.js:13