Back to skill

Security audit

小红书转Obsidian

Security checks for vulnerabilities and agentic risk

Overview

The skill’s main goal is coherent, but it handles live browser cookies with unsafe network validation that could expose a user’s Xiaohongshu session.

Install only if you are comfortable exporting Xiaohongshu browser cookies and storing them locally. Before use, the script should be fixed to validate Xiaohongshu hostnames, keep TLS verification enabled, restrict redirects and video URLs, protect temporary files, and avoid passing unvalidated values into inline code.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract_post.py:23
Finding

Authenticated cookies can be disclosed to an attacker-controlled host

Content
View full analysis
tuple[str, str]: """Extract note ID and xsec_token from URL.""" # Pattern: xiaohongshu.com/explore/xxx or xiaohongshu.com/discovery/item/xxx patterns = [ r'xiaohongshu\.com/explore/([a-f0-9]{24})', r'xiaohongshu\.com/discovery/item/([a-f0-9]{24})', ] for p in patterns: m = re.search(p, url) if m: post_id = m.group(1) xsec_m = re.search(r'xsec_token=([a-f0-9]+)', url) xsec = xsec_m.group(1) if xsec_m else '' return post_id, xsec raise ValueError(f"Cannot parse post ID from URL: {url}") ``` ```python req = urllib.request.Request(url) req.add_header('Cookie', cookie_str) req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36') req.add_header('Referer', 'https://www.xiaohongshu.com/') ``` ```python cookie_str = load_cookies(cookie_path) post_id, xsec = extract_post_id(args.url) url_with_token = args.url if xsec: url_with_token = args.url if 'xsec_token' in args.url else f"{args.url}&xsec_token={xsec}" try: note = fetch_post(url_with_token, cookie_str) ``` ### Technical Analysis The validation logic uses an unanchored regular-expression search against the complete URL. It verifies only that the text contains a Xiaohongshu-looking path; it does not parse or validate the actual hostname, scheme, port, or user-information component. For example, this attacker-controlled URL passes the post-ID check: ```text https://attacker.example/xiaohongshu.com/explore/0123456789abcdef01234567 ``` The complete Xiaohongshu cookie string is ...[truncated 1907 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract_post.py:38
Finding

TLS certificate and hostname verification are explicitly disabled

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/video_transcribe.sh:37
Finding

Unvalidated post identifiers are interpolated into executable Python source

Content
View full analysis
/dev/null; then python3 -c " import mlx_whisper result = mlx_whisper.transcribe('$WAV_PATH', path_or_hf_repo='mlx-community/whisper-large-v3-turbo', language='zh', verbose=False) with open('$TRANSCRIPT_PATH', 'w') as f: f.write(result['text']) " ``` The interpolated paths are derived from an unvalidated command-line argument: ```bash POST_ID="$2" VIDEO_PATH="$TMP_DIR/${POST_ID}.mp4" WAV_PATH="$TMP_DIR/${POST_ID}.wav" TRANSCRIPT_PATH="$TMP_DIR/${POST_ID}.txt" ``` ### Technical Analysis Shell quoting protects the shell expansion from ordinary word splitting, but it does not make the resulting text safe as Python source code. Both `$WAV_PATH` and `$TRANSCRIPT_PATH` are embedded inside single-quoted Python string literals in a dynamically generated `python3 -c` program. A `POST_ID` containing a single quote, newline, backslash sequence, or suitable Python syntax can terminate the intended string literal and inject additional Python statements. The script performs no validation of the argument. The documented workflow normally uses hexadecimal Xiaohongshu post IDs, but the helper is directly callable according to its usage declaration. Defensive validation is therefore required at this trust boundary. Remote post metadata may also influence the value passed by an automated caller. ### Attack Path 1. An attacker causes the transcription helper to be invoked with a crafted second argument. 2. The crafted `POST_ID` contains characters that terminate the Python string literal and insert valid Python code. 3. The shell expands `$WAV_PATH` or `$TRANSCRIPT_PATH` into the multiline `python3 -c` argument. 4. `python3` parses the attacker-influenced text as source code. 5. If `mlx_whisper` is installed ...[truncated 850 chars]
Remediation
View remediation
&2 exit 1 fi ``` 2. Do not construct Python source code through shell interpolation. 3. Move transcription logic into a standalone Python file and pass paths through `sys.argv`. 4. If an inline program is unavoidable, pass values through environment variables and read them with `os.environ`; never insert them into source literals. 5. Validate and canonicalize all file paths independently. 6. Treat values returned by remote post metadata as untrusted even if the expected format is restrictive. 7. Add tests containing quotes, newlines, traversal sequences, shell metacharacters, and Unicode edge cases. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/video_transcribe.sh:8
Finding

Predictable temporary paths permit traversal, overwrite, and symlink attacks

