Back to skill

Security audit

DeckLingo for PPTX

Security checks for vulnerabilities and agentic risk

Overview

This PPTX translation skill is coherent, but it should go to Review because it can send deck text to Google Translate without a clear consent step and creates world-writable presentation files.

Review before installing on confidential decks. Use only if you are comfortable with selected PPTX text being sent to Google through deep-translator, run it in a private directory or isolated environment, avoid untrusted PPTX files, and consider pinning dependencies before use.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/translate_pptx_text.py:198
Finding
Unhardened XML and ZIP Processing of Untrusted PPTX Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/translate_pptx_text.py:198-211`; equivalent parsing also occurs at `scripts/translate_pptx_text.py:232-241`, `scripts/translate_pptx_text.py:286-289`, and `scripts/scan_pptx_text.py:95-107` **Vulnerability Type**: Unsafe processing of untrusted XML and compressed archives **Risk Level**: High ### Vulnerable Code ```python def collect_unique_paragraphs( files: dict[str, bytes], prefixes: list[str], source_lang: str, skip_patterns: list[re.Pattern[str]], ) -> list[str]: source_pattern = pattern_for(source_lang) found: Counter[str] = Counter() for name in iter_text_files(list(files), prefixes): root = etree.fromstring(files[name]) for paragraph in root.findall(".//a:p", namespaces=NS): text, _ = paragraph_text_nodes(paragraph) if text and matches_source_lang(text, source_lang) and not should_skip(text, skip_patterns): found[text] += 1 ``` The entire PPTX archive is also loaded into memory without limits: ```python with zipfile.ZipFile(temp_path, "r") as src: files = {name: src.read(name) for name in src.namelist()} ``` The scan-only implementation uses the same pattern: ```python with zipfile.ZipFile(path) as archive: for name in archive.namelist(): if not name.endswith(".xml") or not any(name.startswith(prefix) for prefix in prefixes): continue root = etree.fromstring(archive.read(name)) ``` ### Technical Analysis PPTX files are ZIP archives containing attacker-controlled XML. The scripts pass this XML directly to `lxml.etree.fromstring()` without constructing a hardened parser that explicitly disables DTD loading, external entity resolution, and network access. They also do not reject `DOCTYPE` declarations. In addition, ZIP entries are read without limits on: - Total uncompressed package size - Individual entry size - Number of entries - Compression ratio - XML depth, nod ...[truncated 1732 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Construct and consistently reuse a hardened XML parser: ```python SAFE_XML_PARSER = etree.XMLParser( resolve_entities=False, load_dtd=False, no_network=True, huge_tree=False, recover=False, ) if b"<!DOCTYPE" in payload.upper(): raise ValueError("DTD declarations are not permitted") root = etree.fromstring(payload, parser=SAFE_XML_PARSER) ``` 2. Apply this parser at every `etree.fromstring()` call in both scripts. 3. Validate the ZIP central directory before reading content: - Set a maximum number of entries. - Set maximum compressed and uncompressed sizes. - Set a maximum cumulative uncompressed package size. - Reject suspicious compression ratios. - Reject duplicate or malformed entry names. 4. Stream or selectively read only required PPTX entries instead of loading the entire archive into a dictionary. 5. Set limits on XML depth, paragraph count, text length, and translation request size. 6. Catch `zipfile.BadZipFile`, `lxml.etree.XMLSyntaxError`, memory errors, and limit violations, then fail safely without replacing the output. 7. Add regression tests using oversized archives, deeply nested XML, DTD declarations, and entity-expansion payloads. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/translate_pptx_text.py:189
Finding
Presentation Content Is Transmitted to an External Translation Service Without Explicit Consent<![CDATA[ ## Vulnerability Details **File Location**: `scripts/translate_pptx_text.py:189-225` **Vulnerability Type**: Undisclosed external transmission of potentially sensitive presentation data **Risk Level**: High ### Vulnerable Code ```python def make_translator(source_lang: str, target_lang: str) -> GoogleTranslator: source = normalize_translator_lang(source_lang) target = normalize_translator_lang(target_lang) return GoogleTranslator(source=source, target=target) def translate_paragraphs( texts: list[str], translator: GoogleTranslator, glossary: dict[str, str], ) -> tuple[dict[str, str], int]: mapping: dict[str, str] = {} glossary_hits = 0 for text in texts: if text in glossary: mapping[text] = glossary[text] glossary_hits += 1 else: mapping[text] = translator.translate(text) return mapping, glossary_hits ``` The external backend is imported directly: ```python from deep_translator import GoogleTranslator ``` ### Technical Analysis The Skill extracts text from presentations and passes every selected non-glossary paragraph to `GoogleTranslator.translate()`. The affected data can include: - Slide text - Speaker notes - Layout text - Slide-master text - Personal names - Internal project information - Confidential business or technical material This network transmission is intrinsic to the implementation, but the user-facing documentation describes the package as supporting “local automation” and does not clearly state that presentation text is sent to Google through `deep-translator`. The workflow does not require an explicit network opt-in, provide a pre-transmission preview, or offer a bundled offline translation backend. Skip patterns only exclude explicitly matched text. They do not provide general secret detection, redaction, or data-loss prevention. ### Attack Path 1. A user supplies a confidential PPTX and requests translation. 2. The Skill extract ...[truncated 884 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly disclose in `README.md`, `SKILL.md`, CLI help, and runtime prompts that selected presentation text will be transmitted to Google through `deep-translator`. 2. Require explicit informed consent before the first network translation request. 3. Display the selected scopes and the number of paragraphs that will leave the local environment. 4. Add an explicit backend option, for example: ```text --translator google --translator offline --allow-network ``` 5. Refuse to use a network backend unless `--allow-network` or an equivalent affirmative setting is present. 6. Provide an offline translation backend for confidential documents. 7. Keep speaker notes, layouts, and masters excluded unless separately requested and approved. 8. Add optional secret detection and redaction before transmission. 9. Document the destination service, privacy implications, retention uncertainty, and applicable terms. 10. Avoid logging translated source text and ensure exceptions do not expose full confidential paragraphs. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/translate_pptx_text.py:259
Finding
World-Writable Output, Backup, and Temporary Presentation Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/translate_pptx_text.py:259-275` and `scripts/translate_pptx_text.py:300-307` **Vulnerability Type**: Unsafe temporary-file handling and excessive filesystem permissions **Risk Level**: High ### Vulnerable Code ```python temp_path = output_path.with_suffix(output_path.suffix + ".tmp") same_path = input_path.resolve() == output_path.resolve() backup_path = None if same_path: backup_path = input_path.with_name(input_path.name + backup_suffix) if backup_path.exists(): backup_path.chmod(0o666) backup_path.unlink() shutil.copy2(input_path, backup_path) for candidate in (output_path, temp_path): if candidate.exists(): if same_path and candidate.resolve() == input_path.resolve(): continue candidate.chmod(0o666) candidate.unlink() shutil.copy2(input_path, temp_path) temp_path.chmod(0o666) ``` The final output is also made world-writable: ```python with zipfile.ZipFile(temp_path, "w", compression=zipfile.ZIP_DEFLATED) as dst: for name, payload in updated.items(): dst.writestr(name, payload) temp_path.replace(output_path) output_path.chmod(0o666) ``` ### Technical Analysis Mode `0666` grants read and write permission to every local user, subject only to filesystem ACLs and other platform-specific restrictions. The script forcibly applies this mode to temporary and final presentation files rather than preserving restrictive source permissions or honoring a secure default. The temporary path is predictable because it is derived directly from the requested output path by appending `.tmp`. Existing destination, temporary, and backup paths are modified and deleted without explicit symlink rejection. Calling `Path.chmod()` on an attacker-prepared symlink or filesystem object before unlinking it may affect the linked target on platforms where `chmod` follows symlinks. The use of `exists()` followed by `chmod()`, `unlink()`, copyi ...[truncated 1420 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove every `chmod(0o666)` call. 2. Create temporary files atomically with restrictive permissions, preferably mode `0600`, using `tempfile.NamedTemporaryFile()` or `tempfile.mkstemp()` in the destination directory. 3. Preserve source permissions only when appropriate; otherwise use owner-only permissions for files containing confidential material. 4. Reject symlinks and unexpected file types before modifying existing output or backup paths: ```python if path.is_symlink(): raise ValueError(f"Refusing to operate on symlink: {path}") ``` 5. Open files using platform-supported no-follow and exclusive-creation flags such as `O_NOFOLLOW`, `O_CREAT`, and `O_EXCL` where available. 6. Require an explicit overwrite option rather than automatically deleting existing output. 7. Avoid changing permissions merely to delete an existing file. If deletion fails, stop and report the error. 8. Generate a unique temporary filename in the output directory, flush and `fsync()` the completed archive, and then use an atomic replacement operation. 9. Validate destination-directory ownership and permissions before writing sensitive files. 10. Add multi-user and symlink-race tests on supported operating systems. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unbounded Third-Party Dependency Versions Create Supply-Chain Exposure<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-2` **Vulnerability Type**: Unpinned executable third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```text lxml>=5.0.0 deep-translator>=1.11.4 ``` The documented installation command is: ```bash pip install -r requirements.txt ``` ### Technical Analysis Both dependencies use lower-bound-only constraints. A fresh installation may therefore select any future version accepted by the resolver, including versions that were never reviewed or tested by the project. Transitive dependencies are likewise not locked or hash-verified. Python packages can execute code during installation and at runtime. This is particularly security-sensitive for this project because: - `lxml` processes attacker-controlled presentation XML. - `deep-translator` handles presentation text and controls outbound communication with the translation provider. - No lock file or package hashes establish the exact reviewed dependency set. - Builds performed at different times may install materially different code. No evidence was found that either named dependency is currently malicious or typosquatted. The confirmed weakness is the absence of reproducible, integrity-verified dependency resolution. ### Attack Path 1. A user follows the documented `pip install -r requirements.txt` command. 2. The resolver selects the newest versions satisfying the lower bounds, including transitive packages. 3. A future compromised, malicious, or unexpectedly incompatible release is selected automatically. 4. Package-controlled code executes during installation or when the translation scripts import and invoke the dependency. 5. Such code would run with the privileges of the user performing installation or translation and could access the files and network resources available to that user. ### Impact Assessment The potential scope equals the permissions of the account installing or running the Skill. A compromised dep ...[truncated 271 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin exact, reviewed versions instead of using unrestricted lower bounds. 2. Generate a lock file that includes all transitive dependencies. 3. Require package hashes during installation, for example through a hash-locked requirements file and `pip install --require-hashes`. 4. Install only from an explicitly trusted package index. 5. Review dependency release notes and security advisories before updating. 6. Use automated vulnerability scanning while keeping updates subject to testing and approval. 7. Test each locked dependency set against malicious PPTX fixtures and privacy requirements. 8. Run installation in an isolated virtual environment and execute the Skill with least privilege. 9. Document a controlled dependency-update process and retain a software bill of materials. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose says the skill translates PowerPoint decks into Chinese, English, Japanese, and other languages while preserving layout and glossary consistency. The supplied code does something much narrower: it opens a PPTX as a ZIP archive, parses XML for slide-related text, filters paragraphs by likely source language, and outputs a JSON summary of matched text occurrences. While scanning notes/layouts/masters is relevant to presentation processing, the primary behavior is auditing/extracting text statistics, not localization. There is no translation engine, no glossary application, no PPTX rewriting, and no preservation/editability workflow implementation. Therefore the description materially overstates and misrepresents the actual behavior.

