Back to skill

Security audit

x-rss-feishu-daily

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent RSS-to-Feishu automation package, but its relay exposes an unauthenticated port that can let reachable callers post Feishu messages and trigger report actions through the user's app.

Review before installing. Do not expose port 7070 to untrusted networks; bind it to loopback or keep it internal, add webhook_secret/signature validation, and enforce request-size limits before using this relay. Store feishu.env, .env, and user_token.json as sensitive secrets, and prefer a low-risk X account because auth_token is effectively account access.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/relay.py:514
Finding

Unauthenticated Webhook Allows Abuse of Privileged Feishu Messaging

Content
View full analysis

Vulnerability Details

File Location: scripts/relay.py:514-546; exposure configured at scripts/docker-compose.yml:64-65
Vulnerability Type: Missing authentication on a privileged webhook endpoint
Risk Level: High

Vulnerable Code

python
def do_POST(self):
    try:
        n = int(self.headers.get("Content-Length") or 0)
        data = json.loads(self.rfile.read(n) or b"{}")
    except Exception as e:
        log("bad json:", e)
        return self._reply(400)
    entries = data.get("entries") or []
    if not entries:
        log("Ignoring event without entries:", data.get("type") or data.get("event"))
        return self._reply(200)
    skip_ids = {int(x) for x in CONF.get("SKIP_FEED_IDS", "").split(",") if x.strip().isdigit()}
    if skip_ids:
        filtered = [e for e in entries if e.get("feed_id") not in skip_ids]
        if len(filtered) < len(entries):
            log(f"Skipped {len(entries)-len(filtered)} overview entries")
        entries = filtered
    if not entries:
        return self._reply(200)
    try:
        send_card(build_card(entries))
        log(f"Forwarded {len(entries)} entries to Feishu")
        self._reply(200)
        try:
            trigger_report()
        except Exception as e:
            log("Report trigger failed:", e)
    except Exception as e:
        log("Delivery failed:", e)
        self._reply(500)

The service is published on a host port:

yaml
ports:
  - "7070:8080"

Technical Analysis

The POST handler accepts arbitrary JSON containing a non-empty entries array without validating a webhook secret, signature, bearer credential, source identity, or other authentication proof. The deployment documentation configures a Miniflux webhook_secret, but the relay does not inspect or validate that secret.

