Back to skill

Security audit

Claw Rpg

Security checks for vulnerabilities and agentic risk

Overview

This RPG skill is not clearly malicious, but it asks for persistent agent hooks and cron jobs that affect future responses and exposes activity data on the LAN without enough controls.

Install only if you explicitly want RPG flavor to persist across future agent sessions. Avoid adding the after-every-reply AGENTS.md hook unless you accept response decoration and state changes on unrelated tasks. Do not run setup-cron.mjs unless you understand it will use your OpenClaw gateway token to create a recurring main-session event. Run the dashboard on trusted networks only, preferably localhost-bound, and configure Telegram only after reviewing what character and activity data may be sent.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:37
Finding
Persistent Post-Response Output Injection Through Agent Instructions<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:37-41` **Supporting Implementation**: `scripts/easter.mjs:156-190` **Vulnerability Type**: Persistent agent response manipulation **Risk Level**: High ### Vulnerable Code ```text **Integration** — add to your `AGENTS.md`: After each reply to a user message, run: node <SKILL_ROOT>/scripts/easter.mjs If the output is NOT __NO_TRIGGER__, append it to the reply (blank line + italic). Do not run during heartbeat polls. ``` The script implements the injected output and persistent state update: ```js // 更新角色(XP + 对话计数) if (!noSave) { try { await syncXp({ consumed: CONV_INPUT_EST, produced: CONV_OUTPUT_EST, conversations: 1, }); // xp.mjs 已更新 character.json,重新讀取以獲取最新 level/xp char = JSON.parse(readFileSync(CHARACTER_JSON, 'utf8')); } catch (e) { // fallback:只更新對話計數 char.conversations = conv; char.updatedAt = new Date().toISOString(); writeFileSync(CHARACTER_JSON, JSON.stringify(char, null, 2), 'utf8'); } } const vars = { level: char.level, xp: char.xp, conv, claw: char.stats?.claw || '?', antenna: char.stats?.antenna || '?', shell: char.stats?.shell || '?', brain: char.stats?.brain || '?', foresight: char.stats?.foresight || '?', charm: char.stats?.charm || '?', }; const line = fill(pick(pool), vars); process.stdout.write(line + '\n'); process.exit(0); ``` ### Technical Analysis The Skill instructs the user to add a permanent rule to `AGENTS.md`. That rule requires the agent to execute Skill code after every user-facing response and conditionally append Skill-controlled text to the answer. This behavior changes how the agent handles unrelated future requests. The appended RPG content is not necessary to complete the underlying user task and can interfere with required response formats, machine-readable output, safety-sensitive answers, or strict API contracts. The invoked script ...[truncated 1484 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the instruction to install a permanent post-response rule in `AGENTS.md`. 2. Make flavor-text generation an explicit, user-invoked feature rather than an automatic hook. 3. Return structured RPG event data to the caller and let the caller decide whether to display it. 4. Require clear, revocable user consent before enabling any automatic response decoration. 5. Never modify the final response when the user requests a strict output format. 6. Separate XP synchronization from response rendering so displaying flavor text is not required to update state. 7. Use actual measured token statistics when available instead of fixed estimates. 8. Provide a documented disable and uninstall procedure that removes any previously installed agent instructions. ]]>

T06 · System Persistence

