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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
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.

Content

Scanner excerpt · scripts/chuhaijiang_ad_related_products.py (reported line 90)May include surrounding context.

python
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

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/chuhaijiang_ad_detail.py (reported line 90)May include surrounding context.

python
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

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/chuhaijiang_ad_search.py (reported line 90)May include surrounding context.

python
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

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

Critical
Category
Data Flow
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.

Content

Scanner excerpt · scripts/chuhaijiang_creative_detail.py (reported line 90)May include surrounding context.

python
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

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

Critical
Category
Data Flow
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.

Content

Scanner excerpt · scripts/chuhaijiang_creative_search.py (reported line 90)May include surrounding context.

python
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

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

Critical
Category
Data Flow
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.

Content

Scanner excerpt · scripts/onboarding.py (reported line 195)May include surrounding context.

python
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 = ""

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

Critical
Category
Data Flow
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.

Content

Scanner excerpt · scripts/onboarding.py (reported line 247)May include surrounding context.

python
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

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.

Content

No source excerpt is available for this finding.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · SKILL.md (reported line 94)May include surrounding context.

md
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.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [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 · references/onboarding.md (reported line 14)May include surrounding context.

md
示 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

Description-Behavior Mismatch

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/api.md (reported line 229)May include surrounding context.

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

curl 示例

bash
curl -X POST "${LINKFOX_TOOL_GATEWAY}/chuhaijiang/ad-creative/ads/search" \

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

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

Content

No source excerpt is available for this finding.