Back to skill

Security audit

Kai Html Export

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but its sharing and rendering paths can expose local content or public deployments more broadly than users may expect.

Install only if you are comfortable reviewing what will be rendered or published. Use local export on trusted HTML when possible, avoid native export on untrusted HTML, and before using share-html.py make sure the folder contains no secrets, hidden files, backups, source maps, or confidential material. Prefer preinstalled, trusted Wrangler/Vercel CLIs, and be especially cautious with Vercel because the helper may make a linked project public.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/export-native-pptx.py:1097
Finding

Unrestricted Remote and Local Image Retrieval During Native Export

Content
View full analysis
bool: """ For elements with http(s)/file sources and object-fit: cover: download the original image, PIL-crop to the exact visible pixel region, and embed the cropped image. Only handles cover — other fits fall back to Playwright screenshot so that CSS opacity and rendering are captured correctly. Returns True on success, False to fall back to screenshot. """ # Handle cover, contain, fill, and empty (default) — other values fall back to screenshot. if object_fit not in ('cover', 'contain', 'fill', ''): return False try: import urllib.request from PIL import Image as _PILImg import ssl if source.startswith('file://'): img_path = source[len('file://'):] with open(img_path, 'rb') as f: img_bytes = f.read() else: req = urllib.request.Request(source, headers={'User-Agent': 'Mozilla/5.0'}) try: import certifi _ssl_ctx = ssl.create_default_context(cafile=certifi.where()) except ImportError: _ssl_ctx = ssl._create_unverified_context() with urllib.request.urlopen(req, context=_ssl_ctx, timeout=15) as resp: img_bytes = resp.read() ``` The retrieval function is invoked for URLs extracted from HTML image elements: ```python def export_raster_element(page: Page, slide, elem: Dict[str, Any]): """Render DOM-backed raster elements (, , url() backgrounds) as PPT pictures.""" export_id = elem.get('exportId') if not export_id: raise ValueError("Raster element ...[truncated 2834 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/export-native-pptx.py:1733
Finding

Chromium Sandbox Is Disabled Unconditionally on Linux

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/deploy-cloudflare.py:115
Finding

Unpinned CLI Packages Are Automatically Downloaded and Executed Through NPX

