Back to skill

Security audit

Follow News

Security checks for vulnerabilities and agentic risk

Overview

This is broadly a news digest skill, but it needs Review because it can use authenticated browser tooling, auto-update OpenCLI during a run, and fetch user-configured URLs without strong network boundaries.

Install only after reviewing the OpenCLI settings. Disable OpenCLI auto-update, prefer explicit API tokens or explicitly approved browser-session use, pin optional Python dependencies, and allow only trusted source configuration because feed URLs can make requests from the machine running the skill. Verify the missing config/defaults files are present in the actual install before relying on the advertised built-in sources.

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 (4)

T01 · Skill Instruction Hijacking

Error
Location
references/digest-prompt.md:181
Finding

Mandatory Promotional Content Is Injected into Generated Digests

Content
View full analysis
` is `chat`, use the footer from `references/templates/chat.md`. --- 📊 Data Sources: RSS {{rss}} | Twitter {{twitter}} | Reddit {{reddit}} | Web {{web}} 🤖 Generated by follow-news v | | Powered by OpenClaw ``` Equivalent branding is also embedded in the output-specific templates: ```markdown 🤖 Generated by follow-news v | 🔗 https://github.com/tangwz/follow-news | Powered by OpenClaw ``` ### Technical Analysis The Skill routes digest generation through `references/digest-prompt.md` and directs the agent to follow the selected output template. These instructions require generated reports to contain fixed promotional branding and external links that are not necessary to collect, summarize, or deliver news. Because the content is imposed by Skill instructions rather than requested by the user, it modifies the agent's user-facing output for the benefit of the Skill project and platform. The same behavior appears across multiple output formats, making the modification systematic rather than an incidental example. This does not grant operating-system privileges or execute code, but it compromises output integrity and alters the agent's current-session behavior whenever the Skill is used. ### Attack Path 1. A user requests a technology-news digest. 2. `SKILL.md` routes the agent to `references/digest-prompt.md`. 3. The prompt instructs the agent to apply one of the bundled output templates. 4. The selected template or default footer adds the fixed `follow-news` a ...[truncated 633 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch-rss.py:380
Finding

Configurable RSS URLs Permit Server-Side Request Forgery

Content
View full analysis
bool: """Return whether value is an HTTP(S) URL with a hostname.""" if not isinstance(value, str): return False if any(char.isspace() or ord(char) < 32 for char in value): return False try: parsed = urlparse(value) hostname = parsed.hostname parsed.port except ValueError: return False if not hostname: return False if any(char.isspace() or ord(char) < 32 for char in hostname): return False return parsed.scheme in {"http", "https"} ``` The RSS fetcher then requests the configured URL directly: ```python def fetch_feed_with_retry(source: Dict[str, Any], cutoff: datetime, no_cache: bool = False) -> Dict[str, Any]: """Fetch RSS feed with retry mechanism and conditional requests.""" source_id = source["id"] name = source["name"] url = source["url"] priority = source["priority"] topics = source["topics"] global _rss_cache, _rss_cache_dirty for attempt in range(RETRY_COUNT + 1): try: req_headers = {"User-Agent": "FollowNews/2.0"} # Add conditional headers from cache (thread-safe) with _rss_cache_lock: cache = _rss_cache if _rss_cache is not None else {} cache_entry = cache.get(url) now = time.time() ttl_seconds = RSS_CACHE_TTL_HOURS * 3600 if cache_entry and not no_cache and (now - cache_entry.get("ts", 0)) < ttl_seconds: if cache_entry.get("etag"): req_headers["If-None-Match"] = cache_entry["eta ...[truncated 2734 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch-podcast.py:797
Finding

Podcast Fetching Accepts Unrestricted HTTP Destinations

