Back to skill

Security audit

Xhs Skills

Security checks for vulnerabilities and agentic risk

Overview

This is a real Xiaohongshu automation skill, but it grants broad control over a logged-in browser account and has under-disclosed handling of sensitive login and browser data.

Install only if you are comfortable giving this skill and its Chrome extension broad control over your logged-in Xiaohongshu browser session. Before use, the bridge should be authenticated, generic JavaScript evaluation and cookie access should be removed or tightly limited, QR login data should not be sent to third-party services by default, and publishing or social actions should require explicit confirmation.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (4)

other

Error
Location
scripts/xhs/login.py:149
Finding

Live login QR code is disclosed to an external service

Content
View full analysis
str | None: import http.client boundary = "----XhsQrBoundary" body = ( f"--{boundary}\r\n" f'Content-Disposition: form-data; name="file";' f' filename="qr.png"\r\n' f"Content-Type: image/png\r\n\r\n" ).encode() + png_bytes + f"\r\n--{boundary}--\r\n".encode() try: conn = http.client.HTTPSConnection( "api.qrserver.com", timeout=5 ) conn.request( "POST", "/v1/read-qr-code/", body=body, headers={ "Content-Type": ( f"multipart/form-data; boundary={boundary}" ), }, ) resp = conn.getresponse() if resp.status != 200: return None result = json.loads(resp.read().decode()) data = result[0]["symbol"][0].get("data") return data if data else None except Exception: return None def make_qrcode_url( png_bytes: bytes, ) -> tuple[str, str | None]: import base64 import urllib.parse qr_content = _decode_qr_content(png_bytes) if qr_content: image_url = ( "https://api.qrserver.com/v1/create-qr-code/" "?size=300x300&data=" + urllib.parse.quote(qr_content, safe="") ) return image_url, qr_content b64 = base64.b64encode(png_bytes).decode() return "data:image/png;base64," + b64, None ``` ### Technical Analysis The login workflow extracts a live Xiaohongshu QR image from the browser and sends the entire image to `api.qrserver.com` for decoding. This discloses an authentication artifact to a third party even though the QR image is already available locally and can be displayed ...[truncated 1377 chars]
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/bridge_server.py:29
Finding

Unauthenticated localhost bridge exposes privileged browser automation

Content
View full analysis
None: self._extension_ws: ServerConnection | None = None self._pending: dict[str, asyncio.Future[Any]] = {} async def handle(self, ws: ServerConnection) -> None: try: raw = await asyncio.wait_for(ws.recv(), timeout=10) except (asyncio.TimeoutError, Exception) as e: logger.warning("Handshake timed out or failed: %s", e) return try: msg = json.loads(raw) except json.JSONDecodeError: return role = msg.get("role") if role == "extension": await self._handle_extension(ws) elif role == "cli": await self._handle_cli(ws, msg) else: logger.warning("Unknown role: %s", role) ``` ```python async def _handle_cli(self, ws: ServerConnection, msg: dict) -> None: if msg.get("method") == "ping_server": await ws.send(json.dumps({ "result": {"extension_connected": self._extension_ws is not None} })) return if not self._extension_ws: await ws.send(json.dumps({"error": "Extension is not connected"})) return msg_id = str(uuid.uuid4()) msg["id"] = msg_id loop = asyncio.get_event_loop() future: asyncio.Future[Any] = loop.create_future() self._pending[msg_id] = future await self._extension_ws.send(json.dumps(msg)) ``` The extension accepts sensitive commands from this bridge: ```javascript async function handleCommand(msg) { const { method, params = {} } = msg; switch (method) { case "navigate": return await cmdNavigate(params); case "screenshot_element": ret ...[truncated 3621 chars]
Remediation
View remediation

T01 · Skill Instruction Hijacking

Warning
Location
SKILL.md:25
Finding

Skill instructions force exclusive use of the bundled implementation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/image_downloader.py:23
Finding

Remote image downloader permits SSRF and unbounded response downloads

Content
View full analysis
bool: return path.lower().startswith(("http://", "https://")) ``` ```python def download_image(self, image_url: str) -> str: if not is_image_url(image_url): raise ValueError(f"Invalid image URL: {image_url}") url_hash = hashlib.sha256(image_url.encode()).hexdigest()[:16] ext = self._detect_extension(image_url) filename = f"img_{url_hash}_{int(time.time())}{ext}" filepath = os.path.join(self.save_path, filename) existing = self._find_existing(url_hash) if existing: return existing parsed = urlparse(image_url) headers = { "User-Agent": _USER_AGENT, "Referer": f"{parsed.scheme}://{parsed.hostname}/", } resp = self._session.get(image_url, headers=headers) if resp.status_code != 200: raise RuntimeError( f"Download failed (status={resp.status_code}): {image_url}" ) with open(filepath, "wb") as f: f.write(resp.content) logger.info("Download completed: %s -> %s", image_url, filepath) return filepath ``` The constructor also attempts to configure a session timeout: ```python self._session = requests.Session() self._session.timeout = 30 ``` Requests does not use `Session.timeout` as a default timeout for `Session.get()`, so the shown request may wait indefinitely unless `timeout` is passed directly. ### Technical Analysis Any string beginning with HTTP or HTTPS is accepted. The downloader does not reject: - Loopback destinations such as `127.0.0.1` or `localhost`. - Private network destinations. - Link-local addresses. - Cloud metadata endpoints. - IPv6 local or private ranges. - Redirects from a public URL to an internal destination. The request follows redirects by default. ...[truncated 1718 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (68)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill is presented as platform-specific automation, but the detected behavior includes generic remote media download, local directory creation, file writing, and cache/dedup handling for arbitrary HTTP/HTTPS image URLs. This broadens the file-system and network attack surface beyond what users would infer from the description and can be abused to fetch untrusted content or persist unexpected files locally.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is presented as platform-specific automation, but the detected behavior includes generic remote media download, local directory creation, file writing, and cache/dedup handling for arbitrary HTTP/HTTPS image URLs. This broadens the file-system and network attack surface beyond what users would infer from the description and can be abused to fetch untrusted content or persist unexpected files locally.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill is presented as platform-specific automation, but the detected behavior includes generic remote media download, local directory creation, file writing, and cache/dedup handling for arbitrary HTTP/HTTPS image URLs. This broadens the file-system and network attack surface beyond what users would infer from the description and can be abused to fetch untrusted content or persist unexpected files locally.

