Back to skill

Security audit

出海匠 TikTok 广告与创意情报

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches TikTok ad research, but it also includes account, API-key, and payment onboarding plus unsafe endpoint and file handling that warrant review before installation.

Install only if you trust LinkFox with your API key, phone-based login flow, usage metadata, and payment onboarding. Use the default LinkFox endpoints, avoid custom gateway environment variables unless you fully trust them, review the saved `linkfox` response files, and prefer a short-lived or narrowly scoped API key rather than storing a long-lived key in a shell profile.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/chuhaijiang_ad_search.py:47
Finding
Configurable Network Endpoints Can Receive Credentials and Session Metadata## Vulnerability Details **File Location**: `scripts/chuhaijiang_ad_search.py:47-52, 68-90`; equivalent behavior exists in `scripts/chuhaijiang_ad_detail.py`, `scripts/chuhaijiang_ad_related_products.py`, `scripts/chuhaijiang_creative_search.py`, and `scripts/chuhaijiang_creative_detail.py`. Sensitive onboarding endpoints are configurable at `scripts/onboarding.py:76-85, 193-195, 229-247, 399-418, 451-458`. **Vulnerability Type**: Unvalidated destination for authenticated network requests **Risk Level**: High ### Vulnerable Code ```python def get_api_base() -> str: """Gateway base URL: LINKFOX_TOOL_GATEWAY takes priority.""" return (os.environ.get("LINKFOX_TOOL_GATEWAY") or "https://tool-gateway.linkfox.com").rstrip("/") def get_api_url(): return get_api_base() + API_PATH def call_api(params): global _LAST_CALL_WAS_HTTP_ERROR _LAST_CALL_WAS_HTTP_ERROR = False api_url = get_api_url() api_key = get_api_key() data = json.dumps(params).encode("utf-8") headers = { "Authorization": api_key, "Content-Type": "application/json", "User-Agent": "LinkFox-Skill/2.0", "SESSION_ID": (os.environ.get("SESSION_ID") or "").strip(), "MESSAGE_ID": os.environ.get("MESSAGE_ID", ""), "MODE_ID": os.environ.get("MODE_ID", ""), "APP_NAME": os.environ.get("APP_NAME", ""), } req = Request( api_url, data=data, headers=headers, method="POST", ) try: with urlopen(req, timeout=150) as response: return json.loads(response.read().decode("utf-8")) ``` Onboarding uses the same trust model for more sensitive login and account endpoints: ```python def _agent_base() -> str: return _env_base("LINKFOX_AGENT_API_URL", "https://tool-gateway.linkfox.com", "LINKFOX_TOOL_GATEWAY") def _login_base() -> s ...[truncated 3709 chars]
Remediation
## Remediation Suggestions 1. Enforce HTTPS for every endpoint carrying credentials. 2. Allow only explicit production hostnames such as `tool-gateway.linkfox.com`, `api.linkfox.com`, and `agent-api.linkfox.com`. 3. Reject URLs containing user information, fragments, unexpected ports, ambiguous host encodings, or non-empty paths where only a base origin is expected. 4. Resolve and compare normalized hostnames rather than using suffix or substring checks. 5. Disable redirects for authenticated requests, or validate every redirect destination against the same allowlist before following it. 6. If custom enterprise gateways are required, place them behind an explicit opt-in configuration and require informed user approval before forwarding credentials. 7. Use separate, narrowly scoped credentials for search, onboarding, and billing operations. 8. Avoid forwarding `SESSION_ID`, `MESSAGE_ID`, `MODE_ID`, and `APP_NAME` unless each field is operationally necessary. 9. Add automated tests confirming that HTTP URLs, attacker domains, malformed URLs, and cross-host redirects are rejected before any sensitive request is sent.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/chuhaijiang_ad_search.py:272
Finding
Path Traversal Through Unsanitized SESSION_ID## Vulnerability Details **File Location**: `scripts/chuhaijiang_ad_search.py:272-290`; equivalent output-path logic exists in the other four advertising scripts. A related implementation exists at `scripts/onboarding.py:153-159`. **Vulnerability Type**: Path traversal and arbitrary file placement **Risk Level**: High ### Vulnerable Code ```python def _session_id(ts: float) -> str: """Prefer SESSION_ID; otherwise generate a stable process ID.""" env = (os.environ.get("SESSION_ID") or "").strip() if env: return env if "_auto" not in _SESSION_CACHE: _SESSION_CACHE["_auto"] = ( time.strftime("%H%M%S", time.localtime(ts)) + "-" + secrets.token_hex(3) ) return _SESSION_CACHE["_auto"] def _ensure_session(ts: float) -> tuple[str, str]: """Return the linkfox root and session directory.""" date_str = time.strftime("%Y-%m-%d", time.localtime(ts)) sid = _session_id(ts) root = _linkfox_root() session_dir = os.path.join(root, date_str, sid) os.makedirs(session_dir, exist_ok=True) _ensure_meta(root, session_dir, date_str, sid, ts) return root, session_dir ``` The onboarding implementation also uses the value directly: ```python def session_dir() -> str: ts = time.time() sid = (os.environ.get("SESSION_ID") or "").strip() or ( time.strftime("%H%M%S", time.localtime(ts)) + "-" + secrets.token_hex(3) ) path = os.path.join( _linkfox_root(), time.strftime("%Y-%m-%d", time.localtime(ts)), sid ) os.makedirs(path, exist_ok=True) return path ``` ### Technical Analysis `SESSION_ID` is treated as a trusted filesystem component. The code does not reject absolute paths, parent-directory components such as `..`, platform-specific separators, or other path-control characters. `os.path.join` does not enforce ...[truncated 1825 chars]
Remediation
## Remediation Suggestions 1. Validate `SESSION_ID` before filesystem use with a restrictive pattern such as `^[A-Za-z0-9_-]{1,128}$`. 2. Reject absolute paths, `.` and `..` components, forward slashes, backslashes, null bytes, drive prefixes, and platform-specific separators. 3. Resolve the candidate directory and verify containment: ```python root_real = os.path.realpath(root) candidate = os.path.realpath( os.path.join(root_real, date_str, safe_session_id) ) if os.path.commonpath([root_real, candidate]) != root_real: raise ValueError("Invalid SESSION_ID path") ``` 4. Apply the same centralized validation function to all five business scripts and `onboarding.py`. 5. Use exclusive file creation where replacing an existing file is not required. 6. Set restrictive directory and file permissions for stored responses and payment artifacts. 7. Add regression tests for parent traversal, absolute Unix paths, Windows drive paths, UNC paths, mixed separators, empty identifiers, and oversized identifiers.

