Back to skill

Security audit

Birdfolio

Security checks for vulnerabilities and agentic risk

Overview

Birdfolio is a coherent bird-tracking skill, but it automatically persists and publicly uploads user-derived bird cards with weak scoping and consent controls.

Review before installing. This skill can store your Telegram ID, region, bird photos, sightings, generated HTML/PNG cards, and upload card images to public cloud storage. Install only if you are comfortable with that sharing model, and prefer a version that requires confirmation before logging or uploading, restricts file paths and image URLs, and documents deletion and retention controls.

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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/upload_card.py:18
Finding

Arbitrary Local File Upload Through Unrestricted Path Arguments

Content
View full analysis
Remediation
View remediation
/cards`. - Resolve both the permitted root and input path with `os.path.realpath`, then enforce containment using `os.path.commonpath`. - Reject symbolic links and non-regular files. - Validate the PNG magic bytes and decode the image before upload instead of trusting the extension or MIME label. - Enforce strict file-size limits. - Remove or tightly constrain the arbitrary `--secrets` path. - Require explicit user confirmation before external publication. - Apply the same workspace-containment controls to `generate_card.py --image-path`. - Run the Skill under a dedicated operating-system account with access only to its workspace and required secrets. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_card.py:58
Finding

Server-Side Request Forgery Through Unrestricted Image URLs

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_card.py:119
Finding

Unescaped Data Is Executed as HTML During Card Rendering

Content
View full analysis
{s["dateFound"]}' if s["found"] else "" rows += f'
{check} {s["species"]}{date}
\n' ``` ### Technical Analysis Values derived from user input, search results, API responses, and local checklist data are inserted into HTML by raw string replacement or interpolation. No context-aware encoding is applied. The values appear in multiple contexts, including text nodes, HTML attributes, image sources, and CSS declarations. Consequently, a crafted value can terminate its current ...[truncated 1249 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/log_sighting.py:37
Finding

User API Requests Lack an Authentication Credential

Content
View full analysis
Remediation
View remediation
` or a cryptographically signed request mechanism over HTTPS. - Never treat a Telegram ID or URL path component as proof of identity. - Replace Telegram IDs in public PWA URLs with high-entropy, revocable identifiers that do not authorize API mutations. - Implement endpoint-level authorization tests for cross-user reads and writes. - Add rate limiting and monitoring for identifier enumeration. - Avoid storing long-lived authentication tokens in broadly readable plaintext configuration files. ]]>

other

Warning
Location
SKILL.md:203
Finding

User Card Images Are Published to Public Storage Without an Explicit Consent Gate

Content
View full analysis
" Capture `url` from output. Update the sighting's card URL in the API (use the `id` from the log_sighting output): PATCH /users/{telegram_id}/sightings/{sighting_id}/card Body: {"card_png_url": ""} Send the PNG via Telegram: message(action="send", media="") ``` ```python with open(png_path, "rb") as f: s3.put_object( Bucket=cfg["bucket"], Key=key, Body=f, ContentType="image/png", ) public_url = cfg.get("public_url", "").rstrip("/") if not public_url: print(json.dumps({ "status": "ok", "url": "", "note": "No public_url configured in r2-birdfolio.json. Enable public access on the bucket and add public_url to secrets." })) return url = f"{public_url}/{key}" print(json.dumps({"status": "ok", "url": url})) ``` ### Technical Analysis The declared flow uploads every generated card to R2 and expects public bucket access. This external publication is automatic and is separate from sending the local PNG through Telegram. The instructions do not require explicit user consent, provide a private-storage option, define retention, or provide deletion controls. The rendered card contains the submitted bird photo, region, sighting date, and collection details. Even if the screenshot does not retain original image metadata, visual content may reveal a residence, landmark, person, or other location-sensitive information. ### Attack Path 1. A user submits a bird photograph expecting identification and a Telegram response. 2. The Skill embeds the photograph in a card and renders it to PNG. 3. The PNG is automatical ...[truncated 527 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code chunk implements just one narrow feature: trading card generation from already-supplied bird details. It does not perform bird identification, maintain or query a life list, set user region, report checklist/progress, or answer bird-information questions. Those are major declared behaviors but are absent from this code. Additionally, the script fetches an image from a remote URL or reads a local file and writes HTML output to disk; while image retrieval may support card generation, the broader declared description does not accurately represent the actual scope of this specific code chunk. Therefore the description materially overstates the implemented behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a broad end-user skill covering bird photo identification, life-list analytics, bird information, and trading card generation. The supplied code chunk only performs initial setup: it creates local directories, writes config.json, and registers a user/region with an external API. While first-time setup is mentioned in the description, this code does not implement the core advertised behaviors and instead exposes a narrower initialization function. It also performs network access and local filesystem writes not reflected in the declared permissions. This is a material description-behavior mismatch rather than a mere supporting detail.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding

The description claims a broad multi-function Birdfolio skill, but this code chunk performs a narrow function: posting a sighting and retrieving summary stats. While sighting logging supports life list tracking in a limited sense, the major advertised capabilities—photo-based bird identification, user setup, region configuration, checklist display, progress reporting on demand, rarest bird lookup, species info, and trading card generation—are absent from this code. There is no evident undeclared harmful capability; the mismatch is that the actual behavior is substantially narrower than the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents a broad interactive Birdfolio skill that identifies birds, manages setup, answers checklist/progress questions, provides species details, and generates trading cards. The supplied code chunk does something much narrower and different: it loads local configuration and checklist data, transforms it into a flat item list, and sends it to a remote bulk import endpoint keyed by telegram ID. While checklist synchronization could be a supporting part of a Birdfolio system, this specific code does not implement the user-facing behaviors described and introduces an undeclared capability of uploading local data to an external API. That makes the code’s actual behavior materially different from the declared purpose.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill invokes filesystem writes, network access, and external scripts, but it does not declare an explicit tool scope or allowed-tools boundary. That increases the blast radius because the runtime may permit broader capabilities than users or reviewers expect, making accidental overreach and misuse harder to detect.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs the agent to search temp/media folders and %APPDATA% to locate user images when no attachment path is provided, which is a form of arbitrary local file discovery outside the intended attachment channel. This can expose unrelated files, other users' media, or sensitive local application data, especially on shared or multi-tenant hosts.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
76% confidence
Finding

The skill permits automatic logging and downstream processing when model confidence is 'high', with no user confirmation before writing sightings, updating checklists, generating cards, and potentially uploading images. That creates integrity and privacy risks because an incorrect classification or misread context can trigger irreversible or externally visible actions without user approval.

Content

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

md
- Bird is NOT on the checklist → use `bonus` (shows a neutral "Bonus Find" badge, no rarity assigned)

**Confidence rules:**
- `"high"` → proceed automatically, no confirmation needed
- `"medium"` → ask: *"I think this might be a [species] — based on [features]. Does that look right to you?"* → wait for confirmation before continuing
- `"low"` → reply: *"This photo isn't clear enough for me to be confident. Could you send a clearer shot?"* → stop, do not log anything

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The contradiction between 'always use the user's actual submitted photo' and the fallback to a stock image creates deceptive behavior and weakens data-handling guarantees. In security-sensitive review, inconsistent instructions are dangerous because they encourage the agent to bypass expected provenance controls and fetch external content instead.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill uploads generated cards containing user-submitted photos to a public Cloudflare R2 URL without explicit user notice or consent. That can publicly expose personal images, metadata, and behavioral history, creating privacy leakage and possible unauthorized dissemination of user content.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The schema explicitly states that the checklist is populated by the agent using external You.com search results and written into workspace files, but it provides no user-facing disclosure or consent boundary for those writes or outbound data use. In a skill that handles user media and ongoing personal tracking, silent persistence and transmission can expose user-derived data unexpectedly and undermine privacy expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The schema allows the agent to create per-species cache files from external search results without warning that third-party content will be stored locally. This can lead to unanticipated retention of externally sourced data, including image URLs and descriptive content tied to the user's usage patterns, which is a privacy and transparency issue rather than a code-execution flaw.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation says card screenshots and self-contained HTML artifacts are saved automatically, but it omits a warning that generated assets will persist in the workspace. Because these artifacts may encode bird images, timestamps, and sighting-related details, automatic storage increases privacy and data-retention risk, especially for users who may assume cards are only transiently generated for messaging.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code reads local checklist content and sends it via an HTTP POST request to a remote endpoint tied to a user identifier. Although the module docstring says it will push checklist.json to the API, there is no runtime confirmation, warning, or disclosure about transmitting user data from the local workspace.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This script reads a local PNG file and uploads it to an external Cloudflare R2 bucket, but it provides no built-in user consent, warning, or policy check before transmitting data off-host. In a skill context, that is a real data-handling risk because a locally generated or user-supplied image may contain sensitive content or metadata and the transfer is automatic once invoked.

Content

No source excerpt is available for this finding.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The call to s3.put_object sends local file contents to cloud storage, which is a direct exfiltration primitive if the input path can be influenced or if users are unaware that files are being published externally. The surrounding code also constructs a public URL for the uploaded object, increasing the exposure from private transmission to potential public disclosure if bucket public access is enabled.

Content

Scanner excerpt · scripts/upload_card.py (reported line 61)May include surrounding context.

python
key = "cards/" + os.path.basename(png_path)

    with open(png_path, "rb") as f:
        s3.put_object(
            Bucket=cfg["bucket"],
            Key=key,
            Body=f,

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The documentation frames the feature as generating a trading card from the user's actual bird photo, and line L167 explicitly says to always use the submitted photo. Allowing a fallback to search for and use an external stock image introduces a separate web-image acquisition capability that is not necessary for life-list tracking and weakens the user-intent boundary around processing only their submission.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.