Content

No source excerpt is available for this finding.

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

High
Category
YARA Match
Confidence
75% confidence
Finding

The YARA hit is broad, but in this file it is supported by concrete capabilities associated with information theft: cookie extraction, screenshot capture, arbitrary page script execution, and a command bridge that returns results to another process. While not definitive malware by signature alone, the combined behavior materially resembles credential/session theft tooling.

Content

Scanner excerpt · extension/background.js (reported line 9)May include surrounding context.

js
**
 * XHS Bridge - Background Service Worker
 *
 * 连接 Python bridge server(ws://localhost:9333),接收命令并执行:
 * - navigate / wait_for_load: chrome.tabs.update + onUpdated
 * - evaluate / has_element 等: chrome.scripting.executeScript (MAIN world)
 * - click / input 等 DOM 操作: chrome.tabs.sendMessage → content.js
 * - screenshot: chrome.tabs.captureVisibleTab
 * - get_cookies: chrome.cookies.getAll
 */

const BRIDGE_URL = "ws://localhost:9333";
let ws = null;

// 保持 service worker 存活:有开放的 WebSocket 连接时 Chrome 不会终止 SW
// 额外加 alarm 作为保底
chrome.alarms.create("keepAlive", { periodInMinutes: 0.4 });
chrome.alarms.onAlarm.addListener(() => {
  if (!ws || ws.readyState !== WebSocket.OPEN) connect();
});

// ───────────────────────── WebSocket ─────────────────────────

function connect() {
  if (ws && (ws.readyState ===

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The background worker exposes an 'evaluate' path that forwards arbitrary JavaScript expressions into the page MAIN world via chrome.scripting.executeScript and Function(...). A process listening on ws://localhost:9333 can therefore execute arbitrary code in any tab the extension targets, bypassing the narrow XHS automation purpose and enabling theft of page data, account state, or DOM-manipulating actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The navigate command accepts an arbitrary URL and passes it to chrome.tabs.update without enforcing an Xiaohongshu-only allowlist. This lets the local bridge steer the browser to attacker-chosen sites, where the extension can subsequently script pages, take screenshots, or interact with content outside the declared scope.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

cmdGetCookies exposes chrome.cookies.getAll with a caller-controlled domain parameter, enabling extraction of cookies for arbitrary domains accessible to the extension. Cookie theft can lead to account/session hijacking and is especially dangerous because the data is returned over the WebSocket bridge with no user approval.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Cookie extraction occurs programmatically and is sent back to the bridge without any user-facing warning or approval. Because cookies often contain live session material, this enables silent credential/session theft with immediate account compromise potential.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Arbitrary JavaScript is executed in the page MAIN world on command, with no user warning, confirmation, or runtime restriction. This allows silent extraction of page globals, tokens, forms, and account data, and can alter page state in ways the user cannot observe or authorize.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
92% confidence
Finding

The debugger-based file-input injection accepts local file paths and pushes them into a page without any user-facing approval. This can silently upload local files to a website, which is a serious privacy and data-exfiltration risk even if initially intended for content publishing.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Line L45 states that user-visible error information must use Chinese, which imposes a specific language policy on users. The file does not mention any opt-in, fallback, or region-specific justification for this restriction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README promotes automated commenting, liking, and favoriting through a real logged-in browser session, but the example commands do not prominently warn that these are immediate account-affecting actions. In an agent setting, this increases the chance of users triggering irreversible social actions unintentionally, which can cause account, reputation, and platform-policy consequences.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The one-step publish commands are presented as normal usage without a strong warning that invoking them will immediately post content from the user's real account. In an AI-agent workflow, this can lead to accidental public posting of draft, incorrect, or sensitive content because the operation is not clearly framed as irreversible and externally visible.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · README.md (reported line 231)May include surrounding context.

md
## Star History

[![Star History Chart](https://api.star-history.com/image?repos=autoclaw-cc/xiaohongshu-skills&type=date&legend=top-left)](https://www.star-history.com/?repos=autoclaw-cc%2Fxiaohongshu-skills&type=date&legend=top-left)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill exposes meaningful capabilities (shell, network, file read, env) but does not declare an explicit tool/permission scope, creating a least-privilege gap. In practice this makes it easier for the skill to invoke commands or access resources beyond what a reviewer or runtime policy would expect.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger text is very broad and may activate on many common Xiaohongshu-related requests, increasing the chance that the skill is invoked in contexts where the user did not intend browser automation, login handling, posting, or social actions. In a skill with shell, network, and browser-control characteristics, over-broad triggering raises the likelihood of unintended sensitive operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The extension accepts WebSocket commands from a local server and can return screenshots of the active target tab without any visible notice or consent gate. Silent screen capture can expose private messages, account details, drafts, or other sensitive on-screen information.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code attaches the Chrome debugger and uses DOM.setFileInputFiles to inject local file paths into page file inputs. This is a powerful browser-debug capability that expands the attack surface and can cause unintended local file disclosure or silent uploads when combined with remote commands.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The content script exposes a remove_element command that directly deletes a matched DOM element with el.remove() and provides no confirmation prompt, warning comment, or user-facing disclosure at the point of execution. Removing page elements can alter page behavior or user-visible content in a potentially irreversible way during a session.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest requests the highly privileged Chrome debugger permission even though the stated purpose is Xiaohongshu automation via content scripts and a local bridge. The debugger API can inspect and modify network traffic, page state, and browser behavior far beyond normal automation needs, so if the extension is compromised or misused it could enable broad account/session abuse and data theft.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module-level docstring, CLI descriptions, help text, prompts, and status messages are all hard-coded in Chinese, and stdout/stderr are explicitly reconfigured to UTF-8 to support that choice. There is no indication that users can opt into another language or that the CLI is intentionally limited to a China-specific compliance context, which makes this a natural-language locale policy concern.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The helper adds local file-opening capability that is not necessary for core Xiaohongshu automation and can interact with the host OS outside the platform boundary. In a skill context, this expanded capability increases risk because generated QR files or other paths could be used as a vehicle to trigger local applications without clear user intent.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
72% confidence
Finding

This opens an arbitrary path with the system's default handler, and the path originates from upstream logic rather than being constrained to a trusted directory in this function. If an attacker can influence that path, the skill could trigger opening untrusted local files or remote-handler targets, causing unintended local application execution or exposure of sensitive content.

Content

Scanner excerpt · scripts/cli.py (reported line 49)May include surrounding context.

python
if system == "Windows":
            os.startfile(path)
        elif system == "Darwin":
            subprocess.Popen(["open", path])
        else:
            subprocess.Popen(["xdg-open", path])
    except Exception:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
72% confidence
Finding

On Linux, xdg-open dispatches the supplied path or URL to whatever application or handler is registered on the host. Because this helper does not validate that the input is a safe local file in an approved location, an attacker who controls the path could cause unintended opening of hostile content or external resources.

Content

Scanner excerpt · scripts/cli.py (reported line 51)May include surrounding context.

python
elif system == "Darwin":
            subprocess.Popen(["open", path])
        else:
            subprocess.Popen(["xdg-open", path])
    except Exception:
        logger.debug("无法自动打开文件: %s", path)

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code automatically starts a local bridge server and launches Chrome when not already connected, extending the skill's power from Xiaohongshu page automation to managing host processes and browser state. That broader control surface is dangerous in an agent setting because it can create side effects on the user's machine without an explicit action requesting local app/process management.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.