Back to skill

Security audit

MiniMax XLSX Pro

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent spreadsheet generator, but its recalculation helper permanently changes the user's LibreOffice macro profile and can trust or overwrite application-level macro code.

Install only if you are comfortable with the skill running LibreOffice locally and modifying workbooks. Review or sandbox scripts/recalc.py before use, especially on machines with existing LibreOffice macros or enterprise macro controls; a safer design would use a temporary LibreOffice profile and avoid touching the user's shared macro library.

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

Error
Location
scripts/recalc.py:18
Finding
Untrusted Pre-existing LibreOffice Macro Can Hijack Workbook Recalculation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/recalc.py`, lines 18–52 and 67–73 **Vulnerability Type**: Insufficient validation of an executable application-level macro **Risk Level**: High ### 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 accepted macro is subsequently invoked: ```python cmd = [ "soffice", "--headless", "--norestore", "vnd.sun.star.script:Standard.Module1.RecalculateAndSave?language=Basic&location=application", abs_path, ] ``` ### Technical Analysis The script treats an existing LibreOffice macro as trusted if its source contains the substring `RecalculateAndSave`. It does not validate the exact macro impleme ...[truncated 2142 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Use an isolated LibreOffice profile** - Create a unique temporary directory for every recalculation. - Start LibreOffice with a dedicated profile, such as `-env:UserInstallation=file:///...`. - Ensure the directory is accessible only to the current process or user. - Remove the profile in a `finally` block after recalculation. 2. **Avoid application-level persistent macros** - Prefer a supported non-macro LibreOffice or UNO recalculation interface. - Do not execute macros stored in the user's shared `Standard` library. 3. **Validate trusted executable content** - If a macro remains necessary, write a uniquely named module into the isolated profile. - Compare the complete module content or a cryptographic digest before execution. - Do not use a substring or procedure-name check as a trust decision. 4. **Harden file handling** - Reject symlinked macro paths. - Verify ownership and restrictive permissions before using any existing file. - Use atomic file creation with exclusive semantics rather than overwriting an arbitrary existing module. 5. **Constrain execution** - Run LibreOffice in a sandbox or container with access limited to the target workbook and temporary profile. - Disable network access where recalculation does not require it. - Apply operating-system resource and process restrictions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/recalc.py:18
Finding
Recalculation Helper Permanently Modifies and May Overwrite the Shared LibreOffice User Profile<![CDATA[ ## Vulnerability Details **File Location**: `scripts/recalc.py`, lines 18–52 **Vulnerability Type**: Unsafe persistent modification of shared application state **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 helper writes `Module1.xba` directly into LibreOffice's shared user-level `Standard` macro library. This is global application state, not an execution-scoped temporary location. If `Module1.xba` already exists but does not contain the expected procedure name, the script opens it with mode `"w"` and replaces its entire contents. It does not request consent, create a backup, preserve unrelated user macros ...[truncated 1673 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Move all macro state into a temporary profile** - Allocate a private temporary LibreOffice user profile for each invocation. - Install the macro only into that profile. - Delete the profile after execution, including failure and timeout paths. 2. **Do not overwrite user-owned macro modules** - Never write to the default `Standard/Module1.xba`. - If shared-profile modification is unavoidable, obtain explicit consent and preserve the original file. - Restore the original file after processing. 3. **Use safe file-update semantics** - Check for symbolic links and unexpected file types. - Create files with restrictive permissions. - Write to a new file and atomically rename it only within an isolated directory. - Handle partial writes and crashes without leaving corrupted profile state. 4. **Minimize privileges and scope** - Grant LibreOffice access only to the target workbook and temporary working directory. - Avoid access to the user's full home directory where platform sandboxing permits. - Run recalculation under a dedicated low-privilege account or container for untrusted workbooks. 5. **Document and verify cleanup** - Add automated tests confirming that the normal LibreOffice profile is unchanged after both successful and failed recalculation attempts. ]]>
Vulnerability Patterns
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill explicitly instructs use of powerful capabilities such as shell execution, file reads, and file writes, but it does not declare a restrictive tool scope or permissions boundary. That increases the chance the skill can be invoked with broader-than-necessary authority, enabling command execution on local files and helper binaries without an explicit least-privilege contract.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The description says to engage for any task involving tabular data, numeric analysis, or spreadsheet generation, which is broad enough to capture many ordinary requests. Over-broad routing can cause the agent to invoke this high-capability skill in situations where shell/file tooling is unnecessary, expanding exposure to risky operations and making prompt-trigger abuse easier.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The brief mandates that every engagement produce at least one .xlsx file, regardless of whether a spreadsheet artifact is actually needed. This can force unnecessary file creation and associated shell/file activity, making the skill activate and perform side-effectful operations in ambiguous situations where a simpler, safer response would suffice.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The script writes executable Basic macro code into the user's global LibreOffice profile and then invokes it against a workbook. This permanently changes local application configuration beyond the scope of the current task, increases the attack surface for future LibreOffice use, and can conflict with user security expectations or enterprise macro policies.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code silently creates a macro in the user's LibreOffice profile and saves the workbook as part of recalculation, but the interface and output do not clearly warn that both local application state and the target file will be modified. In an agent skill context processing user-supplied spreadsheets, this hidden side effect is especially risky because users may expect analysis-only behavior.

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)

    macro_content = """<?xml version="1.0" encoding="UTF-8"?>
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.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
Line L064 prescribes a region-specific presentation rule based on market locale: mainland China must use red for gains and green for losses, while all other markets must use the reverse. This is a natural-language locale policy constraint presented as non-optional, and the file does not offer user choice or opt-in.

Static analysis

No suspicious patterns detected.