Back to skill

Security audit

Miniflux Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Miniflux management helper, but its README can direct users to send their API token to a fixed third-party Miniflux URL and its destructive commands lack confirmation safeguards.

Review this skill before installing. Use only a MINIFLUX_URL for the Miniflux instance you control or explicitly trust, never send a real API token to the README's fixed reader.etereo.cloud example unless that is your intended server, and rotate any token already used against the wrong host. Treat delete and bulk mark-read commands as account-changing operations that should be explicitly confirmed. Prefer installing the Python dependency in an isolated, pinned environment.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
README.md:32
Finding
API Credentials May Be Sent to a Fixed Third-Party Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `README.md:32`, `README.md:154`, `README.md:182-183`; credential use in `scripts/miniflux-cli.py:20-29` **Vulnerability Type**: Credential disclosure through unsafe endpoint configuration **Risk Level**: Critical ### Vulnerable Code `README.md:32`: ```bash export MINIFLUX_URL="https://reader.etereo.cloud" export MINIFLUX_TOKEN="your-api-token-here" ``` `README.md:154`: ```markdown | `MINIFLUX_URL` | Miniflux base URL | https://reader.etereo.cloud | | `MINIFLUX_TOKEN` | API authentication token | Required | ``` `README.md:182-183`: ```bash export MINIFLUX_URL="https://reader.etereo.cloud" # Correct export MINIFLUX_URL="https://reader.etereo.cloud/v1/" # Wrong ``` `scripts/miniflux-cli.py:20-29`: ```python def get_client(): """Create and return a Miniflux client instance.""" url = os.environ.get('MINIFLUX_URL') token = os.environ.get('MINIFLUX_TOKEN') if not url or not token: print("Error: MINIFLUX_URL and MINIFLUX_TOKEN environment variables must be set.", file=sys.stderr) sys.exit(1) return miniflux.Client(url, api_key=token) ``` ### Technical Analysis The README repeatedly identifies `https://reader.etereo.cloud` as the default or “correct” Miniflux endpoint. This conflicts with `SKILL.md:34`, which uses a placeholder representing the user's own Miniflux instance. The implementation trusts `MINIFLUX_URL` without validating ownership or checking that the endpoint is the instance that issued `MINIFLUX_TOKEN`. It then constructs an authenticated client using the supplied API token. Consequently, a user who follows the README while substituting a real token can transmit that credential to the fixed endpoint. HTTPS protects the credential in transit but does not protect it from the operator of the destination server. The remote endpoint necessarily receives the authentication request and may log or otherwise retain the supplied credential. ### Attack Path 1. ...[truncated 1290 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace every fixed endpoint with an unmistakable placeholder: ```bash export MINIFLUX_URL="https://miniflux.example.com" ``` 2. State explicitly that `MINIFLUX_URL` must identify the same trusted Miniflux instance that issued `MINIFLUX_TOKEN`. 3. Remove the “Default” designation for any project-controlled or third-party endpoint. 4. Add URL validation in `get_client()`: - Require an absolute HTTPS URL, except for explicitly permitted local development addresses. - Reject embedded credentials, fragments, and malformed hostnames. - Warn users before sending credentials to a newly configured host. 5. Consider storing a previously approved hostname and requiring explicit confirmation if it changes. 6. Add documentation explaining that API tokens must never be tested against endpoints not controlled or explicitly trusted by the user. 7. Rotate any real token that may already have been used with the documented fixed endpoint. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:16
Finding
Unpinned Dependency Is Installed into Broad Python Environments<![CDATA[ ## Vulnerability Details **File Location**: `README.md:16-26`, `README.md:162-166`; related instructions in `SKILL.md:22-26`, `SKILL.md:186-188`, and `scripts/miniflux.sh:26-33` **Vulnerability Type**: Mutable third-party dependency installation without version or integrity constraints **Risk Level**: Medium ### Vulnerable Code `README.md:16-26`: ```bash python3 -m pip install --user --break-system-packages miniflux ``` ```bash uv pip install --system miniflux ``` `SKILL.md:22-26`: ```bash # Install the miniflux Python package uv pip install miniflux ``` `scripts/miniflux.sh:26-33`: ```bash if ! python3 -c "import miniflux" 2>/dev/null; then echo "Error: miniflux Python package not installed." echo "Please install it manually:" echo " uv pip install miniflux" echo "" echo "Or with pip:" echo " pip install miniflux" exit 1 fi ``` ### Technical Analysis The installation commands request the package by name without a pinned version or cryptographic hash. The effective dependency can therefore change after the Skill has been reviewed. A later compromised, malicious, or incompatible package release would be installed without further source review. The README compounds the exposure by recommending `--system` and `--break-system-packages`. These options install into broad Python environments or bypass operating-system package-management protections rather than isolating the dependency in a dedicated virtual environment. Python packages may execute code during installation, and imported package code executes in the context of the invoking user. This Skill imports `miniflux` at startup, so a compromised installed package would also execute whenever the CLI runs. ### Attack Path 1. An attacker compromises the upstream package, package maintainer account, publication pipeline, or package-index delivery path. 2. A malicious release becomes the version resolved by the unpinned installation command. 3. A user follows the ...[truncated 1079 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the dependency to a reviewed version rather than resolving the latest release: ```text miniflux==REVIEWED_VERSION ``` 2. Record cryptographic hashes in a requirements or lock file and install with hash verification. 3. Use a dedicated virtual environment: ```bash python3 -m venv .venv . .venv/bin/activate python3 -m pip install --require-hashes -r requirements.txt ``` 4. Remove `--system` and `--break-system-packages` from all installation instructions. 5. Commit the lock file and review dependency changes before updating it. 6. Use an approved package index and avoid untrusted additional indexes. 7. Add automated dependency vulnerability and provenance checks to the release process. 8. Document the expected package version and verify it at runtime where practical. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
scripts/miniflux-cli.py:145
Finding
Attacker-Controlled Feed Content Is Emitted Without a Trust Boundary<![CDATA[ ## Vulnerability Details **File Location**: `scripts/miniflux-cli.py:32-64`, `scripts/miniflux-cli.py:145-157`, and `scripts/miniflux-cli.py:386-398` **Vulnerability Type**: Untrusted remote content exposed to an AI Agent session **Risk Level**: Medium ### Vulnerable Code `scripts/miniflux-cli.py:32-64`: ```python def format_entry(entry, full_content=False): """Format an entry for display.""" status_icon = "📖" if entry.get('status') == 'unread' else "✅" star_icon = "⭐" if entry.get('starred') else "" output = f"\n{status_icon} {entry.get('title', 'No title')}" if star_icon: output += f" {star_icon}" output += f"\n URL: {entry.get('url', 'N/A')}" if entry.get('feed'): feed_title = entry.get('feed', {}).get('title', 'Unknown feed') output += f"\n Feed: {feed_title}" if entry.get('published_at'): pub_date = datetime.fromisoformat(entry['published_at'].replace('Z', '+00:00')) output += f"\n Published: {pub_date.strftime('%Y-%m-%d %H:%M')}" if entry.get('reading_time'): output += f"\n Reading time: {entry.get('reading_time')} min" if full_content and entry.get('content'): # Strip HTML tags for cleaner output import re content = re.sub(r'<[^>]+>', '', entry['content']) content = re.sub(r'\s+', ' ', content).strip() if len(content) > 500: content = content[:500] + "..." output += f"\n Content: {content}" output += f"\n ID: {entry.get('id')}" return output ``` `scripts/miniflux-cli.py:145-157`: ```python def cmd_entry(args): """Get a specific entry.""" client = get_client() entry = client.get_entry(args.entry_id) print(format_entry(entry, full_content=True)) if entry.get('content'): print(f"\n📝 Full Content:\n{entry['content']}") return 0 ``` `scripts/miniflux-cli.py:386-398`: ```python entries_parser.add_argument('--status', choices=['unread' ...[truncated 3299 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every feed field as untrusted data and label the output accordingly. 2. Return structured JSON with distinct data fields instead of prose that can resemble Agent instructions. 3. Wrap article content in explicit untrusted-data delimiters and include a warning that embedded instructions must not be followed. 4. Remove ASCII and Unicode control characters, including terminal escape sequences. 5. Sanitize HTML with a maintained parser or sanitization library rather than a regular expression. 6. Avoid printing the raw article body by default. Require an explicit option and apply a strict length limit. 7. Consider returning only metadata and a short text extract to the Agent, with full content made available through a separate user-approved operation. 8. Add Skill-level instructions requiring the Agent to interpret feed content only as quoted source material and never as operational guidance. 9. Test the output path with common prompt-injection payloads and terminal-control sequences. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README documents destructive operations such as deleting feeds and categories and marking items as read, but it does not warn users that these actions modify server-side state and may be irreversible or hard to recover. In a skill designed to be invoked by an agent or copied by users, omission of cautions increases the risk of accidental data loss or unintended bulk changes.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documents capabilities that require environment access and outbound network access, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates an authorization gap where an agent may invoke broader-than-expected capabilities, increasing the chance of secret exposure from MINIFLUX_TOKEN or unintended API actions against the Miniflux instance.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill advertises destructive operations such as deleting feeds and categories and marking items as read, but it provides no warning, confirmation guidance, or safety notes about irreversible or state-changing effects. In an agent setting, this raises the risk of accidental data loss or unwanted account changes if a user request is ambiguous or the agent acts too aggressively.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The delete-feed command performs an irreversible state-changing action immediately with no confirmation prompt, dry-run mode, or force flag. In an agent or CLI context, this increases the chance of accidental deletion from user error, prompt misinterpretation, or unsafe automation, causing loss of subscriptions and associated configuration.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The mark-feed-read command bulk-modifies all unread entries in a feed without warning or confirmation. Because this action can silently alter large amounts of user state, an accidental or manipulated invocation can cause users to lose track of unread content and undermine trust in automation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The delete-category command deletes a category immediately without a confirmation barrier. In a management skill that exposes destructive administrative actions, lack of confirmation makes accidental data loss more likely, especially when invoked by higher-level agents that may misunderstand user intent.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
Line L91 states that all documentation must be in English, which is a language policy constraint expressed in natural language. Under the policy rules, forcing a specific language without opt-in or clear regional/compliance justification is a reportable violation.

Missing User Warnings

Low
Confidence
85% confidence
Finding
The skill instructs users to export an API token into the environment but does not include any warning about secret handling, logging, shell history, or avoiding token disclosure. While this is common setup guidance, omission of basic token-safety instructions can lead to credential leakage in shared terminals, transcripts, or debugging output.

Static analysis

No suspicious patterns detected.