Back to skill

Security audit

sn-ppt-standard

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent PPT-generation pipeline, but its normal workflow includes credential handling, service launch, remote downloads, and automatic installs that need review before use.

Install only if you trust the publisher and runtime environment. Use narrowly scoped API keys, avoid setting untrusted gateway base URLs or public workbench URLs, run it in a sandboxed workspace with restricted network access, and review uploaded images/documents before allowing model or VLM processing. Preinstall and pin dependencies rather than letting export install them automatically.

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/launch_workbench.py:204
Finding

Gateway Bearer Credential Sent to an Unvalidated Configurable URL

Content
View full analysis
str: """Resolve the server-only Gateway bridge key for the Node child process.""" return ( args.gateway_api_key.strip() or _clean_env("WORKBENCH_GATEWAY_API_KEY") or _clean_env("API_SERVER_KEY") or _clean_env("HERMES_GATEWAY_API_KEY") or _clean_env("AI_GATEWAY_API_KEY") ) def _gateway_models_url(base_url: str) -> str: """Build a models URL while accepting bases with or without /v1.""" normalized = base_url.rstrip("/") return f"{normalized}/models" if normalized.endswith("/v1") else f"{normalized}/v1/models" def _is_hermes_gateway_base(base_url: str, api_key: str) -> bool: """Return true when a base URL looks like the Hermes Gateway API.""" if not base_url or not api_key: return False request = urllib.request.Request( _gateway_models_url(base_url), headers={"Authorization": f"Bearer {api_key}"}, method="GET", ) try: with urllib.request.urlopen(request, timeout=4) as response: body = response.read(10000).decode("utf-8", errors="replace") return response.status == 200 and "hermes-agent" in body except (OSError, urllib.error.URLError, urllib.error.HTTPError): return False def _resolve_gateway_base_url(args: argparse.Namespace, api_key: str) -> str: """Resolve a Hermes Gateway URL and avoid direct model endpoints.""" configured = args.gateway_base_url.strip() or _clean_env("HERMES_GATEWAY_BASE_URL") if _is_hermes_gateway_base(configured, api_key): return configured for candidate in ("http://127.0.0.1:8642", "http://localhost:8642", "http://127.0.0.1:8643"): if _is_hermes_gateway_ ...[truncated 2402 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/launch_workbench.py:470
Finding

API Keys Exposed Through Child-Process Command-Line Arguments

Content
View full analysis
/cmdline`, process monitoring software, diagnostic snapshots, endpoint telemetry, or crash reports. Although exploitation generally requires local process-observation access, that access may be available to other users, containers, monitoring agents, or support tooling that should not receive gateway credentials. The source session identifier is also passed through the command line. While it may not always be equivalent to an API key, it can be security-sensitive if it identifies or authorizes access to an agent session. ### Attack Path 1. The user launches the workbench with an agent API ke ...[truncated 972 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/export_pptx/lib/image_downloader.mjs:13
Finding

Server-Side Request Forgery and Unbounded Remote Downloads During Export

Content
View full analysis
]*\bsrc\s*=\s*)(["'])(https?:\/\/[^"'\>\s]+)\2/gi; const CSS_URL_RE = /(background(?:-image)?\s*:\s*[^;"'>]*url\(\s*)(["']?)(https?:\/\/[^"'\>\s\)]+)\2(\s*\))/gi; function sanitizeName(url) { let name; try { name = basename(new URL(url).pathname) || 'image'; } catch { name = 'image'; } const safe = name.replace(/[^\w\-\.]/g, '_'); const ext = extname(safe).toLowerCase(); const allowed = ['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp']; if (allowed.includes(ext)) return safe; if (!ext) return safe + '.jpg'; return safe; } function fetchBuffer(url) { return new Promise((resolve, reject) => { const client = url.startsWith('https:') ? get : httpGet; client(url, (res) => { if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}`)); return; } const chunks = []; res.on('data', c => chunks.push(c)); res.on('end', () => resolve(Buffer.concat(chunks))); }).on('error', reject); }); } ``` The download is automatically invoked during PPTX export in `scripts/export_pptx/html_to_pptx.mjs`, lines 67-70: ```javascript if (args.deckDir && !args.batch) { await downloadRemoteImages(args.deckDir); } ``` The response is subsequently written to disk in `image_downloader.mjs`, lines 83-86: ```javascript try { const buf = await fetchBuffer(url); writeFileSync(localPath, buf); const rel = `../images/${basename(localPath)}`; seen.set(url, rel); ``` ### Technical Analysis The exporter extracts HTTP and HTTPS URLs from page HTML and requests them from the machine running the Skill. Page HTML is generated by an LLM or can be influenced b ...[truncated 2615 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/export_pptx/lib/browser_setup.mjs:58
Finding

Automatic Runtime Installation of Third-Party Packages and Chromium

Content
View full analysis
&1', { ...hiddenProcessOptions, cwd: baseDir, encoding: 'utf-8', timeout: 10000, }); if (out.includes('is already installed')) return; } catch { // Dry-run can fail on older Playwright builds; try executable detection. } try { execSync('node -e "require(\'playwright\').chromium.executablePath()"', { ...hiddenProcessOptions, cwd: baseDir, encoding: 'utf-8', timeout: 5000, }); } catch { console.error('[setup] 正在安装 Playwright Chromium(仅首次)...'); try { execSync('npx playwright install chromium', { ...hiddenProcessOptions, cwd: baseDir, stdio: 'inherit' }); } catch (e) { throw new Error(`Chromium installation failed: ${e.message}. Cannot install headless browser in this environment.`); } } } ``` Relevant dependency declarations in `scripts/export_pptx/package.json`, lines 8-12: ```json "dependencies": { "pptxgenjs": "^3.12.0", "playwright": "1.58.2", "echarts": "^5.4.3" } ``` ### Technical Analysis A routine export automatically invokes `npm i ...[truncated 2348 chars]
Remediation
View remediation
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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (62)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This mismatch is security-relevant because the skill describes a PPT pipeline but also instructs starting a WebUI, discovering network/public URLs, and passing environment-derived provider configuration and secrets to child processes. Hidden operational behavior around service launch and secret propagation expands the attack surface and can expose credentials or internal endpoints unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This mismatch is security-relevant because the skill describes a PPT pipeline but also instructs starting a WebUI, discovering network/public URLs, and passing environment-derived provider configuration and secrets to child processes. Hidden operational behavior around service launch and secret propagation expands the attack surface and can expose credentials or internal endpoints unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This mismatch is security-relevant because the skill describes a PPT pipeline but also instructs starting a WebUI, discovering network/public URLs, and passing environment-derived provider configuration and secrets to child processes. Hidden operational behavior around service launch and secret propagation expands the attack surface and can expose credentials or internal endpoints unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This mismatch is security-relevant because the skill describes a PPT pipeline but also instructs starting a WebUI, discovering network/public URLs, and passing environment-derived provider configuration and secrets to child processes. Hidden operational behavior around service launch and secret propagation expands the attack surface and can expose credentials or internal endpoints unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

This mismatch is security-relevant because the skill describes a PPT pipeline but also instructs starting a WebUI, discovering network/public URLs, and passing environment-derived provider configuration and secrets to child processes. Hidden operational behavior around service launch and secret propagation expands the attack surface and can expose credentials or internal endpoints unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This mismatch is security-relevant because the skill describes a PPT pipeline but also instructs starting a WebUI, discovering network/public URLs, and passing environment-derived provider configuration and secrets to child processes. Hidden operational behavior around service launch and secret propagation expands the attack surface and can expose credentials or internal endpoints unexpectedly.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 206)May include surrounding context.

