Back to skill

Security audit

Xiaohongshu Search

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to perform the advertised Xiaohongshu scraping, but it requires live login cookies and persists session/token data with weak containment.

Install only if you are comfortable giving the skill a live Xiaohongshu session. Prefer a dedicated account, keep cookie and data directories outside shared repos/backups, enforce owner-only permissions, delete data/state/token caches after use, and avoid running it on shared hosts or CI logs where command arguments and output may be captured.

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

T09 · Insecure Skill Coding Practices

Warning
Location
xhs-keepalive.py:229
Finding
Authenticated Browser State Is Saved Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `xhs-keepalive.py`, lines 229–242 **Vulnerability Type**: Insecure storage of authenticated session state **Risk Level**: Medium ### Vulnerable Code ```python if args.action == 'save': # 先 open 一下建立 session subprocess.run( ['agent-browser', 'open', 'https://www.xiaohongshu.com/explore'], capture_output=True, text=True, timeout=30 ) r = subprocess.run( ['agent-browser', 'state', 'save', str(STATE_FILE)], capture_output=True, text=True ) print(r.stdout, r.stderr) if r.returncode == 0: ok(f"state saved to {STATE_FILE}") return r.returncode ``` The sensitivity of this state is also documented in `docs/pitfalls.md`, line 44: ```text 36 字节的 state file 是 skeleton, 实际需要大几百 KB (含 user data dir / cookies / cache) ``` ### Technical Analysis The browser state can contain reusable authentication cookies, browser cache, and other session material. The code saves that state without setting a restrictive mode on either the state file or its containing directory. By contrast, the converted cookie file is explicitly protected with mode `0600` at `xhs-keepalive.py:95`. No equivalent protection is applied to `STATE_FILE`. Its effective permissions therefore depend on the process umask and the behavior of `agent-browser`. On systems with a permissive umask, the resulting state may be readable by other local users or processes. This violates the principle that authentication material should only be accessible to its owner. ### Attack Path 1. A victim authenticates to Xiaohongshu and loads cookies into `agent-browser`. 2. The victim runs `xhs-keepalive.py state save`. 3. `agent-browser` writes authenticated state to `data/state/xhs.state`. 4. The state file receives permissions derived from the environment rather than an enforced `0600` mode. 5. A local attacker or unrelated process reads the file. 6. The attacker loads the copied browser state into ...[truncated 710 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the state directory with owner-only permissions: ```python STATE_FILE.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(STATE_FILE.parent, 0o700) ``` 2. Immediately enforce mode `0600` after a successful state save: ```python if r.returncode == 0: os.chmod(STATE_FILE, 0o600) ok(f"state saved to {STATE_FILE}") ``` 3. Before loading a state file, inspect its mode and refuse files that are readable or writable by group or others. 4. Document that browser-state files contain authentication credentials and must not be shared, backed up to untrusted locations, or committed to source control. 5. Add `data/state/` and other runtime credential paths to `.gitignore`. 6. Consider encrypting long-lived state at rest when the execution environment supports a suitable operating-system credential store. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
xhs-harvest.py:247
Finding
Session-Derived Access Tokens and Authenticated Content Are Persisted With Default Permissions<![CDATA[ ## Vulnerability Details **File Locations**: - `xhs-harvest.py`, lines 247–270 - `xhs-harvest.py`, lines 609–626 - `xhs-harvest.py`, lines 657–665 **Vulnerability Type**: Plaintext storage of session-derived tokens **Risk Level**: Medium ### Vulnerable Code Candidate and result files are written with ordinary filesystem defaults: ```python # 保存候选清单 (out_dir / 'candidates.json').write_text( json.dumps(top, ensure_ascii=False, indent=2), encoding='utf-8' ) # Phase 2: 逐个抓详情 print(f"\n━━ Phase 2: 抓详情 + 评论 ━━") print(f" (每条之间 sleep {args.sleep}s 防 300012)\n") results = [] for i, c in enumerate(top, 1): out = notes_dir / f"n{i:02d}-{c['note_id'][:8]}.json" print(f" [{i}/{len(top)}] {c['title'][:38]} ", end='', flush=True) ok_status, msg = fetch_one_note( c['note_id'], c.get('xsec_token'), out, comments=args.comments, sleep_after=args.sleep ) status_icon = '✅' if ok_status else '❌' print(f"{status_icon} {msg}") results.append({**c, 'file': out.name if ok_status else None, 'fetch_ok': ok_status, 'fetch_msg': msg}) (out_dir / 'results.json').write_text( json.dumps(results, ensure_ascii=False, indent=2), encoding='utf-8' ) ``` The token cache is also stored without an explicit restrictive mode: ```python cache_path = out_dir / 'token_cache.json' token_cache = {} if cache_path.exists(): try: token_cache = json.load(open(cache_path)) except Exception: pass needing = [c for c in top if not c.get('xsec_token')] print(f"\n🔧 auto-token: 拿 {len(needing)} 篇的 xsec_token (sleep {args.sleep}s/次)...") resolved, token_cache = auto_resolve_tokens( needing, sleep=args.sleep, sort='time', token_cache=token_cache ) for c in top: if not c.get('xsec_token') and c['note_id'] in resolved: c['xsec_token'] = resolved[c['note_id']] # 写缓存 try: cache_path.write_text(json.dumps(token_cache, ensure_ascii=False, indent=2)) except Exception: pass ``` Token valu ...[truncated 2471 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `xsec_token` from `results.json` after each note has been fetched successfully. 2. Avoid maintaining `token_cache.json` unless caching is explicitly requested. Delete the cache at workflow completion or enforce a short retention period. 3. Create sensitive output directories with mode `0700` and token-bearing files with mode `0600`. 4. Use atomic protected writes, for example: ```python import os import tempfile def secure_write(path, content): path.parent.mkdir(parents=True, exist_ok=True, mode=0o700) fd, temp_name = tempfile.mkstemp(dir=path.parent) try: os.fchmod(fd, 0o600) with os.fdopen(fd, 'w', encoding='utf-8') as handle: handle.write(content) os.replace(temp_name, path) except Exception: os.unlink(temp_name) raise ``` 5. Add a `.gitignore` containing at least: ```gitignore data/ *.state cookies*.txt token_cache.json ``` 6. Clearly distinguish ordinary public harvest output from credential-bearing metadata. Store token-bearing files in a separate protected directory. 7. Provide a cleanup command that securely removes expired token caches and saved browser state. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
xhs-fetch.py:334
Finding
Short-Lived Access Tokens Are Exposed in Process Arguments and Console Output<![CDATA[ ## Vulnerability Details **File Locations**: - `xhs-fetch.py`, lines 334–350 - `xhs-harvest.py`, lines 124–133 **Vulnerability Type**: Sensitive token exposure through command lines and logs **Risk Level**: Low ### Vulnerable Code The fetcher embeds access tokens into URLs and prints those URLs: ```python if args.via_user_profile: if not args.token or not args.user_id: err_struct('parse_fail', '--via-user-profile 需要 --token <XSEC_TOKEN> 和 --user-id <USER_ID>', hint='提示: 从 xhs-fetch.py user <user_id> 拿主页,那里有 xsec_token + xsec_source') return 1 # xsec_source 必须为 pc_user (主页里的 token 都是这个) url = f"{WEB_BASE}/user/profile/{args.user_id}/{note_id}?xsec_token={args.token}&xsec_source=pc_user" print(f"Opening (via-user-profile): {url}") elif args.via_search: if not args.token: err_struct('parse_fail', '--via-search 需要 --token <XSEC_TOKEN>', hint='提示: 先跑 `xhs-fetch.py search <keyword>` 拿 note 的 xsec_token') return 1 # search_result 路径默认 xsec_source=pc_note xsrc = args.xsec_source or 'pc_note' url = f"{WEB_BASE}/search_result/{note_id}?xsec_token={args.token}&xsec_source={xsrc}" print(f"Opening (via-search): {url}") ``` The harvester passes tokens through a child process's argument vector: ```python # 构造 note 命令参数 note_args = ['note', note_id, '--token', xsec_token, '--comments', str(comments), '--out', str(out_path)] if user_id: note_args += ['--via-user-profile', '--user-id', user_id] else: note_args.append('--via-search') if xsec_source: note_args += ['--xsec-source', xsec_source] rc, out, err_out = run_fetch(note_args) ``` ### Technical Analysis Command-line arguments may be visible through process inspection facilities, monitoring agents, diagnostic tooling, shell history, or process-accounting systems. The fetcher also prints complete token-bearing URLs to standard output, where they can be captured by terminal logs, CI log ...[truncated 1485 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Redact tokens from console output: ```python print( f"Opening (via-user-profile): " f"{WEB_BASE}/user/profile/{args.user_id}/{note_id}" f"?xsec_token=[REDACTED]&xsec_source=pc_user" ) ``` 2. Avoid accepting tokens directly through command-line arguments where practical. 3. Pass tokens through a protected temporary file, an inherited file descriptor, or standard input. Ensure temporary files are created with mode `0600` and deleted immediately. 4. Refactor the harvester to import a shared fetch module directly instead of starting `xhs-fetch.py` as a child process with the token in its arguments. 5. Configure logs to redact query parameters named `xsec_token`. 6. Warn users against placing real tokens directly in shell commands because shell history may retain them. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
docs/pitfalls.md:58
Finding
Troubleshooting Guidance Uses Overly Broad Forced Process Termination<![CDATA[ ## Vulnerability Details **File Location**: `docs/pitfalls.md`, lines 58–63 **Additional Reference**: `docs/pitfalls.md`, line 116 **Vulnerability Type**: Unsafe destructive troubleshooting command **Risk Level**: Low ### Vulnerable Code ```bash pkill -9 -f "Chrome|chromium|agent-browser" # 强杀 agent-browser open "https://example.com" # 测试是否恢复 # 恢复后: agent-browser open "https://www.xiaohongshu.com/..." ``` The command is later characterized as a general solution: ```text 3. **agent-browser 卡死 → `close --all && pkill -9`** 是万能解 ``` ### Technical Analysis `pkill -9 -f` matches against full process command lines and immediately sends `SIGKILL`. The expression matches not only the Skill's own `agent-browser` daemon but potentially every Chrome or Chromium process owned by the invoking user. `SIGKILL` prevents graceful shutdown, cleanup, session persistence, and profile database flushing. This can cause unrelated browser sessions to terminate and may corrupt browser state or lose unsaved work. The command exceeds the minimum privilege and scope required to recover this Skill's browser process. It is especially risky in automated environments where the command may affect other jobs sharing the same account or host. ### Attack Path 1. `agent-browser` appears unresponsive. 2. A user follows the troubleshooting documentation. 3. The user executes `pkill -9 -f "Chrome|chromium|agent-browser"`. 4. The expression matches unrelated Chrome or Chromium processes. 5. The operating system forcibly terminates every matching process without cleanup. 6. Other browser sessions lose unsaved state, and browser-profile files may be left inconsistent. No malicious third party is required; the risk arises from following unsafe operational guidance. ### Impact Assessment The command can cause: - Termination of unrelated browser processes. - Loss of unsaved browser work. - Corruption or inconsistency of browser profiles and session databases. - Availability ...[truncated 190 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use the supported graceful shutdown first: ```bash agent-browser close --all ``` 2. Track the PID of the specific daemon started for this Skill and terminate only that PID. 3. Send `SIGTERM` and allow a timeout before escalating to `SIGKILL`: ```bash kill -TERM "$AGENT_BROWSER_PID" sleep 5 kill -KILL "$AGENT_BROWSER_PID" 2>/dev/null || true ``` 4. If PID tracking is unavailable, narrow process matching to a unique state directory, profile directory, or exact daemon command line rather than matching all Chrome and Chromium processes. 5. Add a prominent warning that broad process matching can terminate unrelated browser sessions. 6. Remove language describing forced termination as a universal solution. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (34)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill metadata understates sensitive behaviors such as session/cookie management, local credential file handling, and anti-abuse/risk checks, while the documented core operations depend on those mechanisms. When a skill omits these security-relevant behaviors from its declared purpose, operators may enable it without understanding that it processes authenticated session material and persistent local state.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill metadata understates sensitive behaviors such as session/cookie management, local credential file handling, and anti-abuse/risk checks, while the documented core operations depend on those mechanisms. When a skill omits these security-relevant behaviors from its declared purpose, operators may enable it without understanding that it processes authenticated session material and persistent local state.

Ssd 3

High
Confidence
96% confidence
Finding
The skill explicitly requires users to supply authenticated Xiaohongshu cookies and browser fingerprint values so the tool can reuse a live logged-in session. Reusing session tokens outside the browser creates a high-value credential handling path: leakage of these files or logs could enable account takeover, impersonation, or abuse of the user's authenticated access.

Ssd 3

High
Confidence
98% confidence
Finding
The setup instructions tell users to copy all browser cookies via developer tools and save them into a local file consumed by the skill. This encourages bulk extraction of sensitive session material, potentially including more tokens than necessary, and normalizes unsafe credential handling that could expose the account if the file is read, backed up insecurely, or exfiltrated by another tool.

External Script Fetching

High
Category
Supply Chain
Content
```bash
# 1. 看 IP / 城市
curl -s "https://ipinfo.io/json" | python3 -m json.tool | head -10

# 2. 看 agent-browser 当前 cookies
agent-browser cookies get
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The document is written primarily in Chinese and does not offer an English alternative or indicate that the skill is intentionally limited to a Chinese-speaking contributor base for a documented regional reason. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The file's instructional and descriptive content is written entirely in Chinese, including contributor guidance, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-only audience. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README explicitly instructs users to export live Xiaohongshu authentication cookies from their browser and store them locally in a plaintext file. Those cookies appear sufficient to impersonate the user session, so compromise of the file, accidental inclusion in logs/backups, or reuse by the skill beyond user expectations could enable account takeover or access to private account data.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill declares no explicit tool scope or permission boundaries despite clearly requiring environment access, filesystem reads/writes, network access, and shell execution. In an agent setting, this weakens least-privilege controls and makes it easier for the skill to be invoked with broader capabilities than users may reasonably expect.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The manifest description is entirely in Chinese and presents the skill purpose and requirements only in that language. Under the policy, locale or language constraints should not be imposed without user opt-in or a clearly documented justification, and this file does not offer either.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The operational instructions, warnings, examples, and troubleshooting sections are all presented only in Chinese. This creates a natural-language policy issue because users are not offered a language choice, and no explicit justification for a Chinese-only interface is stated.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The entire skill document is written in Chinese and includes user-facing operational guidance without any indication that language selection is optional or that the skill is intended only for a Chinese-speaking audience. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script automatically checks for and loads authenticated browser cookies into agent-browser without a clear runtime warning or consent step. In a skill explicitly designed to scrape Xiaohongshu using a logged-in session, this creates real credential/privacy risk because the tool can act with the user's authenticated account and access account-linked content.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
r = run(['agent-browser', 'cookies', 'get'], timeout=10)
    if not r.stdout.strip() or 'web_session' not in r.stdout:
        print("Loading cookies into agent-browser...")
        subprocess.run(
            ['python3', str(Path(__file__).parent / 'xhs-keepalive.py'), 'load'],
            timeout=30
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code reads document.cookie and extracts a session-linked user identifier from the browser context to influence scraping logic. Even though it only targets a specific cookie value, accessing browser cookies from automation code is sensitive and can expose or misuse authenticated session metadata without clear disclosure.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This Python file contains user-facing natural-language instructions, help text, status output, and report content entirely in Chinese, starting from the module docstring and continuing through the CLI experience. Under the policy rule, forcing a specific language without offering the user a language/locale option is a natural-language policy violation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
不要在 stdout/stderr 里字符串 grep (脆弱,fetch 改字面量 harvest 就漏判)。
    """
    cmd = ['python3', str(FETCH_SCRIPT)] + args
    r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
    return r.returncode, r.stdout, r.stderr
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
不要在 stdout/stderr 里字符串 grep (脆弱,fetch 改字面量 harvest 就漏判)。
    """
    cmd = ['python3', str(FETCH_SCRIPT)] + args
    r = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
    return r.returncode, r.stdout, r.stderr
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
注意:
  - 不依赖任何第三方库 (除 agent-browser 自身)
  - cookies 必须 chmod 600 (含 web_session + id_token 等敏感字段)
  - xhs cookie 6-12 小时会失效 (web_session 短效),需要定期重导
"""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
注意:
  - 不依赖任何第三方库 (除 agent-browser 自身)
  - cookies 必须 chmod 600 (含 web_session + id_token 等敏感字段)
  - xhs cookie 6-12 小时会失效 (web_session 短效),需要定期重导
"""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
注意:
  - 不依赖任何第三方库 (除 agent-browser 自身)
  - cookies 必须 chmod 600 (含 web_session + id_token 等敏感字段)
  - xhs cookie 6-12 小时会失效 (web_session 短效),需要定期重导
"""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
注意:
  - 不依赖任何第三方库 (除 agent-browser 自身)
  - cookies 必须 chmod 600 (含 web_session + id_token 等敏感字段)
  - xhs cookie 6-12 小时会失效 (web_session 短效),需要定期重导
"""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
注意:
  - 不依赖任何第三方库 (除 agent-browser 自身)
  - cookies 必须 chmod 600 (含 web_session + id_token 等敏感字段)
  - xhs cookie 6-12 小时会失效 (web_session 短效),需要定期重导
"""
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return 1

    # 清掉旧的
    subprocess.run(['agent-browser', 'cookies', 'clear'],
                   capture_output=True, text=True)

    # 读 Netscape
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
continue
            domain, include_subdomain, path, secure, expires, name, value = parts[:7]
            flags = _cookie_to_agent_browser_flags(name)
            r = subprocess.run(
                ['agent-browser', 'cookies', 'set', name, value] + flags,
                capture_output=True, text=True
            )
Confidence
83% confidence
Finding
The subprocess call itself is safely parameterized, but it forwards sensitive cookie names and values into command-line arguments. On many systems, command-line arguments can be exposed to other local processes/users through process listings, logs, crash reports, or audit tooling, which can leak authenticated session tokens such as web_session and id_token.

Static analysis

No suspicious patterns detected.