Back to skill

Security audit

Pptx

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a PowerPoint helper, but it bundles broader Office document tooling and a risky LibreOffice native-code workaround that should be reviewed before installation.

Use this only in an isolated environment and review it before giving it access to confidential decks or documents. Pay particular attention to the LibreOffice LD_PRELOAD shim, unpinned/global dependency installs, and the bundled DOCX/XLSX redline tooling that goes beyond a narrow PPTX-only skill.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (5)

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/office/soffice.py:24
Finding
Predictable Temporary Shared Library Enables LD_PRELOAD Tool Hijacking<![CDATA[ ## Vulnerability Details **File Location**: `scripts/office/soffice.py:24-31, 41-67` **Vulnerability Type**: Insecure predictable temporary file and dynamic-library preloading **Risk Level**: High ### Vulnerable Code ```python def get_soffice_env() -> dict: env = os.environ.copy() env["SAL_USE_VCLPLUGIN"] = "svp" if _needs_shim(): shim = _ensure_shim() env["LD_PRELOAD"] = str(shim) return env _SHIM_SO = Path(tempfile.gettempdir()) / "lo_socket_shim.so" def _needs_shim() -> bool: try: s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.close() return False except OSError: return True def _ensure_shim() -> Path: if _SHIM_SO.exists(): return _SHIM_SO src = Path(tempfile.gettempdir()) / "lo_socket_shim.c" src.write_text(_SHIM_SOURCE) subprocess.run( ["gcc", "-shared", "-fPIC", "-o", str(_SHIM_SO), str(src), "-ldl"], check=True, capture_output=True, ) src.unlink() return _SHIM_SO ``` ### Technical Analysis The generated shared library uses the fixed path `/tmp/lo_socket_shim.so`. If that path already exists, `_ensure_shim()` accepts it without validating: - File ownership - File permissions - Whether it is a regular file or symbolic link - Its cryptographic digest or expected contents - Whether it was produced by the current process When UNIX-domain socket creation fails, `get_soffice_env()` places this unverified file in `LD_PRELOAD`. The dynamic loader will load the library before starting LibreOffice, giving its initialization routines and intercepted functions native code execution inside the `soffice` process. The subprocess call itself does not use a shell and is not vulnerable to shell command injection. The vulnerability instead arises from trusting a predictable, cross-process temporary artifact and explicitly preloading it. ### Attack Path 1. An attacker with access to the same host or shared t ...[truncated 1252 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a private temporary directory for every execution: ```python with tempfile.TemporaryDirectory(prefix="lo-shim-") as temp_dir: private_dir = Path(temp_dir) private_dir.chmod(0o700) ``` 2. Generate both the C source and shared library inside that private directory. 3. Create source and output files atomically and reject symbolic links, using protections such as `O_CREAT | O_EXCL | O_NOFOLLOW`. 4. Never accept a pre-existing shared object solely because it exists. 5. Verify that temporary artifacts are regular files owned by the current effective user and are not group- or world-writable. 6. Prefer avoiding `LD_PRELOAD` entirely. If the shim is necessary, ship a reviewed binary as a protected package resource and verify its digest before use. 7. Pass the preload environment only to the exact LibreOffice subprocess that requires it. 8. Consider clearing any inherited `LD_PRELOAD` value before constructing the subprocess environment. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/office/soffice.py:53
Finding
Predictable Temporary C Source Permits Symlink-Based File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/office/soffice.py:53-65` **Vulnerability Type**: Unsafe temporary-file creation and symlink following **Risk Level**: Medium ### Vulnerable Code ```python def _ensure_shim() -> Path: if _SHIM_SO.exists(): return _SHIM_SO src = Path(tempfile.gettempdir()) / "lo_socket_shim.c" src.write_text(_SHIM_SOURCE) subprocess.run( ["gcc", "-shared", "-fPIC", "-o", str(_SHIM_SO), str(src), "-ldl"], check=True, capture_output=True, ) src.unlink() return _SHIM_SO ``` ### Technical Analysis The C source is written to the predictable path `/tmp/lo_socket_shim.c`. `Path.write_text()` follows symbolic links and does not request exclusive creation. Consequently, another local process can create the path before the Skill and redirect the write to a different file. The same shared temporary namespace also permits race conditions in which another process modifies the source between the write and compiler invocation. These are time-of-check/time-of-use weaknesses because no private directory, exclusive descriptor, or ownership validation protects the build artifacts. ### Attack Path 1. An attacker creates `/tmp/lo_socket_shim.c` as a symbolic link to a file writable by the victim account. 2. The victim invokes a LibreOffice conversion in an environment where the socket shim is required. 3. `_ensure_shim()` calls `src.write_text(_SHIM_SOURCE)`. 4. The operation follows the symbolic link and truncates or overwrites its target. 5. Depending on the selected target, the attacker can corrupt victim-owned configuration, project, or output files. 6. Alternatively, an attacker can race the compiler invocation and replace the source with attacker-controlled C code before `gcc` reads it. ### Impact Assessment The direct overwrite is limited to files writable by the account running the Skill; it does not bypass operating-system permissions. Within that boundary, ex ...[truncated 383 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Place all build artifacts in a process-private directory created by `tempfile.TemporaryDirectory()`. - Restrict the private directory to mode `0700`. - Create files with exclusive, no-follow semantics rather than `Path.write_text()` on a shared predictable path. - Keep an open file descriptor while writing and compiling where practical, reducing replacement races. - Verify with `lstat()` that each artifact is a regular file and not a symbolic link. - Ensure files are owned by the effective user and are not group- or world-writable. - Delete the entire private directory after conversion instead of unlinking predictable global files. - Serialize shim creation if multiple threads or processes can execute this path concurrently. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/add_slide.py:90
Finding
Unvalidated Slide Source Path Allows Local File Inclusion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/add_slide.py:90-107` **Vulnerability Type**: Path traversal and unintended local-file inclusion **Risk Level**: Medium ### Vulnerable Code ```python def duplicate_slide(unpacked_dir: Path, source: str) -> None: slides_dir = unpacked_dir / "ppt" / "slides" rels_dir = slides_dir / "_rels" source_slide = slides_dir / source if not source_slide.exists(): print(f"Error: {source_slide} not found", file=sys.stderr) sys.exit(1) next_num = get_next_slide_number(slides_dir) dest = f"slide{next_num}.xml" dest_slide = slides_dir / dest source_rels = rels_dir / f"{source}.rels" dest_rels = rels_dir / f"{dest}.rels" shutil.copy2(source_slide, dest_slide) ``` ### Technical Analysis The command-line `source` value is appended directly to the expected slides directory. It is not required to be a basename matching the documented `slideN.xml` format, and the resolved path is not checked to ensure that it remains under `unpacked_dir/ppt/slides`. A source containing `../` components can therefore escape the slides directory. An absolute path also replaces the preceding `Path` components. If the selected local file contains well-formed XML, it can be copied into the unpacked presentation as a slide and subsequently included in the packed Office archive. The later packing process parses files with an `.xml` destination name, so arbitrary non-XML files may cause packing to fail. Nevertheless, readable XML files and attacker-prepared local XML files can be included, making the traversal exploitable under realistic conditions. ### Attack Path 1. The attacker can influence the `source` argument supplied to `add_slide.py`. 2. The attacker chooses a traversal or absolute path pointing to a readable, well-formed XML file outside the presentation workspace. 3. `source_slide = slides_dir / source` resolves to the external file. 4. The existence check succeeds be ...[truncated 880 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce the documented source filename format: ```python if not re.fullmatch(r"slide\d+\.xml", source): raise ValueError("Invalid slide source name") ``` 2. Reject absolute paths and any value containing directory separators. 3. Resolve and verify the source path: ```python base = slides_dir.resolve() candidate = (base / source).resolve(strict=True) if not candidate.is_relative_to(base): raise ValueError("Source escapes slides directory") ``` 4. Require `candidate.is_file()` and reject symbolic links unless explicitly needed and safely resolved. 5. Apply equivalent basename and containment checks to layout-file inputs. 6. Parse the selected source as expected presentation XML and verify its root namespace and element before copying. 7. Use structured XML APIs when creating relationship entries rather than string interpolation. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:226
Finding
Unpinned and Globally Installed Third-Party Dependencies Create Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:226-232` **Vulnerability Type**: Unpinned dependency installation from mutable package registries **Risk Level**: Medium ### Vulnerable Code ```markdown ## Dependencies - `pip install "markitdown[pptx]"` - text extraction - `pip install Pillow` - thumbnail grids - `npm install -g pptxgenjs` - creating from scratch - LibreOffice (`soffice`) - PDF conversion (auto-configured for sandboxed environments via `scripts/office/soffice.py`) - Poppler (`pdftoppm`) - PDF to images ``` An additional global installation instruction appears in `pptxgenjs.md:238`: ```markdown Install: `npm install -g react-icons react react-dom sharp` ``` ### Technical Analysis The Skill instructs users or Agents to install dependencies without exact versions, hashes, or a lockfile. The npm instructions also install packages globally. Package-manager installation can execute package build or lifecycle logic, especially for native modules such as `sharp`. Because dependency names resolve to the latest registry-selected versions at installation time, the reviewed Skill does not uniquely define the code that will execute. Future upstream compromise, malicious maintainer releases, account takeover, or an unsafe registry configuration could introduce code not present during this audit. No evidence was found that the named packages are intentionally malicious. The finding concerns the insecure and non-reproducible dependency acquisition process. ### Attack Path 1. An Agent follows the documented setup command. 2. The package manager queries its configured mutable registry. 3. An upstream package, transitive dependency, maintainer account, or registry response has been compromised or unexpectedly changed. 4. The package manager downloads an unreviewed version because no exact version or integrity hash is required. 5. Installation or package lifecycle code executes under the Agent’s user account. 6. For global npm installatio ...[truncated 662 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin every direct dependency to a reviewed exact version. - Maintain Python and npm lockfiles that include transitive dependencies. - Use integrity hashes, such as pip’s `--require-hashes`, where supported. - Install dependencies inside a dedicated virtual environment or project-local npm directory. - Replace global npm installation with project-local installation and invoke binaries through a controlled project script. - Disable unnecessary package lifecycle scripts where feasible. - Document the expected official registry and reject untrusted registry overrides. - Use automated dependency scanning and promptly review security advisories. - Reproduce builds in an isolated environment with minimal filesystem and network privileges. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/office/unpack.py:52
Finding
Unbounded Office Archive Extraction Enables Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/office/unpack.py:52-54` **Vulnerability Type**: Unrestricted archive extraction and ZIP-bomb denial of service **Risk Level**: Medium ### Vulnerable Code ```python with zipfile.ZipFile(input_path, "r") as zf: zf.extractall(output_path) ``` Equivalent unrestricted extraction is also performed at: ```python # scripts/office/validate.py:71-74 temp_dir = tempfile.mkdtemp() with zipfile.ZipFile(path, "r") as zf: zf.extractall(temp_dir) # scripts/office/validators/base.py:800-802 with zipfile.ZipFile(self.original_file, "r") as zip_ref: zip_ref.extractall(temp_path) # scripts/office/validators/docx.py:186-189 with zipfile.ZipFile(original, "r") as zip_ref: zip_ref.extractall(temp_dir) # scripts/office/validators/redlining.py:62-64 with zipfile.ZipFile(self.original_docx, "r") as zip_ref: zip_ref.extractall(temp_path) ``` ### Technical Analysis PPTX, DOCX, and XLSX files are ZIP archives and should be treated as untrusted compressed input. These extraction paths do not inspect archive metadata before extraction and impose no limits on: - Number of members - Per-member uncompressed size - Total uncompressed size - Compression ratio - Directory depth - Processing time A malicious archive can contain a small amount of compressed data that expands to a very large size. Even if extraction paths remain within the destination, unrestricted expansion can exhaust disk space. Subsequent recursive XML discovery, parsing, pretty-printing, and validation can additionally consume excessive CPU and memory. ### Attack Path 1. An attacker supplies a PPTX, DOCX, or XLSX file containing highly compressible oversized members or an excessive number of entries. 2. The victim follows the documented workflow and invokes unpacking, validation, rendering, or repacking validation. 3. The affected code calls `extractall()` without preflight limits. 4. The archive expands until disk space, file-count ...[truncated 645 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Implement a shared safe-extraction function and use it at every archive extraction site. Before writing any member: 1. Set a maximum archive-member count. 2. Reject members exceeding a defined uncompressed-size limit. 3. Enforce a maximum aggregate uncompressed size. 4. Reject suspicious compression ratios. 5. Normalize member names and reject absolute paths, parent-directory traversal, duplicate normalized paths, and special-file entries. 6. Enforce a maximum directory depth and filename length. 7. Stream extraction in bounded chunks while tracking bytes actually written. 8. Abort and delete the temporary directory when any quota is exceeded. 9. Apply process-level CPU, memory, disk, and wall-clock limits when processing untrusted documents. 10. Avoid `tempfile.mkdtemp()` without cleanup management; use `TemporaryDirectory()` so normal exits remove extracted content. ]]>
Vulnerability Patterns
  • 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (40)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If the skill actually handles Word tracked-change validation workflows, original ZIP extraction, and document comparison, then it is not merely mislabeled but likely to process sensitive editorial history under a presentation-themed entry point. That can leak or alter hidden metadata and review information unexpectedly.

Vague Triggers

High
Confidence
98% confidence
Finding
The trigger conditions are extremely broad, activating on generic terms like 'deck,' 'slides,' or 'presentation' regardless of intended operation. In an agent environment this can hijack many ordinary tasks, causing unnecessary file access and shell-based document processing on untrusted content when a simpler or safer path would suffice.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This helper operates on WordprocessingML content (word/document.xml, w:ins, w:del) inside a skill explicitly scoped to PPTX presentation files. That scope mismatch is dangerous because it expands the skill's effective capabilities beyond what users and reviewers would expect, enabling silent modification or inspection of DOCX-style content under a presentation-oriented trust boundary.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The helper compiles and injects an LD_PRELOAD shared library into LibreOffice, which is a powerful process-hijacking mechanism unrelated to normal PPTX parsing or generation. In a document-handling skill, this substantially increases risk because it changes runtime behavior at the OS and libc boundary rather than staying within ordinary application functionality.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def get_soffice_env() -> dict:
    env = os.environ.copy()
    env["SAL_USE_VCLPLUGIN"] = "svp"

    if _needs_shim():
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill invokes shell commands and performs file read/write operations but does not declare any explicit tool scope or permissions. This weakens containment and review because downstream systems and operators cannot easily tell what capabilities the skill expects, increasing the chance of over-privileged execution.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
This helper operates on DOCX internals (`word/document.xml`) even though the skill is explicitly scoped to PPTX files. That scope mismatch is dangerous because it can cause the agent to touch and modify a different Office format than the user intended, increasing the chance of unintended file corruption or unauthorized modification of document content in mixed-workflow environments.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The code writes modified XML back to `word/document.xml` in place with no backup, prompt, or transactional safeguard. In an agent context, this can silently alter user documents, destroy recoverable state, or persist unintended changes if the helper is invoked on the wrong file or with malformed content.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code writes back to word/document.xml in place after transforming tracked changes, with no confirmation, backup, or safety guard. In an automated agent setting, this can cause silent destructive edits to user documents, loss of forensic history in redlines, and unexpected tampering with document contents if the helper is invoked on the wrong input.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The module is presented as a PPTX helper but actually modifies LibreOffice runtime semantics and socket behavior process-wide when certain conditions are met. That scope expansion is risky because users and reviewers may expect file-conversion logic, not native runtime interception and altered IPC behavior.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This function launches LibreOffice with an LD_PRELOAD environment variable set when the shim path is deemed necessary, causing implicit code injection into the child process. Hidden preload injection is dangerous because it grants arbitrary interception capability inside the target process and is difficult for downstream callers to notice or reason about.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_soffice(args: list[str], **kwargs) -> subprocess.CompletedProcess:
    env = get_soffice_env()
    return subprocess.run(["soffice"] + args, env=env, **kwargs)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The code silently writes source and a compiled shared library into the temporary directory without transparency or hardening. Besides trust and auditability issues, placing executable artifacts in /tmp increases exposure to local tampering, stale artifact reuse, and confusion about provenance.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
src = Path(tempfile.gettempdir()) / "lo_socket_shim.c"
    src.write_text(_SHIM_SOURCE)
    subprocess.run(
        ["gcc", "-shared", "-fPIC", "-o", str(_SHIM_SO), str(src), "-ldl"],
        check=True,
        capture_output=True,
Confidence
96% confidence
Finding
This code dynamically writes C source into a world-writable temporary directory and invokes gcc to compile a shared object that will later be injected into another process. That creates a dangerous code-loading path and introduces tampering and race-condition risk if another local actor can influence files in the temp directory or executable resolution.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The script explicitly supports DOCX and XLSX in addition to PPTX, which exceeds the declared skill scope. In an agent setting, this scope mismatch can cause the wrong tool to be invoked on non-presentation Office files, leading to unintended access, extraction, and modification of sensitive document or spreadsheet contents beyond what the user expected.

Static analysis

No suspicious patterns detected.