Content
View full analysis
bool: """Return whether value is an HTTP(S) URL with a hostname.""" if not isinstance(value, str): return False if any(char.isspace() or ord(char) < 32 for char in value): return False try: parsed = urlparse(value) hostname = parsed.hostname parsed.port except ValueError: return False if not hostname: return False if any(char.isspace() or ord(char) < 32 for char in hostname): return False return parsed.scheme in {"http", "https"} ``` The validated podcast source is requested directly: ```python request = Request( source["url"], headers={"User-Agent": "FollowNews/2.0"}, ) with urlopen(request, timeout=TIMEOUT) as response: ``` ### Technical Analysis Non-Xiaoyuzhou podcast URLs are accepted when they contain an HTTP(S) scheme and hostname. There is no network-range restriction or destination-address validation before `urlopen` performs the request. This creates the same SSRF class as the RSS fetcher through a separate execution path. Restricting Xiaoyuzhou URLs to known hosts does not protect generic RSS or automatically detected podcast sources. ### Attack Path 1. An attacker with configuration influence adds a podcast source pointing to a loopback, private, link-local, or metadata address. 2. Generic URL validation accepts the source. 3. The podcast fetch stage constructs a `Request` from `source["url"]`. 4. The agent host sends the request to the protected destination. 5. The attacker observes behavioral differences or causes parseable response data to en ...[truncated 460 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
requirements.txt:2
Finding

Python Dependencies Use Open-Ended Version Ranges Without Integrity Hashes

Content
View full analysis
=6.0.0 # JSON Schema validation (optional) jsonschema>=4.0.0 ``` ### Technical Analysis The dependency declarations specify only minimum versions. An installation can therefore resolve to any future version of `feedparser` or `jsonschema`, including versions published after the Skill was reviewed. No hashes are supplied to verify downloaded artifacts. The package names themselves appear legitimate, and the audit found no evidence of typosquatting, dependency confusion, or a currently malicious package. The risk is that installation is not reproducible and cannot guarantee that the executed third-party code matches the version audited by the project. ### Attack Path 1. A user follows the documented command and runs `pip install -r requirements.txt`. 2. pip queries the configured package index and selects the newest versions satisfying the lower bounds. 3. A future compromised, malicious, or unexpectedly incompatible release satisfies those constraints. 4. The selected package is installed and imported by the Skill. 5. Malicious package initialization or build behavior can execute with the privileges of the installing or runtime user. This exploitation path is contingent on package-index or upstream-package compromise; no such compromise was established during this audit. ### Impact Assessment A compromised dependency could execute arbitrary Python code with the privileges of the user running the installation or pipeline. That could expose environment variables, workspace files, API tokens, and network access available to the process. The current finding is rated Low because the dependencies are optional, commonly ...[truncated 70 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (170)

Tainted flow: 'req' from os.getenv (line 380, credential/environment) → urllib.request.urlopen (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/fetch-web.py (reported line 83)May include surrounding context.

python
'X-Subscription-Token': api_key,
            'User-Agent': 'FollowNews/2.0'
        })
        with urlopen(req, timeout=TIMEOUT) as resp:
            limit_header = resp.headers.get('x-ratelimit-limit', '1')
            remaining = resp.headers.get('x-ratelimit-remaining', '')
            per_second = int(limit_header.split(',')[0].strip())

Tainted flow: 'req' from os.getenv (line 380, credential/environment) → urllib.request.urlopen (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/fetch-web.py (reported line 213)May include surrounding context.

python
'X-Subscription-Token': api_key,
            'User-Agent': 'FollowNews/2.0'
        })
        with urlopen(req, timeout=TIMEOUT) as resp:
            limit_header = resp.headers.get('x-ratelimit-limit', '1')
            remaining = resp.headers.get('x-ratelimit-remaining', '')
            per_second = int(limit_header.split(',')[0].strip())

Tainted flow: 'req' from os.getenv (line 380, credential/environment) → urllib.request.urlopen (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/fetch-web.py (reported line 384)May include surrounding context.

python
'X-Subscription-Token': api_key,
            'User-Agent': 'FollowNews/2.0'
        })
        with urlopen(req, timeout=TIMEOUT) as resp:
            limit_header = resp.headers.get('x-ratelimit-limit', '1')
            remaining = resp.headers.get('x-ratelimit-remaining', '')
            per_second = int(limit_header.split(',')[0].strip())

Self-Modification

High
Category
Rogue Agent
Confidence
90% confidence
Finding

The README documents an auto-update path for an external executable via OPENCLI_AUTO_UPDATE and a configurable OPENCLI_UPDATE_COMMAND such as self-update. In an agentic/tool-executing environment, allowing a dependency to self-modify at runtime increases supply-chain risk and reduces version pinning, reproducibility, and operator control; if the updater source or invocation is compromised, the agent could fetch and run untrusted code.

