Back to skill

Security audit

Identity Guess Game

Security checks for vulnerabilities and agentic risk

Overview

This multiplayer game skill is mostly purpose-aligned, but it needs Review because its instructions and engine expose avoidable command and file-write risks around player-controlled input.

Review before installing. Use this only in groups that expect a Chinese-language game host, and do not run its documented command templates by interpolating raw chat text through a shell. Prefer a safe argv-array or stdin wrapper, validate group IDs, and periodically clear scripts/data if retained player/game data is not wanted.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/game-engine.mjs:87
Finding
Path Traversal in Game and Ranking File Storage<![CDATA[ ## Vulnerability Details **File Location**: `scripts/game-engine.mjs:87-93` **Vulnerability Type**: Path traversal and unrestricted filesystem access **Risk Level**: Medium ### Vulnerable Code ```js function gamePath(groupId) { return path.join(GAMES_DIR, `${groupId}.json`); } function rankingPath(groupId) { return path.join(RANKINGS_DIR, `${groupId}.json`); } ``` The untrusted `groupId` reaches these functions from command handlers at `scripts/game-engine.mjs:142`, `217`, `239`, `273`, `352`, `399`, and `494`. ### Technical Analysis The `--group` command-line argument is used directly as part of a filesystem path without format validation or a containment check. Node.js `path.join()` normalizes traversal sequences such as `../`, allowing a crafted group ID to escape `scripts/data/games` or `scripts/data/rankings`. The generated path is passed to the following filesystem operations: ```js function loadJSON(filePath) { try { return JSON.parse(fs.readFileSync(filePath, 'utf-8')); } catch { return null; } } function saveJSON(filePath, data) { fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8'); } ``` Because `.json` is appended automatically, exploitation is generally limited to paths ending in `.json`. Nevertheless, the application can access or overwrite JSON files outside its designated data directories wherever the Node.js process has filesystem permission. ### Attack Path 1. An attacker supplies a group identifier containing traversal components, such as `../../target`. 2. The application constructs a path equivalent to: ```text scripts/data/games/../../target.json ``` 3. Path normalization resolves this outside the intended `games` directory. 4. A command such as `create` loads the attacker-selected path while checking for an existing game and can subsequently write game data to that path. 5. Other commands similarly use attacker-selected paths for game or ranking reads and writes, subject ...[truncated 681 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict group IDs to a conservative allowlist: ```js function validateGroupId(groupId) { if ( typeof groupId !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(groupId) ) { fail('Invalid group identifier'); } return groupId; } ``` 2. Resolve and verify every generated path remains under its designated directory: ```js function safeJsonPath(baseDir, identifier) { const safeId = validateGroupId(identifier); const base = path.resolve(baseDir); const candidate = path.resolve(base, `${safeId}.json`); if (!candidate.startsWith(`${base}${path.sep}`)) { fail('Resolved path is outside the permitted data directory'); } return candidate; } function gamePath(groupId) { return safeJsonPath(GAMES_DIR, groupId); } function rankingPath(groupId) { return safeJsonPath(RANKINGS_DIR, groupId); } ``` 3. Apply validation before both reads and writes rather than relying only on command-entry validation. 4. Run the game engine under an account with write access limited to its dedicated data directories. 5. Add tests covering `../`, encoded separators, absolute-looking paths, long identifiers, and platform-specific path separators. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:53
Finding
Shell Command Injection Through Documented Argument Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:53-81` **Vulnerability Type**: OS command injection through unsafe shell-command construction **Risk Level**: High ### Vulnerable Code The Skill instructs the Agent to insert session and player data into shell command strings: ```bash node {baseDirectory}/scripts/game-engine.mjs create \ --group <groupId> \ --players '[{"id":"<userId>","name":"<displayName>"}, ...]' ``` It similarly places player-controlled clue text inside double quotes: ```bash node {baseDirectory}/scripts/game-engine.mjs clue \ --group <groupId> \ --player <playerId> \ --text "<线索内容>" ``` Guess data is embedded in another shell argument: ```bash node {baseDirectory}/scripts/game-engine.mjs guess \ --group <groupId> \ --player <playerId> \ --guesses '{"<targetName>":"<guessedIdentity>", ...}' ``` The gameplay procedure at `SKILL.md:145-148` directs the Agent to extract player messages and invoke the `clue` command with that content. ### Technical Analysis Group IDs, player IDs, display names, clue text, target names, and guessed identities may originate from chat or session data. The documented invocation pattern interpolates these values into quoted shell strings without defining a safe argv-array execution mechanism. Shell quoting does not make arbitrary input safe when the input itself can contain quote terminators, command separators, command substitutions, or other shell metacharacters. For example, a clue containing a double quote can terminate the documented `--text "..."` argument. If the resulting command is executed through a shell, subsequent characters are interpreted by the shell before Node.js receives the argument. The game engine does not itself invoke a shell or spawn subprocesses. The vulnerability arises from the Skill instructions requiring an Agent or host runtime to construct and execute shell-form commands using untrusted values. ### Attack Path 1. A player submits a malicious ...[truncated 1660 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Explicitly require a process API that accepts an argument array and disables shell processing: ```js import { spawn } from 'node:child_process'; spawn( process.execPath, [ '/absolute/path/to/game-engine.mjs', 'clue', '--group', groupId, '--player', playerId, '--text', clueText ], { shell: false, stdio: ['ignore', 'pipe', 'pipe'] } ); ``` 2. Do not concatenate or interpolate untrusted values into a command string. 3. Prefer structured input over command-line JSON. For example, provide JSON through standard input and parse it inside the game engine. 4. Validate identifiers such as group and player IDs using strict length and character allowlists. 5. Treat display names, clues, and guessed identities as arbitrary text; do not attempt to make shell interpolation safe using ad hoc escaping. 6. Update `SKILL.md` so all examples describe argv-array invocation or a dedicated tool interface rather than shell commands containing placeholders. 7. Add regression tests using quotes, semicolons, backticks, dollar-sign command substitution, newlines, and platform-specific shell metacharacters. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (4)

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger list includes very broad phrases such as '猜猜猜' and 'identity guess', which can match ordinary conversation and cause the skill to activate unintentionally. Because this skill initiates multiplayer game orchestration and private identity distribution, accidental invocation can disrupt chats, confuse users, and lead to unintended DM sends or state creation.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The skill content mandates Chinese-language hosting behavior rather than adapting to the user's language, which can override user expectations and reduce transparency or informed consent for participants. In a group game context, forcing a language without opt-in can mislead users about what is happening, especially when the skill also performs private identity messaging and multi-step coordination.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script's user-facing documentation and command responses are entirely in Chinese, with no indication that the language is optional or that the tool is intentionally limited to a Chinese-speaking audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code persistently stores game and ranking information as JSON files, and the stored objects include player IDs, names, clues, guesses, scores, and timestamps. Although file storage is mentioned in comments, there is no runtime confirmation, user-facing disclosure, or help text warning that personal/game data will be retained on disk.

Static analysis

No suspicious patterns detected.