Back to skill

Security audit

Li Sentry Check

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent SSH server health checker, but it contains a real service-name injection path that can execute unintended commands on a configured server.

Review before installing. The service-name handling should be fixed so invalid service names are rejected locally and never sent to bash over SSH. Use a pinned installer version, pre-populate known_hosts instead of relying on accept-new, protect targets.yaml and checks.yaml from untrusted edits, run with a least-privilege SSH inspection account, and treat generated reports as sensitive system data.

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/inspect.py:155
Finding
Remote Command Injection Through Invalid Service Names<![CDATA[ ## Vulnerability Details **File Location**: `scripts/inspect.py:155-161` and execution sink at `scripts/inspect.py:248-252`; equivalent implementation in `scripts/inspect.mjs:142-149` and execution sink at `scripts/inspect.mjs:357-360` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code Python implementation: ```python if not re.match(r'^[a-zA-Z0-9_-]+$', name): safe_name = re.sub(r'[^a-zA-Z0-9_-]', '_', name) out.append({ "id": f"svc_{safe_name}_invalid", "cmd": f"echo 'Invalid service name (only alphanumeric, hyphens, underscores allowed): {name}'", "timeoutSec": 3, }) continue ``` The generated command is subsequently passed to a remote shell: ```python remote = f"bash -lc '{cmd.replace(chr(39), chr(39) + '\"' + chr(39) + chr(39))}'" full_cmd = ssh_base + [dest, remote] try: result = subprocess.run( full_cmd, ``` Equivalent Node.js implementation: ```javascript if (!/^[a-zA-Z0-9_-]+$/.test(name)) { out.push({ id: `svc_${name.replace(/[^a-zA-Z0-9_-]/g, '_')}_invalid`, cmd: `echo 'Invalid service name (only alphanumeric, hyphens, underscores allowed): ${name}'`, timeoutSec: 3, }); continue; } ``` The command reaches this execution sink: ```javascript for (const c of group.commands) { const timeoutMs = Number(c.timeoutSec ?? 10) * 1000; const remote = `bash -lc ${shellQuote(c.cmd)}`; const { error, stdout, stderr } = await execFileP('ssh', [...sshBase, dest, remote], { timeoutMs }); ``` ### Technical Analysis The application correctly recognizes that a service name fails the allowlist expression, but it then embeds that invalid value directly inside a single-quoted shell command. The input is not safely encoded before becoming part of the command string. A service name containing a single quote can terminate the intended `echo` argument and append additional shell syntax. For example, a configuration value conceptually shaped like ...[truncated 2114 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not construct or execute any remote command for an invalid service name. Record the validation failure locally in the report and skip SSH execution. 2. Treat the service-name allowlist as a hard rejection boundary: ```python if not re.fullmatch(r"[A-Za-z0-9_-]+", name): out.append({ "id": f"invalid_service_{safe_name}", "validation_error": "Invalid service name", "timeoutSec": 0, }) continue ``` 3. Apply the same hard-rejection behavior in the Node.js implementation. 4. Avoid `bash -lc` where possible. Use a fixed remote helper with data passed as positional arguments rather than interpolating configuration values into shell source. 5. If a shell cannot be avoided, use a well-tested shell-quoting routine for every variable and never reuse rejected input in executable text. 6. Validate the complete target configuration before opening an SSH connection. Reject invalid host, port, user, key path, service, timeout, and command fields. 7. Protect `references/targets.yaml` and `references/checks.yaml` with restrictive local permissions and trusted deployment controls. 8. Add regression tests using service names containing single quotes, semicolons, command substitutions, newlines, redirections, and shell operators. Tests should verify that no SSH command is launched for invalid input. 9. Configure the remote inspection account with least privilege, no interactive shell where practical, no passwordless unrestricted `sudo`, and an SSH `authorized_keys` forced command that permits only approved inspection operations. ]]>

T08 · Insecure Dependencies

Warning
Location
README.en.md:28
Finding
Mutable Package Execution Through an Unpinned npx Installation Command<![CDATA[ ## Vulnerability Details **File Location**: `README.en.md:28-35`; the same command appears at line 33 in the other localized README files **Vulnerability Type**: Unpinned executable dependency and supply-chain risk **Risk Level**: Medium ### Vulnerable Code ```bash # nanobot ./manage.sh skill install li_sentry_check # OpenClaw npx clawhub@latest install li_sentry_check # Hermes hermes skill install li_sentry_check ``` ### Technical Analysis The documented OpenClaw installation procedure directs users to execute `clawhub` through `npx` using the mutable `latest` distribution tag. `npx` may retrieve and execute package code from the configured package registry. Because `latest` is not an immutable version, the code executed by a future user can differ from the code available when this Skill was audited. The instruction also provides no package integrity hash or lockfile constraint. This does not demonstrate that the current `clawhub` package is malicious. The vulnerability is that installation trust is delegated to mutable registry state, so compromise of the package publisher, registry account, release process, dependency graph, or package resolution can turn the documented installation command into a local code-execution channel. ### Attack Path 1. An attacker compromises the package publisher, publishing credentials, release pipeline, registry entry, or a dependency used by a future `clawhub` release. 2. The attacker publishes a malicious release and points the `latest` tag to it. 3. A user follows the README and runs `npx clawhub@latest install li_sentry_check`. 4. `npx` resolves and downloads the attacker-controlled release from the configured registry. 5. Package lifecycle behavior or the invoked CLI executes with the privileges of the local user. 6. The attacker can perform actions allowed to that user before or during Skill installation. ### Impact Assessment Successful supply-chain exploitation can provide arbitrary local code execu ...[truncated 606 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the mutable `latest` tag with an exact, reviewed package version: ```bash npx clawhub@<audited-exact-version> install li_sentry_check ``` 2. Document the expected registry and verified package publisher so users can detect registry or namespace substitution. 3. Where supported, provide and verify a package integrity digest or signed release provenance. 4. Recommend installation through a lockfile-backed workflow for automated deployments. 5. Avoid recommending elevated privileges for package execution. Installation should run under a least-privileged account. 6. Establish a release process that reviews the pinned CLI version and its transitive dependency tree before updating documentation. 7. Update every localized README consistently so no alternate documentation continues to recommend `@latest`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (44)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The static finding indicates the implementation does more than the skill declares: it can write reports to arbitrary local paths, execute built-in commands outside the stated checks.yaml allowlist, and auto-trust new SSH host keys via accept-new. This mismatch is dangerous because users and orchestrators may rely on the documented read-only, tightly constrained behavior while the actual code permits broader file modification and weaker SSH trust guarantees, enabling tampering or man-in-the-middle exposure.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The security comments explicitly promise the script never writes files, but the implementation later conditionally calls writeFile(args.output, report). This kind of mismatch is dangerous because operators and higher-level agents may trust the documented read-only behavior and grant the skill broader use, enabling unexpected local file writes and possible clobbering of important files.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The security documentation says the tool never writes files, but later code conditionally persists reports to disk. This kind of false security claim materially increases operational risk because reviewers, agents, or permission systems may authorize the skill under the assumption that it is read-only locally, when in fact it can create or overwrite files containing sensitive infrastructure data.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The script documentation asserts it 'NEVER ... writes files', but the code accepts an arbitrary --output path and writes the full inspection report there. In an agent setting, that mismatch is dangerous because callers or orchestrators may trust the no-write guarantee and permit execution in contexts where local file creation can overwrite files, leak sensitive server data into unexpected locations, or violate sandbox assumptions.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
This markdown file contains user-facing instructions only in German, which effectively forces a specific language on users without any opt-in or alternative. Under the policy, locale or language constraints should either be optional for the user or clearly justified as region-specific.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The skill is described as read-only server inspection, but the extension guide adds a database check using `mysql -e 'SHOW STATUS...'`, expanding the skill into direct database command execution. Even though the example appears read-only, this broadens the operational scope from host inspection to database access, increases credential and data exposure risk, and normalizes adding commands that may no longer fit the claimed safety boundary.

Natural-Language Policy Violations

Medium
Confidence
86% confidence
Finding
L'intero file di documentazione è scritto in italiano e non indica alternative linguistiche, opt-in dell'utente o che la skill sia limitata a un contesto italofono specifico. In base alla policy, imporre una lingua senza scelta esplicita può costituire una violazione di policy linguistica/locale.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
O arquivo de documentação está integralmente em português e não oferece alternativa de idioma, opt-in do usuário, nem explica que a habilidade é destinada exclusivamente a um público lusófono ou contexto regional específico. Pelas regras, impor um idioma sem escolha explícita configura violação de política de linguagem/locale em linguagem natural.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Rp1

Medium
Category
MCP Rug Pull
Confidence
86% confidence
Finding
The installation example uses `npx clawhub@latest install li_sentry_check`, which relies on an unpinned package version at execution time. If the upstream package is compromised or a breaking release is published, users may execute unintended code during installation, creating a supply-chain risk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill advertises significant capabilities in prose, but does not declare an explicit tool scope such as permissions or allowed-tools. In an agent environment, missing tool scoping can let the runtime expose broader file, shell, environment, and network access than the README-style safety claims imply, weakening enforcement and increasing the blast radius if the implementation is compromised or behaves unexpectedly.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The invocation guidance is broad enough that the skill may trigger on generic requests about server status, inspection, or reports. In an agent system with SSH and shell access, over-broad activation can cause the skill to run in contexts the user did not specifically intend, increasing the chance of unnecessary remote access, disclosure of system data, or use against the wrong target.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
- ❌ Exfiltrate data to external services
- ❌ Access local files other than: `references/targets.yaml`, `references/checks.yaml`, and the SSH private key specified in `keyPath`
- ❌ Make any network connections other than SSH to the target server specified in `targets.yaml`
- ❌ Execute arbitrary commands — only commands from `references/checks.yaml` are allowed

**This skill ONLY:**
- ✅ Reads system information via predefined read-only commands
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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Security Best Practices

- **Key permissions**: `chmod 600 ~/.ssh/li_sentry_check`
- **Host verification**: For production, pre-populate `known_hosts` instead of `accept-new`
- **Service names**: Only alphanumeric, hyphens, underscores allowed (validated before use)
- **Command allowlist**: Never modify `checks.yaml` with state-changing commands
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Security Best Practices

- **Key permissions**: `chmod 600 ~/.ssh/li_sentry_check`
- **Host verification**: For production, pre-populate `known_hosts` instead of `accept-new`
- **Service names**: Only alphanumeric, hyphens, underscores allowed (validated before use)
- **Command allowlist**: Never modify `checks.yaml` with state-changing commands
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
## Security Best Practices

- **Key permissions**: `chmod 600 ~/.ssh/li_sentry_check`
- **Host verification**: For production, pre-populate `known_hosts` instead of `accept-new`
- **Service names**: Only alphanumeric, hyphens, underscores allowed (validated before use)
- **Command allowlist**: Never modify `checks.yaml` with state-changing commands
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/inspect.mjs:119