Back to skill

Security audit

ZSXQ Digest

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with a private Knowledge Planet digest workflow, but it handles live session cookies in ways that could leak account access if misused or logged.

Review before installing. This skill should only be used in a private local environment, with a dedicated low-risk ZSXQ session if possible. Do not run probe or custom API-base options with untrusted URLs, do not share tool logs, and treat `state/session.token.json` and `state/captured-cookies.json` as account-access credentials.

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/collect_from_session.py:66
Finding
Authenticated session cookie can be transmitted to arbitrary caller-controlled destinations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/collect_from_session.py:66-81, 143-149, 607-626`; related argument forwarding in `scripts/run_digest_pipeline.py:59, 79-85`, `scripts/run_stream_pipeline.py:60, 87-93`, and `scripts/run_browser_bootstrap.py:123-128` **Vulnerability Type**: Unrestricted authenticated network destination / credential exfiltration **Risk Level**: High ### Vulnerable Code ```python def build_request(url: str, token_info: dict): headers = { "Cookie": f"{token_info['cookie_name']}={token_info['cookie_value']}", "User-Agent": token_info.get("user_agent") or DEFAULT_USER_AGENT, "Accept": "application/json, text/plain, */*", "Referer": "https://wx.zsxq.com/", "Origin": "https://wx.zsxq.com", } return urllib.request.Request(url, headers=headers, method="GET") def fetch_url(url: str, token_info: dict, timeout: int): request = build_request(url, token_info) context = ssl.create_default_context() try: with urllib.request.urlopen(request, timeout=timeout, context=context) as response: status_code = getattr(response, "status", 200) body = response.read().decode("utf-8", errors="replace") content_type = response.headers.get("Content-Type", "") return status_code, content_type, body ``` ```python def api_get(path: str, token_info: dict, timeout: int, api_base: str, retries: int = DEFAULT_API_RETRIES, retry_delay: float = DEFAULT_API_RETRY_DELAY): url = urllib.parse.urljoin(api_base.rstrip("/") + "/", path.lstrip("/")) last_error: Optional[SessionError] = None attempts = max(1, retries) for attempt in range(attempts): try: status_code, content_type, body = fetch_url(url, token_info, timeout) ``` ```python parser.add_argument("--token-file", required=True, help="Path to state/session.token.json") parser.add_argument("--mode", choices=["probe", "groups", "group-topics", "multi ...[truncated 3739 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Enforce an explicit destination allowlist** - Permit API requests only to `https://api.zsxq.com`. - If probe functionality is retained, allow only explicitly approved HTTPS ZSXQ hosts such as `api.zsxq.com` and `wx.zsxq.com`. - Compare normalized hostnames exactly; do not use suffix checks that would accept domains such as `zsxq.com.attacker.example`. 2. **Validate the complete URL** - Require the `https` scheme. - Reject user-information components. - Reject unexpected ports. - Normalize internationalized domain names before comparison. - Reject malformed, relative, or protocol-relative URLs. 3. **Control redirects** - Disable automatic redirects for authenticated requests, or validate every redirect destination. - Never forward the cookie when the origin changes. - Apply a strict redirect-count limit. 4. **Reduce the public interface** - Remove authenticated arbitrary-URL probe mode from normal workflows. - Remove or restrict `--api-base`, `--verify-url`, and equivalent options. - If a custom API base is required for development, place it behind an explicit unsafe-development flag and refuse to attach production credentials. 5. **Bind credentials to their expected domain** - Validate the token file’s domain against an approved ZSXQ domain. - Construct authenticated requests through a dedicated ZSXQ client rather than a general-purpose URL fetcher. 6. **Add regression tests** - Confirm that attacker domains, subdomain tricks, HTTP URLs, custom ports, user-information URLs, and cross-origin redirects are rejected before any request is sent. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/capture_browser_cookies.js:72
Finding
Browser-captured authentication cookies are exposed through stdout and insufficiently protected files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/capture_browser_cookies.js:72-89`; related wrapper behavior in `scripts/run_browser_bootstrap.py:80-94, 142-151` **Vulnerability Type**: Plaintext credential exposure and overly broad browser-cookie capture **Risk Level**: Medium ### Vulnerable Code ```javascript const result = await cdpCall(args.wsUrl, 'Network.getCookies', { urls: args.urls.length ? args.urls : undefined, }); let cookies = Array.isArray(result.cookies) ? result.cookies : []; if (args.cookieName) { cookies = cookies.filter((cookie) => cookie && cookie.name === args.cookieName); } const payload = { status: 'ok', count: cookies.length, ws_url: args.wsUrl, urls: args.urls, cookies, }; const text = JSON.stringify(payload, null, 2); if (args.output) { fs.mkdirSync(path.dirname(args.output), { recursive: true }); fs.writeFileSync(args.output, text + '\n', 'utf8'); } console.log(text); ``` The normal wrapper supplies filters but persists the raw cookie artifact and retains the secret-bearing capture payload: ```python capture_cmd = [ "node", str(CAPTURE), "--ws-url", capture_ws_url, "--cookie-name", args.cookie_name, "--output", str(args.cookies_file), ] capture_urls = args.capture_url or list(DEFAULT_CAPTURE_URLS) for url in capture_urls: capture_cmd += ["--url", url] capture_payload = run_json(capture_cmd) ``` ```python return { "status": "ok", "capture": capture_payload, "finalize": finalize_payload, "verify": verify_payload, "verification_warning": verification_warning, "capture_ws_url": capture_ws_url, "cookies_file": str(args.cookies_file), "token_file": str(args.token_file), } ``` ### Technical Analysis The CDP helper places complete cookie objects, including their secret values, into a JSON payload. It then: - Writes the plaintext payload with `fs.writeFileSync()` without explicitly setting restrictive permissions. - Prints the complete p ...[truncated 2492 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Never print secret values** - Remove the `cookies` array from stdout. - Return only non-sensitive metadata such as status, count, cookie name, domain, and destination path. - Ensure the wrapper does not embed secret-bearing capture results in its final JSON. 2. **Require minimum-scope capture** - Require `--cookie-name` and restrict it to `zsxq_access_token`. - Require approved HTTPS ZSXQ capture URLs. - Reject unfiltered `Network.getCookies` calls. 3. **Protect secret files** - Create output files atomically with mode `0600`. - Refuse to follow symbolic links. - Ensure parent directories are private where practical. - Avoid relying solely on the process umask. 4. **Eliminate the raw intermediate artifact** - Extract the single required token and write it directly to the protected token file. - If an intermediate file is unavoidable, create it in a private temporary directory and delete it immediately after successful finalization. 5. **Harden repository protections** - Add an actual `.gitignore` covering `state/`, `captured-cookies.json`, `session.token.json`, and other private exports. - Expand release-package verification to scan for likely credential fields and unexpected JSON files, rather than checking only conventional filenames. 6. **Document CDP trust requirements** - Require loopback-only CDP WebSocket endpoints by default. - Warn that CDP access grants extensive control over the attached browser target. - Reject remote or unencrypted WebSocket endpoints unless the user explicitly opts into a secured administrative workflow. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (47)

Credential Access

High
Category
Privilege Escalation
Content
---
name: zsxq-digest
description: Summarize new updates from followed Knowledge Planet (知识星球 / zsxq.com) circles and produce a daily triage digest that helps decide whether to click through and read in depth. Use when the user wants: (1) a daily summary of what changed across followed planets, (2) extraction of new posts/topics from Knowledge Planet through a locally stored private access token/session file, (3) browser-assisted fallback extraction when token mode is unavailable or needs verification, (4) prioritization of which circles or posts are worth opening, or (5) a reusable workflow for private-membership content where secrets must stay local and never be published with the skill.
---

# ZSXQ Digest
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Ae1

High
Category
analysis-evasion
Content
- Never hardcode personal circle names or secrets into `SKILL.md`, scripts, or references unless the user explicitly wants a private fork.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

High
Confidence
95% confidence
Finding
This script explicitly queries browser cookies via the Chrome DevTools Protocol and then prints them to stdout and optionally writes them to disk, but it provides no user-facing warning, consent prompt, masking, or restriction on what cookies may be collected. Browser cookies often contain session tokens or authentication state, so collecting and persisting them can enable account takeover or lateral movement if the output is exposed, logged, or reused.

Missing User Warnings

High
Confidence
98% confidence
Finding
Probe mode accepts an arbitrary --url and unconditionally attaches the session cookie in the Cookie header. That means a user can be induced to send their authenticated session token to any attacker-controlled domain, causing direct credential exfiltration and possible account takeover or unauthorized API access.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The sample output fixes the digest title to Chinese (`知识星球信息流摘要`) and the surrounding output guidance prefers Chinese field labels such as `摘要时间跨度` and `作者`, but the document does not offer the user a language choice or state that Chinese output is optional. This creates a natural-language locale policy issue because it implicitly forces a specific language in the user-facing result.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The flow tells the agent to send a visible login QR or prompt to the user in chat without warning that chat systems may retain images, expose them to other viewers, or create an authentication replay/social-engineering risk. For authentication material, lack of privacy guidance is a meaningful security weakness even if the QR is short-lived.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The markdown describes collecting cookies and writing a reusable session token but does not provide a prominent warning that these artifacts are sensitive credentials equivalent to account access. This omission increases the chance that operators will store, transmit, or troubleshoot with those files unsafely, leading to credential compromise and account takeover.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The document explicitly proposes connecting to a browser via CDP, extracting cookies from an already logged-in session, and converting them into a reusable local token file. That is credential harvesting behavior by design, and in a public skill with no clear manifest-scoped justification or strong consent boundaries, it creates a serious risk of unauthorized account access, token theft, and persistence beyond the original browser session.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The workflow explicitly instructs operators to extract data from a logged-in browser session and save the resulting page-derived content to a local JSON file, but it provides no guidance on minimizing sensitive data, securing storage, redacting private content, or handling retention/deletion. In the context of a private or authenticated feed, this creates a real risk of unintentionally storing and redistributing sensitive user content outside the browser's access controls.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
No manifest is available, so there is no stated purpose that justifies handling private session tokens or instructing users to extract authentication cookies from browser developer tools. This file specifies obtaining and storing a reusable `zsxq_access_token`, which is a sensitive capability beyond a purpose-neutral baseline.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The example configuration uses Chinese planet names as the only illustrated content, and elsewhere the plan centers on a specific Chinese service without explicitly stating that the skill is intended only for that locale or that language is configurable. This can amount to a locale/language assumption in natural-language documentation without clear opt-in or justification.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The planned `collect_from_session.py` component performs token validation and accesses `/v2/groups` and `/v2/groups/{group_id}/topics`, which enables direct retrieval of account-associated content. Without a manifest describing the skill as an authenticated Knowledge Planet digest tool, this capability is context-inappropriate relative to an unknown-purpose skill.

External Transmission

Medium
Category
Data Exfiltration
Content
- Keep `zsxq-digest` focused on lightweight summarization, bounded state, and public-friendly distribution.

### 4. RSSHub proves a narrow route can work, but it is API-coupled
RSSHub's ZSXQ route uses a token and fetches group topics through API paths under `https://api.zsxq.com/v2`.
It is elegant, but tightly tied to the platform's API behavior.

**Takeaway for this skill:**
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Extraction guidance
- Document a simple user path such as: browser devtools -> Application/Storage or Network -> Cookies -> copy `zsxq_access_token` -> paste into `state/session.token.json`.
- Keep the README explanation beginner-friendly.
- Never ask the user to paste the token into public chat if a local file path will do.

### Requirements
- Never commit these files.
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Extraction guidance
- Document a simple user path such as: browser devtools -> Application/Storage or Network -> Cookies -> copy `zsxq_access_token` -> paste into `state/session.token.json`.
- Keep the README explanation beginner-friendly.
- Never ask the user to paste the token into public chat if a local file path will do.

### Requirements
- Never commit these files.
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The recommended response template is written entirely in Chinese and presented as the default output format. This imposes a specific language on users without indicating a choice, opt-in, or a clear region-specific requirement, which matches the language/locale policy violation criteria.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The guide explicitly instructs users to extract a live authentication cookie from browser storage or network traffic and save it into a local JSON file, with optional User-Agent capture to improve replayability. While it includes 'do not commit' advice, it normalizes manual credential harvesting and local token storage without stronger guidance such as restricting file permissions, minimizing retention, redacting examples, warning that the token grants account access, or preferring safer auth flows; this increases the risk of credential leakage or misuse.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The document's natural-language instructions are entirely in Chinese and directly specify the skill's output framing and presentation, implying a fixed Chinese-language experience. There is no indication that users may choose another language or that the Chinese-only constraint is required for a region-specific compliance or product reason.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code extracts a reusable authentication cookie and persists its raw value to a local JSON file on disk. Even though it includes a note saying 'stored locally only; do not commit', there is no technical protection such as file permission hardening, encryption, secure storage, or confirmation gating, so any local user, malware, backup process, or accidental source-control inclusion could expose the token and enable account/session hijacking.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This script enumerates visible links and extracts page title, URL, author names, timestamps, engagement counts, and content previews from the current page, then returns them in a structured object. In an agent skill context, that is page scraping of potentially sensitive user-visible content without any in-script consent gate, minimization control, or disclosure, which creates a privacy and data-handling risk if the data is transmitted or stored elsewhere.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This script emits user-facing natural-language strings in Chinese, such as the fallback title `(无标题)`, and similar fixed Chinese messages appear throughout classification and CLI defaults. Because the file provides no opt-in or locale selection, it enforces a specific language, which matches the language/locale policy-violation category.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code performs HTTP requests using a session cookie in the Cookie header, which transmits credentials and may retrieve user-associated data from the remote service. While the script's purpose is collection, there is no confirmation prompt, warning print, or explanatory comment/docstring near the network operation to disclose that authenticated requests will be sent.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This code file contains natural-language strings that force the generated digest into Chinese, including action labels and later defaults/headings, with no option for the user to select another language. That is a language/locale policy concern under SQP-3 because the skill imposes a specific language rather than offering a choice or documenting a justified locale restriction.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_json(cmd: List[str]) -> dict:
    proc = subprocess.run(cmd, text=True, capture_output=True)
    payload = None
    for candidate in (proc.stdout, proc.stderr):
        if not candidate or not candidate.strip():
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_json(cmd: List[str]) -> dict:
    proc = subprocess.run(cmd, text=True, capture_output=True)
    payload = None
    for candidate in (proc.stdout, proc.stderr):
        if not candidate or not candidate.strip():
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.