Ae1

High
Category
analysis-evasion
Content
environments, and other agent runtimes that can execute local scripts and read `SKILL.md`.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises and documents shell execution plus file read/write behavior, but it does not declare any explicit tool scope such as permissions or allowed-tools. In an agent runtime, that ambiguity can cause the skill to receive broader capabilities than intended, increasing the chance of unsafe file access or command execution when processing user-supplied paths and arguments.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The skill enables implicit invocation without defining any bounded trigger conditions, exclusions, or user-confirmation safeguards. That can cause the agent to automatically route presentation files or translation requests into this skill in situations the user did not clearly intend, increasing the risk of unintended data exposure or unauthorized processing of potentially sensitive deck contents.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The language-detection logic falls back to Chinese when no language is provided, and the CLI also defaults `--source-lang` to `zh`. This imposes a specific language/locale behavior by default rather than offering a neutral default or explicit user choice, which can violate language/locale policy expectations.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The CLI sets `--source-lang` default to `zh`, which means users who do not opt in are subject to a specific language assumption. This is a natural-language policy concern unless the tool is clearly documented as a China-specific workflow.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script sends slide text to Google Translate via `deep_translator.GoogleTranslator`, which means potentially sensitive presentation content leaves the local environment and is transmitted to a third-party service. In a PPTX localization skill, decks often contain confidential business, legal, or product information, so the lack of an explicit warning, consent step, or offline option creates a real data exposure risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
lxml>=5.0.0
deep-translator>=1.11.4
Confidence
96% confidence
Finding
The dependency specifier `lxml>=5.0.0` is unpinned, so builds may resolve to different versions over time, reducing reproducibility and making it harder to ensure that only vetted releases are installed. In a translation skill that processes user-supplied PPTX/XML content, relying on an uncontrolled parser version increases supply-chain and patch-management risk, even if it is not an immediate exploitable flaw by itself.