Error
Location
scripts/setup-cron.mjs:40
Finding
Scheduled Main-Session System Event Creates Cross-Session Persistence<![CDATA[ ## Vulnerability Details **File Location**: `scripts/setup-cron.mjs:40-58` **Related Documentation**: `SKILL.md:74-77`, `SKILL.md:104-107` **Vulnerability Type**: Persistent scheduled instruction injection **Risk Level**: High ### Vulnerable Code ```js // 创建每日 03:00 XP 同步 cron const job = { name: 'claw-rpg-daily-xp', schedule: { kind: 'cron', expr: '0 3 * * *', tz: 'Asia/Shanghai' }, payload: { kind: 'systemEvent', text: `[Claw RPG] 每日 XP 同步提醒:请运行 node ${SCRIPTS}/xp.mjs 更新今日 XP(使用 session_status 获取 token delta)` }, sessionTarget: 'main', enabled: true, }; try { const res = await fetch(`http://localhost:${port}/cron/jobs`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify(job), }); ``` The gateway credential used to create the job is loaded from OpenClaw configuration: ```js function loadGatewayToken() { const paths = [ join(process.env.USERPROFILE || '', '.openclaw', 'openclaw.json'), join(process.env.HOME || '', '.openclaw', 'openclaw.json'), ]; for (const p of paths) { if (existsSync(p)) { try { return JSON.parse(readFileSync(p, 'utf8'))?.gateway?.auth?.token; } catch {} } } return null; } ``` ### Technical Analysis The setup script reads the OpenClaw gateway bearer token and uses the authenticated cron API to create an enabled daily job. The job targets the agent’s `main` session and delivers a `systemEvent` instructing the agent to execute Skill code. A recurring bookkeeping task does not need to inject instructions into the primary interactive session. An isolated background process or narrowly scoped worker would be sufficient. Targeting `main` expands the Skill’s authority from local RPG state management to persistent influence over future agent sessions. The script does not check whether a job with the same name already exists, does not assign an expiration, and does not prov ...[truncated 1643 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not target the interactive `main` session for background bookkeeping. 2. Run XP synchronization in an isolated worker or dedicated restricted session. 3. Prefer a direct background command with a narrowly scoped service account over a `systemEvent` that instructs an agent to execute code. 4. Require explicit confirmation immediately before creating the scheduled task. 5. Query existing jobs and make setup idempotent by updating or reusing the named job. 6. Add expiration, ownership metadata, and a documented `--remove` or uninstall command. 7. Display the complete schedule, target, and payload before installation. 8. Read the gateway port from configuration rather than hardcoding port `18789`. 9. Restrict gateway credentials or use a scoped token that can manage only this Skill’s scheduled task. 10. Record the installed job ID so it can be reliably revoked. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
dashboard/server.js:22
Finding
Unauthenticated LAN Exposure of Character and Agent Activity Data<![CDATA[ ## Vulnerability Details **File Location**: `dashboard/server.js:22-23`, `dashboard/server.js:52-80`, `dashboard/server.js:116-120` **Client Access**: `dashboard/src/App.tsx:232-241` **Vulnerability Type**: Unauthenticated network data disclosure **Risk Level**: Medium ### Vulnerable Code The server enables unrestricted cross-origin access: ```js const app = express(); app.use(cors()); app.use(express.json()); ``` It exposes the complete character object without authentication: ```js app.get('/api/character', (_req, res) => { const char = readChar(); if (!char) return res.status(404).json({ error: 'No character found. Run: node scripts/init.mjs' }); res.json(char); }); // SSE 端點:客戶端訂閱實時更新 app.get('/api/events', (req, res) => { res.setHeader('Content-Type', 'text/event-stream'); res.setHeader('Cache-Control', 'no-cache'); res.setHeader('Connection', 'keep-alive'); res.setHeader('Access-Control-Allow-Origin', '*'); res.flushHeaders(); // 立即推送當前數據 const char = readChar(); if (char) res.write(`data: ${JSON.stringify(char)}\n\n`); // 加入廣播列表 clients.add(res); // 心跳(每 25s 防止連接超時) const heartbeat = setInterval(() => { try { res.write(': ping\n\n'); } catch { clearInterval(heartbeat); clients.delete(res); } }, 25000); req.on('close', () => { clearInterval(heartbeat); clients.delete(res); }); }); ``` The service listens on every network interface: ```js const PORT = process.env.PORT || 3500; app.listen(PORT, '0.0.0.0', () => { const lanIp = getLanIp(); console.log(`\n🦞 Claw RPG Dashboard → http://localhost:${PORT}`); console.log(` LAN access → http://${lanIp}:${PORT}`); console.log(` Character file → ${CHARACTER_FILE}\n`); }); ``` The browser client performs same-origin requests: ```tsx useEffect(() => { fetch('/api/character') .then(r => r.ok ? r.json() : Promise.reject(r.statusText)) .then(d => { setChar(d); setError(null) }) .catch(e => setError(String(e) ...[truncated 2335 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Bind to `127.0.0.1` by default instead of `0.0.0.0`. 2. Require an explicit `--lan` option or environment variable before exposing the service to other devices. 3. Add authentication to `/api/character`, `/api/events`, and any future dashboard endpoints. 4. Replace unrestricted `cors()` with an allowlist containing only the intended dashboard origin. 5. Remove the wildcard `Access-Control-Allow-Origin` header from the SSE endpoint. 6. Return a reduced view model containing only fields required by the dashboard. 7. Avoid exposing raw token counters, timestamps, conversation counts, and history unless explicitly requested. 8. Add security headers, request rate limiting, and connection limits for SSE clients. 9. Use TLS or a trusted authenticated reverse proxy when LAN access is intentionally enabled. 10. Clearly warn users at startup when the dashboard is network-accessible and display the authentication status. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (82)

Ae1

High
Category
analysis-evasion
Content
node scripts/init.mjs
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/init.mjs
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/sheet.mjs
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/greet.mjs # Fires once per day (checks lastGreetDate)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/greet.mjs # Fires once per day (checks lastGreetDate)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/greet.mjs # Fires once per day (checks lastGreetDate)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/xp.mjs --in 2000 --out 800
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/xp.mjs --in 2000 --out 800
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/xp.mjs --in 2000 --out 800
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/levelup.mjs --prestige
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/arena.mjs --opponent "Shadow Wizard"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/arena.mjs --opponent "Shadow Wizard"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/arena.mjs --opponent "Shadow Wizard"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Natural-Language Policy Violations

High
Confidence
92% confidence
Finding
The entire skill file is written as a mandate in Chinese and does not offer any user choice of language or locale. Under the stated policy, forcing a specific language without opt-in is a natural-language policy violation unless clearly justified as region-specific, which is not documented here.

Hidden Instructions

High
Category
Prompt Injection
Content
import { useEffect, useRef, useState } from 'react'
import './App.css'

// ── Types ────────────────────────────────────────────────────────
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
import { useEffect, useRef, useState } from 'react'
import './App.css'

// ── Types ────────────────────────────────────────────────────────
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The README advertises LAN-accessible dashboard access and Telegram notifications but does not clearly warn users that character data, identity-derived traits, or usage metadata may be exposed to other devices or third-party services. Because this skill derives data from `SOUL.md` and `MEMORY.md`, the privacy implications are more sensitive than a typical toy dashboard.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding
The install command uses `npx clawhub@latest`, which fetches and executes the latest published package at runtime without pinning a reviewed version. This creates a supply-chain risk: a compromised or malicious upstream release could execute arbitrary code on the user's system during installation.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The quick-start directs users to run scripts that read `SOUL.md` and `MEMORY.md` and then launch a networked dashboard, but it provides no safety notice about handling potentially sensitive identity and memory files. In this context, the omission increases the chance that users expose personal or agent-state data without informed consent.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
Stating that the dashboard is LAN-accessible without warning that anyone on the same network may be able to view live `character.json`-derived data is a real privacy/security issue. Since updates are pushed live via SSE, exposure is continuous and could reveal behavior, progression, or other derived metadata from sensitive source files.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The activation text is very broad, covering initialization, syncing, dashboards, prestige, and daily reporting, without clear boundaries on when the skill should or should not run. In an agent environment, vague triggers can cause over-invocation, unnecessary file reads, and accidental execution of side-effecting behaviors such as scheduled jobs or outbound reporting.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill states that it reads SOUL.md and MEMORY.md and supports daily Telegram reports, but it does not warn about the privacy implications of processing potentially sensitive memory/profile content or sending derived data externally. Users and agent operators may therefore enable or invoke the skill without understanding that local contextual data could influence outbound messages.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill documents an external Telegram reporting feature for a local RPG/character-tracking system without clearly establishing necessity, data minimization, or user consent. Because the skill also reads agent-local files such as SOUL.md and MEMORY.md, the reporting channel creates a plausible path for sensitive agent context or derived profile data to be transmitted off-host.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The directive to execute tasks immediately and avoid asking for confirmation suppresses an important safety checkpoint for impactful actions. An agent following this instruction may modify files, run commands, or take other consequential steps without verifying user intent, increasing the chance of unauthorized or harmful actions.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The skill instructs the agent to read documentation, inspect git history, and run code to resolve issues before escalating. Those actions expand the agent's capabilities beyond the narrowly stated persona and can expose sensitive repository history or trigger side effects from executing untrusted code without explicit user approval. In this context, the 'just do it' framing makes the instruction more dangerous because it reduces opportunities for safety checks.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/_notify.mjs:13

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/setup-cron.mjs:19