Accepted entries are transformed into Feishu Markdown cards by build_card() and delivered through send_card() using the configured Feishu application token. After successfu ...[truncated 2013 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require and validate the Miniflux webhook secret or a cryptographic request signature before parsing and processing webhook content.
  2. Compare secret values with a constant-time comparison function and return HTTP 401 or 403 for missing or invalid credentials.
  3. Configure Miniflux and the relay to use the same randomly generated, high-entropy secret.
  4. Do not publish the relay port unless external access is required. Prefer an internal Compose network; otherwise bind it to loopback, such as 127.0.0.1:7070:8080.
  5. Restrict ingress with host firewall rules or an authenticated reverse proxy.
  6. Validate the webhook event schema and constrain accepted event types and fields.
  7. Treat entry titles and URLs as untrusted content, and prevent untrusted webhook submissions from automatically triggering an agent workflow.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/relay.py:514
Finding

Unbounded Webhook Request Bodies Permit Remote Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: scripts/relay.py:514-520; network service started at scripts/relay.py:560
Vulnerability Type: Unbounded request-body processing and unauthenticated denial of service
Risk Level: Medium

Vulnerable Code

python
def do_POST(self):
    try:
        n = int(self.headers.get("Content-Length") or 0)
        data = json.loads(self.rfile.read(n) or b"{}")
    except Exception as e:
        log("bad json:", e)
        return self._reply(400)
    entries = data.get("entries") or []

The handler is served by an unrestricted threaded HTTP server:

python
ThreadingHTTPServer(("0.0.0.0", 8080), Handler).serve_forever()

Technical Analysis

The handler converts the caller-supplied Content-Length header into an integer and reads that many bytes into memory without applying a maximum size. It then parses the entire body with json.loads(). There is no authentication gate before this memory- and CPU-intensive work, and the service uses ThreadingHTTPServer, allowing multiple requests to be processed concurrently.

A remote caller who can reach the published service can send oversized JSON bodies, deeply nested JSON, very large entry arrays, or concurrent slow/large requests. These operations can consume process memory, CPU time, and worker threads. The code also lacks an explicit per-connection read timeout, increasing exposure to connections that occupy threads while sending request data slowly.

Attack Path

  1. The supplied Compose configuration exposes the relay on host TCP port 7070.
  2. An attacker who can reach that port opens one or more HTTP connections.
  3. The attacker sends requests with very large Content-Length values and corresponding bodies, deeply nested JSON, or many concurrent slowly transmitted bodies.
  4. Each handler thread reads the declared body into memory and attempts to parse it as JSON.
  5. Concurrent allocations and parsing operations exhaust available memory, CPU, or ...[truncated 787 chars]
Remediation
View remediation

Remediation Suggestions

  1. Enforce a strict request-body limit before calling read(), and return HTTP 413 when Content-Length is absent, invalid, or exceeds that limit.
  2. Authenticate requests before performing expensive JSON parsing wherever the authentication scheme permits it.
  3. Limit the maximum number of entries and the lengths of titles, URLs, and other accepted fields.
  4. Configure socket and request read timeouts to mitigate slow-client attacks.
  5. Place the endpoint behind a reverse proxy that enforces body-size limits, connection limits, timeouts, and per-source rate limits.
  6. Bound request concurrency rather than relying on an unrestricted thread-per-request model.
  7. Keep the relay on the internal Compose network or bind the published port to loopback unless broader network access is explicitly required.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill’s declared purpose is narrowly framed around X/Twitter-to-Feishu reporting, but the content also includes materially different behavior and operational guidance for Bilibili monitoring, batch subscription onboarding, and other side workflows. Description-behavior drift is dangerous because it can bypass user consent and security review expectations, especially when additional external services, cookies, proxies, and automation paths are introduced beyond the stated scope.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module docstring explicitly states that running the script will '换取并保存 user token' and produce a same-directory user_token.json. In the actual code shown, there is no file-write operation for user_token.json, and lines L81-L83 claim success while using data[...] even though only d is defined, so the documented outcome contradicts the implementation.

Content

No source excerpt is available for this finding.

Tainted flow: 'req' from open (line 150, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/relay.py (reported line 75)May include surrounding context.

python
).encode(),
            headers={"Content-Type": "application/json"},
        )
        d = json.load(urllib.request.urlopen(req, timeout=15))
        if d.get("code") != 0:
            raise RuntimeError(f"get token failed: {d}")
        _token["value"] = d["tenant_access_token"]

Tainted flow: 'req' from open (line 150, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Confidence
80% confidence
Finding

File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Content

Scanner excerpt · scripts/relay.py (reported line 97)May include surrounding context.

python
).encode(),
            headers={"Content-Type": "application/json"},
        )
        d = json.load(urllib.request.urlopen(req, timeout=15))
        if d.get("code") != 0:
            raise RuntimeError(f"get token failed: {d}")
        _token["value"] = d["tenant_access_token"]