md
This applies to BOTH `launch_workbench.py` and `progress_event.py` when slash-prefixed arguments are used.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 462)May include surrounding context.

md
- `output_format == "pptx"` invokes `scripts/export_pptx/html_to_pptx.mjs --force`. This skips built-in motif / real-photo gates (this skill doesn't use the mot

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/model_client.py (reported line 3)May include surrounding context.

python
"""Lightweight, self-contained LLM/VLM client for ppt-* skills.

Reads env from .env (via python-dotenv) and hits two chat endpoints:

    llm(system, user) -> str                              # SN_TEXT_* / SN_CHAT_* /v1/chat/completions
    vlm(system, user, images) -> str                      # SN_VISION_* / SN_CHAT_* /v1/chat/completions

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/model_client.py (reported line 38)May include surrounding context.

python
"""Lightweight, self-contained LLM/VLM client for ppt-* skills.

Reads env from .env (via python-dotenv) and hits two chat endpoints:

    llm(system, user) -> str                              # SN_TEXT_* / SN_CHAT_* /v1/chat/completions
    vlm(system, user, images) -> str                      # SN_VISION_* / SN_CHAT_* /v1/chat/completions

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · lib/model_client.py (reported line 46)May include surrounding context.

python
# skills/sn-ppt-standard/lib/model_client.py → parents[3] = repo root
    repo_root = here.parents[3]
    loaded: list[Path] = []
    for candidate in (repo_root / ".env", repo_root / "skills" / ".env", Path.cwd() / ".env"):
        if candidate.exists():
            load_dotenv(candidate, override=False)
            loaded.append(candidate)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/launch_workbench.py (reported line 166)May include surrounding context.

python
# skills/sn-ppt-standard/lib/model_client.py → parents[3] = repo root
    repo_root = here.parents[3]
    loaded: list[Path] = []
    for candidate in (repo_root / ".env", repo_root / "skills" / ".env", Path.cwd() / ".env"):
        if candidate.exists():
            load_dotenv(candidate, override=False)
            loaded.append(candidate)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/launch_workbench.py (reported line 170)May include surrounding context.

python
# skills/sn-ppt-standard/lib/model_client.py → parents[3] = repo root
    repo_root = here.parents[3]
    loaded: list[Path] = []
    for candidate in (repo_root / ".env", repo_root / "skills" / ".env", Path.cwd() / ".env"):
        if candidate.exists():
            load_dotenv(candidate, override=False)
            loaded.append(candidate)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/launch_workbench.py (reported line 171)May include surrounding context.

python
# skills/sn-ppt-standard/lib/model_client.py → parents[3] = repo root
    repo_root = here.parents[3]
    loaded: list[Path] = []
    for candidate in (repo_root / ".env", repo_root / "skills" / ".env", Path.cwd() / ".env"):
        if candidate.exists():
            load_dotenv(candidate, override=False)
            loaded.append(candidate)

Known Vulnerable Dependency: image-size==1.2.1 — 2 advisory(ies): CVE-2025-71329 (image-size: JXL and HEIF parsers allow denial of service through infinite loops); CVE-2025-71330 (image-size: ICNS parser allows denial of service through an infinite loop)

High
Category
Supply Chain
Confidence
97% confidence
Finding

The lockfile includes image-size 1.2.1, which has reported infinite-loop denial-of-service issues in several image parsers. This PPT pipeline likely ingests external or user-supplied images, so a crafted JXL, HEIF, or ICNS file could hang the export process, consume CPU, and stall presentation generation, making the issue materially relevant in this context.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
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.

Content

Scanner excerpt · scripts/run_stage.py (reported line 89)May include surrounding context.

python
def _subprocess_env() -> dict[str, str]:
    env = os.environ.copy()
    env.setdefault("PYTHONIOENCODING", "utf-8")
    env.setdefault("PYTHONUTF8", "1")
    return env

Env Variable Harvesting

High
Category
Data Exfiltration
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.

Content

Scanner excerpt · scripts/run_stage_with_progress.py (reported line 41)May include surrounding context.

python
def _subprocess_env() -> dict[str, str]:
    env = os.environ.copy()
    env.setdefault("PYTHONIOENCODING", "utf-8")
    env.setdefault("PYTHONUTF8", "1")
    return env

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill declares broad shell, file, network, and environment-dependent behavior but provides no explicit tool/permission scope. That creates an over-privileged execution surface where an agent could read secrets, write arbitrary files under user-controlled paths, launch services, and access the network without a clear allowlist boundary.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This markdown file documents entering the skill via /skill sn-ppt-entry or the phrase "生成 PPT". While the slash-command is specific, the natural-language phrase is broad and could match many generic requests to make a presentation, without clear exclusion conditions or tighter invocation scope in this file.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

The skill explicitly instructs autonomous decision-making and proceeding without asking the user in fast mode, including generating content, using sample data, and exporting artifacts. In a skill with shell, file-write, network, and service-launch capabilities, reducing confirmation requirements increases the chance of unwanted actions, misleading outputs, and unintended external calls without informed user approval.

Content

Scanner excerpt · SKILL.md (reported line 38)May include surrounding context.

md
Any missing → stop and tell user to enter via `/skill sn-ppt-entry`.

When `ppt_mode == "fast"`: **build first, then iterate.** Make decisions autonomously — do not ask the user about colors, fonts, page count, or layout preferences. Infer reasonable defaults from the query and start building immediately. Skip optional web search and image search. Run the full pipeline including PPTX export. **Data**: use uploaded documents first; if none, use mock data labeled `[Sample Data]` and tell the user in chat which data needs replacement. **Images**: AI generation for decorative images, ECharts for charts — no questions asked.

### Post-generation (fast mode only)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file mandates that all user-visible text must match the user's query language, including forcing Chinese output whenever the query is Chinese. This is a language-policy constraint applied automatically, and the file does not offer the user a language choice or opt-in mechanism.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

L475 states the skill does not retry failed model calls. However, L73 limits retries to two attempts, L283 says timeout/gateway errors should follow the retry rules, and L320 explicitly instructs retrying once after a first timeout before switching to manual fallback. This is an active contradiction in the skill's own documentation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The VLM helper reads arbitrary local image files, base64-encodes them, and sends the full contents to a remote chat endpoint. In a presentation-generation skill, those images may contain sensitive internal screenshots, documents, or personal data, and this module provides no consent, policy gate, allowlist, or provenance check before exfiltrating them off-host.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The prompt mandates Chinese section headers such as "整体评估" and "建议下一步" and provides Chinese-only output examples, which imposes a specific language/locale on the user. The file does not indicate any opt-in, user preference handling, or region-specific justification for this constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The prompt explicitly instructs the agent to use every uploaded reference image across the deck, which can cause user-provided files to be incorporated into outputs without an explicit consent or review gate. In a presentation-generation pipeline, uploaded images may contain sensitive, irrelevant, or copyrighted material, so an automatic 'use everything' rule increases the risk of unintended disclosure or misuse.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/export_pptx/lib/browser_setup.mjs:67