Back to skill

Security audit

lhx11

Security checks for vulnerabilities and agentic risk

Overview

The skill is for legitimate spreadsheet work, but formula recalculation can automatically create or reuse a persistent LibreOffice macro in the user's profile.

Review this skill before installing if you use LibreOffice locally. Formula recalculation may modify your LibreOffice user profile and leave a macro behind, so prefer running it in an isolated profile or on a disposable account, and use copies of important workbooks unless the skill is updated to require consent and avoid persistent macro changes.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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)

T07 · Tool Hijacking and Spoofing

Warning
Location
recalc.py:20
Finding
Unsafe Persistent Installation and Reuse of a Global LibreOffice Macro<![CDATA[ ## Vulnerability Details **File Location**: `recalc.py`, lines 20-76 **Vulnerability Type**: Persistent modification and unsafe trust of a local tool macro **Risk Level**: Medium ### Vulnerable Code ```python def setup_libreoffice_macro(): """Setup LibreOffice macro for recalculation if not already configured""" if platform.system() == 'Darwin': macro_dir = os.path.expanduser('~/Library/Application Support/LibreOffice/4/user/basic/Standard') else: macro_dir = os.path.expanduser('~/.config/libreoffice/4/user/basic/Standard') macro_file = os.path.join(macro_dir, 'Module1.xba') if os.path.exists(macro_file): with open(macro_file, 'r') as f: if 'RecalculateAndSave' in f.read(): return True if not os.path.exists(macro_dir): subprocess.run(['soffice', '--headless', '--terminate_after_init'], capture_output=True, timeout=10) os.makedirs(macro_dir, exist_ok=True) macro_content = '''<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd"> <script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic"> Sub RecalculateAndSave() ThisComponent.calculateAll() ThisComponent.store() ThisComponent.close(True) End Sub </script:module>''' try: with open(macro_file, 'w') as f: f.write(macro_content) return True except Exception: return False ``` The resulting global macro is subsequently invoked as follows: ```python cmd = [ 'soffice', '--headless', '--norestore', 'vnd.sun.star.script:Standard.Module1.RecalculateAndSave?language=Basic&location=application', abs_path ] ``` ### Technical Analysis The script installs `Module1.xba` in LibreOffice's persistent per-user profile instead of creating an isol ...[truncated 2692 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a unique temporary LibreOffice user profile for every recalculation run. - Launch LibreOffice with an isolated profile, for example through `-env:UserInstallation=file:///path/to/temporary/profile`. - Install the generated macro only inside that temporary profile and remove the profile after processing. - Do not write to or overwrite the user's global `Standard/Module1.xba`. - If persistent installation is unavoidable, validate the complete file contents against the exact expected macro rather than checking for a substring. - Check file ownership and permissions before trusting any existing macro. - Use a uniquely named module and procedure to reduce collisions with user-managed macros. - Back up existing configuration before any modification and restore it after the operation. - Restrict permissions on generated profile files so that other local users cannot modify them. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
recalc.py:79
Finding
Recalculation Timeout Is Not Enforced on All Supported Platforms<![CDATA[ ## Vulnerability Details **File Location**: `recalc.py`, lines 79-95 **Vulnerability Type**: Missing subprocess timeout and local denial of service **Risk Level**: Low ### Vulnerable Code ```python # Handle timeout command differences between Linux and macOS if platform.system() != 'Windows': timeout_cmd = 'timeout' if platform.system() == 'Linux' else None if platform.system() == 'Darwin': # Check if gtimeout is available on macOS try: subprocess.run(['gtimeout', '--version'], capture_output=True, timeout=1, check=False) timeout_cmd = 'gtimeout' except (FileNotFoundError, subprocess.TimeoutExpired): pass if timeout_cmd: cmd = [timeout_cmd, str(timeout)] + cmd result = subprocess.run(cmd, capture_output=True, text=True) ``` ### Technical Analysis The final `subprocess.run()` call does not use Python's native `timeout` parameter. Timeout enforcement instead depends on an external utility: - Linux assumes that the `timeout` command is installed and callable. - macOS only enforces the timeout when the optional `gtimeout` utility is available. - Windows receives no timeout enforcement at all. Consequently, on Windows and on macOS systems without `gtimeout`, LibreOffice can run indefinitely. Linux can also fail unexpectedly if the assumed utility is unavailable. The user-supplied timeout argument therefore does not reliably provide the behavior documented by the script. Spreadsheet parsing and formula recalculation operate on potentially untrusted and complex input. A malformed, unusually expensive, or problematic workbook could cause LibreOffice to hang or consume resources for an extended period. ### Attack Path 1. An attacker supplies a workbook that causes LibreOffice recalculation to stall or take an excessive amount of time. 2. The victim runs `recalc.py` on Windows or on macOS without `gtimeout`. ...[truncated 827 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pass the requested limit directly to Python: ```python try: result = subprocess.run( cmd, capture_output=True, text=True, timeout=timeout, check=False, ) except subprocess.TimeoutExpired: return { 'error': 'LibreOffice recalculation timed out', 'timeout_seconds': timeout, } ``` - Do not rely on platform-specific external timeout utilities as the primary control. - On timeout, terminate the entire LibreOffice process tree rather than only the immediate child. - Run LibreOffice in an isolated process group or session so descendant processes can be reliably terminated. - Apply reasonable bounds to the timeout argument and reject zero, negative, or excessively large values. - Consider additional workbook size, sheet-count, and cell-count limits before invoking LibreOffice. - Return a structured timeout status so callers can distinguish a timeout from configuration and workbook errors. ]]>
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill is presented as a general spreadsheet capability, but the documented behavior includes running an external recalculation workflow and automatically configuring LibreOffice by writing a macro on first run. This mismatch is dangerous because users or higher-level orchestrators may invoke the skill assuming routine spreadsheet handling, while it performs local environment modification and code execution side effects not clearly disclosed in the core declaration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill content clearly directs file reads/writes and shell execution (`python recalc.py ...`) but declares no explicit tool scope or permissions boundaries. In an agent environment, this can enable broader-than-expected filesystem and command execution behavior without policy guardrails or user visibility.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The activation description is extremely broad and could cause the skill to trigger for many spreadsheet-related requests, including read-only analysis tasks that do not need file mutation or shell execution. Over-broad routing increases the chance that powerful file-write and command behaviors are invoked in contexts where a safer, more limited tool would suffice.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs execution of `recalc.py` and says it will automatically configure LibreOffice on first run, which implies local code execution and modification of user application configuration. Running scripts and installing/configuring macros without clear warning or consent materially raises the risk of persistence, environment tampering, and unexpected side effects.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The examples demonstrate saving new spreadsheets and modifying existing workbooks without any user-facing disclosure that local files may be changed. In agent settings, silent mutation of user files can cause data loss, template corruption, or unintended overwrites, especially when users expect analysis rather than modification.

Behavior Manipulation

Medium
Category
Prompt Injection
Content
## CRITICAL: Use Formulas, Not Hardcoded Values

**Always use Excel formulas instead of calculating values in Python and hardcoding them.** This ensures the spreadsheet remains dynamic and updateable.

### ❌ WRONG - Hardcoding Calculated Values
```python
Confidence
70% confidence
Finding
Subtle instructions detected that may alter agent decision-making or introduce hidden biases.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The file presents itself as a spreadsheet recalculation utility, but it also modifies global LibreOffice configuration by creating or overwriting user macro files. In an agent skill context, this is more dangerous because the skill may be invoked automatically on user content, and hidden environment modification exceeds the apparent scope of the requested operation.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The script writes a persistent LibreOffice application macro into the user's profile, changing global application state outside the target spreadsheet. That creates an unexpected trust boundary violation: future LibreOffice sessions may invoke or retain this macro, and the skill modifies executable macro content in a location users would not expect from a file-recalculation helper.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The code edits LibreOffice macro files under the user's profile without any explicit user-facing warning or consent, which is a problematic hidden side effect. Because macros are executable application artifacts, silently creating them can undermine user trust and may affect later document handling or security posture.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return True
    
    if not os.path.exists(macro_dir):
        subprocess.run(['soffice', '--headless', '--terminate_after_init'], 
                      capture_output=True, timeout=10)
        os.makedirs(macro_dir, exist_ok=True)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if platform.system() == 'Darwin':
            # Check if gtimeout is available on macOS
            try:
                subprocess.run(['gtimeout', '--version'], capture_output=True, timeout=1, check=False)
                timeout_cmd = 'gtimeout'
            except (FileNotFoundError, subprocess.TimeoutExpired):
                pass
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
if timeout_cmd:
            cmd = [timeout_cmd, str(timeout)] + cmd
    
    result = subprocess.run(cmd, capture_output=True, text=True)
    
    if result.returncode != 0 and result.returncode != 124:  # 124 is timeout exit code
        error_msg = result.stderr or 'Unknown error during recalculation'
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The script invokes external executables like soffice, timeout, and gtimeout to perform recalculation, which is a safety-relevant shell/subprocess action. While subprocess use is part of the skill's purpose, the user-facing CLI output does not explicitly mention that external system commands will be launched.

Static analysis

No suspicious patterns detected.