Content

Scanner excerpt · README.md (reported line 139)May include surrounding context.

md
export OPENCLI_MAX_WORKERS="10"  # optional; increase parallel OpenCLI workers
export OPENCLI_AUTO_UPDATE="1"      # auto-update OpenCLI if support exists (default: 1)
export OPENCLI_NO_UPDATE="0"        # set 1 to skip OpenCLI auto-update
export OPENCLI_UPDATE_COMMAND="self-update"  # optional; try this command if auto-update
export OPENCLI_UPDATE_CHECK_INTERVAL_SECONDS="86400"  # optional; defaults to 24h
export OPENCLI_CLOSE_TABS_AFTER_RUN="1"  # optional; close OpenCLI-created X/Twitter tabs after fetch
export OPENCLI_CLOSE_CHROME_WINDOWS_AFTER_RUN="1"  # optional; close Chrome automation windows opened by OpenCLI

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents the skill as a news collection and digest-generation pipeline. However, the supplied code does not collect news, rank items, deduplicate content, or render outputs for Discord/email/markdown. Instead, it performs operational diagnostics for dependencies used by the broader project: checking OpenCLI installation/version, inspecting OpenCLI capabilities, verifying browser bridge health, testing X login readiness, and probing Brave/Tavily API setup. These are supporting maintenance checks, but this specific code chunk’s primary purpose is materially different from the declared end-user functionality, so this chunk does not accurately match the description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The supplied code chunk is a standalone PDF renderer for an already-produced markdown digest. Its main function is local file processing: read markdown, sanitize and transform it to HTML, style it, and write a PDF. That is materially different from the declared purpose, which emphasizes multi-source tech news collection, internal ranking, deduplication, retries, and distribution via Discord/email/markdown. While PDF generation could be considered a possible output format related to digest publishing, it is not mentioned in the declaration, and the code shown does not implement the declared core collection and digest-generation pipeline. Therefore this chunk does not accurately represent the declared description and should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The declared description presents a full tech-news aggregation and digest-generation system. In contrast, this code chunk only performs markdown-to-sanitized-HTML conversion for email output. While HTML email output is loosely related to the declared email template/support portion, the primary behavior here is a narrow sanitization/rendering utility rather than source collection, ranking, deduplication, or multi-source digest generation. This is therefore a material description-to-behavior mismatch for the supplied code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This code chunk does not implement tech news collection, unified source modeling, ranking, deduplication, or multi-source digest generation. It only handles one output-side utility: sending an already-prepared HTML email with an optional PDF attachment. While email support is mentioned in the declared description, the actual code’s primary purpose is much narrower and operationally different from the declared end-to-end digest pipeline. It also performs a concrete capability—sending outbound email via local mail tools—that is not explicitly represented as the main function of the skill description. Therefore this chunk is a material mismatch relative to the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This code chunk does not implement or test a tech news aggregation/digest pipeline. Instead, it focuses on server-side configuration editing and security/validation behavior for a local HTTP API. There is no evidence here of collecting data from RSS/Twitter/GitHub/Reddit/web search/podcasts for digest generation, no ranking or deduplication pipeline, and no Discord/email/markdown output logic. The primary purpose and capabilities in the code are materially different from the declared description, so this is a clear mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The code chunk does not implement tech news collection, ranking, deduplication, or multi-format digest output. Instead, it contains tests for a diagnostic 'doctor' command that verifies operational prerequisites such as OpenCLI version, browser bridge availability, X/Twitter authentication, and Brave/Tavily web-search configuration. While some checked services loosely relate to potential upstream data sources, the actual primary behavior here is environment validation/testing, which is materially different from the declared purpose of generating tech news digests.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied code is entirely a front-end editor for configuration files, not a tech news digest generator. It manages UI state, internationalized labels, source/topic table editing, JSON parsing/validation, pagination, and fetch-based load/save operations against /api/file for local config files. While some edited fields reference source types like RSS, X, GitHub, Reddit, and podcasts, the code does not actually collect content from those sources, rank results, deduplicate items, run pipeline scripts, or produce multi-format digests. Its primary purpose is materially different from the declared description, so this is a clear mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description is for the main follow-news aggregation/digest pipeline, but this code chunk does not collect news, rank content, deduplicate items, query external sources, or generate Discord/email/markdown outputs. Instead, it implements a small local backend for editing configuration files related to that system. While the config schema references source types like RSS, Twitter, GitHub, Reddit, web, and podcasts, the code’s actual function is limited to serving a local editor UI and reading/writing config JSON. That is a materially different primary purpose and introduces undeclared file-editing and local HTTP server capabilities, so this is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

