T09 · Insecure Skill Coding Practices
- Location
scripts/compile.py:151- Finding
Arbitrary Local File Disclosure Through Unrestricted Typst Include Paths
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This PDF-building skill mostly matches its stated purpose, but it can upload manuscript content and potentially unintended local include files to a remote service without tight safeguards.
Review this skill before installing if you handle private manuscripts or sensitive project files. Use it only on trusted input, avoid direct Typst files with untrusted #include directives, verify the TypeTex endpoint, and prefer a pinned virtual environment for dependencies.
scripts/compile.py:151Arbitrary Local File Disclosure Through Unrestricted Typst Include Paths
SKILL.md:13Unpinned Python Dependencies Create Supply-Chain Exposure
The endpoint is taken directly from the TYPETEX_API_URL environment variable and used for outbound network access without validation. In an agent or CI environment, an attacker who can influence environment variables can redirect health checks to attacker-controlled infrastructure, enabling SSRF-style egress, internal service probing, or trust decisions based on a forged health endpoint.
def health_check():
try:
r = requests.get(f"{API_URL}/public/compile/health", timeout=10)
return r.json().get("status") == "ok"
except Exception:
return False
The script sends the full Typst source and auxiliary files to a remote endpoint derived from an unvalidated environment variable. If that variable is attacker-controlled, sensitive local document contents can be exfiltrated to arbitrary servers, and the skill context increases severity because included files are automatically collected and transmitted.
print(f"[KDP] Sende an TypeTex API...")
start = time.time()
try:
r = requests.post(f"{API_URL}/public/compile/typst", json=payload, timeout=120)
elapsed = round(time.time() - start, 1)
if r.status_code != 200:
return {"success": False, "error": f"HTTP {r.status_code}: {r.text[:300]}"}
The example trigger phrase "Kompiliere das komplette Buch als PDF" is broad and does not constrain which files, directories, or trust boundaries the skill may operate on. In an agentic environment, vague build requests can cause the skill to ingest unintended local content, process sensitive files, or act on a wider workspace than the user intended, especially if the implementation auto-discovers chapters or source material.
The description includes broad invocation phrases like 'Bau mir ein PDF aus [Dateiname]' and 'Kompiliere Kapitel 3' without clear scoping or guardrails. This can cause accidental or overly broad triggering by an agent, increasing the chance that unintended files are processed or sensitive content is sent through the build pipeline.
The skill states that content is sent to the external TypeTex API to generate PDFs, but it does not present a clear user-facing warning that chapter or book contents will leave the local environment. This creates a real data exfiltration and privacy risk, especially if users compile unpublished manuscripts, proprietary material, or sensitive embedded data.
Hard-coding the skill language to German without signaling user choice or opt-in can cause the skill to engage users in an unexpected language, increasing the risk of misunderstanding prompts, outputs, or permissions. While this is primarily a safety and usability issue rather than a direct exploit path, confusion can contribute to unintended actions in an agent setting.
The command trigger "Erstelle ein PDF" is very generic and can match many unrelated user requests, causing the skill to activate outside its intended KDP/Markdown-to-book workflow. Overbroad activation can route sensitive or irrelevant user content into this skill unexpectedly, especially since the manifest also declares network access, increasing the chance of unintended processing or external transmission.
The top-level documentation says the tool compiles Typst to PDF via the TypeTex API, which implies a network-backed compilation flow. However, when the API health check fails, the script imports subprocess and runs a local typst compile command instead, introducing a materially different execution model than the docstring describes.
This file contains natural-language instructions presented to users only in German, including the module description and usage text. Under the policy, forcing a specific language without user opt-in is a locale/language policy violation unless the constraint is explicitly justified.
The script performs external transmission of main_content and auxiliary_files to a remote compilation service. In this skill context, that is particularly sensitive because the code automatically reads local include files and uploads them, which can expose proprietary or confidential manuscript contents if the remote service is untrusted or misconfigured.
print(f"[KDP] Sende an TypeTex API...")
start = time.time()
try:
r = requests.post(f"{API_URL}/public/compile/typst", json=payload, timeout=120)
elapsed = round(time.time() - start, 1)
if r.status_code != 200:
return {"success": False, "error": f"HTTP {r.status_code}: {r.text[:300]}"}
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
print(f"[KDP] API nicht erreichbar — versuche lokales Typst...")
import subprocess
try:
r = subprocess.run(["typst","compile",main_file,output_file],
capture_output=True, text=True, timeout=60)
if r.returncode == 0:
print(f"[KDP] ✓ Lokal: {output_file}")
The manifest description specifies German invocation phrases and presents the skill usage entirely in German, which can amount to a locale/language constraint without explicit user opt-in. There is no statement that other languages are supported or that German is required for a justified regional reason.
The BOOK_HEADER sets language: "de", and the script’s user-facing usage/help text is also German-only. Per the policy, forcing a specific language without user opt-in can be a natural-language policy violation when no choice or justification is provided.
The natural-language and configuration context indicate this skill is oriented to German output, and the language parameter is hard-coded to a default of "de". Under the policy, forcing a specific language or locale without user opt-in can be a language/locale policy violation when no explicit choice or justification is provided.
The module docstring presents the tool description and usage entirely in German, which imposes a specific language on users. Under the policy, locale or language constraints should be optional or clearly justified as region-specific; this file does not provide such justification or an opt-in choice.
The CLI prints a German-only error message when the input file is missing. This is a natural-language policy issue because the skill enforces one locale in user-facing output without offering a language choice or stating that the tool is intentionally limited to German users.
No suspicious patterns detected.