Back to skill

Security audit

xiaohongshu-cli

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real Xiaohongshu CLI, but it gives an agent sensitive browser-session access and account-changing powers with broad default scope.

Install only if you are comfortable letting an agent use your Xiaohongshu browser session to read and change your account. Use a dedicated browser profile or test account, review every mutating command before it runs, avoid running the integration tests casually, and clear the cookie cache with xhs logout when finished.

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

Warning
Location
xhs_cli/cookies.py:58
Finding
Plaintext Session Cookies Are Created Before Restrictive Permissions Are Applied<![CDATA[ ## Vulnerability Details **File Location**: `xhs_cli/cookies.py:58-64` **Vulnerability Type**: Insecure plaintext credential storage and non-atomic file creation **Risk Level**: Medium ### Vulnerable Code ```python def save_cookies(cookies: dict[str, str]) -> None: """Save cookies to local storage with restricted permissions and TTL timestamp.""" cookie_path = get_cookie_path() payload = {**cookies, "saved_at": time.time()} cookie_path.write_text(json.dumps(payload, indent=2)) cookie_path.chmod(0o600) logger.debug("Saved cookies to %s", cookie_path) ``` ### Technical Analysis The application saves active Xiaohongshu browser cookies in plaintext. It creates or truncates `cookies.json` through `Path.write_text()` and only applies mode `0600` after the write completes. The file's initial permissions therefore depend on the process umask. With a permissive umask, another local user may be able to read the file between its creation and the subsequent `chmod()` call. If the process crashes, is terminated, or encounters an error before `chmod()` completes, the credential file may remain accessible with broader permissions. The operation is also non-atomic. A concurrent reader can potentially observe partially written data, and interruption can leave a truncated credential cache. Although the intended destination is `~/.xiaohongshu-cli/cookies.json`, the containing directory is created without explicitly enforcing mode `0700`. The cookies are authentication credentials capable of authorizing account reads and writes. Their local storage is part of the declared functionality, but creating the file before securing it is not necessary. ### Attack Path 1. A victim uses a multi-user system and has an authenticated Xiaohongshu browser session. 2. A local attacker monitors `~/.xiaohongshu-cli/` for creation or modification of `cookies.json`. 3. The victim runs `xhs login`, or an authenticated command automatically refreshes stale co ...[truncated 1306 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create credential files with mode `0600` at the moment of creation rather than correcting permissions afterward. Use `os.open()` with explicit flags and permissions: ```python import os fd = os.open( cookie_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as handle: json.dump(payload, handle, indent=2) ``` 2. Prefer an atomic replacement strategy: - Create a temporary file inside the same configuration directory. - Create it with mode `0600`. - Write and flush the complete payload. - Call `os.fsync()` where durability is required. - Atomically replace the destination with `os.replace()`. 3. Explicitly enforce mode `0700` on `~/.xiaohongshu-cli`, including when the directory already exists. 4. Reject symlinked credential paths and validate that the destination is a regular file owned by the current user before replacing it. 5. Where supported, store session credentials in an operating-system credential store instead of a plaintext JSON file. 6. Add tests verifying that both the configuration directory and credential file have restrictive permissions immediately upon creation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
tests/test_integration.py:26
Finding
Integration Test Collection Automatically Reads Browser Credentials and Contacts the Production Service<![CDATA[ ## Vulnerability Details **File Location**: `tests/test_integration.py:26-47`; related default test configuration at `pyproject.toml:54-60` **Vulnerability Type**: Test-collection side effects involving production credentials and network access **Risk Level**: Medium ### Vulnerable Code ```python def _get_test_cookies(): """Try to get valid cookies for integration testing.""" try: cookies = get_cookies("chrome") with XhsClient(cookies) as client: client.get_self_info() return cookies except SessionExpiredError: try: cookies = get_cookies("chrome", force_refresh=True) with XhsClient(cookies) as client: client.get_self_info() return cookies except (NoCookieError, SessionExpiredError, XhsApiError, Exception): return None except (NoCookieError, Exception): return None # Skip all integration tests if no valid cookies are available cookies = _get_test_cookies() pytestmark = pytest.mark.skipif(cookies is None, reason="No valid XHS cookies available for integration testing") ``` The relevant default pytest configuration is: ```toml [tool.pytest.ini_options] testpaths = ["tests"] python_files = ["test_*.py"] addopts = "-m 'not smoke'" markers = ["smoke: real-API integration tests (run with: pytest -m smoke)"] ``` ### Technical Analysis `_get_test_cookies()` executes at module import time because it is assigned directly to the module-level `cookies` variable. Pytest imports test modules during collection, so merely discovering `tests/test_integration.py` causes the following sensitive operations: 1. Loading cached Xiaohongshu cookies or extracting cookies from Chrome. 2. Creating a production API client. 3. Sending an authenticated `get_self_info()` request to Xiaohongshu. 4. If the saved session is expired, forcing a new browser-cookie extraction and retrying the authenticated request. The `skipif` marker does n ...[truncated 2613 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all credential access and network requests from module-level initialization. 2. Move credential acquisition into an opt-in fixture: ```python import os import pytest @pytest.fixture(scope="session") def integration_cookies(): if os.environ.get("XHS_RUN_INTEGRATION") != "1": pytest.skip("Set XHS_RUN_INTEGRATION=1 to run production integration tests") cookies = get_cookies("chrome") with XhsClient(cookies) as client: client.get_self_info() return cookies ``` 3. Register a dedicated `integration` marker and exclude it by default: ```toml [tool.pytest.ini_options] addopts = "-m 'not smoke and not integration'" markers = [ "smoke: real-API smoke tests", "integration: tests requiring production credentials and network access", ] ``` 4. Require two explicit controls for write tests, such as: - `XHS_RUN_INTEGRATION=1` for authenticated read tests. - `XHS_ALLOW_WRITE_TESTS=1` for tests that modify account state. 5. Use a dedicated test account rather than a developer's personal browser session. 6. Avoid automatic browser-cookie extraction in tests. Prefer credentials supplied through a purpose-specific, protected test configuration or operating-system credential store. 7. Keep cleanup logic, but also record created resource identifiers and provide a separate recovery command capable of removing artifacts after interrupted test runs. 8. Ensure IDE discovery and `pytest --collect-only` never access credentials, open browser databases, or make network requests. ]]>
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (57)

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
d counts (likes, mentions, follows)
xhs notifications                      # 评论和@ notifications
xhs notifications --type likes        # 赞和收藏 notifications
xhs notifications --type connections   # 新增关注 notifications
```

## Authentication

xiaohongshu-cli uses a 2-tier authentication strategy:

1. **Saved cookies** — loads from `~/.xiaohongshu-cli/cookies.json`
2. **Browser cookies** — auto-extracts from Chrome, Firefox, Safari, Edge, Brave

`xhs login` always refreshes cookies from the selected browser and overwrites the local cache.
Other authenticated commands automatically retry once with fresh browser cookies when the saved session has expired.

Most commands require authentication. Use `--cookie-source` to specify browser (default: chrome; also supports firefox, edge, safari, brave).

### Cookie TTL

Saved cookies are valid for **7 days** by default. After that, the client automatically attempts to refresh from the browser. If browser extraction fails, th
Confidence
93% confidence
Finding
Automatic extraction of authentication cookies from installed browsers matches credential-harvesting behavior and can expose active session tokens from Chrome, Firefox, Safari, Edge, or Brave. Even if intended for user convenience, in a local agent-skill context this capability materially increases the risk of account compromise, unauthorized access, and misuse of browser-derived credentials.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The file describes browser fingerprint consistency, request signing, and anti-detection behavior, but these low-level identity and request-shaping mechanics are not reflected in the top-level description. In a security review context, omission of anti-detection and fingerprint-related behavior is risky because it obscures techniques that may bypass platform controls or increase privacy sensitivity.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The file tests session-stable synthetic fingerprints, browser header shaping, and request timing jitter, all of which are designed to make automated traffic appear like a consistent human-operated browser. Because the skill's stated purpose is ordinary Xiaohongshu operations, these stealth features are unjustified and materially increase the capability to bypass anti-abuse defenses for scraping, mass interaction, or unauthorized posting.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
ssert "a1=v1" in result
        assert "web_session=v2" in result
        assert "; " in result


class TestGetCookies:
    def test_prefers_saved_cookies_by_default(self, monkeypatch):
        monkeypatch.setattr("xhs_cli.cookies.load_saved_cookies", lambda: {"a1": "saved"})
        monkeypatch.setattr("xhs_cli.cookies.extract_browser_cookies", lambda source: {"a1": "fresh"})

        assert get_cookies("chrome") == {"a1": "saved"}

    def test_force_refresh_bypasses_saved_cookies(self, monkeypatch):
        monkeypatch.setattr("xhs_cli.cookies.load_saved_cookies", lambda: {"a1": "saved"})
        monkeypatch.setattr("xhs_cli.cookies.extract_browser_cookies", lambda source: {"a1": "fresh"})
        saved = []
        monkeypatch.setattr("xhs_cli.cookies.save_cookies", lambda cookies: saved.append(cookies))

        assert get_cookies("chrome", force_refresh=True) == {"a1": "fresh"}
        assert saved == [{"a1": "fresh"}]
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
- comment + delete-comment → net effect = zero
  The undo operation always runs in a `finally` block.
"""

import time

import pytest

from xhs_cli.client import XhsClient
from xhs_cli.cookies import get_cookies
from xhs_cli.exceptions import NoCookieError, SessionExpiredError, XhsApiError


def _get_test_cookies():
    """Try to get valid cookies for integration testing."""
    try:
        cookies = get_cookies("chrome")
        with XhsClient(cookies) as client:
            client.get_self_info()
        return cookies
    except SessionExpiredError:
        try:
            cookies = get_cookies("chrome", force_refresh=True)
            with XhsClient(cookies) as client:
                client.get_self_info()
            return cookies
        except (NoCookieError, SessionExpiredError, XhsApiError, Exception):
            return None
    except (NoCookieError, Exception):
        return None


# Skip all integration tests if no valid cookies are available
cookies = _get_test_
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Credential Access

High
Category
Privilege Escalation
Content
def _extract_in_process(source: str) -> dict[str, str] | None:
    """Extract cookies in-process for macOS Keychain compatibility."""
    try:
        loader = _browser_loaders().get(source)
    except ImportError:
Confidence
98% confidence
Finding
This code path is explicitly designed to access browser cookies, including from Keychain-backed browsers on macOS, in order to recover authenticated Xiaohongshu session material. Browser session cookie extraction is credential access behavior because those cookies can impersonate the user and bypass normal authentication.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
in data:
            logger.debug("Cookie extraction error: %s", data["error"])
            return None

        return data["cookies"]

    except subprocess.TimeoutExpired:
        logger.debug("Cookie extraction timed out")
        return None
    except (json.JSONDecodeError, KeyError) as e:
        logger.debug("Cookie extraction parse error: %s", e)
        return None


def extract_browser_cookies(source: str = "chrome") -> dict[str, str] | None:
    """
    Extract XHS cookies from browser using browser-cookie3.

    macOS requires an in-process attempt for Keychain-backed browsers; when that
    fails we fall back to a subprocess to avoid SQLite DB locks.
    """
    cookies = _extract_in_process(source)
    if cookies:
        return cookies
    return _extract_via_subprocess(source)


def get_cookies(cookie_source: str = "chrome", *, force_refresh: bool = False) -> dict[str, str]:
    """
    Multi-strategy cookie acquisition with TTL-based auto-refresh.

    1. Load saved c
Confidence
95% confidence
Finding
The YARA hit is substantively supported by the code: it programmatically harvests browser cookies for a target domain, refreshes them, and stores them locally for later authenticated use. Those are information-stealer-like behaviors even if the apparent goal is convenience rather than overt malware, and in an agent skill they materially increase the risk of unauthorized account use.

Credential Access

High
Category
Privilege Escalation
Content
"""
    Extract XHS cookies from browser using browser-cookie3.

    macOS requires an in-process attempt for Keychain-backed browsers; when that
    fails we fall back to a subprocess to avoid SQLite DB locks.
    """
    cookies = _extract_in_process(source)
Confidence
98% confidence
Finding
The documented behavior confirms intentional fallback logic for extracting cookies from Keychain-backed browsers and browser databases, which is credential acquisition rather than ordinary application configuration. In this skill's context, harvested cookies can be used to take actions on the user's Xiaohongshu account without a separate login flow.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README explicitly advertises automatic extraction of browser cookies and persistent storage of those cookies in a local file, but it does not clearly warn that these are highly sensitive authentication artifacts that can grant account access if exposed. In an agent-skill context, this is more dangerous because an autonomous tool may trigger login flows or handle the saved cookie file without the user appreciating the account takeover risk.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The README promotes state-changing capabilities such as posting, following, commenting, and deleting notes/comments, but does not clearly caution that these actions modify the user's account and public content. In an AI agent setting this increases risk because an agent may execute high-impact social actions on behalf of a user without a strong confirmation boundary.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
xhs my-notes --page 1                 # Next page
xhs post --title "标题" --body "正文" --images img.jpg  # Post note
xhs delete <note_id>                   # Delete note
xhs delete <note_id> -y               # Skip confirmation

# ─── Notifications ────────────────────────────────
xhs unread                             # Unread counts (likes, mentions, follows)
Confidence
87% confidence
Finding
The documented `-y` flag allows deletion of notes without confirmation, enabling fully non-interactive destructive behavior. In an agent context this is especially risky because accidental or prompt-injected execution can cause irreversible content deletion at machine speed.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# Clone into your project's skills directory
mkdir -p .agents/skills
git clone git@github.com:jackwener/xiaohongshu-cli.git .agents/skills/xiaohongshu-cli

# Or just copy the SKILL.md
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Skill Enumeration

Medium
Category
Agent Snooping
Content
git clone git@github.com:jackwener/xiaohongshu-cli.git .agents/skills/xiaohongshu-cli

# Or just copy the SKILL.md
curl -o .agents/skills/xiaohongshu-cli/SKILL.md \
  https://raw.githubusercontent.com/jackwener/xiaohongshu-cli/main/SKILL.md
```
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding
The skill describes shell execution, network access, file access, environment use, and browser-cookie handling, but it does not declare any explicit tool scope such as allowed-tools or permissions. In an agent environment, this broad undeclared capability increases the chance of over-privileged execution and makes review and enforcement harder, especially because the skill can access browser-derived credentials and perform write actions on a social-media account.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The invocation rule says to use this skill for ALL Xiaohongshu operations, which is overly broad for a skill with credential access and account-mutating actions. In an agent setting, broad auto-invocation can cause the system to reach for a high-privilege tool when a safer, read-only, or non-tool response would suffice, increasing the chance of unintended account actions or unnecessary secret handling.

Static analysis

No suspicious patterns detected.