Back to skill

Security audit

Calculator Chat

Security checks for vulnerabilities and agentic risk

Overview

The skill is meant to show chat-derived numbers in a calculator, but it ships under-scoped desktop automation and command-execution code that users should review before installing.

Review this skill before installing. It has no obvious exfiltration or persistence, but it can launch and manipulate local calculator applications, close existing calculator windows, and includes exported platform modules with command-injection flaws. Use only in a controlled desktop environment unless the publisher narrows triggers, removes unsafe platform modules, avoids pkill and shell interpolation, and documents explicit per-use consent for GUI automation.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/platform/linux.js:30
Finding
Shell Command Injection in the Linux Calculator Module<![CDATA[ ## Vulnerability Details **File Location**: `src/platform/linux.js`, lines 30–41 **Vulnerability Type**: OS command injection through unvalidated shell interpolation **Risk Level**: High ### Vulnerable Code ```javascript async function typeNumber(number) { if (!number) { throw new Error('Number parameter is required'); } try { // Use --solve flag to directly display the number // This works without xdotool! await execAsync(`gnome-calculator -s ${number} &`, { stdio: 'ignore' }); console.log(`Displayed ${number} on calculator`); } catch (error) { ``` ### Technical Analysis The exported `typeNumber()` function interpolates `number` directly into a command string passed to `child_process.exec()`. The `exec` API invokes a system shell, so shell metacharacters contained in `number` are interpreted as command syntax rather than calculator data. The function only checks whether `number` is nonempty. It does not enforce the documented numeric-expression character set, escape shell metacharacters, or separate the executable from its arguments. Although the current `src/index.js` entry point does not import this platform module, the function is exported and can be invoked by another package component or consumer. Consequently, this is an exploitable API boundary if caller-controlled input reaches it. ### Attack Path 1. An application imports `src/platform/linux.js`. 2. The application passes user-controlled calculator content to `typeNumber()`. 3. The attacker includes shell separators or substitution syntax in the supplied value. 4. The resulting string is passed to `execAsync()`. 5. The system shell parses the injected syntax and executes additional commands with the privileges of the Node.js process. ### Impact Assessment Successful exploitation permits arbitrary command execution as the account running the skill or host agent. This can enable reading or modifying accessible files, launching processes, accessing locall ...[truncated 155 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `exec()` with `spawn()` or `execFile()` and pass arguments as a separate array. - Enforce a strict allowlist at the exported function boundary, such as `^[0-9+\-*/.() ]{1,100}$`. - Reject unexpected input rather than attempting shell escaping. - Do not use `&` for detachment; use the process API's `detached` and `stdio` options. Example: ```javascript const { spawn } = require('child_process'); function typeNumber(number) { const value = String(number); if (!/^[0-9+\-*/.() ]{1,100}$/.test(value)) { throw new Error('Invalid calculator expression'); } const child = spawn('gnome-calculator', ['-s', value], { detached: true, stdio: 'ignore', shell: false }); child.unref(); } ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/platform/macos.js:21
Finding
Shell Command Injection in the macOS AppleScript Launcher<![CDATA[ ## Vulnerability Details **File Location**: `src/platform/macos.js`, lines 21–36 **Vulnerability Type**: Shell command injection caused by unsafe nested quoting **Risk Level**: High ### Vulnerable Code ```javascript async function typeNumber(number) { if (!number) { throw new Error('Number parameter is required'); } const escapedNumber = escapeAppleScript(number); const chars = escapedNumber.split('').join(' '); const script = ` tell application "Calculator" activate end tell delay ${TYPING_DELAY_SEC} tell application "System Events" keystroke "${chars}" end tell `; execSync(`osascript -e '${script}'`, { stdio: ['ignore', 'ignore', 'pipe'] }); } ``` The escaping function used by this code is: ```javascript function escapeAppleScript(str) { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/'/g, '\\\''); } ``` ### Technical Analysis The generated AppleScript is embedded inside a shell command enclosed by single quotes. The `escapeAppleScript()` function attempts to escape single quotes with backslashes, but a backslash does not escape a single quote while the POSIX shell is inside a single-quoted string. An attacker can therefore terminate the shell-quoted `osascript` argument and append additional shell syntax. The input is also embedded in generated AppleScript source, creating a second parsing boundary that should not receive untrusted text directly. The function performs no numeric-expression validation. Its only input check rejects empty values. ### Attack Path 1. A consumer imports the macOS platform module. 2. Attacker-controlled text is passed to `typeNumber()`. 3. The input contains quoting designed to terminate the shell’s single-quoted argument. 4. `escapeAppleScript()` adds a backslash but does not preserve the shell quoting boundary. 5. `execSync()` invokes the shell with the constructed command. 6. The shell interprets the attacker-controlled suffix as addit ...[truncated 455 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not construct an `osascript` shell command as one interpolated string. - Use `execFileSync()` or `spawnSync()` with `shell: false` and argument arrays. - Validate the value against a strict numeric-expression allowlist. - Pass untrusted data to AppleScript through `argv` rather than embedding it in AppleScript source. For example, use an AppleScript `on run argv` handler and invoke it with separate arguments: ```javascript const { execFileSync } = require('child_process'); const value = String(number); if (!/^[0-9+\-*/.() ]{1,100}$/.test(value)) { throw new Error('Invalid calculator expression'); } const script = ` on run argv set calculatorValue to item 1 of argv tell application "Calculator" to activate delay 0.3 tell application "System Events" to keystroke calculatorValue end run `; execFileSync('osascript', ['-e', script, value], { stdio: ['ignore', 'ignore', 'pipe'] }); ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/platform/windows.js:75
Finding
Command Injection in the Windows PowerShell Invocation<![CDATA[ ## Vulnerability Details **File Location**: `src/platform/windows.js`, lines 75–81 **Vulnerability Type**: Command injection through an interpolated Windows shell command **Risk Level**: High ### Vulnerable Code ```javascript fs.writeFileSync(scriptPath, psScript, 'utf8'); try { execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}" -Num "${number}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); ``` The attacker-controlled parameter originates at the exported function boundary: ```javascript async function typeNumber(number) { if (!number) { throw new Error('Number parameter is required'); } ``` ### Technical Analysis `execSync()` receives a string rather than an executable and argument array. Node.js therefore invokes the platform shell to interpret the string. The caller-controlled `number` value is inserted directly inside double quotes without input validation or Windows command-line escaping. A crafted value containing quotes and command-shell metacharacters can terminate the intended `-Num` argument and alter the command interpreted by the shell. The use of `-ExecutionPolicy Bypass` also unnecessarily disables a PowerShell policy safeguard for the spawned process. The generated PowerShell script later supplies `$Num` to `WScript.Shell.SendKeys()`. Even after command injection is fixed, input should be constrained because SendKeys assigns special meanings to characters such as braces and modifier markers. ### Attack Path 1. An application imports `src/platform/windows.js`. 2. User-controlled content is supplied as the `number` argument. 3. The content includes characters that terminate the quoted `-Num` argument and add command-shell syntax. 4. The interpolated string is passed to `execSync()`. 5. The Windows shell parses the injected syntax. 6. Additional commands execute with the privileges of the Node.js process. ### Impact Assessment Successful exploitation provides arb ...[truncated 284 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace string-based `execSync()` with `execFileSync()` or `spawnSync()` and an argument array. - Set `shell: false` explicitly. - Remove `-ExecutionPolicy Bypass` unless there is a documented and security-reviewed operational requirement. - Validate the input as a bounded numeric expression before invoking PowerShell. - Restrict or correctly encode characters with special meaning to `SendKeys()`. Example: ```javascript const { execFileSync } = require('child_process'); const value = String(number); if (!/^[0-9+\-*/.() ]{1,100}$/.test(value)) { throw new Error('Invalid calculator expression'); } execFileSync( 'powershell.exe', ['-NoProfile', '-File', scriptPath, '-Num', value], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], shell: false } ); ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/platform/windows.js:75
Finding
Predictable and Unsafely Cleaned Temporary PowerShell Script<![CDATA[ ## Vulnerability Details **File Location**: `src/platform/windows.js`, lines 18–19 and 75–84 **Vulnerability Type**: Unsafe predictable temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```javascript const baseDir = getBaseDir(); const scriptPath = path.join(baseDir, 'temp_calc.ps1'); ``` ```javascript fs.writeFileSync(scriptPath, psScript, 'utf8'); try { execSync(`powershell -ExecutionPolicy Bypass -File "${scriptPath}" -Num "${number}"`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); } catch (error) { throw error; } if (fs.existsSync(scriptPath)) { fs.unlinkSync(scriptPath); ``` ### Technical Analysis The module writes executable PowerShell content to a fixed filename, `temp_calc.ps1`, in the executable’s base directory. The predictable shared path can collide with another invocation or with a pre-existing file. Where another local process can modify that directory or target path, there is a race window between writing the script and executing it. Cleanup is not placed in a `finally` block. If `execSync()` throws, the catch block immediately rethrows the error, so the subsequent deletion code is never reached. This leaves the script behind after failed executions. The function also overwrites an existing file at the predictable path without checking whether it was created by the current invocation. ### Attack Path 1. The calculator module selects the fixed `temp_calc.ps1` path. 2. Another invocation or a local actor targets the same writable path before PowerShell finishes using it. 3. The file may be replaced, modified, or involved in a path collision before execution. 4. PowerShell executes content from that path. 5. Alternatively, an execution failure causes the function to rethrow before cleanup, leaving the script on disk. Practical exploitation of the race requires local write access to the selected directory or path. Even without an attacker, concurrent ca ...[truncated 392 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer avoiding temporary script files entirely, for example by shipping a read-only script as part of the package or invoking a fixed command safely. - If a temporary script is necessary, create a unique directory with `fs.mkdtempSync()` under `os.tmpdir()`. - Use a cryptographically unpredictable per-invocation filename and restrictive file permissions. - Never place temporary executable content in a shared or package installation directory. - Perform cleanup in a `finally` block so it runs after both successful and failed execution. - Avoid check-then-delete sequences where possible and tolerate cleanup errors safely. - Ensure concurrent invocations cannot share the same script path. Example structure: ```javascript const os = require('os'); const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'calc-chat-')); const scriptPath = path.join(tempDir, 'calculator.ps1'); try { fs.writeFileSync(scriptPath, psScript, { encoding: 'utf8', mode: 0o600, flag: 'wx' }); execFileSync('powershell.exe', ['-NoProfile', '-File', scriptPath, '-Num', value], { shell: false, stdio: ['ignore', 'pipe', 'pipe'] }); } finally { fs.rmSync(tempDir, { recursive: true, force: true }); } ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (40)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the implementation uses a custom GTK window or arbitrary text display instead of the stated system calculator and phrase-mapping flow, the skill is materially different from what it claims. While this may not always be directly exploitable, deceptive or inaccurate behavior weakens trust boundaries and can conceal unexpected UI spoofing or broader input acceptance.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
If the implementation uses a custom GTK window or arbitrary text display instead of the stated system calculator and phrase-mapping flow, the skill is materially different from what it claims. While this may not always be directly exploitable, deceptive or inaccurate behavior weakens trust boundaries and can conceal unexpected UI spoofing or broader input acceptance.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
If the implementation uses a custom GTK window or arbitrary text display instead of the stated system calculator and phrase-mapping flow, the skill is materially different from what it claims. While this may not always be directly exploitable, deceptive or inaccurate behavior weakens trust boundaries and can conceal unexpected UI spoofing or broader input acceptance.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
If the implementation uses a custom GTK window or arbitrary text display instead of the stated system calculator and phrase-mapping flow, the skill is materially different from what it claims. While this may not always be directly exploitable, deceptive or inaccurate behavior weakens trust boundaries and can conceal unexpected UI spoofing or broader input acceptance.

Vague Triggers

High
Confidence
96% confidence
Finding
Auto-triggering on broadly defined emotional expressions can cause the skill to activate unexpectedly during normal conversation, launching GUI actions without clear user intent. In this context, opening or manipulating a desktop calculator is low-to-moderate impact, but the trigger ambiguity increases the chance of nuisance behavior, focus stealing, and accidental execution of local actions.

Vague Triggers

High
Confidence
95% confidence
Finding
Examples like common everyday phrases make the activation surface too broad, so ordinary chat may trigger the skill unintentionally. Because the skill affects the local desktop environment, accidental activations can repeatedly open or alter calculator windows and degrade user control over the system session.

Self-Modification

High
Category
Rogue Agent
Content
**Files:**
- Create: `calculator-chat/SKILL.md`

**Step 1: Write SKILL.md**

```markdown
---
Confidence
85% confidence
Finding
Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The file implements a standalone GTK calculator GUI, while the skill metadata describes a chat-triggered calculator-display behavior. This mismatch is dangerous because it can conceal undeclared functionality, mislead reviewers about what will execute, and expand the runtime/UI attack surface beyond the documented skill behavior.

eval() call detected

High
Category
Dangerous Code Execution
Content
}
        
        # 使用 eval 但只允许数字和运算符
        result = eval(expr, {"__builtins__": {}}, allowed_names)
        return result
    except Exception:
        return None
Confidence
85% confidence
Finding
Direct eval() call evaluates arbitrary expressions. This can be exploited to execute malicious code or exfiltrate data.

Missing User Warnings

High
Confidence
95% confidence
Finding
The AppleScript subprocess uses System Events keystroke automation to type content into the GUI, which is dangerous because keystrokes may be delivered to the wrong window if focus changes and because this pattern can be repurposed for broader host interaction. The escaping is also not a strong safety boundary for shell-embedded AppleScript, so the combination of subprocess execution and UI injection creates elevated risk relative to the skill's simple stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill declares executable dependencies and its documentation describes shell/process interactions, but it does not declare any explicit tool scope or permissions. This creates a transparency and containment problem: a host may grant or infer broader shell/file/env access than users expect, making local process control and command execution harder to review safely.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The description specifies responding with calculator-number meanings based on Chinese homophonic interpretation and states support for Chinese phonetic translation, but it does not indicate that this is optional or user-selected. This can violate language or locale policy when a skill imposes one language behavior by default.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The design document defines the trigger as `/calc <message>`, while the skill metadata describes activation on `/calc-chat` or emotional expressions. This mismatch is security-relevant because users, reviewers, and enforcement systems may misunderstand when the skill activates and what scope of behavior to expect, increasing the chance of unintended execution or insufficient review of the real behavior.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The design proposes OS-level window discovery and synthetic input mechanisms such as Win32 messaging, AppleScript, and xdotool to control a system application. Even if intended for a calculator, these primitives are broadly applicable to desktop automation and can be repurposed to manipulate other windows, inject input without focus, or bypass normal user awareness, making the skill materially more dangerous in context.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The design explicitly states that it will launch the system calculator and send input in the background without focus, yet provides no user warning or consent model. Hidden application launching and background UI manipulation reduce transparency and can surprise users, creating opportunities for misuse, social engineering, or unauthorized desktop interaction under the guise of a harmless chat feature.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The implementation plan expands a narrowly described skill into a generic CLI that accepts arbitrary user-provided input and automates OS GUI actions based on that input. This increases capability beyond the declared scope, making the skill easier to invoke in unintended contexts and reducing opportunities for policy gating or user consent.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The plan grants the skill subprocess-based GUI automation across Windows, macOS, and Linux, including PowerShell, AppleScript, and xdotool execution. These are powerful primitives that exceed a simple text-mapping feature and materially raise the risk of abuse, accidental interaction with the wrong window, or future extension into arbitrary desktop control.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The SKILL.md documentation changes activation from the declared `/calc-chat` or emotional-expression behavior to `/calc <message>`, creating a mismatch between user expectations, manifest semantics, and actual execution. Such trigger drift can bypass review assumptions and cause the skill to run in broader situations than intended.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The user-facing skill description and usage omit a clear warning that the skill will launch and control a local GUI application through OS automation. Lack of transparency can lead to surprising execution, unsafe consent assumptions, and users authorizing a skill without understanding that it can manipulate desktop state.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The module docstring explicitly describes a full arithmetic calculator, contradicting the stated chat-based emotional-number skill. While not directly exploitable on its own, misleading internal documentation can help hide unauthorized behavior and reduce the chance that reviewers detect functionality drift or policy violations.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The skill launches local processes and opens a system calculator based on user-controlled input. While the input is somewhat constrained by pattern matching and digit extraction, the code still triggers host-side application execution, which is a real security concern for agent skills because it crosses from text handling into local OS interaction and could be abused for unwanted application launches or user disruption.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""检查计算器是否正在运行"""
    try:
        # 使用 pgrep 检查进程,避免直接杀进程
        result = subprocess.run(
            ['pgrep', '-x', 'gnome-calculator'],
            capture_output=True,
            timeout=2
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
Closing existing calculator processes is unnecessary for the advertised function of displaying calculator-chat responses. In a desktop automation setting, this broad side effect can interfere with unrelated user activity and indicates overbroad permissions/capability relative to the skill's declared purpose.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def close_calculator():
    """关闭已打开的计算器"""
    try:
        subprocess.run(
            ['pkill', '-x', 'gnome-calculator'],
            capture_output=True,
            timeout=2
Confidence
95% confidence
Finding
This code kills all `gnome-calculator` processes on the desktop, which exceeds the stated purpose of merely showing a number in the calculator. In an agent/skill context, terminating user applications is a disruptive side effect and can cause denial of service or loss of unsaved calculator state for the current user.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The ability to kill desktop calculator processes is not justified by a skill that should only display numeric messages. Even without privilege escalation, an agent that can terminate user applications creates an avoidable denial-of-service capability and weakens trust boundaries on the desktop.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.dynamic_code_execution

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/index.js:146

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/platform/macos.js:15

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
src/platform/windows.js:78

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
src/calculator.py:40