Back to skill

Security audit

Awesome Deck Pdf

Security checks for vulnerabilities and agentic risk

Overview

This slide-to-PDF skill is coherent, but its install and export workflow has enough local security risk that users should review it before use.

Install only in a trusted project, prefer pinned dependency versions, avoid sudo unless you are intentionally doing system setup, and do not export untrusted HTML or sensitive internal URLs without isolation. Run the exporter in a clean directory or container because it creates and deletes fixed helper paths in the working directory.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export_pdf.js:153
Finding
Predictable Temporary Directory Is Recursively Deleted Without Ownership Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_pdf.js`, lines 153–155 and 215 **Vulnerability Type**: Unsafe temporary-file handling and uncontrolled recursive deletion **Risk Level**: Medium ### Vulnerable Code ```javascript const imgDir = path.resolve(process.cwd(), '_pdf_pages'); if (fs.existsSync(imgDir)) fs.rmSync(imgDir, { recursive: true }); fs.mkdirSync(imgDir); ``` Cleanup later repeats the recursive deletion: ```javascript await browser.close(); fs.rmSync(imgDir, { recursive: true }); fs.unlinkSync(tmpHtml); ``` ### Technical Analysis The exporter uses the fixed `_pdf_pages` path beneath the current working directory as temporary storage. Before export, it recursively deletes any existing object at that path without checking whether the directory was created by the current invocation or contains user-owned data. A predictable temporary path violates safe temporary-file handling principles. An existing legitimate directory with the same name will be destroyed. Depending on platform and filesystem behavior, directory junctions or other redirection mechanisms may increase the affected scope. The implementation also lacks a `try/finally` cleanup boundary, so failures may leave temporary slide images behind. The operation is performed with the filesystem permissions of the user running the exporter. It does not independently elevate privileges, but it can delete data accessible to that user. ### Attack Path 1. The exporter is run from a directory writable by another local process, collaborator, or untrusted build step. 2. The attacker or conflicting process creates `<working-directory>/_pdf_pages` and places data there. On platforms where directory junction behavior permits it, the attacker may attempt to redirect the path to another accessible directory. 3. The user runs `node export_pdf.js`. 4. The initialization code resolves the predictable path and calls `fs.rmSync(imgDir, { recursive: true })`. 5. Existing content is d ...[truncated 507 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a unique temporary directory with the operating system's secure temporary-directory API: ```javascript const os = require('os'); const imgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'awesome-deck-pdf-')); ``` - Never recursively delete a predictable user-controlled path. - Keep the exact path returned by `mkdtempSync` and remove only that directory. - Put browser shutdown and temporary-file cleanup in a `finally` block: ```javascript let browser; const imgDir = fs.mkdtempSync( path.join(os.tmpdir(), 'awesome-deck-pdf-') ); try { browser = await launch(); // Perform export. } finally { if (browser) await browser.close(); fs.rmSync(imgDir, { recursive: true, force: true }); } ``` - Create temporary files using generated names and restrictive permissions where supported. - Avoid putting `_slides.html` in the working directory; place it inside the unique temporary directory as well. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/export_pdf.js:111
Finding
Chromium Sandbox Is Unconditionally Disabled While Rendering Input HTML<![CDATA[ ## Vulnerability Details **File Location**: `scripts/export_pdf.js`, lines 111–115 **Vulnerability Type**: Unsafe browser isolation configuration **Risk Level**: Medium ### Vulnerable Code ```javascript return puppeteer.launch({ headless: true, executablePath, args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'], }); ``` The selected HTML file is subsequently loaded as follows: ```javascript const filePath = path.resolve(process.cwd(), HTML_FILE); await page.goto(`file://${filePath}`, { waitUntil: 'networkidle0' }); await new Promise(r => setTimeout(r, 1500)); ``` ### Technical Analysis The script always starts Chrome with `--no-sandbox` and `--disable-setuid-sandbox`. These switches disable Chromium's primary process-isolation boundary even when the host supports sandboxed execution. The exporter accepts a user-selected HTML path and renders the document with JavaScript enabled. It does not validate the HTML's trust level, intercept outbound requests, disable script execution, or restrict the browser in a separate OS-level sandbox. A crafted deck may therefore execute active web content and initiate network requests. Exploitation beyond ordinary browser behavior would require a suitable Chromium vulnerability, but disabling the sandbox materially increases the impact of a renderer compromise. Disabling the sandbox is not intrinsically required for HTML-to-PDF conversion. It may be necessary in a limited set of container environments, but making it unconditional exceeds minimum privilege for systems where the Chromium sandbox is available. ### Attack Path 1. An attacker supplies or modifies an HTML deck that the victim is asked to export. 2. The victim runs `node export_pdf.js attacker-deck.html`. 3. Puppeteer launches Chromium with both sandbox-disabling switches. 4. The attacker-controlled document is loaded from a `file://` URL with JavaScript enabled. 5. The page executes its active content and can make netw ...[truncated 819 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `--no-sandbox` and `--disable-setuid-sandbox` from the default launch configuration: ```javascript return puppeteer.launch({ headless: true, executablePath, args: ['--disable-dev-shm-usage'], }); ``` - If a particular environment cannot support the Chromium sandbox, require explicit opt-in through a clearly named option such as `ALLOW_UNSANDBOXED_CHROME=1` and display a prominent warning. - Treat all supplied HTML as untrusted. - If generated slides do not require JavaScript, disable it before navigation: ```javascript await page.setJavaScriptEnabled(false); ``` - Intercept requests and permit only the local deck and generated temporary resources. Block unnecessary `http:`, `https:`, WebSocket, and other external requests. - Run unsandboxed export, when unavoidable, inside a dedicated unprivileged container or VM with: - no host filesystem mounts beyond required input and output; - no secrets in environment variables; - restricted or disabled outbound networking; - dropped Linux capabilities; - resource limits and a read-only root filesystem. - Keep Chrome/Puppeteer patched to reduce exposure to known renderer vulnerabilities. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:45
Finding
Installation Instructions Use Unpinned Executable Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 45–57 **Additional Locations**: `references/install.md`, lines 34–37, 52–56, and 65–72; `README.md`, lines 43–46 **Vulnerability Type**: Unpinned third-party dependencies and mutable package execution **Risk Level**: Medium ### Vulnerable Code ```bash # Option A: skip Chromium download, install separately npm install puppeteer --ignore-scripts npx puppeteer browsers install chrome # Option B: use puppeteer-core (no bundled Chrome download) npm install puppeteer-core # script will auto-find system Chrome # Option C: install system Chrome first, then run script # macOS: brew install --cask google-chrome # Linux: sudo apt install chromium-browser npm install puppeteer-core ``` Related installation instructions also use mutable package versions: ```bash npm install puppeteer pip install python-pptx ``` ### Technical Analysis The documentation instructs users to install `puppeteer`, `puppeteer-core`, and `python-pptx` without exact versions, lockfiles, or package hashes. Consequently, the resolved code can change after the Skill has been reviewed. Puppeteer normally performs installation-time behavior related to browser acquisition. The `npx puppeteer browsers install chrome` command executes package-provided tooling and may rely on locally resolved or downloaded package content. Although the package names shown are legitimate and no typosquatting or intentionally malicious dependency was identified, the installation procedure lacks reproducibility and supply-chain integrity controls. This finding concerns exposure to future registry compromise, malicious package releases, dependency takeover, or unreviewed breaking changes. It is not evidence that the currently named dependencies are malicious. ### Attack Path 1. A package account, registry entry, transitive dependency, or distribution channel used by one of the unpinned dependencies is compromised. 2. A malicious or otherwise unsa ...[truncated 986 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin reviewed exact dependency versions rather than version ranges or implicit latest versions: ```bash npm install --save-exact puppeteer@<reviewed-version> pip install python-pptx==<reviewed-version> ``` - Commit a reviewed `package.json` and lockfile, then direct users to run: ```bash npm ci ``` - Use a Python requirements file with exact versions and verified hashes: ```text python-pptx==<reviewed-version> \ --hash=sha256:<reviewed-distribution-hash> ``` Install it with: ```bash pip install --require-hashes -r requirements.txt ``` - Avoid commands that may cause `npx` to download and execute an undeclared package. Invoke a pinned local binary instead: ```bash npm exec --offline -- puppeteer browsers install chrome ``` - Pin and document the expected browser revision where practical. - Review transitive dependencies, installation scripts, and package provenance before updating pins. - Use automated dependency scanning and controlled update pull requests rather than resolving latest releases during end-user installation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (15)

Ae1

High
Category
analysis-evasion
Content
**`export_pdf.js` auto-detects Chrome in this order:**
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**`export_pdf.js` auto-detects Chrome in this order:**
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**`export_pdf.js` auto-detects Chrome in this order:**
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**`export_pdf.js` auto-detects Chrome in this order:**
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**`export_pdf.js` auto-detects Chrome in this order:**
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**`export_pdf.js` auto-detects Chrome in this order:**
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases are very broad and overlap with common user requests for presentations, causing the skill to activate in many routine contexts. Over-broad activation increases attack surface because the skill may initiate network fetching, file parsing, or shell-oriented workflows in situations where the user did not explicitly request those higher-risk behaviors.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill tells the agent to fetch and analyze website URLs but does not require a user-facing notice that external network access will occur or that URL contents may be transmitted to tooling such as Puppeteer. This can lead to privacy surprises, unintended access to internal or sensitive URLs, and possible SSRF-style misuse in agent environments with network reachability.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
The skill instructs use of `npx puppeteer`, which can fetch and execute a package version not explicitly pinned by the skill. In an agent or automated environment, this creates supply-chain risk because behavior may change over time or a compromised upstream release could be executed implicitly.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
# Option C: install system Chrome first, then run script
# macOS: brew install --cask google-chrome
# Linux: sudo apt install chromium-browser
npm install puppeteer-core
```
**Do NOT fall back to wkhtmltopdf, pdfkit, weasyprint, or any other tool.**
Confidence
85% confidence
Finding
The documented fallback includes `sudo apt install chromium-browser`, which encourages privileged system modification from within the skill workflow. In agent-assisted or sandboxed environments, prompting for root-level package installation expands blast radius and can normalize unsafe privilege escalation for a non-essential task.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The template title and instructions are entirely in Chinese and direct the author to fill the document accordingly, but there is no indication that language selection is optional or limited to a China-specific use case. This creates a natural-language locale policy concern because the skill appears to mandate a specific language without user opt-in.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### Linux
```bash
# Puppeteer may need extra Chrome deps
sudo apt-get install -y libx11-xcb1 libxcomposite1 libxcursor1 libxdamage1 \
  libxi6 libxtst6 libnss3 libcups2 libxss1 libxrandr2 libasound2 libatk1.0-0 \
  libgtk-3-0
npm install puppeteer
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The top-of-file documentation states 'Do NOT use page.pdf() directly' and describes screenshot-and-compose as the only method to use. However, the implementation later invokes Puppeteer's PDF generation API via overlayPage.pdf(), which directly contradicts that guidance rather than merely omitting detail.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
❌ No Chrome/Chromium found. Please install one of the following:

  macOS:   brew install --cask google-chrome
  Linux:   sudo apt install chromium-browser
  npm:     npm install puppeteer   (downloads bundled Chromium)
`);
    process.exit(1);
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Static analysis

No suspicious patterns detected.