Unverifiable Dependency: lxml has 14 known advisory(ies) (CVE-2021-43818 (lxml's HTML Cleaner allows crafted and SVG embedded scripts to pass through); CVE-2014-3146 (lxml Cross-site Scripting Via Control Characters); CVE-2021-28957 (lxml vulnerable to Cross-Site Scripting ) +11 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
88% confidence
Finding
`lxml` has multiple historical advisories, but because the manifest does not pin an exact version, it is impossible to verify from this file whether the installed release is patched. Given this skill handles editable PowerPoint decks, which are ZIP/XML-based and can contain attacker-controlled content, uncertainty around the XML parsing library version is a real security concern.

Unpinned Dependencies

Low
Category
Supply Chain
Content
lxml>=5.0.0
deep-translator>=1.11.4
Confidence
98% confidence
Finding
The dependency specifier `deep-translator>=1.11.4` is unpinned, allowing installation of any later release, including potentially compromised or breaking versions. This is more concerning here because `deep-translator` has a prior supply-chain advisory, so leaving it open-ended increases the chance of pulling an unsafe package during installation or rebuilds.

Unverifiable Dependency: deep-translator has 1 known advisory(ies) (PYSEC-2022-252 (The deep-translator project on PyPI was taken over via user account compromise v)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
97% confidence
Finding
`deep-translator` has a known supply-chain-related advisory, and the unpinned manifest prevents verification that the installed version avoids the affected release(s). Because this skill may automatically process sensitive deck text and send content through translation workflows, a compromised dependency could affect confidentiality or execution integrity.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The `ui_messages` function returns Chinese only for `zh*` inputs and defaults every other locale to English. This imposes a specific language choice without explicit opt-in or documentation that English is the only supported non-Chinese UI language.

Static analysis

No suspicious patterns detected.