Back to skill

Security audit

html-ppt-to-pdf

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims, but it converts arbitrary HTML in a real browser without clearly warning that the HTML can run JavaScript and make network requests.

Review before installing if you will convert HTML from other people or automated sources. Use it only on trusted decks, or run conversion in a sandbox/container with restricted outbound network access. Prefer a pinned Playwright install and a clean lockfile before using it in production workflows.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/html-to-pdf.mjs:133
Finding
Untrusted HTML Executes with Unrestricted Network Access<![CDATA[ ## Vulnerability Details **File Location**: `scripts/html-to-pdf.mjs`, lines 133-134 and 153-155 **Vulnerability Type**: Active-content execution and unrestricted browser networking **Risk Level**: Medium ### Vulnerable Code ```js const context = await browser.newContext({ viewport: { width, height }, deviceScaleFactor: 2 }); const page = await context.newPage(); ``` ```js await page.goto(pathToFileURL(inputAbs).href, { waitUntil: 'domcontentloaded', timeout: 30_000 }); await page.waitForLoadState('load', { timeout: 15_000 }).catch(() => {}); await page.waitForLoadState('networkidle', { timeout: 5_000 }).catch(() => {}); ``` ### Technical Analysis The converter accepts arbitrary HTML slide decks and opens them as active browser documents. The browser context does not disable JavaScript, and no request interception policy restricts the hosts, protocols, or network destinations that the document may contact. Consequently, scripts, images, stylesheets, fonts, iframes, and other active or passive resources in an attacker-controlled deck can initiate requests to: - Attacker-controlled Internet services - Services reachable only from the victim's network - Localhost services - Private-network addresses accessible from the conversion host The converter explicitly waits for document loading and network activity, providing embedded scripts with an execution window before PDF generation. Chromium's sandbox and same-origin policy reduce the likelihood of direct host compromise or reading arbitrary cross-origin responses, but they do not categorically prevent outbound requests, blind internal-service interactions, or disclosure of information already available to the malicious document. The project's stated purpose requires rendering HTML, but it does not inherently require arbitrary script execution or unrestricted networking. These capabilities should therefore be disabled by default or exposed only through explicit trusted-content options. ### Attack ...[truncated 1490 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Disable JavaScript by default** when static slide rendering permits it: ```js const context = await browser.newContext({ viewport: { width, height }, deviceScaleFactor: 2, javaScriptEnabled: false, }); ``` 2. **Implement a default-deny request policy** with `context.route('**/*', ...)`. Permit only: - The input HTML file - Explicitly approved files beneath the input deck directory - Deliberately configured font or asset hosts 3. **Block sensitive destinations**, including: - Loopback addresses - Link-local addresses - RFC 1918 private networks - Cloud metadata endpoints - Non-HTTP protocols not required by conversion 4. **Add explicit trust controls**, such as: - `--allow-javascript` - `--allow-remote-assets` - `--allow-host example.com` These options should be disabled by default and accompanied by clear warnings. 5. **Use process-level isolation** for untrusted decks: - Run Chromium as an unprivileged user - Use a container or sandbox with no sensitive mounts - Apply outbound firewall restrictions - Enforce CPU, memory, file-size, and execution-time limits 6. **Document the trust boundary** clearly. State that trusted mode executes scripts contained in the source deck and may contact network resources. ]]>

T08 · Insecure Dependencies

Note
Location
scripts/package-lock.json:8
Finding
Inconsistent Dependency Manifests and Mixed Package Registries<![CDATA[ ## Vulnerability Details **File Location**: `scripts/package.json`, lines 9-11; `scripts/package-lock.json`, lines 8-11, 38, 70, and 88 **Vulnerability Type**: Dependency integrity and supply-chain configuration weakness **Risk Level**: Low ### Vulnerable Code The declared runtime manifest contains only Playwright: ```json "dependencies": { "playwright": "^1.49.0" } ``` The lockfile root additionally declares `pdf-lib`, which is absent from `package.json`: ```json "dependencies": { "pdf-lib": "^1.17.1", "playwright": "^1.49.0" } ``` The lockfile also mixes the official npm registry with an alternate mirror: ```json "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz" ``` ```json "resolved": "https://registry.npmmirror.com/playwright/-/playwright-1.59.1.tgz" ``` ```json "resolved": "https://registry.npmmirror.com/playwright-core/-/playwright-core-1.59.1.tgz" ``` The installation instructions use dependency resolution rather than a strictly reproducible clean installation: ```bash npm install npx playwright install chromium ``` ### Technical Analysis The dependency manifest and lockfile do not represent the same dependency graph. `package-lock.json` records `pdf-lib` as a direct dependency, while `package.json` does not declare it and the reviewed executable does not import it. The documentation also states that installation installs `pdf-lib`, reinforcing the stale or inconsistent dependency state. In addition, locked archives are fetched from both `registry.npmjs.org` and `registry.npmmirror.com`. Package integrity hashes provide meaningful protection against an archive that does not match the lockfile, but the alternate mirror still expands the project's supply-chain trust boundary and introduces avoidable availability and provenance concerns. The Playwright declaration also uses a permissive caret range, while the lockfile resolves a substantially newer compatible release. This is valid npm behavior, but it ...[truncated 1768 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the existing lockfile and regenerate it from the authoritative `package.json` after confirming the intended dependency set. 2. Remove `pdf-lib` and its transitive packages from the lockfile unless the application genuinely uses them. If it is required, declare it explicitly in `package.json` and document its purpose. 3. Configure and use a single trusted registry, preferably the official npm registry: ```bash npm config set registry https://registry.npmjs.org/ ``` 4. Commit the regenerated lockfile and use reproducible installation in documentation and CI: ```bash npm ci ``` 5. Pin Playwright to an intentionally reviewed version if deterministic dependency upgrades are required, rather than relying solely on a caret range. 6. Add automated dependency controls: - Lockfile consistency checks - Dependency vulnerability scanning - Automated update review - License and provenance checks - CI verification that the lockfile contains only approved registry hosts 7. Update `README.md` and `SKILL.md` so the documented dependency list matches the actual manifest. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The README instructs users to run `npx playwright install chromium` without pinning a specific Playwright version. `npx` may resolve and execute the latest package version from the registry at runtime, which creates a supply-chain risk: a compromised upstream release, dependency confusion, or unexpected breaking change could cause unreviewed code to run on the user's machine. In this skill context, the command is especially relevant because it is presented as a one-time setup step that users are likely to copy-paste directly.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
Using `npx playwright install chromium` without pinning a specific package version can fetch the latest Playwright package at execution time, making builds non-reproducible and exposing users to supply-chain risk if a malicious or compromised upstream version is published. Because this skill explicitly instructs users to run the command, the risk is operationally relevant even though it appears in documentation rather than code.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
This script renders attacker-controlled local HTML in a real browser and explicitly supports outbound networking through remote asset loads and an optional proxy. That creates an SSRF/data-exfiltration surface inconsistent with a local HTML-to-PDF converter, because malicious HTML can trigger requests to arbitrary internal or external endpoints during rendering.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
This HTML file includes a user-facing instruction that tells the user to ask the agent in Chinese: "把这份 HTML 幻灯片转 PDF。" There is no accompanying note that other languages are supported or that Chinese is required for a justified region-specific reason, which can violate language/locale policy expectations.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"html-to-pdf": "./html-to-pdf.mjs"
  },
  "dependencies": {
    "playwright": "^1.49.0"
  }
}
Confidence
91% confidence
Finding
The dependency uses a caret range (^1.49.0), which allows automatic installation of newer minor and patch releases instead of a single reviewed version. In a build or automation skill that drives Chromium, this can introduce supply-chain risk or unexpected behavior changes if an upstream release is compromised or incompatible.

Static analysis

No suspicious patterns detected.