T09 · Insecure Skill Coding Practices
- Location
kb.py:204- Finding
Arbitrary Code Execution Through Python Source Injection in PDF Extraction
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a plausible personal knowledge-base skill, but it stores and sends saved content externally and contains unsafe URL/PDF handling that should be reviewed before use.
Review before installing. Do not use this skill with secrets, regulated data, private company documents, internal URLs, or untrusted PDFs unless it is remediated. It should clearly disclose remote AI-provider processing, add confirmation/local-only controls, restrict URL destinations, and fix the PDF and temporary-file handling issues.
kb.py:204Arbitrary Code Execution Through Python Source Injection in PDF Extraction
kb.py:104Server-Side Request Forgery Through Unrestricted User-Supplied URLs
kb.py:302Sensitive Knowledge-Base Content and Queries Are Transmitted to External AI Providers
kb.py:153Predictable Shared Temporary Files Permit Transcript Poisoning and Local File Disclosure
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def _api_get(url: str, timeout: int = 15) -> str:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 (compatible; OEEBot/1.0)"})
with urllib.request.urlopen(req, timeout=timeout) as r:
return r.read().decode("utf-8", errors="replace")
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
for attempt in range(EMBED_RETRIES):
try:
with urllib.request.urlopen(req, timeout=30) as r:
data = json.loads(r.read())
return [d["embedding"] for d in sorted(data["data"], key=lambda x: x["index"])]
except Exception as e:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
},
)
with urllib.request.urlopen(req, timeout=60) as r:
data = json.loads(r.read())
answer = data["content"][0]["text"]
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.
Save context: `python ingest.py "TEXT"`
Retrieve context: `python query.py "QUESTION"`
<!-- 🐾 Muninn never forgets -->
The skill encourages saving arbitrary text, URLs, files, and notes, and it requires external embedding providers via API keys, but it does not warn users that sensitive content may be stored locally in SQLite and transmitted to third-party embedding services. This omission can cause users or agents to send secrets, personal data, or confidential material into storage and external processing without informed consent.
The trigger phrases include generic language such as "remember this" and "what do I know about," which can easily overlap with normal conversation and cause unintended activation. In an agent setting, that can lead to accidental ingestion of user content into persistent storage or retrieval from the knowledge base without sufficiently explicit user intent.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if not match:
raise ValueError(f"Not a valid tweet URL: {url}")
user, tweet_id = match.groups()
api_url = f"https://api.fxtwitter.com/{user}/status/{tweet_id}"
try:
data = json.loads(_retry(lambda: _api_get(api_url)))
tweet = data.get("tweet", {})
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
if not match:
raise ValueError(f"Not a valid tweet URL: {url}")
user, tweet_id = match.groups()
api_url = f"https://api.fxtwitter.com/{user}/status/{tweet_id}"
try:
data = json.loads(_retry(lambda: _api_get(api_url)))
tweet = data.get("tweet", {})
The module is described as a personal knowledge base with semantic retrieval, but this implementation adds subprocess execution via yt-dlp for YouTube transcript extraction. Without any manifest declaring shell/tool execution as part of scope, spawning external binaries is a broader capability than the stated high-level KB purpose implies.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def extract_youtube(url: str) -> tuple[str, str]:
"""Extract YouTube transcript via yt-dlp.""" # 🐾
try:
title_out = subprocess.run(
["yt-dlp", "--get-title", url],
capture_output=True, text=True, timeout=30
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
except Exception:
title = "YouTube Video"
result = subprocess.run(
["yt-dlp", "--write-auto-sub", "--sub-lang", "en", "--skip-download",
"--sub-format", "vtt", "-o", "/tmp/kb_yt_%(id)s", url],
capture_output=True, text=True, timeout=60
Invoking external tooling to process PDFs materially increases risk because PDFs are attacker-controlled inputs and parser chains are historically bug-prone. In this implementation, the danger is compounded by dynamic code construction for python3 -c, turning a content-extraction feature into a potential local code-execution path when given a crafted local filename.
This code builds Python source with an f-string and injects the PDF path directly into a python3 -c script. If path contains quotes or Python syntax, an attacker controlling the local path argument to extract_pdf can break out of the string literal and execute arbitrary Python code in the subprocess.
path = tmp.name
try:
result = subprocess.run(
["python3", "-c", f"""
import sys
try:
The skill sends ingested content to external embedding and model providers using environment-backed credentials without any visible access control, redaction, or sensitivity checks. In a personal knowledge-base context, that can expose stored private notes, fetched documents, or other sensitive material to third parties unexpectedly.
Text passed to embed_texts is uploaded to OpenAI without any user-facing warning at the transmission point. For a knowledge base that may hold personal or confidential content, silent third-party transmission creates a meaningful privacy and compliance risk.
This line sends potentially sensitive ingested text to OpenAI's embeddings API. In the context of a personal knowledge base, external transmission of document content is a real privacy/security concern if done without clear consent, minimization, or redaction.
payload = json.dumps({"input": texts, "model": EMBED_MODEL}).encode()
req = urllib.request.Request(
"https://api.openai.com/v1/embeddings",
data=payload,
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
)
Remote content fetched from arbitrary URLs is embedded via a third-party API without a clear disclosure step, which may surprise users and may violate expectations around handling copyrighted, confidential, or regulated content. The ingestion workflow makes this more concerning because transmission is automatic after fetch and validation.
Retrieved chunk content is inserted verbatim into the Anthropic prompt, so any sensitive text previously ingested can be disclosed in plain language to the external model and then to whoever can query the system. In a personal knowledge-base setting, this is especially risky because the corpus may contain secrets, private notes, credentials, or proprietary documents.
The ask() flow transmits retrieved KB context verbatim to Anthropic, potentially including sensitive information from previously ingested sources. Because retrieval is query-driven and the content is inserted wholesale, this can expose private corpus contents to an external LLM provider and to unauthorized users of the skill.
}).encode()
req = urllib.request.Request(
"https://api.anthropic.com/v1/messages",
data=payload,
headers={
"x-api-key": api_key,
The yt-dlp invocation forces --sub-lang en, which imposes a specific language/locale behavior without user opt-in. This can violate language-choice policy because users are not given any option to select another subtitle language or accept the English-only constraint.
No suspicious patterns detected.