Back to skill

Security audit

trip-scout

Security checks across malware telemetry and agentic risk

Overview

This travel-planning skill has a coherent purpose, but it needs Review because it handles account cookies, uses reverse-engineered anti-detection platform access, and includes under-scoped executable code paths.

Review before installing. Use this only in an isolated environment, avoid entering personal platform cookies unless you accept local plaintext storage, delete or restrict cookie files after use, and be cautious with generated itinerary HTML that includes external review or hotel text. Prefer pinned dependencies and official platform APIs where possible.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
Findings (6)

T03 · Remote Payload Retrieval and Execution

Error
Location
vendor/xhs_api/xhs_utils/common_util.py:66
Finding
Remote Server Response Is Compiled and Executed as JavaScript<![CDATA[ ## Vulnerability Details **File Location**: `vendor/xhs_api/xhs_utils/common_util.py:66-99` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: High ### Vulnerable Code ```python def fetch_sec_cookies(cookies, headers): """获取 sec_poison_id 和 websectiga""" sec_poison_id = None websectiga = None api = '/api/sec/v1/scripting' data = {"callFrom": "web", "callback": "seccallback"} h = dict(headers) h['content-type'] = 'application/json;charset=UTF-8' sign_h = _generate_xsc(cookies['a1'], api, data) h.update(sign_h) data_str = json.dumps(data, separators=(',', ':'), ensure_ascii=False) try: resp = requests.post( _AS_URL + api, headers=h, cookies=cookies, data=data_str.encode('utf-8'), timeout=REQUEST_TIMEOUT ) res = resp.json() sec_poison_id = res.get('data', {}).get('secPoisonId') jsvmp_code = res.get('data', {}).get('data', '') if jsvmp_code: env = _load_websectiga_env() if env: try: js_code = env + '\n' + jsvmp_code + '\nvar __result = _websectiga_result;' ctx = execjs.compile(js_code) websectiga = ctx.eval('__result') or None except Exception as e: logger.debug(f'websectiga jsvmp execution failed: {e}') except Exception as e: logger.debug(f'fetch sec cookies failed: {e}') return sec_poison_id, websectiga ``` ### Technical Analysis The function retrieves JavaScript from `https://as.xiaohongshu.com/api/sec/v1/scripting`, concatenates the response with a local JavaScript environment, and passes the result to `execjs.compile()`. PyExecJS normally invokes a locally installed JavaScript runtime such as Node.js, so the downloaded payload executes with the privileges and environment of the local Skill process. The effective execu ...[truncated 1373 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `fetch_sec_cookies()` if it is not required by the documented execution path. 2. Do not compile or evaluate JavaScript supplied in an HTTP response. 3. Replace the remote payload with a locally vendored, readable implementation whose source and hash are pinned and reviewed. 4. If remote execution is operationally unavoidable, verify a cryptographic signature from a separately managed trust root before execution. 5. Execute the signing component in a sandboxed process or container with: - No inherited environment secrets. - No access to cookie or Agent configuration directories. - A read-only filesystem. - No network access after the payload has been obtained. - Strict CPU, memory, and execution-time limits. 6. Add tests that fail if `execjs.compile()` or equivalent evaluation receives network-derived input. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
vendor/xhs_api/apis/xhs_pc_login_apis.py:401
Finding
Authenticated Xiaohongshu Session Cookies Are Printed in Full<![CDATA[ ## Vulnerability Details **File Location**: `vendor/xhs_api/apis/xhs_pc_login_apis.py:401-411` and `442-452` **Vulnerability Type**: Sensitive credential disclosure through logs **Risk Level**: High ### Vulnerable Code QR-code login: ```python # 防御性校验:web_session 缺失会导致后续 search/feed 报"无登录信息" if 'web_session' not in cookies: logger.error( '⚠️ 登录cookie缺少 web_session,search/feed 将失败。' '请改用 set-cookie 手动补全:python scripts/xhs.py set-cookie --cookie "a1=...; web_session=..."' ) cookies_str = self.cookies_to_str(cookies) logger.success(f'登录成功!\ncookies:\n{cookies_str}') return cookies_str ``` Phone login: ```python logger.info('[4/4] 验证登录状态...') success, user_info, cookies = self.get_user_info(cookies) if success: logger.info(f'用户: {user_info.get("nickname", "未知")} (RedID: {user_info.get("red_id", "未知")})') cookies_str = self.cookies_to_str(cookies) logger.success(f'登录成功!\ncookies:\n{cookies_str}') return cookies_str ``` ### Technical Analysis After authentication, the complete cookie collection is converted to a string and written through Loguru. This collection can contain `web_session` and other reusable authentication tokens. In an AI Agent environment, process output commonly becomes part of tool results, conversation traces, orchestration logs, debugging records, or centralized telemetry. Printing a session credential therefore exposes it beyond the component that needs to make authenticated requests. Returning the cookie string internally may be necessary for the current API design, but logging the same value is not necessary for the declared travel-research functionality. ### Attack Path 1. A user initiates QR-code or phone-based Xiaohongshu login. 2. Xiaohongshu returns an authenticated session cookie. 3. The code serializes all cookies, including `web_session`. 4. `logger.success()` emits the entire serialized cookie string. 5. An attacker with access to Agent tool output, execution logs, terminal history, ...[truncated 545 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove both statements that log `cookies_str`. 2. Log only a generic authentication-success event and non-sensitive metadata. 3. Introduce a centralized redaction filter that masks values for cookie names such as `web_session`, `a1`, `gid`, and similar credentials. 4. Ensure exceptions and debug logging never serialize request cookie dictionaries or authentication headers. 5. Keep credentials in an internal credential object rather than returning or passing plain strings where possible. 6. Review existing Agent, terminal, and centralized logs and purge exposed cookie values. 7. Advise affected users to revoke existing sessions after upgrading. 8. Add automated tests that assert known cookie values never appear in stdout or stderr. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/xhs.py:47
Finding
Authentication Cookies Are Persisted as Plaintext Without Explicit Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/xhs.py:47-72` **Additional Location**: `vendor/ctrip/client.py:22-26, 126-134` **Vulnerability Type**: Insecure local credential storage **Risk Level**: Medium ### Vulnerable Code Xiaohongshu cookie storage: ```python # Cookie 持久化路径 DEFAULT_COOKIE_PATH = os.path.expanduser("~/.xiaohongshu/cookies.json") def _load_cookie_str(): """从文件加载 cookie 字符串""" if not os.path.exists(DEFAULT_COOKIE_PATH): return None try: with open(DEFAULT_COOKIE_PATH, 'r', encoding='utf-8') as f: data = json.load(f) # 兼容两种格式:字符串或 {"cookie_str": "..."} if isinstance(data, str): return data if isinstance(data, dict): return data.get("cookie_str") or data.get("cookies_str") return None except Exception: return None def _save_cookie_str(cookie_str): """保存 cookie 字符串到文件""" os.makedirs(os.path.dirname(DEFAULT_COOKIE_PATH), exist_ok=True) with open(DEFAULT_COOKIE_PATH, 'w', encoding='utf-8') as f: json.dump({"cookie_str": cookie_str}, f, ensure_ascii=False, indent=2) ``` Ctrip cookie storage: ```python cookies = self.context.cookies() os.makedirs(os.path.dirname(self.cookie_path), exist_ok=True) with open(self.cookie_path, 'w', encoding='utf-8') as f: json.dump(cookies, f, ensure_ascii=False, indent=2) ``` ### Technical Analysis The Skill stores reusable platform authentication cookies in plaintext JSON files. File and directory permissions are inherited from the process umask; the code does not explicitly create the credential directory as mode `0700` or the cookie file as mode `0600`. On systems with a permissive umask, shared home directories, broad backup access, or multiple local users, these files may become readable by principals that should not receive the user’s authenticated session. The code also does not guard against a pre-existing symbolic link at the cookie path. Persistent ...[truncated 1135 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create credential directories with mode `0700`. 2. Create credential files atomically with mode `0600`, independent of the current umask. 3. Reject symbolic links by using secure open flags such as `O_NOFOLLOW` where supported. 4. Write to a securely created temporary file in the same directory, set permissions, flush and synchronize it, and then atomically rename it. 5. Prefer an operating-system credential store or keychain instead of plaintext JSON. 6. Apply equivalent protections to `~/.ctrip/browser-data`, which stores broader browser session state. 7. Provide explicit logout and credential-deletion commands. 8. Document retention, backup, and session-revocation behavior. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
assets/template.html:506
Finding
Generated Itinerary Page Permits Stored HTML and JavaScript Injection<![CDATA[ ## Vulnerability Details **File Location**: `assets/template.html:506-583` and `707-729` **Vulnerability Type**: Stored client-side HTML injection and cross-site scripting **Risk Level**: High ### Vulnerable Code Map information-window rendering: ```javascript var info = new AMap.InfoWindow({ content: '<b>' + l.name + '</b><br><span style="color:#86868b">' + d.label + '</span>', offset: new AMap.Pixel(0, -10), closeWhenClickMap: true, }); ``` ```javascript var gmapQ = l.gmap || encodeURIComponent(l.name + ' Tokyo'); var infoHtml = '<b>' + l.name + '</b>'; if (l.time && l.time !== '—') infoHtml += '<br><span style="color:#86868b">' + l.time + '</span>'; infoHtml += '<br>' + l.desc; if (l.budget) infoHtml += '<br><span style="color:#ff9500">' + l.budget + '</span>'; if (l.type === 'road-trip' && (l.distance || l.driveTime || l.toll || l.roadType)) { infoHtml += '<br><span style="color:#34c759;font-size:12px;">'; if (l.distance) infoHtml += '📏 ' + l.distance + ' '; if (l.driveTime) infoHtml += '⏱️ ' + l.driveTime + ' '; if (l.toll) infoHtml += '💰 ¥' + l.toll + ' '; if (l.roadType) infoHtml += '🛣️ ' + l.roadType; infoHtml += '</span>'; } infoHtml += '<br><a href="#" onclick="event.preventDefault();openAS(\'' + l.name.replace(/'/g, "\\'") + '\',' + l.lat + ',' + l.lng + ',\'' + gmapQ + '\')">📍 导航</a>'; if (l.xhs) infoHtml += ' · <a href="' + l.xhs + '" target="_blank">📕 小红书</a>'; ``` Itinerary-card rendering: ```javascript let lnk = `<button class="btn nav" onclick="openAS('${loc.name.replace(/'/g,"\\'")}',${loc.lat},${loc.lng},'${gmapQ}')">📍 导航</button>`; if(loc.xhs||loc.xhsKeyword) { const xhsHref = IN_APP_WEBVIEW ? xhsWebUrl(loc) : xhsAppUrl(loc); const xhsLabel = IN_APP_WEBVIEW ? '📕 小红书' : '📕 小红书 App'; lnk += `<a href="${xhsHref}" onclick="return openXhs('${locKey.replace(/'/g,"\\'")}')" class="btn xhs">${xhsLabel}</a>`; } const dpEnabled = loc.dianping !== false && (loc.dianping || loc.dianpingKeyword || loc.ty ...[truncated 3102 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Stop constructing UI elements by concatenating untrusted strings. 2. Build elements with `document.createElement()` and assign external text through `textContent`. 3. Use context-specific encoding where string templates cannot be eliminated. 4. Validate coordinates as finite numbers and validate colors against a strict allowlist or format. 5. Parse all external URLs with `new URL()` and allow only explicitly required schemes and hosts. 6. Reject `javascript:`, `data:`, `file:`, and unexpected custom schemes. 7. Remove inline `onclick` handlers and bind events with `addEventListener()`. 8. Add `rel="noopener noreferrer"` to links opened with `target="_blank"`. 9. Apply a restrictive Content Security Policy that disallows inline script and limits network destinations. 10. Treat all hotel, review, Xiaohongshu, Ctrip, map, and user-supplied text as untrusted. 11. Add automated payload tests covering HTML text, quoted attributes, URLs, CSS values, and JavaScript string contexts. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:3
Finding
Mutable and Unpinned Dependencies Are Installed and Executed<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:3-12` **Additional Locations**: `vendor/xhs_api/package.json:6-8`, `SKILL.md:517-539` **Vulnerability Type**: Dependency and package supply-chain exposure **Risk Level**: Medium ### Vulnerable Code Python dependencies: ```text playwright>=1.40.0 requests>=2.31.0 PyExecJS>=1.5.1 loguru>=0.7.0 qrcode>=7.4.0 ``` Node dependencies: ```json "dependencies": { "crypto-js": "^4.2.0", "jsdom": "^26.0.0" } ``` Installation instructions include mutable package resolution: ```bash npm i -g @fly-ai/flyai-cli npx @meituan-travel/ht-ai pip install -r requirements.txt playwright install chromium ``` ### Technical Analysis The Python requirements use open-ended lower bounds, while the Node manifest uses caret ranges. The documented `npx` and global npm commands do not specify exact versions. No Python hash constraints or Node lockfile were identified in the supplied project structure. Consequently, two installations of the same reviewed Skill can resolve to different dependency code. Package installation and import occur with the local user’s permissions, and npm packages may also execute lifecycle scripts. This does not establish that any current dependency is malicious, but it creates a preventable supply-chain execution path. ### Attack Path 1. A package maintainer account, registry entry, or transitive dependency is compromised, or a future release introduces malicious behavior. 2. A user follows the Skill’s documented setup instructions. 3. The package manager resolves the mutable range to the compromised release. 4. Malicious code executes during installation, an npm lifecycle script, import, CLI invocation, or browser installation. 5. The package inherits the user’s environment, network access, filesystem access, and available API credentials. ### Impact Assessment A compromised dependency can obtain the privileges of the user installing or running the Skill. This may include ac ...[truncated 216 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct Python and Node dependency to an exact reviewed version. 2. Generate and commit a Node lockfile with integrity metadata. 3. Use hash-verified Python requirements, such as `pip --require-hashes`. 4. Specify exact versions for all `npx` and global npm invocations. 5. Prefer project-local CLI installation over global installation. 6. Review transitive dependencies and npm lifecycle scripts before release. 7. Use an isolated virtual environment and project-local Node modules. 8. Add automated dependency vulnerability and provenance scanning. 9. Establish a controlled update process that reviews diffs and regenerates hashes and lockfiles. 10. Pin externally installed OpenClaw Skills to immutable versions or revisions where the platform supports it. ]]>

other

Warning
Location
vendor/xhs_api/static/xhs_rap.js:420
Finding
Opaque Decoded Bytecode Executes Inside the Xiaohongshu Signing Runtime<![CDATA[ ## Vulnerability Details **File Location**: `vendor/xhs_api/static/xhs_rap.js:420 onward` **Execution Location**: `vendor/xhs_api/xhs_utils/xhs_util.py:62-65` **Vulnerability Type**: Obfuscated executable component with an unauditable trust boundary **Risk Level**: Medium ### Vulnerable Code The signing JavaScript contains a custom interpreter that decodes an embedded Base64-like payload and executes its instruction sequence: ```javascript y=(function(B){ if(!B)return""; for(var Q="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""), A=B.length,E=0,C=[];E<A;){ var g=Q.indexOf(B.charAt(E++)), H=Q.indexOf(B.charAt(E++)), I=Q.indexOf(B.charAt(E++)), c=Q.indexOf(B.charAt(E++)), J=g<<2|H>>4, k=(15&H)<<4|I>>2, w=(3&I)<<6|c; C.push(String.fromCharCode(J)), 64!=I&&C.push(String.fromCharCode(k)), 64!=c&&C.push(String.fromCharCode(w)) } return function(B){ for(var Q=[],A=B.length,E=0,E=0;E<A;E++){ var C=B.charCodeAt(E); if((C>>7&255)==0)Q.push(B.charAt(E)); else if((C>>5&255)==6){ var g=B.charCodeAt(++E),H=(31&C)<<6,I=63&g,c=H|I; Q.push(String.fromCharCode(c)) } else if((C>>4&255)==14){ var g=B.charCodeAt(++E),J=B.charCodeAt(++E), H=C<<4|g>>2&15,I=(3&g)<<6|63&J,c=(255&H)<<8|I; Q.push(String.fromCharCode(c)) } } return Q.join("") }(C.join("")) })(E.b).split("").reduce(function(B,Q){ return(!B.length||5==B[B.length-1].length)&&B.push([]), B[B.length-1].push(-1+Q.charCodeAt()),B },[]); ``` The decoded instruction array is then interpreted: ```javascript _garp_2846=function(B){ for(var Q=0;Q<B.length;){ var A=B[Q]; Q=(0,G[A[0]])(A[1],A[2],A[3],A[4],Q,y,B) } } ``` The Python code compiles the complete script ...[truncated 2126 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the obfuscated virtual-machine implementation with a readable signing implementation. 2. Record the exact upstream repository revision and cryptographic hash from which each vendored JavaScript file was obtained. 3. Do not advise users to synchronize signing files from an unpinned upstream branch. 4. Require code review and reproducible hash verification for every update. 5. Run the signing runtime in a dedicated sandbox with no inherited secrets, no network access, and no access to user credential directories. 6. Expose only the minimum signing inputs and output through a narrow inter-process interface. 7. Add runtime monitoring that rejects unexpected filesystem, process, or network operations. 8. Document that the component is reverse-engineered executable code and make the related Xiaohongshu integration opt-in. ]]>

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (522)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
) -> dict[str, Any]:
    """调用 flyai search-flight 交叉验证价格"""
    try:
        result = subprocess.run(
            [
                "flyai", "search-flight",
                "--origin", dep_city,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Content
}

    try:
        r = __import__("requests").post(url, json=payload, headers=headers, timeout=15)
        if r.status_code != 200:
            return []
        data = r.json()
Confidence
75% confidence
Finding
Dynamic __import__() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""跨平台用系统默认程序打开文件(macOS=open / Linux=xdg-open / Windows=startfile)"""
        try:
            if sys.platform == 'darwin':
                subprocess.Popen(['open', path])
            elif os.name == 'nt':
                os.startfile(path)  # type: ignore[attr-defined]
            else:
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
elif os.name == 'nt':
                os.startfile(path)  # type: ignore[attr-defined]
            else:
                subprocess.Popen(['xdg-open', path])
        except Exception as e:
            logger.debug(f'打开图片查看器失败(可手动打开上面的路径): {e}')
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
data = {"qr_type": 1}

        headers, data = generate_headers(cookies['a1'], api, data)
        resp = requests.post(
            self.base_url + api,
            headers=headers, cookies=cookies, data=data,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
data = {"qrId": qr_id, "code": code}

        headers, data = generate_headers(cookies['a1'], api, data)
        resp = requests.post(
            self.base_url + api,
            headers=headers, cookies=cookies, data=data,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
splice_api = splice_str(api, params)

        headers, _ = generate_headers(cookies['a1'], splice_api, method='GET')
        resp = requests.get(
            self.base_url + splice_api,
            headers=headers, cookies=cookies,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
api = '/api/sns/web/v2/user/me'

        headers, _ = generate_headers(cookies['a1'], api)
        resp = requests.get(
            self.base_url + api,
            headers=headers, cookies=cookies,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
splice_api = splice_str(api, params)

        headers, _ = generate_headers(cookies['a1'], splice_api)
        resp = requests.get(
            self.base_url + splice_api,
            headers=headers, cookies=cookies,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.get (network output)

Medium
Category
Data Flow
Content
splice_api = splice_str(check_api, params)

        headers, _ = generate_headers(cookies['a1'], splice_api)
        resp = requests.get(
            self.base_url + splice_api,
            headers=headers, cookies=cookies,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
login_api = '/api/sns/web/v2/login/code'
        data = {"mobile_token": mobile_token, "zone": zone, "phone": phone}
        headers, data = generate_headers(cookies['a1'], login_api, data)
        resp = requests.post(
            self.base_url + login_api,
            headers=headers, cookies=cookies, data=data,
            timeout=REQUEST_TIMEOUT
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
data_str = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
        try:
            resp = requests.post(
                self.as_url + api,
                headers=headers, cookies=cookies,
                data=data_str.encode('utf-8'),
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Tainted flow: 'headers' from requests.get (line 291, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
data_str = json.dumps(data, separators=(',', ':'), ensure_ascii=False)
        try:
            resp = requests.post(
                self.as_url + api,
                headers=headers, cookies=cookies,
                data=data_str.encode('utf-8'),
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The manifest describes a travel assistant focused on flight/hotel search, airfare monitoring, road-trip itinerary planning, and car-rental pickup/dropoff lookup. This file instead defines a Dianping/OpenCLI workflow for searching and evaluating restaurants using dining-specific signals such as taste, queueing, cuisine, and review counts, which is a materially different capability not reflected in the manifest description.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The document is presented as a '酒店搜索与智能筛选工作流', but it instructs the skill to write stay history into `~/.trip-scout/MEMORY.md` after the user selects a hotel. Persisting profile/history data is not necessary to perform the immediate hotel search and filtering task described in this file, and goes beyond the core hotel-query capability.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
L007 says the memory file contains '用户偏好和入住历史', which suggests a relatively narrow persistence scope. The actual documented schema later includes membership benefits and tiers (L030-L041), search history (L055-L059), brand trust changes (L060-L064), and accumulated behavioral rules (L065-L079), which is materially broader than the stated description.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The manifest focuses on flight/hotel search, fare monitoring, itinerary planning, map rendering, and car-rental outlet lookup. L142 and L148-L157 introduce a separate capability: scanning existing Feishu guide documents to infer and persist historical stays and user sentiment, which goes beyond the obvious operational needs of on-demand search/planning and expands into background profile construction.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The module docstring states that it reverse-engineers Xiaohongshu signing algorithms and uses direct API calls because browser automation leaves detectable traces, explicitly framing the implementation around avoiding platform risk controls. That intent goes beyond ordinary route-recommendation or travel-search behavior described in the manifest and documents evasive behavior rather than user-facing travel assistance.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The script modifies NODE_PATH and process working directory so PyExecJS/Node can execute bundled JavaScript dependencies needed for Xiaohongshu signature generation. For a travel search and itinerary assistant, executing a reverse-engineered signing stack is not an obvious or justified capability on its own; it is specifically tied to emulating private platform clients rather than normal trip-planning logic.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The parser description and epilog repeat that the tool uses direct HTTP API calls to lower the chance of being detected by platform controls. This is an active statement of evasive implementation intent, which contradicts the manifest's presentation as a travel assistant for route recommendation and hotel linkage.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The manifest describes flight/hotel search and trip planning, where browser automation itself can be an expected implementation detail. However, explicitly injecting a stealth script to remove automation markers and evade bot detection goes beyond ordinary search functionality and adds a capability aimed at concealing automated access rather than serving the user-facing travel-planning purpose.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The STEALTH_JS payload changes navigator.webdriver, chrome.runtime, plugins, languages, permissions behavior, and WebGL renderer values to mask automation. Those behaviors are not inherent to searching flights, hotels, routes, or rental locations; they implement fingerprint spoofing and anti-bot evasion instead.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The manifest mentions 小红书 only as an input source for route recommendation, but this file implements much broader account-centric collection: self-profile retrieval, full user post enumeration, liked/collected notes, unread messages, mentions, likes/collects notifications, and new follower data. Those behaviors materially exceed a travel-planning assistant’s described purpose and are not an obvious implementation detail of generating self-drive itineraries.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
Functions for retrieving the authenticated user's own profile data are not justified by the manifest’s travel-search and road-trip-planning scope. This is especially true when combined with other endpoints that inspect personal social activity, making the skill capable of harvesting account information unrelated to flights, hotels, routes, or car-rental lookup.

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

Detected: suspicious.dynamic_code_execution, suspicious.env_credential_access

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
vendor/xhs_api/static/xhs_xray_pack2.js:3443

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
vendor/xhs_api/static/xhs_xray.js:207

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
vendor/xhs_api/static/xhs_xray_pack1.js:35836