Back to skill

Security audit

blog-to-kindle

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its blog-to-Kindle purpose, but it has unsafe defaults and implementation flaws that can send documents or credentials to unexpected external destinations.

Review before installing. Remove the hard-coded Kindle address, require an explicit recipient and confirmation before sending, fix AppleScript argument handling, document one trusted cover-generation endpoint, and add URL, redirect, file-size, and content-type limits before using this with sensitive documents or real API credentials.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send_to_kindle.py:7
Finding

Hard-Coded Kindle Recipient Can Cause Unauthorized Document Disclosure

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send_to_kindle.py:10
Finding

AppleScript Injection Through Unescaped Delivery Arguments

Content
View full analysis
bool: """Send file to Kindle using Mail.app AppleScript.""" applescript = f''' tell application "Mail" set newMessage to make new outgoing message with properties {{subject:"{subject}", visible:false}} tell newMessage make new to recipient at end of to recipients with properties {{address:"{kindle_email}"}} set content to "Sent from blog-to-kindle skill." make new attachment with properties {{file name:"{file_path}"}} at after the last paragraph end tell send newMessage end tell ''' result = subprocess.run( ["osascript", "-e", applescript], capture_output=True, text=True ) return result.returncode == 0 ``` ### Technical Analysis The values `subject`, `kindle_email`, and `file_path` are inserted directly into AppleScript source code inside quoted string literals. No AppleScript escaping or input validation is applied. Although `subprocess.run` does not invoke a shell, the generated argument itself is executable AppleScript source. A value containing a quotation mark followed by valid AppleScript syntax can terminate the intended string and introduce additional statements. AppleScript can automate applications and can invoke shell commands through `do shell script`, so successful injection can become arbitrary local code execution with the privileges of the user running the Skill. The file path is resolved before use, but resolution does not remove quotation marks or other AppleScript metacharacters from a filename. The email and subject are directly controllable through command-line arguments. ### Attack Path 1. An attacker convinces a us ...[truncated 957 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/fetch_blog.py:40
Finding

Unrestricted Custom Blog Fetching Permits Server-Side Request Forgery

Content
View full analysis
str: """Fetch a page with retries.""" for i in range(retries): try: resp = httpx.get(url, follow_redirects=True, timeout=30) resp.raise_for_status() return resp.text except Exception as e: if i == retries - 1: raise time.sleep(2 ** i) return "" ``` ```python def get_article_links(archive_html: str, config: dict, base_url: str) -> list[str]: """Extract article links from archive page.""" soup = BeautifulSoup(archive_html, "html.parser") links = [] seen = set() for a in soup.select(config["link_selector"]): href = a.get("href", "") if not href or href.startswith("#"): continue full_url = urljoin(base_url, href) if full_url not in seen: seen.add(full_url) links.append(full_url) return links ``` ```python if args.site == "custom": if not args.url: print("Error: --url required for custom sites") return 1 config = SITES["paulgraham"].copy() # Use as template config["archive_url"] = args.url config["base_url"] = urljoin(args.url, "/") ``` ```python archive_html = fetch_page(config["archive_url"]) links = get_article_links(archive_html, config, config["base_url"]) for i, url in enumerate(links): print(f"[{i+1}/{len(links)}] Fetching {url}...") try: html = fetch_page(url) article = extract_article(html, url, config) ``` ### Technical Analysis The custom-site mode accepts an arbitrary archive URL and passes it to `httpx.get`. Article links extracted from the returned HTML are also fetched without enforcing a scheme, host, port, ...[truncated 1637 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_cover.py:9
Finding

API Credential Is Transmitted to an Endpoint Inconsistent with Primary Documentation