T08 · Insecure Dependencies

Warning
Location
scripts/onboarding.py:163
Finding
Unpinned Runtime Dependency Installation Instructions## Vulnerability Details **File Location**: `scripts/onboarding.py:163-185` **Vulnerability Type**: Uncontrolled third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code ```python def render_qr(content: str, out_dir: str) -> dict: try: import qrcode except ImportError: err = ( "Missing qrcode dependency; run: " "pip install qrcode pillow" ) print(f"{TAG} render_qr: {err}", file=sys.stderr) return { "png_path": None, "ascii_qr": None, "error": err } def _require_requests() -> None: if requests is None: raise RuntimeError( "Missing requests dependency; run: " "pip install requests" ) ``` ### Technical Analysis When imports fail, the Skill instructs the user to install packages by mutable names without version constraints, hashes, a lock file, or a trusted package-index policy. Package resolution therefore depends on the user's current pip configuration and whatever versions are available at installation time. The package names shown are established packages rather than apparent typosquats. The risk is consequently not evidence of an intentionally malicious dependency, but an avoidable supply-chain weakness: future releases, a compromised configured index, or dependency-resolution changes can introduce unreviewed code into the execution environment. ### Attack Path 1. The onboarding command runs in an environment where `requests`, `qrcode`, or `Pillow` is unavailable. 2. The script emits an installation command with no pinned versions or hashes. 3. The user executes the suggested command. 4. pip resolves packages and transitive dependencies from the configured package index. 5. A compromised index, malicious mirror, compromised package release, or unexpected future version supplies unreviewed code. 6. That cod ...[truncated 593 chars]
Remediation
## Remediation Suggestions 1. Provide a reviewed dependency lock file with exact versions. 2. Include cryptographic hashes and use `pip install --require-hashes`. 3. Pin all transitive dependencies, not only direct package names. 4. Document and enforce the expected trusted package index. 5. Install dependencies in an isolated virtual environment rather than the global interpreter. 6. Run dependency vulnerability and provenance checks as part of release validation. 7. Replace free-form runtime installation messages with a documented, reproducible setup command based on the reviewed lock file.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (32)