The skill explicitly relies on numerous secrets and authenticated sessions, including API keys, bearer tokens, GitHub credentials, and potentially browser-backed OpenCLI sessions. In a skill that also has shell, network, and file capabilities, broad credential access materially raises the risk of secret leakage or misuse if any downstream script, prompt, or helper CLI behaves unsafely.

Content

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

md
- `WEB_SEARCH_BACKEND` - Web search backend: auto|brave|tavily (optional, default: auto)
   - `BRAVE_API_KEYS` - Brave Search API keys, comma-separated for rotation (optional)
   - `BRAVE_API_KEY` - Single Brave key fallback (optional)
   - `GITHUB_TOKEN` - GitHub personal access token (optional, improves rate limits)
   - `YTDLP_BIN` - yt-dlp executable path override for YouTube podcast metadata and transcripts (optional)

   OpenCLI is the preferred Twitter/X backend in `auto` mode. In OpenClaw environments where `jackwener/opencli` is installed, the agent should use that skill to validate `opencli doctor`, browser bridge state, and X login before asking for API keys.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
#### `fetch-twitter.py` - Twitter/X KOL Monitor

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
#### `fetch-twitter.py` - Twitter/X KOL Monitor

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
#### `fetch-twitter.py` - Twitter/X KOL Monitor

Exfiltration Commands

High
Category
Prompt Injection
Confidence
90% confidence
Finding

The skill is explicitly designed to send collected content to external destinations such as Discord, email, and potentially other providers via separate cron jobs. Outbound messaging is legitimate for a digest skill, but it is also a direct exfiltration channel if the skill processes sensitive local data, misroutes content, or is prompted to include unintended information.

Content

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

  • Anti-pattern: Do NOT copy pipeline steps into the cron prompt — it will drift out of sync

Multi-Channel Delivery Limitation

OpenClaw enforces cross-provider isolation: a single session can only send messages to one provider (e.g., Discord OR Telegram, not both). If you need to deliver digests to multiple platforms, create separate cron jobs for each provider:

text
# Job 1: Discord + Email

Self-Modification

High
Category
Rogue Agent
Confidence
90% confidence
Finding

The plan instructs modification of SKILL.md, which is the skill's runtime-facing metadata/instructions file. In an agentic system, allowing a skill implementation plan to alter its own skill definition can create self-modification risk: an agent following the plan could expand capabilities, weaken safeguards, or change how future runs are interpreted, especially because skill content is part of the trusted execution context.

Content

Scanner excerpt · docs/superpowers/plans/2026-05-14-podcast-support.md (reported line 1743)May include surrounding context.

md
Expected: FAIL until docs and metadata mention podcast and `yt-dlp`.

- [ ] **Step 3: Update SKILL frontmatter metadata**

Modify `SKILL.md` frontmatter JSON:

Self-Modification

High
Category
Rogue Agent
Confidence
92% confidence
Finding

This step explicitly updates the body of SKILL.md, again creating a self-referential modification path where implementation instructions can rewrite the skill's own behavior description and operational metadata. Even though the apparent goal is documentation, the skill file influences future agent behavior, so this increases the chance of persistence, policy drift, or hidden instruction injection across subsequent executions.

Content

Scanner excerpt · docs/superpowers/plans/2026-05-14-podcast-support.md (reported line 1762)May include surrounding context.

md
Keep the frontmatter single-line JSON format, because existing tests require single-line top-level entries.

- [ ] **Step 4: Update SKILL body**

In `SKILL.md`:

Self-Modification

High
Category
Rogue Agent
Confidence
98% confidence
Finding

