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. ]]>