Content
View full analysis
dict: r = requests.post( f"{API_BASE}/pilot", headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"}, json=body, timeout=60, ) return r.json() ``` The primary Skill instructions identify a different endpoint: ```python result = requests.post( "https://api.skillbossai.com/v1/pilot", headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"}, json={ "type": "image", "inputs": { "prompt": "Book cover for '[Author Name]: [Subtitle]'. Minimalist design with elegant typography. [Brand color] accent. Clean white/cream background. Simple geometric or abstract motif related to [topic]. Professional literary feel. No photos, no faces. Portrait book cover dimensions." }, "prefer": "quality" } ).json() ``` ### Technical Analysis The Skill declares `SKILLBOSS_API_KEY` as a required credential and the main documentation demonstrates its use with `api.skillbossai.com`. However, the executable implementation and manual workflow send that credential as a bearer token to `api.heybossai.com`. The repository provides no evidence that the two domains belong to the same operator, share an authorization boundary, or are both valid audiences for the token. Bearer tokens confer access to whoever possesses them, so sending one to an unintended origin can disclose the credential and its associated API privileges. The network transfer is necessary for remote i ...[truncated 1029 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/generate_cover.py:22
Finding

API-Controlled Image Download Permits Blind SSRF and Unbounded Resource Consumption

Content
View full analysis
str: """Generate cover image via SkillBoss API Hub and save to file.""" result = pilot({ "type": "image", "inputs": {"prompt": prompt}, "prefer": "quality" }) image_url = result["result"]["image_url"] img_resp = requests.get(image_url, timeout=60) img_resp.raise_for_status() with open(output_path, "wb") as f: f.write(img_resp.content) return output_path ``` ### Technical Analysis The external API fully controls `image_url`, and the script immediately retrieves that URL. It does not verify the URL scheme, host, port, resolved IP address, or redirect destination. A compromised or malicious API response can therefore induce a request to localhost, private networks, link-local services, or cloud metadata endpoints. The call also accesses `img_resp.content`, which buffers the complete response in memory, and then writes it without a maximum byte limit. The 60-second timeout limits elapsed request time but does not impose a safe response-size limit. Finally, the code does not verify `Content-Type`, file signatures, image dimensions, or successful image decoding. Arbitrary response data can be saved under an image filename and passed to downstream tooling. ### Attack Path 1. The image-generation service or its response path is compromised or returns a malicious `image_url`. 2. The supplied URL points to an internal resource or redirects to one. 3. `requests.get()` follows the destination without network-boundary validation. 4. The script makes a request from the user's network context. 5. For resource exhaustion, the URL returns a very large response. 6. The entire body is buffered into memory and written to disk. 7. Fo ...[truncated 546 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (29)

Tainted flow: 'SKILLBOSS_API_KEY' from os.environ (line 9, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/generate_cover.py (reported line 14)May include surrounding context.

python
def pilot(body: dict) -> dict:
    r = requests.post(
        f"{API_BASE}/pilot",
        headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"},
        json=body,

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code clearly performs only the fetching/extraction stage of the declared workflow. It scrapes archive pages and article content, then stores output as markdown plus a manifest. There is no EPUB creation, no cover generation, and no Kindle integration or sending behavior. The supported-site list in code is narrower than the description, omitting at least Wait But Why and Astral Codex Ten. While scraping blogs is aligned with part of the description, the primary end-to-end purpose described is broader than what this code chunk actually does, so this is a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared purpose centers on acquiring web content from specific blogs or custom sites and transforming it into a Kindle-friendly ebook with an AI-generated cover. This code does none of that. Its sole function is to send a preexisting local file to a Kindle email address through Mail.app. While emailing a finished ebook to Kindle could be a downstream step in a larger workflow, this chunk’s primary behavior is materially different from the declared description and introduces an undeclared local-file email-sending capability.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill requests or demonstrates capabilities spanning environment access, filesystem read/write, network access, and shell execution, but it does not declare any explicit tool scope or permissions boundary. That makes it easier for an agent runtime to grant broader access than users expect, increasing risk of unintended scraping, file manipulation, or outbound transmission.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs sending generated EPUBs to a Kindle email address without warning users that document contents will be transmitted to an external service. Users may include private material, scraped data, or copyrighted content without understanding that it leaves the local environment.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

Cover Generation

Use SkillBoss API Hub image generation to create covers. Prompt template:

text
Book cover for '[Author Name]: [Subtitle]'.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill sends cover-generation prompts to a third-party image API but does not warn that prompts and related metadata may be shared externally. If prompts include personal names, reading history, project details, or sensitive themes, that information may be disclosed outside the local system.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
82% confidence
Finding

This duplicate finding points to the same outbound POST behavior to the SkillBoss endpoint. While the transmission aligns with the stated cover-generation feature, it still creates privacy and policy concerns if prompt content is user-derived and users are not clearly informed.

Content

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

md
SKILLBOSS_API_KEY = os.environ["SKILLBOSS_API_KEY"]

result = requests.post(
    "https://api.skillbossai.com/v1/pilot",
    headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
82% confidence
Finding

This duplicate finding points to the same outbound POST behavior to the SkillBoss endpoint. While the transmission aligns with the stated cover-generation feature, it still creates privacy and policy concerns if prompt content is user-derived and users are not clearly informed.

Content

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

md
SKILLBOSS_API_KEY = os.environ["SKILLBOSS_API_KEY"]

result = requests.post(
    "https://api.skillbossai.com/v1/pilot",
    headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

The hard-coded remote endpoint confirms the skill communicates with an external service. That is contextually expected here, but any fixed third-party endpoint expands the trust boundary and should be treated as a controlled data egress path rather than silently embedded behavior.

Content

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

md
SKILLBOSS_API_KEY = os.environ["SKILLBOSS_API_KEY"]

result = requests.post(
    "https://api.skillbossai.com/v1/pilot",
    headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"},
    json={
        "type": "image",

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Publishing a real default Kindle email address without caution creates a realistic risk of unintended content delivery to another person's device/account. In this context, the skill's purpose is outbound document transmission, so exposing a ready-to-use personal address makes misuse more likely and more damaging.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation exposes a hard-coded personal Kindle email address and presents it as a default delivery target. This can cause accidental delivery of user-generated or scraped content to an unintended recipient and leaks personal contact information embedded in the skill.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The plain-language instructions reveal a real personal Kindle email address for operational reuse. This is dangerous because the skill directly supports automated delivery, so users or downstream agents may send arbitrary content to that personal endpoint, causing privacy, abuse, and harassment risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The example pulls a credential from the local keychain and immediately uses it in an external API request without a user-facing warning. This is risky because it silently accesses a local secret store and transmits authenticated traffic off-device, which exceeds normal expectations for a simple blog-to-EPUB workflow.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manual workflow retrieves an API credential from the local keychain and uses it to call an external image-generation service, expanding the skill from local ebook compilation into credential access and third-party network use. Even if intended for convenience, this creates unnecessary secret handling and outbound data flow that could expose credentials or normalize broader system access than users expect from this skill.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

The finding captures the same outbound API request to api.heybossai.com, which is an external transmission outside the local ebook-processing boundary. Although the call appears functionally related to cover generation rather than overtly malicious, it still introduces privacy, dependency, and secret-use risks if performed without clear user awareness.

Content

Scanner excerpt · references/manual-workflow.md (reported line 58)May include surrounding context.

md
SKILLBOSS_API_KEY=$(security find-generic-password -a "aineko" -s "api/skillboss" -w) \
python3 -c "
import requests, os, urllib.request
result = requests.post(
    'https://api.heybossai.com/v1/pilot',
    headers={'Authorization': 'Bearer ' + os.environ['SKILLBOSS_API_KEY'], 'Content-Type': 'application/json'},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

The finding captures the same outbound API request to api.heybossai.com, which is an external transmission outside the local ebook-processing boundary. Although the call appears functionally related to cover generation rather than overtly malicious, it still introduces privacy, dependency, and secret-use risks if performed without clear user awareness.

Content

Scanner excerpt · references/manual-workflow.md (reported line 58)May include surrounding context.

md
SKILLBOSS_API_KEY=$(security find-generic-password -a "aineko" -s "api/skillboss" -w) \
python3 -c "
import requests, os, urllib.request
result = requests.post(
    'https://api.heybossai.com/v1/pilot',
    headers={'Authorization': 'Bearer ' + os.environ['SKILLBOSS_API_KEY'], 'Content-Type': 'application/json'},
    json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
87% confidence
Finding

The hard-coded third-party endpoint identifies a specific external service that receives workflow data during execution. This is not inherently malicious, but in a skill centered on scraping and compilation, an undocumented or automatic transmission to a remote API increases the attack surface and user surprise factor.

Content

Scanner excerpt · references/manual-workflow.md (reported line 59)May include surrounding context.

md
python3 -c "
import requests, os, urllib.request
result = requests.post(
    'https://api.heybossai.com/v1/pilot',
    headers={'Authorization': 'Bearer ' + os.environ['SKILLBOSS_API_KEY'], 'Content-Type': 'application/json'},
    json={
        'type': 'image',

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The workflow includes fully automated outbound email sending via Mail.app and command-line mail tools, which broadens the capability from document preparation to autonomous messaging. This is dangerous because it can send files to recipients without an explicit confirmation step, potentially leaking content or enabling misuse of the user's mail environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The Mail.app AppleScript and mutt examples send an email automatically without any warning, preview, or approval checkpoint. In a skill that handles compiled content, silent transmission increases the risk of accidental data exfiltration, wrong-recipient delivery, or abuse if the workflow is adapted or run unattended.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The generated EPUB frontmatter always sets lang: en, which imposes a specific language/locale regardless of the input essays or user preference. Under the policy, forcing a locale without opt-in is a natural-language policy violation.

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/compile_epub.py (reported line 90)May include surrounding context.

python
print("⚠️  No cover specified! Use --cover to add one.")

    print(f"Running pandoc...")
    result = subprocess.run(cmd, capture_output=True, text=True)

    if result.returncode != 0:
        print(f"pandoc error: {result.stderr}")

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/generate_cover.py (reported line 10)May include surrounding context.

python
from pathlib import Path

SKILLBOSS_API_KEY = os.environ["SKILLBOSS_API_KEY"]
API_BASE = "https://api.heybossai.com/v1"


def pilot(body: dict) -> dict:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/generate_cover.py (reported line 14)May include surrounding context.

python
def pilot(body: dict) -> dict:
    r = requests.post(
        f"{API_BASE}/pilot",
        headers={"Authorization": f"Bearer {SKILLBOSS_API_KEY}", "Content-Type": "application/json"},
        json=body,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

User-supplied cover prompts are transmitted to a third-party image-generation API, but the script provides no explicit notice, consent step, or data-classification guardrails. If users include private reading lists, personal details, or sensitive text in prompts, that data leaves the local environment and is handled by an external provider.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.