Tainted flow: 'req' from open (line 150, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Confidence
86% confidence
Finding

MINIFLUX_URL is loaded from a local file and concatenated directly into a URL for outbound requests without validation. If an attacker can modify feishu.env, they can redirect this service to an arbitrary internal or external host, creating an SSRF-style primitive and potentially exposing the Miniflux admin Basic credentials to that host.

Content

Scanner excerpt · scripts/relay.py (reported line 109)May include surrounding context.

python
req = urllib.request.Request(
                MINIFLUX_URL + "/v1/feeds", headers={"Authorization": BASIC}
            )
            feeds = json.load(urllib.request.urlopen(req, timeout=10))
            _titles["map"] = {f["id"]: f["title"] for f in feeds}
            _titles["at"] = time.time()
            log("feed 标题已刷新:", _titles["map"])

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · scripts/relay.py (reported line 445)May include surrounding context.

python
e1 = {"tag": "div", "text": {"tag": "lark_md",
         "content": "**异常源:**" + rows}}
    e2 = {"tag": "div", "text": {"tag": "lark_md",
         "content": "**常见原因与恢复:** 1) X 的 auth_token 失效 → 重新取 token 填入 rss-stack/.env 后执行 docker compose up -d rsshub;2) Clash 代理断开 → 检查 7891;3) 电脑重启后稍等 Docker 就绪"}}
    e3 = {"tag": "note", "elements": [{"tag": "plain_text",
          "content": "本告警由抓取通道监控自动发出,恢复后会再发 ✅ 通知"}]}
    return {"config": {"wide_screen_mode": True},

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill describes capabilities that inherently require network access, environment-secret handling, and local file/script usage, but it does not declare any explicit tool scope or permission boundaries. In an agent platform, missing scope declarations can cause over-broad execution authority or make reviewers/users unaware that secrets, files, and network resources may be accessed.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The invocation text is broad enough to attract generic Feishu troubleshooting requests that are not limited to this specific RSS/relay pipeline. Over-broad routing is risky because it can cause the skill to be invoked in contexts involving unrelated Feishu administration, OAuth, bots, or group-sharing tasks, potentially exposing credentials or prompting unsafe operational guidance outside its intended boundaries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill overview prominently involves obtaining and using an X auth_token, which is effectively a session credential, but the top-level description does not foreground the associated privacy and account-security risk. Because the skill also recommends proxies, long-lived polling, and token rotation, users may normalize handling highly sensitive credentials without adequate warning, increasing the chance of account compromise or misuse.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest describes a pipeline for monitoring X/Twitter bloggers via RSSHub, Miniflux, and Feishu. This script is explicitly documented and implemented for bulk-adding Bilibili creators ('B站 UP主订阅') and constructs Bilibili RSSHub feed URLs, which is a different monitored platform than the one the skill claims to cover.

Content

No source excerpt is available for this finding.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Confidence
70% confidence
Finding

Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Content

Scanner excerpt · scripts/add_bili.py (reported line 27)May include surrounding context.

python
def miniflux(path, method="GET", payload=None):
    req = urllib.request.Request("http://127.0.0.1:8088" + path, method=method)
    req.add_header("Authorization", BASIC)
    if payload:
        req.add_header("Content-Type", "application/json")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script automatically creates Miniflux categories and feeds, refreshes them, and posts feed entry titles/URLs to a Feishu chat. Although these actions are the script's purpose, there is no inline user disclosure or confirmation before transmitting content to an external messaging platform, which is a safety-relevant operation affecting user data flow.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The operational logic creates Miniflux subscriptions for RSSHub Bilibili user video feeds, refreshes them, and pushes initial entries to a Feishu chat. That behavior is coherent internally, but it diverges from the manifest's stated purpose of monitoring X/Twitter bloggers and supporting an X-to-Feishu daily report pipeline.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code loads Feishu and Miniflux secrets from feishu.env and constructs an authorization header from admin credentials, but there is no confirmation prompt, warning comment, or user-facing disclosure about accessing sensitive credentials. This is a safety-relevant operation under the rule because it handles secrets directly.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script obtains a Feishu access token and later uploads aggregated Miniflux feed content into a Feishu document and sends a chat card, but the code provides no explicit warning that local RSS-derived content will be transmitted to an external cloud service. Although the top docstring mentions Feishu output, it does not clearly warn about the data transfer behavior or its privacy implications.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module docstring is entirely in Chinese, and the script's browser/UI messages and console output are also Chinese-only. This imposes a specific language on users without opt-in or explanation of a region-specific requirement, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code loads Miniflux admin credentials and later uses them to construct a Basic Authorization header. Although the module docstring describes the data source, it does not warn that local secrets from feishu.env are accessed, and there is no runtime notice or confirmation about credential use.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script posts interactive messages to Feishu containing titles and links derived from Miniflux entries. While the header comment says it does not request X, it does not explicitly disclose that local feed data is transmitted to Feishu, and the network calls themselves lack a user-facing warning beyond response-code prints.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This manifest hard-codes a specific locale/timezone setting in natural-language-visible configuration, which can conflict with organizational language/locale policies when no user opt-in or justification is provided. The same pattern also appears elsewhere in the file, indicating the stack is forced into a single locale context.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This manifest hard-codes a specific locale/timezone setting in natural-language-visible configuration, which can conflict with organizational language/locale policies when no user opt-in or justification is provided. The same pattern also appears elsewhere in the file, indicating the stack is forced into a single locale context.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

This manifest hard-codes a specific locale/timezone setting in natural-language-visible configuration, which can conflict with organizational language/locale policies when no user opt-in or justification is provided. The same pattern also appears elsewhere in the file, indicating the stack is forced into a single locale context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

This code accesses a potentially sensitive credential via the MINIFLUX_BASIC environment variable to build an Authorization header. Although the script has a descriptive docstring about refresh behavior, it does not explicitly warn the user that it consumes credentials from the environment.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.