Content
View full analysis
/dev/null ``` ```bash rm -f "$VIDEO_PATH" "$WAV_PATH" ``` ### Technical Analysis The script uses a shared, predictable directory and predictable filenames derived from an unvalidated argument. A `POST_ID` containing traversal components such as `../` can resolve paths outside `/tmp/xhs`. The download, FFmpeg output, transcript write, and cleanup operations may then affect files elsewhere on the filesystem. Even when the identifier has the expected format, another local process can predict the generated paths and pre-create files or symbolic links. The script does not create a private temporary directory, check ownership, reject symlinks, or open files using exclusive creation semantics. Concurrent invocations using the same post ID also share the same video, audio, and transcript paths, creating integrity and confidentiality problems. ### Attack Path **Traversal scenario:** 1. An attacker controls the `POST_ID` argument. 2. The value contains one or more `../` components. 3. The generated path resolves outside `/tmp/xhs`. 4. Curl, FFmpeg, or Python writes to the resolved destination. 5. Cleanup may remove files selected through the crafted path. **Symlink scenario:** 1. A local attacker predicts the target filename. 2. The attacker crea ...[truncated 998 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/video_transcribe.sh:4
Finding

Unrestricted video URLs enable internal requests and unsafe media ingestion

Content
View full analysis
VIDEO_URL="$1" POST_ID="$2" OUTPUT_DIR="$3" TMP_DIR="/tmp/xhs" mkdir -p "$TMP_DIR" VIDEO_PATH="$TMP_DIR/${POST_ID}.mp4" WAV_PATH="$TMP_DIR/${POST_ID}.wav" TRANSCRIPT_PATH="$TMP_DIR/${POST_ID}.txt" echo "Downloading video..." curl -L -o "$VIDEO_PATH" \ -H "Referer: https://www.xiaohongshu.com/" \ -H "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" \ "$VIDEO_URL" if [ $? -ne 0 ]; then echo "ERROR: Download failed" exit 1 fi echo "Extracting audio..." ffmpeg -y -i "$VIDEO_PATH" -vn -acodec pcm_s16le -ar 16000 -ac 1 "$WAV_PATH" 2>/dev/null ``` ### Technical Analysis The script accepts any URL understood by the installed curl build and follows redirects without validating the initial or final destination. It does not restrict the scheme, hostname, port, resolved address, redirect chain, content type, or response size. Downloading a Xiaohongshu video is required by the declared feature, but unrestricted network retrieval is broader than necessary. A crafted URL may target loopback services, private network addresses, cloud metadata services, or other destinations reachable from the Agent host. The response is also passed directly to FFmpeg without validating its type or size. This allows large downloads to consume disk space and exposes a complex media parser to attacker-controlled input. ### Attack Path 1. An attacker influences the `video_url` argument directly or through manipulated post metadata. 2. The URL points to an internal service, local network host, unexpected protocol, or an attacker-controlled redirector. 3. Curl follows the URL and any redirects from the Agent host. 4. The destination receives a ...[truncated 1167 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:67
Finding

Unpinned package and model retrieval creates supply-chain exposure

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill description overstates implemented behavior and omits important operational dependencies, especially the use of a local authenticated cookies file. Description-behavior mismatches are dangerous because they can mislead reviewers and users about what the skill actually does and what sensitive resources it accesses.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script explicitly disables TLS certificate validation and hostname checking before sending authenticated requests with browser cookies. This enables man-in-the-middle interception or modification of traffic, which could expose session cookies and fetched content or allow an attacker to spoof the target site.

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

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · scripts/extract_post.py (reported line 164)May include surrounding context.

python
en = args.url if 'xsec_token' in args.url else f"{args.url}&xsec_token={xsec}"

    try:
        note = fetch_post(url_with_token, cookie_str)
    except ValueError as e:
        if str(e) == 'POST_NOT_AVAILABLE':
            result = {'error': 'POST_NOT_AVAILABLE', 'message': 'This post is not available or requires login'}
        elif str(e) == 'COOKIES_EXPIRED':
            result = {'error': 'COOKIES_EXPIRED', 'message': 'Cookies expired, please re-export from Chrome'}
        else:
            result = {'error': 'PARSE_ERROR', 'message': str(e)}
        print(json.dumps(result))
        sys.exit(1)
    except Exception as e:
        result = {'error': 'FETCH_ERROR', 'message': str(e)}
        print(json.dumps(result))
        sys.exit(1)

    # Build markdown
    video_transcript = None
    if note.get('type') == 'video' and note.get('video', {}).get('media', {}).get('stream'):
        # For now, just include video info (transcription requires ffmpeg + whisper)
        stream = no

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill clearly instructs execution of shell commands, network access to Xiaohongshu, and writing notes into the local Obsidian vault, but it declares no explicit tool scope or permissions. That omission weakens security review and user consent because the skill's real capabilities are broader than what its metadata communicates.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs the user to export live Xiaohongshu authentication cookies from the browser and store them locally, without warning that these are sensitive session credentials. Anyone who obtains that file may be able to impersonate the user, access private account data, or perform actions as the user until the session expires or is revoked.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The script loads cookie values from a local file and attaches them to the outgoing request via the Cookie header, which transmits authentication/session data to the remote service. Although cookies are part of the extraction workflow, this file does not include any explicit disclosure or caution to the user that their browser-exported cookies will be used and transmitted.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script hard-codes Chinese transcription in both transcription paths: language='zh' for mlx_whisper and --language Chinese for the CLI. This imposes a specific language/locale behavior without offering user opt-in or explaining that the skill is intentionally region-specific.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The skill specifies a target Obsidian directory and later states that the extractor automatically saves notes there. Although file creation is part of the skill's purpose, the markdown does not explicitly warn users that running it will create or modify files under that path.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The function returns interaction text using Chinese labels, and later markdown output also uses Chinese field labels such as 来源、日期、互动、标签、链接. This imposes a specific language/locale in user-visible output without opt-in or explanation, which matches the policy violation criteria for forced language behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The author fallback value 未知 is user-visible content and is fixed to Chinese. Combined with the rest of the generated markdown, this forces a locale choice rather than allowing the user to select their preferred language.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The comment at L177 says the current behavior is limited to including video info because transcription is not implemented yet. However, the code stores the video's master stream URL in note['_video_url'] and then returns it as 'video_url' in the JSON result, which is a more specific capability than the comment suggests.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.