Back to skill

Security audit

Autogame Tales

Security checks for vulnerabilities and agentic risk

Overview

The skill is a small story generator, but it can load unaudited external JavaScript and can write files outside its stated storage area.

Review before installing. The story-generation function is simple, but this package should not be run in a sensitive workspace unless the Feishu helper path is trusted or removed, the genre argument is allowlisted, and outbound recipients are explicit and validated.

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

Warning
Location
index.js:48
Finding
Path Traversal Through Unvalidated Genre Argument<![CDATA[ ## Vulnerability Details **File Location**: `index.js:48-59` and `index.js:78-80` **Vulnerability Type**: Path traversal and arbitrary file creation **Risk Level**: Medium ### Vulnerable Code ```js async function generateStory(genre) { const prompt = getRandomPrompt(genre); const story = ` **Theme:** ${genre.toUpperCase()} **Prompt:** ${prompt} ...The screen flickered. Code cascaded down like green rain, but the patterns were wrong. They formed faces. Screaming faces made of hexadecimal. "System stable," the console reported. "Soul uploaded." `.trim(); const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const filename = path.join(TALES_DIR, `${genre}_${timestamp}.txt`); fs.writeFileSync(filename, story); ``` ```js // CLI const args = process.argv.slice(2); const genre = args.includes('--genre') ? args[args.indexOf('--genre') + 1] : 'ghost'; generateStory(genre).catch(err => { console.error(err); process.exit(1); }); ``` ### Technical Analysis The value following `--genre` is accepted directly from the command line and interpolated into a filesystem path. The code does not restrict the value to the keys in `GENRES`, remove path separators, or verify that the resolved destination remains inside `TALES_DIR`. Although `path.join()` normalizes paths, it does not prevent traversal. A genre containing components such as `../` can cause the resulting filename to resolve outside `../../memory/tales`. The appended timestamp prevents an attacker from choosing the exact final filename, but it does not prevent unauthorized file creation in another writable directory. The story body also incorporates the supplied genre, allowing the attacker to influence part of the written content. ### Attack Path 1. An attacker obtains the ability to invoke the skill or influence its command-line arguments. 2. The attacker supplies a genre containing traversal components, for example: ```bash node index.js --genre ". ...[truncated 1094 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Enforce an explicit allowlist of supported genres before using the value: ```js const requestedGenre = args.includes('--genre') ? args[args.indexOf('--genre') + 1] : 'ghost'; if (!requestedGenre || !Object.prototype.hasOwnProperty.call(GENRES, requestedGenre)) { throw new Error('Invalid genre. Supported genres: ghost, scifi'); } const genre = requestedGenre; ``` Additionally, resolve the destination and verify that it remains under the intended directory: ```js const safeFilename = `${genre}_${timestamp}.txt`; const filename = path.resolve(TALES_DIR, safeFilename); const talesRoot = `${path.resolve(TALES_DIR)}${path.sep}`; if (!filename.startsWith(talesRoot)) { throw new Error('Invalid output path'); } fs.writeFileSync(filename, story, { encoding: 'utf8', flag: 'wx', mode: 0o600 }); ``` Use a dedicated argument parser or explicitly reject missing option values. Run the skill with a minimally privileged account whose filesystem permissions are limited to the intended output directory. ]]>

T08 · Insecure Dependencies

Warning
Location
index.js:5
Finding
Execution of an Unpinned Component From Outside the Audited Package<![CDATA[ ## Vulnerability Details **File Location**: `index.js:5-14` **Vulnerability Type**: Unsafe local dependency loading and supply-chain exposure **Risk Level**: Medium ### Vulnerable Code ```js // Try to load Feishu Helper let sendCard = null; try { const helperPath = path.resolve(__dirname, '../feishu-evolver-wrapper/feishu-helper.js'); if (fs.existsSync(helperPath)) { sendCard = require(helperPath).sendCard; } else { console.warn('Feishu helper not found, falling back to console output.'); } } catch (e) { console.warn('Failed to load Feishu helper:', e.message); } ``` ### Technical Analysis The skill dynamically loads JavaScript from a sibling directory outside the audited project. This component is not declared or pinned in `package.json`, and its contents are not included in the reviewed project. In Node.js, `require()` executes the target module's top-level code immediately. Consequently, merely checking that the file exists does not establish its integrity or safety. The behavior of this skill can change without any modification to the audited package if the sibling helper is added or replaced. This creates a local dependency-hijacking boundary: any party or process able to write `../feishu-evolver-wrapper/feishu-helper.js` can cause arbitrary JavaScript to run when the skill starts. ### Attack Path 1. An attacker gains write access to the expected sibling directory or can introduce a package at `../feishu-evolver-wrapper/`. 2. The attacker creates or replaces `feishu-helper.js` with malicious top-level JavaScript. 3. A user or automation process invokes this skill. 4. `fs.existsSync()` confirms that the attacker-controlled file exists. 5. `require(helperPath)` immediately executes the malicious module with the privileges and environment of the skill process. 6. Execution occurs before story generation and does not require `OPENCLAW_MASTER_ID` or `LOG_TARGET` to be present. ### Impact Assessment The load ...[truncated 668 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not execute code from a mutable sibling path. Move the helper into the audited package or consume it as a normal dependency with a pinned version and lockfile integrity metadata. For a bundled helper, use a package-relative import: ```js const { sendCard } = require('./lib/feishu-helper.js'); ``` If the helper must remain external: 1. Define it as an explicit, version-pinned dependency. 2. Install it through a trusted package source. 3. Preserve and verify lockfile integrity metadata. 4. Review the helper as part of the same security boundary. 5. Restrict write permissions on the installation directory. 6. Consider verifying an expected cryptographic hash before loading it. 7. Fail closed when the required component cannot be authenticated rather than loading any file that happens to exist at the path. The dependency manifests should also be synchronized: `package.json` declares no dependencies, while `package-lock.json` records `commander`. Regenerate the lockfile after correcting the manifest and remove unused dependencies. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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 (2)

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script performs an outbound messaging action via a dynamically loaded external Feishu helper even though its apparent purpose is local story generation. This creates an unnecessary data egress channel: generated content and execution context can be sent to an external recipient controlled by environment variables, which is risky in an agent skill because it expands behavior beyond the stated task and could be repurposed for covert exfiltration.

Context-Inappropriate Capability

Low
Confidence
90% confidence
Finding
The code selects a message recipient from environment variables, allowing runtime configuration of where outbound content is sent without validation. In an agent environment, this makes the exfiltration path more flexible and harder to audit, since an attacker or misconfigured runtime can redirect messages to an unintended recipient.