Content
View full analysis
list[str]: wrangler_path = shutil.which("wrangler") if wrangler_path: return [wrangler_path] npx_path = shutil.which("npx") if npx_path: return [npx_path, "--yes", "wrangler"] raise RuntimeError("Node.js is required. Install Node.js so `npx` is available.") ``` The Cloudflare error-recovery path may download another package whose name is extracted from command output: ```python def _run_wrangler(command: list[str], *, cwd: Path | None = None) -> subprocess.CompletedProcess[str]: result = _run(command, cwd=cwd) if result.returncode == 0: return result output = "\n".join(part for part in [result.stdout, result.stderr] if part) missing_package = _extract_missing_workerd_package(output) if not missing_package or len(command) < 3 or command[2] != "wrangler": return result retry_command = [command[0], command[1], "--package", "wrangler", "--package", missing_package, "wrangler", *command[3:]] return _run(retry_command, cwd=cwd) ``` Vercel uses the same unpinned fallback: ```python def _find_vercel_command() -> list[str]: vercel_path = shutil.which("vercel") if vercel_path: return [vercel_path] npx_path = shutil.which("npx") if npx_path: return [npx_path, "--yes", "vercel"] raise RuntimeError("Node.js is required. Install Node.js so `npx` is available.") ``` ### Technical Analysis When a deployment CLI is not installed, the scripts invoke `npx --yes` with an unversioned package name. This allows the package registry's current release to be downloaded and executed without user review or a repository lockfile. ...[truncated 1753 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/deploy-cloudflare.py:72
Finding

Directory Sharing Passes the Entire Selected Folder to Public Deployment Providers

Content
View full analysis
tuple[Path, bool]: input_path = input_path.resolve() if input_path.is_dir(): index_file = input_path / "index.html" if not index_file.exists(): raise ValueError(f"HTML folder must contain index.html: {input_path}") return input_path, False ``` That complete directory is then given to Wrangler: ```python def deploy_with_cloudflare(deploy_dir: Path, *, project_name: str, branch: str = "main") -> str: wrangler = _find_wrangler_command() _ensure_login(wrangler) _ensure_project(wrangler, project_name, branch) deploy = _run_wrangler( wrangler + ["pages", "deploy", str(deploy_dir), "--project-name", project_name, "--branch", branch] ) ``` Vercel has equivalent behavior: ```python def stage_input(input_path: Path) -> tuple[Path, bool]: input_path = input_path.resolve() if input_path.is_dir(): index_file = input_path / "index.html" if not index_file.exists(): raise ValueError(f"HTML folder must contain index.html: {input_path}") return input_path, False ``` ```python def deploy_with_vercel(deploy_dir: Path) -> str: vercel = _find_vercel_command() version_check = _run(vercel + ["--version"]) if version_check.returncode != 0: raise RuntimeError(version_check.stderr.strip() or version_check.stdout.strip() or "Failed to run Vercel CLI.") whoami = _run(vercel + ["whoami"]) if whoami.returncode != 0: raise RuntimeError("Vercel login ...[truncated 2107 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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
Findings (41)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code chunk’s primary behavior is HTML publishing: it validates an HTML file or folder, copies local referenced assets into a staging directory, ensures Cloudflare Wrangler is installed and authenticated, creates a Cloudflare Pages project if needed, deploys the content, and outputs a public URL. This aligns with the 'publish/share HTML online' part of the description, but there is no implementation for converting HTML to PowerPoint or PNG screenshots. Because the declared description presents broader export capabilities that this code does not perform, and the actual code specifically performs Cloudflare deployment/public hosting, the description does not accurately represent this chunk on its own.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code chunk does not implement HTML-to-PPTX conversion, HTML-to-PNG export, or screenshot generation at all. Its sole functional purpose is deployment/public sharing via Vercel: it copies local referenced assets into a staging directory, invokes the Vercel CLI to deploy, extracts the deployment URL, and calls the Vercel API to patch project settings (ssoProtection: None) when a linked project is present. While 'publish HTML to a public share URL' is part of the declared description, the overall description materially overstates the skill by claiming export-to-PPTX/PNG capabilities that are absent from this code. Additionally, making the project public via Vercel settings is a significant behavior that is not clearly disclosed in the description. Therefore the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The code’s primary behavior is exporting .slide-based HTML presentations into a PPTX file. It uses Playwright to render the HTML, extracts text/shapes/tables/images from each slide, and writes a PowerPoint. It also creates an auxiliary preview PNG contact sheet from a few slide screenshots, but that is not the same as a user-facing 'export HTML to PNG' feature. There is no code to publish HTML online, generate a share URL, deploy content, or interact with any remote hosting service. So the declared description overstates the implemented capabilities and includes major functions not represented in this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code’s actual primary purpose is HTML-to-PPTX export. It supports two PPTX modes: image-based and delegated native/editable mode. Although it creates temporary PNG screenshots internally, it does not expose PNG as a user-facing output artifact. It also contains no network upload, deployment, or share-URL generation behavior. Therefore the declared description overstates the code chunk’s capabilities in material ways.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code chunk implements only PNG screenshot functionality for local HTML files. It launches a headless browser, loads a file:// HTML path, disables animations, scrolls the page, and saves a full-page PNG. There is no logic for producing PPTX/PowerPoint output, no upload/network publishing workflow beyond loading local assets, and no creation of public share URLs. Therefore the declared description overstates the skill's capabilities relative to the supplied code.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The description presents a broader multi-function skill for exporting HTML to PPTX or PNG and publishing HTML online. This code chunk only supports the sharing/publishing portion, specifically by selecting a deployment provider and invoking another script via subprocess. It does not implement any HTML-to-PowerPoint conversion, PNG generation, or screenshotting. While sharing HTML is consistent with part of the description, the actual behavior of this chunk is materially narrower than the declared purpose, so the description does not accurately represent what this supplied code actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

There is a clear description-behavior mismatch. The declared purpose centers on transforming or publishing HTML content. The actual code neither reads HTML nor generates PPTX/PNG nor performs any sharing/deployment. Instead, it inspects an existing PowerPoint file using python-pptx and prints style/debug information for text-containing shapes. This is a materially different primary purpose, not a supporting implementation detail of HTML export.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description says the skill performs user-facing HTML export, screenshot, and sharing operations. However, the provided code does not implement any HTML conversion, screenshot capture, publishing, or share URL generation. It only runs tests for those features by selecting pytest files and invoking them through subprocess. This is a materially different primary purpose: developer test orchestration rather than the advertised export/share functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description claims three major capabilities: HTML→PPTX export, HTML→PNG export, and publishing HTML to a share URL. The supplied code chunk only demonstrates and tests screenshot/PNG generation from HTML. It includes no behavior related to PowerPoint generation, editable/native PPT export, or online sharing/deployment. While the PNG portion is consistent with part of the description, the broader declared purpose materially overstates the implemented capabilities shown in this code.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The function explicitly disables deployment protection by PATCHing ssoProtection to null, which can expose deployments that were intentionally access-restricted. In the context of a tool marketed as HTML export/share, this is especially dangerous because users may unknowingly publish sensitive presentations or reports to the public internet.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README encourages publishing HTML to Cloudflare Pages or Vercel and obtaining a live URL, but it does not clearly warn that this makes the content publicly accessible on the internet. In a skill explicitly designed to share generated decks and reports, missing that warning increases the risk of users accidentally exposing sensitive internal presentations, reports, or embedded assets.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README promotes publishing HTML to a public share URL but does not prominently warn that the resulting content becomes Internet-accessible and may include embedded secrets, internal reports, or proprietary slide content. In this skill's context, users are explicitly encouraged to share generated presentations and reports, which increases the chance of accidental sensitive-data disclosure.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill documentation describes capabilities that inherently involve filesystem access, shell execution, network access, and likely environment-variable use, but it does not declare any explicit tool scope or permissions boundary. This increases the risk of overbroad execution in an agent environment, especially because the same skill can both process local HTML and publish it externally.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

Overly broad trigger phrases can cause the agent to invoke this skill for common, ambiguous requests, increasing the chance of unintended shell, file, or network actions. Because the skill includes public-sharing functionality, accidental invocation may escalate from simple export to exposing content online if the workflow is not carefully gated.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill prominently advertises publishing HTML to a public URL but does not provide a strong, front-and-center warning that this may expose confidential reports, embedded secrets, internal links, or proprietary content. In this context, the danger is heightened because users may treat the action as a routine export rather than a data-publication event.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
80% confidence
Finding

Using npx vercel login without pinning a version allows the latest package version to be fetched at runtime, which weakens supply-chain integrity and reproducibility. In an agent or automated environment, this can expose users to unexpected behavior or malicious upstream compromise.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
80% confidence
Finding

Referencing npx vercel login again without a pinned version repeats the same supply-chain risk: the command may execute whatever version is current at invocation time. This is particularly risky for a skill that can publish content publicly and may handle sensitive project files.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/deploy-cloudflare.py (reported line 127)May include surrounding context.

python
def _run(command: list[str], *, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        command,
        cwd=str(cwd) if cwd else None,
        text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/deploy-vercel.py (reported line 133)May include surrounding context.

python
def _run(command: list[str], *, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        command,
        cwd=str(cwd) if cwd else None,
        text=True,

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The script's documented fallback to npx --yes vercel permits execution of whatever Vercel package version is resolved at runtime, creating a supply-chain risk. A compromised or unexpected package version could execute arbitrary code under the user's account and with access to deployment credentials.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding

The user-facing error message instructs users to run npx vercel login, which again encourages use of an unpinned package fetched at execution time. That expands the supply-chain exposure to the authentication flow, where a malicious package could capture tokens or alter login behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.