Back to skill

Security audit

Openclaw Spirits

Security checks for vulnerabilities and agentic risk

Overview

This is a local companion-pet skill with disclosed local generation and storage, with some usability and privacy caveats but no evidence of exfiltration, deception, or destructive behavior.

Install only if you want a persistent companion feature that may use your platform ID, username, or name as a deterministic seed and save a local profile in the skill directory. Prefer explicit commands such as spirit show or spirit rename, and avoid enabling passive appearances in serious work contexts if interruptions matter.

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

Note
Location
scripts/spirit.sh:66
Finding

Unsafe JavaScript Source Construction in the Rename Command

Content
View full analysis
" exit 1 fi node -e " const fs=require('fs'); const c=JSON.parse(fs.readFileSync('$COMPANION_FILE','utf8')); c.name=$(printf '%q' "$new_name"); fs.writeFileSync('$COMPANION_FILE',JSON.stringify(c,null,2)); " ``` ### Technical Analysis The rename operation embeds user-controlled input directly into JavaScript source passed to `node -e`. The command uses Bash `printf '%q'` as an escaping mechanism, but `%q` produces shell-escaped text, not a valid JavaScript string literal. For example, a normal invocation such as: ```bash spirit rename Rune ``` produces an effective JavaScript statement equivalent to: ```javascript c.name=Rune; ``` Rather than assigning the string `"Rune"`, JavaScript treats `Rune` as an identifier and raises a `ReferenceError` unless it happens to be defined. Inputs that are valid JavaScript primitive expressions can change the type of the stored field. For example, `spirit rename true` results in `c.name=true`, and `spirit rename undefined` can cause the `name` property to disappear when the object is serialized. Other specially formatted input can make the generated JavaScript syntactically invalid and reliably terminate the command. The reviewed implementation does not establish shell-command execution from this issue because shell expansion results are not reparsed as shell syntax. Nevertheless, constructing executable JavaScript from user-controlled data is an unsafe coding pattern and permits denial of service or malformed companion state. ### Attack Path 1. An attacker or local user invokes the documented rename command with a JavaScript primitive or malform ...[truncated 1097 chars]
Remediation
View remediation
" exit 1 fi node - "$COMPANION_FILE" "$new_name" <<'NODE' const fs = require('fs'); const [file, name] = process.argv.slice(2); const companion = JSON.parse(fs.readFileSync(file, 'utf8')); if (typeof name !== 'string' || name.length === 0 || name.length > 100) { throw new Error('Invalid companion name'); } companion.name = name; fs.writeFileSync(file, JSON.stringify(companion, null, 2)); NODE ``` Additional hardening should include: 1. Apply the correction to both `scripts/spirit.sh` and `openclaw-spirits/scripts/spirit.sh`. 2. Enforce a reasonable maximum name length. 3. Reject control characters, including terminal escape characters and line breaks, if names are displayed in a terminal. 4. Validate the complete companion schema before writing it. 5. Write updates atomically through a temporary file followed by a rename operation to reduce the risk of partial state corruption. 6. Add regression tests covering ordinary names, spaces, quotation marks, Unicode, JavaScript keywords, shell metacharacters, and empty input. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code substantially matches the core claim of deterministic spirit generation with 24 species and 5 rarity tiers, including a 2% mythic rate. However, several prominent described features are not implemented in this chunk: personality, elemental affinity, evolution with interactions, interaction tracking, and visual prompt generation. Instead, the code only generates appearance/details (eyes, accessories, shiny) and stats. This is a description-to-behavior mismatch because the declared system promises a broader companion/progression feature set than the supplied code actually provides.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared description presents a full spirit-companion system with deterministic per-user generation, rarity mechanics, personality, elemental affinity, evolution, interaction tracking, and visual prompt generation. This code chunk is much narrower: it is a command-line utility that accepts precomputed spirit data, emits a text prompt for an LLM to create name/personality, saves a completed companion record, and shows saved data. While it aligns partially with personality creation and persistence of companion data, most of the core system behaviors in the description are absent from this code. Therefore the description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared description presents a full spirit-generation and progression system with deterministic identity-based creation, rarity, traits, evolution, and interaction tracking. This code chunk does not implement those core behaviors. Instead, it acts as a CLI interface around an already-existing companion.json file, offering display, simple summon gating, stats output, and renaming. While this is related to the same spirit domain, the primary behavior of the supplied code is materially narrower and different from the declared functionality. The file write for renaming is also an undeclared management capability, though benign. Therefore the description does not accurately represent what this specific code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The core declared theme matches partially: the code does implement deterministic per-user spirit generation with 24 species and a 5-tier weighted rarity system. However, several prominently declared features are absent from the supplied code: there is no personality generation, no elemental affinity, no interaction tracking, no evolution with interactions, and no visual prompt output. Instead, the code generates cosmetic/mechanical attributes like eyes, accessories, shiny state, and stats, which are not highlighted in the description. Also, the rarity labels differ from the description’s Common-to-Mythic framing, though the 2% mythic weight does align. This is best classified as a mismatch because the actual code is a narrower generator than the broader companion system described.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code only implements a narrow helper around companion data: it formats a prompt for name/personality generation, saves a companion object, and shows the saved file. It does not contain the core behaviors emphasized in the description, such as deterministic generation from a user's identity, rarity assignment logic (including the stated 2% Mythic rate), elemental affinity, interaction/evolution systems, or a per-user companion model. While it does reference species, rarity, stats, and personality-related content, those are passed in as inputs rather than generated or tracked by this code. Therefore the declared description materially overstates what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a full spirit-generation system with deterministic identity-based assignment and multiple gameplay features. This code chunk is only a CLI wrapper around an already-existing companion.json file, with commands to display, print stats, trigger a first-time display if a file exists, and rename the spirit. The script even states that generate.js and soul.js must be run first, indicating the core generation behavior is outside this chunk. Because the primary declared capabilities are not actually implemented here, and an undeclared local mutation capability (rename/write to assets) is present, this is a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs the agent to trigger on broad terms like a spirit name or generic phrases such as “灵兽,” with no strong disambiguation requirement. This can cause unintended invocation, persona hijacking of normal conversation flow, and increased susceptibility to prompt-trigger collisions where benign user text activates the skill unexpectedly.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The passive appearance rules allow agent-initiated activation based on vague conditions like morning greetings, silence, heartbeat chance, or achievements. Ambiguous autonomous triggering can inject unsolicited content into unrelated conversations, interfere with higher-priority tasks, and make the agent less predictable in contexts where strict user intent boundaries matter.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This manifest-like JSON sets the language to "zh" explicitly, and the file provides no indication that users can opt into another language or that the skill is region-specific. The policy requires flagging language or locale constraints when they are imposed without user choice or clear justification.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger list includes broad natural-language terms such as 'spirit', 'pet', 'companion', and 'creature', plus auto-use 'when the skill is first installed.' This can cause unintended invocation in unrelated conversations, leading the agent to execute local scripts and persist user-derived data without a clearly scoped user request.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Allowing invocation by simply mentioning the spirit's name or using no command prefix creates ambiguous activation boundaries and increases the chance of accidental or adversarial triggering. In a multi-turn chat, ordinary text could be reinterpreted as a skill command, causing persona hijacking, unwanted responses in character, or unnecessary tool/script execution.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The 'What the Spirit CANNOT Do' section explicitly says the spirit cannot access tools or run commands. Earlier instructions tell the agent to run generate.js, soul.js, render.js, and display.js as part of spirit operations, which directly contradicts that claim in the documentation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The spec says the spirit is deterministically generated from a user's identity and a fixed salt, but gives no user-facing notice or consent model for that identity-derived processing. Even if the output is only a game profile, identity-based derivation can surprise users, create linkability across sessions, and encourage use of stable identifiers in ways that have privacy implications.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger phrase "buddy" is very generic and likely to appear in normal conversation, increasing the chance the skill activates unintentionally. In an agent context, accidental invocation can cause unwanted responses, state changes, or disclosure of spirit/profile data when the user did not mean to use the skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documented storage of companion data includes user-associated profile attributes such as name, personality, species, timestamps, and language, but there is no warning about persistence, retention, or access controls. In a shared skill directory, this can expose user-linked data to other components or operators and makes the gamification profile persist beyond user expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The JSON sets "lang": "zh", which indicates a fixed language/locale choice. Under the policy, forcing a specific language without user opt-in or clear region-specific justification is a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code assigns companion.lang = companion.lang || 'zh', which silently defaults all companions to Chinese when no language is provided. This is a natural-language locale policy concern because the skill does not offer an explicit language choice or document a justified region-specific restriction.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest describes a broader companion system with personality traits, elemental affinity, interaction-driven evolution, and visual prompt generation. In this implementation, the main generator returns only a limited cosmetic/stat bundle and contains no logic for those advertised spirit attributes or progression features.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code sets companion.lang = companion.lang || 'zh', which forces a specific locale whenever the caller does not explicitly provide one. This is a natural-language policy issue because it imposes a language default rather than offering or requesting a user choice.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script claims it only writes to assets/, but it writes to the path stored in COMPANION_FILE without checking whether that path is a symlink or resolves outside the intended directory. If an attacker can replace assets/companion.json with a symlink, the rename flow could overwrite an arbitrary file accessible to the current user, violating the tool's stated safety boundary.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

Line L069 says the skill is 'Bilingual by default' and only mentions switching languages with a flag afterward. This indicates a preset language behavior rather than explicitly letting the user choose their preferred language or locale first, which may conflict with language/locale opt-in policy expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The file prescribes 'Chinese (for Feishu / Chinese users)' and separately 'English (for Telegram / Discord / English users),' which ties language choice to platform or presumed user category rather than explicit preference. This can violate language/locale choice expectations because the user is not offered a clear opt-in or override.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The function signature sets the default language to 'zh', which means users who do not explicitly choose a language will receive Chinese output. This is a natural-language locale choice imposed by default rather than selected by the user, and the file does not indicate a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The comment at L03 says the script 'Writes only to assets/', framing the tool as narrowly constrained. However, the code later implements a rename operation that rewrites the companion JSON file contents based on user input, which is a more specific state-modifying behavior than the safety-oriented header suggests.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script sets LANG="zh" as the default display language, which imposes a specific locale unless the user explicitly overrides it. This is a natural-language policy concern because the file forces a language choice by default rather than prompting or offering neutral selection first.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.