The plan explicitly instructs updating SKILL.md, which is the agent skill's own runtime documentation. In an agentic system, self-modification of skill definitions or capability docs is risky because it can silently change future agent behavior, expand trust boundaries, or normalize new privileged dependencies such as OpenCLI and local credential files.

Content

Scanner excerpt · docs/superpowers/plans/2026-05-20-xiaoyuzhou-podcast-support.md (reported line 1185)May include surrounding context.

小宇宙播客源使用 platform: "xiaoyuzhou",需要 OpenCLI 以及本地小宇宙凭据 ~/.opencli/xiaoyuzhou.json。fetcher 只调用 opencli xiaoyuzhou JSON 命令,不会在本项目内直接调用小宇宙 API。

text

- [ ] **Step 5: Update SKILL.md runtime docs**

In `SKILL.md`, add Xiaoyuzhou to podcast capability descriptions. Include this config example in the source configuration section:

Self-Modification

High
Category
Rogue Agent
Confidence
95% confidence
Finding

The plan explicitly instructs modifying SKILL.md, which is the skill's own control/documentation surface. In an agentic environment, allowing a plan artifact to drive self-modification of the skill can let untrusted content alter future behavior, broaden capabilities, or rewrite trust boundaries, especially when paired with instructions to run tests, update docs, and commit changes automatically.

Content

Scanner excerpt · docs/superpowers/plans/2026-05-23-hackernews-topic.md (reported line 193)May include surrounding context.

llm、ai-agent、kol、hackernews、frontier-tech、podcast

text

- [ ] **Step 4: Update SKILL and test-pipeline topic examples**

Search:

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file tells the agent that browser commands reuse the user's Chrome login session and suggests using that path to validate login state. This is dangerous because it operationalizes access to a live authenticated session without a strong consent boundary, creating risk of privacy violations, unintended account interaction, or access to non-public data during execution.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/fetch-github.py (reported line 430)May include surrounding context.

python
python3 fetch-github.py --config workspace/config --verbose  # backward compatibility
    
Environment Variables:
    GITHUB_TOKEN    GitHub personal access token (optional, improves rate limits)
        """
    )

Self-Modification

High
Category
Rogue Agent
Confidence
97% confidence
Finding

The documented support for OPENCLI_UPDATE_COMMAND and self-update behavior signals that the script can alter local software or invoke arbitrary maintenance commands during execution. That is dangerous in a fetcher because it expands privilege and persistence opportunities beyond the minimum needed to retrieve tweets.

Content

Scanner excerpt · scripts/fetch-twitter.py (reported line 18)May include surrounding context.

python
OPENCLI_MAX_WORKERS  - OpenCLI fetch concurrency for twitter tweets (1-10, default: 10)
    OPENCLI_AUTO_UPDATE  - Enable OpenCLI automatic update check (default: 1/true)
    OPENCLI_NO_UPDATE    - Disable OpenCLI automatic update for this run/environment (0/1)
    OPENCLI_UPDATE_COMMAND - Override OpenCLI update command, e.g. "self-update" or "update --yes"
    OPENCLI_UPDATE_CHECK_INTERVAL_SECONDS - Minimum seconds between checks (default: 86400)
    OPENCLI_MIN_VERSION - Minimum supported OpenCLI version (default: 1.7.22)
    GETX_API_KEY        - GetXAPI API key (preferred backend, $0.001 per call)

Self-Modification

High
Category
Rogue Agent
Confidence
97% confidence
Finding

The built-in update command candidates (self-update, update, upgrade) confirm that the script is designed to modify local tooling automatically. This self-modifying behavior is unnecessary for the advertised purpose and increases supply-chain and local-execution risk if the updater or CLI is compromised.

Content

Scanner excerpt · scripts/fetch-twitter.py (reported line 63)May include surrounding context.

python
OPENCLI_BROWSER_RECOVERABLE_RETRIES = 1
OPENCLI_AUTO_UPDATE_DEFAULT = True
OPENCLI_UPDATE_CHECK_INTERVAL_SECONDS = 24 * 60 * 60
OPENCLI_UPDATE_COMMAND_CANDIDATES = ("self-update", "update", "upgrade")
OPENCLI_DEFAULT_MIN_VERSION = "1.7.22"
OPENCLI_UPDATE_ALREADY_UP_TO_DATE_MARKERS = (
    "already up to date",