T09 · Insecure Skill Coding Practices
- 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 atscripts/docker-compose.yml:64-65
Vulnerability Type: Missing authentication on a privileged webhook endpoint
Risk Level: HighVulnerable 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
entriesarray without validating a webhook secret, signature, bearer credential, source identity, or other authentication proof. The deployment documentation configures a Minifluxwebhook_secret, but the relay does not inspect or validate that secret.Accepted entries are transformed into Feishu Markdown cards by
build_card()and delivered throughsend_card()using the configured Feishu application token. After successfu ...[truncated 2013 chars]- Remediation
View remediation
Remediation Suggestions
- Require and validate the Miniflux webhook secret or a cryptographic request signature before parsing and processing webhook content.
- Compare secret values with a constant-time comparison function and return HTTP 401 or 403 for missing or invalid credentials.
- Configure Miniflux and the relay to use the same randomly generated, high-entropy secret.
- 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. - Restrict ingress with host firewall rules or an authenticated reverse proxy.
- Validate the webhook event schema and constrain accepted event types and fields.
- Treat entry titles and URLs as untrusted content, and prevent untrusted webhook submissions from automatically triggering an agent workflow.
