Back to skill

Security audit

lhx

Security checks for vulnerabilities and agentic risk

Overview

This spreadsheet skill is useful and mostly purpose-aligned, but its formula recalculation script makes persistent changes to the user's LibreOffice macro profile that are not fully scoped or contained.

Install only if you are comfortable with an agent running LibreOffice locally and allowing this skill to alter your LibreOffice macro profile. Prefer running it in an isolated user profile or container, avoid elevated privileges, and keep backups of important spreadsheets and LibreOffice macros before using formula recalculation.

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

T09 · Insecure Skill Coding Practices

Warning
Location
recalc.py:18
Finding
Persistent Global LibreOffice Macro Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `recalc.py`, lines 18–51 **Vulnerability Type**: Persistent modification of global user configuration and unsafe file overwrite **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 ``` ### Technical Analysis The recalculation script installs a macro into LibreOffice's persistent, global user profile rather than using an isolated profile created specifically for the current operation. The target filename, `Module1.xba`, is generic and predictable. If that module already exists but does not contain the string `RecalculateAndSave`, opening it with mode `w` tru ...[truncated 2273 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Use an isolated LibreOffice profile** - Create a private temporary directory for each invocation. - Launch LibreOffice with `-env:UserInstallation=file:///...` so that macro installation cannot modify the user's normal profile. - Remove the temporary profile in a `finally` block after recalculation. 2. **Avoid overwriting existing modules** - Use a unique, application-specific module name instead of `Module1.xba`. - If a destination already exists, fail safely rather than truncating it. - Do not use a substring search as the sole integrity check; validate the complete expected module content if reuse is necessary. 3. **Defend against symbolic-link writes** - Confirm that the destination does not exist as a symbolic link. - Create new files with exclusive creation semantics, such as mode `x` or `os.open()` with `O_CREAT | O_EXCL`. - Verify the file type with `lstat()` and use atomic replacement only inside a trusted, private directory. 4. **Restrict permissions** - Create temporary profile directories with permissions accessible only to the invoking user. - Do not run the recalculation script with elevated privileges. 5. **Ensure cleanup** - Terminate the isolated LibreOffice process and delete the temporary macro and profile after each run, including timeout and exception paths. ]]>
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 (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill is described as a general spreadsheet creation/editing/analysis capability, but its documented workflow requires running an external recalculation script that configures LibreOffice and writes a macro into the user's environment. This mismatch prevents informed consent and can hide side effects that materially change the host system beyond simple spreadsheet processing.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill instructs use of file read/write operations and shell execution (`python recalc.py`) but declares no explicit tool scope or permissions boundaries. In an agent setting, this can cause the skill to be invoked with broader capabilities than the user expects, increasing the risk of unintended file modification or command execution.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The activation description is very broad and could match many ordinary spreadsheet-related requests, causing the skill to trigger in situations where shell execution, file writes, or workbook modification are unnecessary. Over-broad routing increases the chance that risky operations occur implicitly rather than only when specifically requested.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The workflow makes formula recalculation mandatory via `python recalc.py`, yet the skill description does not clearly warn that this runs an external script and automatically configures LibreOffice. Hidden command execution and environment modification are significant side effects that can surprise users and expand the attack surface.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The examples demonstrate saving modified workbooks and performing structural changes like row/column insertion and deletion without any user-facing warning. In practice, this can normalize destructive file operations and lead an agent to overwrite or alter user spreadsheets without clear disclosure or confirmation.

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.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The script writes a macro into the user's global LibreOffice application profile, permanently changing office automation behavior outside the scope of this single run. In a skill that processes untrusted spreadsheets, modifying a global macro store increases attack surface, creates cross-session persistence, and may interact dangerously with other documents or user workflows.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The recalculation design depends on spawning external office software to open and process attacker-controlled spreadsheet files. In the context of a spreadsheet skill, this is more dangerous because spreadsheet parsers, embedded content, external links, and file-format handling in LibreOffice expand the attack surface beyond simple file parsing in Python, potentially leading to code execution, data exfiltration, or denial of service if LibreOffice is exploited or coerced into network/resource access.

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.

Static analysis

No suspicious patterns detected.