Tainted flow: 'req' from os.environ.get (line 83, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        _LAST_CALL_WAS_HTTP_ERROR = True
Confidence
92% confidence
Finding
The request URL and several outbound headers are derived from environment variables, including `LINKFOX_TOOL_GATEWAY`, `SESSION_ID`, `MESSAGE_ID`, `MODE_ID`, and `APP_NAME`, and are sent directly to `urlopen` without validation. This allows a caller controlling the environment to redirect traffic to an arbitrary host and exfiltrate the API key and request data, which is especially risky because the script includes authorization credentials in the outbound request.

Tainted flow: 'req' from os.environ.get (line 83, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        _LAST_CALL_WAS_HTTP_ERROR = True
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 83, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        _LAST_CALL_WAS_HTTP_ERROR = True
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 83, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        _LAST_CALL_WAS_HTTP_ERROR = True
Confidence
95% confidence
Finding
The request includes multiple environment-derived values in headers, and the destination host is also overrideable via LINKFOX_TOOL_GATEWAY. This creates a real exfiltration risk: in an untrusted or manipulated runtime, sensitive session/app metadata and the API key can be sent to an attacker-controlled endpoint, not just the intended vendor API.

Tainted flow: 'req' from os.environ.get (line 83, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        _LAST_CALL_WAS_HTTP_ERROR = True
Confidence
96% confidence
Finding
The request target is derived from the LINKFOX_TOOL_GATEWAY environment variable and then sent directly to urlopen, while sensitive headers including the API key and session metadata are attached to every request. An attacker who can influence the execution environment can redirect traffic to an arbitrary host and exfiltrate credentials and request data, effectively creating an SSRF-style outbound sink plus secret leakage.

Tainted flow: 'url' from os.environ.get (line 234, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
90% confidence
Finding
The POST target is derived from environment-controlled base URLs, and the request can carry sensitive material such as SMS login data, access tokens, refresh tokens, uid headers, and group identifiers. If an attacker can influence environment variables in the skill runtime, they can redirect these credential-bearing requests to an attacker-controlled endpoint and exfiltrate secrets.

Tainted flow: 'req' from os.environ.get (line 245, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
    req = Request(url, method=method, data=body_bytes, headers=headers)
    try:
        with urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode())
    except urllib.error.HTTPError as e:
        status = e.code
Confidence
90% confidence
Finding
The gateway URL is also constructed from environment-controlled base configuration, and requests include the LinkFox API key in the Authorization header. A manipulated runtime environment could redirect the request to a rogue server, causing API key disclosure and unauthorized use of the linked account.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose is ad/creative intelligence, but the referenced onboarding flow indicates additional capabilities around authentication, account lookup, package purchase, payment order creation, QR-code generation, and payment-status checks. This is a significant description-behavior mismatch that can mislead users and reviewers about the true privilege and financial action surface of the skill.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
The IDs above were valid during release validation but may age. In user workflows, prefer IDs from the current search response.

## Display Rules

1. State the marketplace, filters, current page, and `data.total_count` for searches.
2. For ads, show title, advertiser, public links, duration, active-day count, views/engagement, GMV/ROAS, product title, and the lookup `id` when available.
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
示 JSON 里的 phone/agreements
   - 收到验证码后:`python scripts/onboarding.py login <phone> <code>`
   - 拿到 `api_key` 后把下面三平台配置转发给用户,提示重启会话生效:
     - Windows PowerShell(永久):`setx LINKFOX_AGENT_API_KEY "<key>"`
     - macOS zsh:`echo 'export LINKFOX_AGENT_API_KEY="<key>"' >> ~/.zshrc && source ~/.zshrc`
     - Linux bash:`echo 'export LINKFOX_AGENT_API_KEY="<key>"' >> ~/.bashrc && source ~/.bashrc`
     - 变量名 `LINKFOX_AGENT_API_KEY`(主推)或 `LINKFOXAGENT_API_KEY`(老规范)任一即可

**billing 场景**:`errcode=402` 或消息含 `算力/余额/quota/insufficient/充值/套餐到期`。
- `python scripts/onboarding.py list-plans` → 有 AskUserQuestion 就弹菜单,否则输出编号清单让用户选
- 校验 `plan_id` ∈ 清单、支付方式 ∈ 该套餐 `available_methods`(通常 `wechat/alipay`)
- `python scripts/onboarding.py order <plan_id> <method>` → 展示优先级 PNG
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The file implements account onboarding, SMS login, API key issuance, plan purchase, order querying, and payment QR generation, which is materially unrelated to the declared Chuhaijiang TikTok ad-research skill. This mismatch is a strong indicator of hidden or unauthorized capability expansion, especially because it handles credentials and payments rather than the advertised data-research function.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The code can create commercial orders and generate payment QR codes, enabling real-money transactions from within a skill that is supposed to research TikTok ads and creatives. In this context, transaction capability is unusually dangerous because it can be used to socially engineer purchases or trigger unauthorized billing workflows under the cover of an unrelated skill.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
This section performs SMS-code login, token handling, team discovery, and API key retrieval/generation, which are credential-management capabilities outside the reasonable scope of an ad-research skill. In a mismatched skill, such functionality is especially risky because it can collect authentication factors and mint reusable secrets that grant broader account access.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill appears to use environment variables, local file writes, and networked scripts, but it does not declare any explicit tool scope or allowed-tools boundary. That makes its effective privileges ambiguous and increases the chance an orchestrator or reviewer will approve broader capabilities than intended, especially given the billing, auth, and file persistence behavior described elsewhere in the skill.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The manifest description is written as a behavioral routing rule in Chinese and says the skill triggers when users mention 出海匠 or Chuhaijiang, but it does not indicate any language choice or opt-in for how the skill should interact. This creates a locale/language policy concern because the skill metadata and operating instructions implicitly privilege a specific language context without documenting a user-selectable alternative.

Ssd 3

Medium
Confidence
95% confidence
Finding
The skill instructs the system to always persist full API responses to session-linked local files under the working directory. Even if the API is intended for public-market intelligence, responses can still include user-supplied query content, identifiers, commercial data, or unexpected fields, and retaining them by default increases the risk of unnecessary data exposure, later prompt leakage, or cross-task access.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
This markdown file contains user-facing operational instructions only in Chinese, which can amount to forcing a specific language without user opt-in. The policy allows locale constraints when explicitly justified, but this file does not state that the skill is Chinese-only or provide an alternative language option.

External Transmission

Medium
Category
Data Exfiltration
Content
网关可能以 HTTP 200 包装业务错误,因此必须同时检查 `errcode` 和 `errmsg`。真实测试中,创意搜索带一组过滤条件时曾收到 `BACKEND_ERROR`,缩减为合法最小请求后成功;不要把一次 5xx 当成字段枚举依据。

## curl 示例

```bash
curl -X POST "${LINKFOX_TOOL_GATEWAY}/chuhaijiang/ad-creative/ads/search" \
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The onboarding instructions explicitly ask the user to provide a phone number so a local script can perform registration, but they do not disclose how that personal data will be transmitted, stored, logged, or retained. In a security-sensitive agent skill, collecting PII without clear privacy and handling guidance increases the risk of unintended disclosure or misuse, especially if command output, terminal history, or logs capture the phone number or verification flow.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The script always writes the full API response to a persistent path under the current project directory, and the response may contain ad intelligence data, account-linked details, or other sensitive business information. Persisting this by default increases the chance of unintended disclosure through source-control commits, workspace sharing, or later access by other tools/users on the same machine.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The code sends the JSON parameters along with SESSION_ID, MESSAGE_ID, MODE_ID, and APP_NAME headers to an external service. While the script purpose implies an API call, there is no visible runtime disclosure or confirmation about what metadata is sent off-host, which matters for privacy-aware users.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script always writes the full API response to a predictable path under the current working directory, even when the response may contain sensitive business data, and does so by default rather than via opt-in. In multi-user workspaces, shared repositories, or agent execution environments, this creates a persistence and disclosure risk because data may be retained, indexed, committed, or read by other processes later.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script always writes the full API response to persistent files under the project directory, regardless of whether the data is needed after execution. This can unnecessarily retain potentially sensitive business data, query results, and associated context on disk, increasing exposure to other local users, later tooling, commits, or artifact collection.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script forwards SESSION_ID, MESSAGE_ID, MODE_ID, and APP_NAME in HTTP headers without explicit consent or strong necessity checks. These identifiers can reveal internal workflow context and, combined with the gateway override behavior, can be exposed to unintended recipients.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
_linkfox_root、session_dir 和 render_qr 会选择工作区/家目录/临时目录创建 linkfox 目录,并持久化二维码图片文件。这种本地状态落盘服务于支付流程,而不是对 Chuhaijiang 广告研究数据的检索、分析或展示所必需的能力。

Static analysis

No suspicious patterns detected.