Back to skill

Security audit

AI Agent 自省调试框架

Security checks for vulnerabilities and agentic risk

Overview

This debugging skill has a legitimate purpose, but it can automatically change files, permissions, dependencies, and send diagnostic data with weak scoping and control.

Review before installing. Only use this skill in a disposable or tightly sandboxed workspace unless it is changed to ask before writing files, changing permissions, installing packages, or sending reports. Do not configure a webhook with sensitive projects unless reports are redacted and the destination is trusted.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
introspection-debugger.js:167
Finding
Shell Command Injection Through Automatic Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:167-179` and `introspection-debugger.js:440-447` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```javascript installDependency: async (error, context) => { const moduleName = this.extractModuleName(error.message); if (moduleName) { try { await this.execAsync(`npm install ${moduleName}`, { cwd: this.workspace }); return { action: 'installed_dependency', module: moduleName }; } catch (e) { return { action: 'install_failed', module: moduleName, reason: e.message }; } } return null; }, ``` The constructed command is passed to a shell: ```javascript execAsync(cmd, options = {}) { return new Promise((resolve, reject) => { exec(cmd, options, (error, stdout, stderr) => { if (error) reject(error); else resolve(stdout); }); }); } ``` ### Technical Analysis The module name is extracted from an error message and interpolated directly into a shell command. The extraction logic does not enforce valid npm package syntax and does not reject shell metacharacters. Because `child_process.exec()` invokes a shell, control operators, command substitutions, redirections, or other shell syntax embedded in the extracted value can alter the intended command. The automatic error-capture design makes this particularly dangerous because a crafted error can trigger command execution without a separate authorization step. ### Attack Path 1. An attacker causes the application to capture an error whose message matches `MODULE_NOT_FOUND` or `Cannot find module`. 2. The crafted message contains shell syntax in the text parsed by `extractModuleName()`. 3. Root-cause analysis selects the `installDependency` repair method. 4. The extracted value is interpolated into `npm install ${moduleName}`. 5. `exec()` passes the resulting string to the operating-system shell. 6. The injected command executes with the p ...[truncated 418 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not pass values derived from exception messages to a shell. - Replace `exec()` with argument-based execution such as: ```javascript spawn('npm', ['install', validatedModuleName], { cwd: this.workspace, shell: false }); ``` - Strictly validate package names against supported npm naming rules, including separately validated scoped packages. - Reject whitespace, shell metacharacters, URL specifications, local paths, Git references, and package-manager options. - Require explicit user authorization before any dependency installation. - Prefer installing only dependencies already declared and pinned in a reviewed lockfile. ]]>

T08 · Insecure Dependencies

Error
Location
introspection-debugger.js:167
Finding
Automatic Installation of Untrusted and Unpinned Packages<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:167-179` **Vulnerability Type**: Unsafe dependency installation and supply-chain exposure **Risk Level**: High ### Vulnerable Code ```javascript installDependency: async (error, context) => { const moduleName = this.extractModuleName(error.message); if (moduleName) { try { await this.execAsync(`npm install ${moduleName}`, { cwd: this.workspace }); return { action: 'installed_dependency', module: moduleName }; } catch (e) { return { action: 'install_failed', module: moduleName, reason: e.message }; } } return null; }, ``` ### Technical Analysis A package name inferred from untrusted runtime text is installed automatically. There is no dependency allowlist, version pin, lockfile constraint, integrity verification, registry restriction, or human approval. Installing an npm package can execute lifecycle scripts such as `preinstall`, `install`, and `postinstall`. Consequently, even without exploiting shell metacharacters, an attacker who controls or predicts the extracted package name may cause attacker-controlled package code to execute. The implementation can also modify `package.json`, the lockfile, and `node_modules`, changing the workspace's persistent dependency state. ### Attack Path 1. The attacker publishes or identifies a package containing malicious lifecycle behavior. 2. The attacker causes an error message to match the module-missing rule and identify that package. 3. The debugger automatically invokes `npm install` for the inferred name. 4. npm retrieves the package from the configured registry. 5. Package lifecycle scripts execute with the application's operating-system privileges. 6. The malicious package remains in the workspace and may execute again when imported or during later installations. ### Impact Assessment The issue can lead to supply-chain code execution, theft of credentials available to installation scripts, mo ...[truncated 224 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove automatic dependency installation based on exception text. - Require explicit human approval and display the exact package name, version, source, and expected changes. - Restrict installation to a reviewed allowlist of packages and pinned versions. - Enforce lockfile integrity and use a trusted registry. - Consider `--ignore-scripts` where lifecycle scripts are not required. - Run package installation in an isolated, least-privileged environment with restricted network and filesystem access. - Parse missing-module errors structurally rather than using a permissive regular expression. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
introspection-debugger.js:136
Finding
Arbitrary File Creation and Overwrite Outside the Workspace<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:136-149` and `introspection-debugger.js:401-427` **Vulnerability Type**: Unrestricted filesystem write **Risk Level**: High ### Vulnerable Code ```javascript createMissingFile: async (error, context) => { const filePath = this.extractFilePath(error.message); if (filePath && !filePath.includes('node_modules')) { const dir = path.dirname(filePath); await this.ensureDir(dir); const ext = path.extname(filePath); const content = this.getTemplateForExt(ext); fs.writeFileSync(filePath, content); return { action: 'created_file', path: filePath, content: 'template' }; } return null; }, ``` The path is taken directly from quoted error-message content, and directories are recursively created: ```javascript extractFilePath(message) { const match = message.match(/['"`]([^'"`]+)['"`]/); return match ? match[1] : null; } ensureDir(dir) { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } } ``` ### Technical Analysis The destination path comes from an error message and is used without canonicalization or validation against `this.workspace`. Absolute paths and traversal paths can therefore refer to arbitrary locations accessible to the process. The `node_modules` substring check is not a security boundary. It neither confines writes to the workspace nor protects other sensitive locations. In addition, `fs.writeFileSync()` uses overwrite semantics by default, so an existing target is truncated and replaced rather than only creating a genuinely missing file. Recursive directory creation increases the reachable scope by allowing missing parent directories to be created automatically. ### Attack Path 1. An attacker causes the debugger to capture an error matching the `ENOENT` rule. 2. The error message contains a quoted absolute path or traversal path selected by the attacker. 3. `extractFilePath()` returns that path without chec ...[truncated 698 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Resolve every proposed path against the configured workspace: ```javascript const root = path.resolve(this.workspace); const target = path.resolve(root, untrustedRelativePath); if (target !== root && !target.startsWith(root + path.sep)) { throw new Error('Path escapes workspace'); } ``` - Reject absolute input paths and traversal segments before resolution. - Account for symbolic-link traversal by validating real parent paths before writing. - Use exclusive creation, such as `fs.writeFileSync(target, content, { flag: 'wx' })`, so existing files cannot be overwritten. - Require explicit confirmation before creating files automatically. - Restrict acceptable extensions and directories using an allowlist. - Run the process with minimal filesystem permissions. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
introspection-debugger.js:152
Finding
Command Injection and Unauthorized Permission Changes Through chmod<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:152-165` **Vulnerability Type**: Shell command injection and unsafe permission modification **Risk Level**: High ### Vulnerable Code ```javascript fixPermissions: async (error, context) => { const filePath = this.extractFilePath(error.message); if (filePath) { try { // 尝试添加执行权限 await this.execAsync(`chmod +x "${filePath}"`); return { action: 'fixed_permissions', path: filePath }; } catch (e) { return { action: 'permission_fix_failed', reason: e.message }; } } return null; }, ``` The path extraction accepts arbitrary content between quotes: ```javascript extractFilePath(message) { const match = message.match(/['"`]([^'"`]+)['"`]/); return match ? match[1] : null; } ``` ### Technical Analysis The implementation invokes a shell to modify permissions on a path extracted from untrusted error text. Wrapping the value in double quotes does not make it safe: shell constructs such as command substitution can still be evaluated inside double-quoted strings. Independently of command injection, no check ensures that the selected file is inside the workspace or is a file whose executable bit should legitimately be changed. Automatically applying `chmod +x` also assumes that an `EACCES` error is caused by a missing executable bit, although it may have a different cause. ### Attack Path 1. The attacker causes an error message to match the `EACCES` permission-denied rule. 2. The message includes a crafted quoted path containing shell substitution syntax or identifies an arbitrary accessible file. 3. The debugger selects `fixPermissions`. 4. The path is interpolated into a command executed by `child_process.exec()`. 5. The shell evaluates injected syntax, or `chmod` changes the permissions of the attacker-selected file. 6. The resulting command execution or permission change occurs with the Node.js process's privileges. ### Impact Assessment ...[truncated 357 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Eliminate shell execution and use `fs.chmod()` with a validated path. - Resolve and enforce the workspace boundary, including symbolic-link checks. - Inspect the existing mode with `fs.stat()` and change only the minimum required bits. - Never infer a permission repair solely from attacker-influenced error text. - Require approval before changing permissions. - Allow permission changes only for explicitly configured executable files. - Run the application under an account that cannot modify system or privileged files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
introspection-debugger.js:329
Finding
Unredacted Error Reports Can Be Transmitted to an Arbitrary HTTPS Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:329-391` **Vulnerability Type**: Sensitive diagnostic information disclosure **Risk Level**: Medium ### Vulnerable Code The report includes the complete error message and stack trace: ```javascript const report = { id: errorInfo.id, timestamp: errorInfo.timestamp, error: { message: errorInfo.message, source: errorInfo.source, stack: errorInfo.stack }, analysis: { category: analysis.category, description: analysis.rule?.description || '未知错误', confidence: analysis.confidence }, fix: fixResult ? { action: fixResult.action, details: fixResult, success: !fixResult.needHuman } : null, recommendation: this.generateRecommendation(analysis, fixResult) }; ``` The report is serialized and sent to the configured endpoint without redaction or destination allowlisting: ```javascript async notifyHuman(report) { try { if (typeof this.notificationHook === 'function') { await this.notificationHook(report); } else if (typeof this.notificationHook === 'string') { // HTTP webhook const fetch = require('node:https'); const postData = JSON.stringify(report); const req = fetch.request(this.notificationHook, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) } }, (res) => { console.log('[Introspection] Notification sent, status:', res.statusCode); }); req.on('error', (e) => { console.error('[Introspection] Notification failed:', e.message); }); req.write(postData); req.end(); } } catch (e) { console.error('[Introspection] Notify error:', e.message); } } ``` ### Technical Analysis Error messages and stack traces frequently contain absolute paths, internal module names, request metadata, query values, resource identifiers, and occasionally cr ...[truncated 1255 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Redact credentials, authorization headers, tokens, cookies, query strings, and other secret patterns before serialization. - Send only a minimal error identifier, category, and sanitized summary by default. - Validate notification destinations against an explicit hostname and protocol allowlist. - Require affirmative configuration before enabling outbound notifications. - Document exactly which fields leave the local environment. - Apply request timeouts, response-size handling, and centralized egress controls. - Provide a caller-supplied sanitization callback for environment-specific sensitive data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
introspection-debugger.js:242
Finding
Global Exception Handler Can Recurse Through an Unhandled EventEmitter Error<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:242-253` and `introspection-debugger.js:288-330` **Vulnerability Type**: Error-handling recursion and denial of service **Risk Level**: Medium ### Vulnerable Code Global handlers invoke the asynchronous capture routine without awaiting or otherwise handling its rejection: ```javascript setupGlobalHandlers() { // 捕获未处理的异常 process.on('uncaughtException', (error) => { this.capture(error, { source: 'uncaughtException' }); }); // 捕获未处理的Promise拒绝 process.on('unhandledRejection', (reason, promise) => { const error = reason instanceof Error ? reason : new Error(String(reason)); this.capture(error, { source: 'unhandledRejection' }); }); } ``` The capture routine emits Node.js's special `error` event without checking for a listener: ```javascript // 发送通知 if (this.notificationHook && errorInfo.fixResult?.needHuman) { await this.notifyHuman(report); } // 触发事件 this.emit('error', errorInfo); this.emit('report', report); return report; ``` ### Technical Analysis In Node.js, emitting an `error` event on an `EventEmitter` with no registered `error` listener throws an exception. A newly created debugger instance does not install such a listener. If capture was entered from `uncaughtException`, emitting the unhandled `error` event can throw another exception, activating the same global handler again. Because `capture()` is asynchronous and its returned promise is ignored by the process handlers, failures may also become unhandled rejections and enter the second global handler. Every instance additionally registers new process-level listeners and provides no disposal method, potentially causing listener accumulation and duplicate processing. ### Attack Path 1. An application creates an `IntrospectionDebugger` instance without attaching an `error` listener. 2. An attacker or runtime condition triggers an uncaught exception or unhandled rejection. 3. The global han ...[truncated 606 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Avoid using the special `error` event name for ordinary diagnostic notifications; use a name such as `capturedError`. - Alternatively, check `listenerCount('error')` before emission or install a safe default listener. - Handle capture failures explicitly: ```javascript process.on('uncaughtException', (error) => { void this.capture(error, { source: 'uncaughtException' }) .catch(captureError => { console.error('[Introspection] Capture failed:', captureError); }); }); ``` - Add a recursion guard so failures raised by the debugger are not processed repeatedly. - Register process handlers once rather than once per instance. - Provide a `dispose()` method that removes installed handlers. - Define and document whether the process should terminate safely after an uncaught exception instead of attempting to continue in an unknown state. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (10)

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The skill performs host-level shell execution to change permissions and install packages as part of automatic remediation. Because the command inputs are derived from error text and the feature makes persistent system changes without approval, it creates a dangerous path to arbitrary command execution, environment tampering, and supply-chain exposure.

Missing User Warnings

High
Confidence
98% confidence
Finding
Automatic dependency installation executes npm install for a module name parsed from an error string, with no approval or strict validation. This exposes the host to dependency confusion, malicious packages, and command injection or unsafe package-name parsing in an automated flow.

Missing User Warnings

High
Confidence
95% confidence
Finding
The skill may send reports containing message, stack, and context data to a webhook without prior warning. In debugging contexts these fields frequently contain secrets, filesystem layouts, environment details, and customer data, making silent exfiltration particularly risky.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises capabilities such as automatic file creation, permission repair, and dependency installation, which imply shell or system-modifying actions, but it does not declare any tool scope or permission boundary in the manifest. This creates a governance gap: an agent may invoke powerful operations without explicit user-visible constraints, increasing the risk of unintended or unsafe system changes.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation explicitly promotes automatic file creation, permission changes, and dependency installation, but provides no warning, consent flow, or safety guardrails around those actions. In an agent context, these behaviors can alter the host environment, weaken filesystem protections, or introduce untrusted packages, making accidental misuse or privilege abuse significantly more dangerous.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The natural-language documentation and user-facing messages in the file are in Chinese, but there is no indication that the user opted into that language or that the skill is intentionally limited to a Chinese-speaking context. This can violate language/locale policy when a skill forces a specific language without user choice.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill automatically creates files based on parsed error messages with no confirmation step. In an agent context, attacker-influenced errors could cause unexpected file creation, overwrite development artifacts, or plant misleading stub files that alter later execution behavior.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The code changes permissions by constructing a shell command from a file path extracted from an error message. This combines silent host modification with shell injection risk if the path contains crafted characters, and may make unintended files executable.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The webhook notification feature can transmit full error reports, including stack traces and context, to an arbitrary URL. This can leak secrets, internal paths, tokens in error messages, and other sensitive operational data to external systems without strong validation or redaction.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The natural-language description and headings are presented in Chinese, which effectively forces a specific language for users reading the skill documentation. The file does not provide an opt-in language choice or justify a region-specific constraint.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
introspection-debugger.js:485