Back to skill

Security audit

buddy

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent local virtual pet feature, but it loads executable JavaScript from another mutable extension directory when users run buddy commands.

Review before installing. The main risk is that buddy commands execute a renderer file from ~/.openclaw/extensions/buddy-companion/sprites.js, so a compromised or replaced companion extension file could run code in the agent process. Install only if you trust the companion extension/state directory and are comfortable with local device/user identity being used to generate a stable pet.

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

Warning
Location
scripts/hatch.js:145
Finding

Unvalidated Execution of an External JavaScript Module

Content
View full analysis

Vulnerability Details

File Location: scripts/hatch.js, lines 14–15 and 145–148
Vulnerability Type: Untrusted local JavaScript module execution
Risk Level: Medium

Vulnerable code:

js
var EXT_DIR = path.join(os.homedir(), ".openclaw", "extensions", "buddy-companion");
var SPRITES_PATH = path.join(EXT_DIR, "sprites.js");
js
function loadSprites() {
  try { return require(SPRITES_PATH); }
  catch (_) { return { renderSprite: function () { return ["(???)"]; }, renderFace: function (b) { return "(" + b.eye + ")"; } }; }
}

Technical Analysis

The Skill dynamically loads ~/.openclaw/extensions/buddy-companion/sprites.js with Node.js require(). Requiring a JavaScript module executes its top-level code; it does not merely load passive sprite data.

The target module is outside the audited Skill package, and the loader does not validate its canonical path, ownership, permissions, integrity, or provenance. Consequently, any process or actor capable of replacing that file can introduce arbitrary JavaScript that will execute when loadSprites() is reached.

The function is invoked by the handlers for /buddy, /buddy pet, and /buddy stats. The broad catch block does not prevent malicious top-level code from running before an exception occurs and may conceal a compromised or malformed module by silently returning fallback renderers.

Attack Path

  1. An attacker first obtains the ability to create or modify ~/.openclaw/extensions/buddy-companion/sprites.js, such as through another compromised extension or a process running under the same account.
  2. The attacker places malicious top-level JavaScript in that file.
  3. The user invokes /buddy, /buddy pet, or /buddy stats.
  4. The relevant command handler calls loadSprites().
  5. require(SPRITES_PATH) loads and executes the attacker-controlled module.
  6. The payload runs with the operating-system permissions and accessible environment of the Agent process.

This ...[truncated 712 chars]

Remediation
View remediation

Remediation Suggestions

  1. Bundle the sprite renderer within the audited Skill and load it using a path anchored to __dirname, rather than loading executable code from a mutable directory under the user's home folder.
  2. Prefer a passive data format such as JSON for sprite definitions. Parse the data and render it using trusted code included in the Skill.
  3. If an external executable module is unavoidable:
    • Resolve and verify its canonical path before loading it.
    • Reject symbolic links and unexpected file types.
    • Validate file ownership and permissions.
    • Verify the file against a pinned cryptographic digest or authenticated signature.
    • Restrict loading to an explicitly installed and trusted component.
    • Validate that renderSprite and renderFace are functions before calling them.
  4. Replace the silent broad exception handler with narrowly scoped error handling and security-relevant logging. Avoid suppressing evidence that an external module failed or behaved unexpectedly.
  5. Run the Skill with least privilege and isolate it from sensitive files and environment variables to reduce the impact of any compromised renderer.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The command dynamically requires and executes code from a path under another extension directory in the user's home folder. That means any tampering with that external sprites.js file can cause arbitrary code execution when this skill runs, which is an unjustified trust boundary for a pet feature.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill description and all user-facing command explanations are written entirely in Chinese, which effectively imposes a specific language on users. The file does not indicate that language selection is optional or that the skill is region-specific, so this appears to violate the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script derives a stable user identifier from device.json, an environment variable, or host/user metadata to deterministically generate pet traits. For a local virtual pet command, collecting or deriving persistent identity from device and account information is not necessary and creates avoidable fingerprinting/privacy risk, especially if this identifier is later reused or exposed by related components.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The hardcoded reaction strings in handlePet are written entirely in Chinese, which imposes a specific language on users regardless of their locale or preferences. The file does not offer a language option or explain that the skill is intentionally region-specific, so this conflicts with the language/locale policy criteria.

Content

No source excerpt is available for this finding.