Back to skill

Security audit

多模态图像识别

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches an image-recognition service, but it also handles public file uploads, account login, API-key creation, billing orders, automatic feedback reporting, and local retention in ways users should review carefully.

Install only if you are comfortable using LinkFox as a paid external image-analysis provider. Avoid private or sensitive local images unless you accept that they may be uploaded as publicly reachable objects, verify endpoint environment variables point only to official LinkFox hosts, do not share SMS codes or API keys casually, and prefer safer secret storage over shell startup files.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (6)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:119
Finding

Autonomous Disclosure of Conversation Content to a Feedback Service

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/onboarding.py:72
Finding

Environment-Controlled Endpoints Can Receive API Keys and Login Tokens

Content
View full analysis
str: for n in (name, *fallbacks): v = os.environ.get(n) if v: return v.rstrip("/") return default.rstrip("/") def _agent_base() -> str: return _env_base("LINKFOX_AGENT_API_URL", "https://tool-gateway.linkfox.com", "LINKFOX_TOOL_GATEWAY") def _login_base() -> str: return _env_base("LINKFOX_LOGIN_API_URL", "https://api.linkfox.com") def _agent_user_base() -> str: return _env_base("LINKFOX_AGENT_USER_API_URL", "https://agent-api.linkfox.com") ``` ```python def _http_post(url: str, body: dict, headers: dict, timeout: int = 30) -> dict: """Generic requests POST returning JSON or {_error, _body}.""" try: _require_requests() 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 = "" resp = getattr(e, "response", None) if resp is not None: try: body_text = resp.text[:500] except Exception: pass return {"_error": str(e), "_body": body_text} def _headers(source: str, origin_host: str, *, access_token: str = "", user_id: str = "", group_id: str = "") -> dict: h = { "Accept": "application/json, text/plain, */*", "Content-Type": "application/json;charset=UTF-8", "Origin": f"https://{origin_host}", "Referer": f"https://{origin_host}/", "source": source, "User-Agent": UA, } if access_token: h["authorization"] = a ...[truncated 3211 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/upload_image.py:90
Finding

Local Images Are Uploaded with Public-Read Access Without a Required Confirmation Step

Content
View full analysis
str: """Extract the base public URL by stripping query parameters.""" return presigned_url.split("?")[0] ``` The only file-content gate is extension-based: ```python extension = os.path.splitext(file_path)[1].lstrip(".").lower() content_type = CONTENT_TYPE_MAP.get(extension) if not content_type: print( f"Unsupported image format: .{extension}\n" f"Supported formats: {', '.join(CONTENT_TYPE_MAP.keys())}", file=sys.stderr, ) sys.exit(1) ``` ### Technical Analysis The uploader reads the entire local file and sends it to a presigned destination with the explicit `x-oss-object-acl: public-read` setting. It then strips the presigned query string and returns the unsigned object URL. The script validates only the filename extension. It does not va ...[truncated 1350 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/onboarding.py:468
Finding

Generated API Keys Are Printed in Full and Recommended for Plaintext Persistence

Content
View full analysis
dict: masked = _mask_phone(phone) if not re.fullmatch(r"\d{11}", phone): return {"error": f"login: 手机号格式不正确: {phone}", "phone": masked} if not re.fullmatch(r"\d{4,8}", code): return {"error": f"login: 验证码格式不正确: {code}", "phone": masked} lg = _login_v3(phone, code, channel) if "error" in lg: return {"error": lg["error"], "phone": masked} if lg.get("is_new_user"): lbt = _login_by_token(lg["access_token"], lg["refresh_token"]) if "error" in lbt: print(f"{TAG} {lbt['error']}(不影响拿 key)", file=sys.stderr) info = _fetch_user_info_v3(lg["access_token"], lg["user_id"]) if "error" in info: return {"error": info["error"], "phone": masked} tok = _get_or_generate_api_token(lg["access_token"], lg["user_id"], info["group_id"]) if "error" in tok: return {"error": tok["error"], "phone": masked} return { "api_key": tok["api_key"], "phone": masked, "group_id": info["group_id"], "member_id": info["member_id"], "source": tok["source"], "nick_name": lg.get("nick_name", ""), "team_name": info.get("team_name", ""), "is_new_user": lg.get("is_new_user", False), } def _emit(obj: dict) -> None: print(json.dumps(obj, ensure_ascii=False, indent=2)) ``` The onboarding instructions recommend: ```bash setx LINKFOX_AGENT_API_KEY "" echo 'export LINKFOX_AGENT_API_KEY=""' >> ~/.zshrc && source ~/.zshrc echo 'export LINKFOX_AGENT_API_KEY=""' >> ~/.bashrc && source ~/.bashrc ``` ### Technical Analysis The login flow returns the complete API key as a JSON value and prints it to standard output. In Agent and au ...[truncated 1268 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/multimodal_recognize_image.py:97
Finding

Complete Image-Analysis Responses Are Duplicated in Cache and Session Archives with Default Permissions

Content
View full analysis
CACHE_TTL_SEC: return None try: with open(path, encoding="utf-8") as f: payload = json.load(f) if isinstance(payload, dict): payload.setdefault("_cache", {})["hit"] = True return payload except (OSError, json.JSONDecodeError): return None def _save_cache(path, payload): try: with open(path, "w", encoding="utf-8") as f: json.dump(payload, f, ensure_ascii=False, indent=2) except OSError: pass ``` The response is separately archived: ```python cache_path = _cache_path(params) result = _load_cache(cache_path) if use_cache else None if result is None: result = call_api(params) if use_cache: _save_cache(cache_path, result) serialized = json.dumps(result, ensure_ascii=False, indent=2) ts = int(time.time()) out_path = _resolve_output_path(ts) try: with open(out_path, "w") as f: f.write(serialized) print(f"Saved full response: {out_path} ({len(serialized)} bytes)") if result.get("_cache", {}).get("hit"): print(f"Cache hit: {cache_path}") except OSError as e: print(f"Failed to save to {out_path}: {e}", file=sys.stderr) if inline or len(serialized.encode("utf-8")) <= SMALL_THRESHOLD: if result.get("_cache", {}).get("hit"): print(f"Cache hit: {cache_path}", file=sys.stderr) print(serialized) else: ...[truncated 1513 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/onboarding.py:163
Finding

Runtime Instructions Recommend Installing Unpinned Dependencies

Content
View full analysis
dict: try: import qrcode except ImportError: err = "缺少 qrcode 依赖,请运行: pip install qrcode pillow" print(f"{TAG} render_qr: {err}", file=sys.stderr) return {"png_path": None, "ascii_qr": None, "error": err} os.makedirs(out_dir, exist_ok=True) png_path = os.path.join(out_dir, f"qr-{int(time.time() * 1_000_000)}.png") qr = qrcode.QRCode(border=1) qr.add_data(content) qr.make(fit=True) qr.make_image(fill_color="black", back_color="white").save(png_path) buf = io.StringIO() qr.print_ascii(out=buf, invert=True) return {"png_path": png_path, "ascii_qr": buf.getvalue()} def _require_requests() -> None: if requests is None: raise RuntimeError("缺少 requests 依赖,请运行: pip install requests") ``` ### Technical Analysis When dependencies are absent, the program tells the user to execute unrestricted `pip install` commands without version constraints, cryptographic hashes, a lockfile, an isolated environment, or a specified trusted package index. Package installation can execute build backends and installation-time code with the invoking user’s privileges. Even when the package names are legitimate, future version compromise, index substitution, dependency compromise, or incompatible updates can alter behavior after the Skill has been reviewed. ### Attack Path 1. The environment lacks `requests`, `qrcode`, or Pillow. 2. Onboarding emits a `pip install` instruction. 3. The user executes it against the currently configured package index. 4. Pip resolves an unpinned current version and its transitive dependencies. 5. A compromised index, package release, dependency, or build backend executes code during installation or later import. ### Im ...[truncated 377 chars]
Remediation
View remediation
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 (36)

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

Critical
Category
Data Flow
Confidence
96% confidence
Finding

The request sent via urlopen includes environment-derived values in headers, and the destination host is also overridable through LINKFOX_TOOL_GATEWAY. This allows sensitive session/app metadata and the API key-backed request to be transmitted to an attacker-controlled endpoint if the environment is poisoned, creating a real exfiltration and SSRF-style risk in an agent runtime where environment variables may be influenced by upstream tooling.

Content

Scanner excerpt · scripts/multimodal_recognize_image.py (reported line 80)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:
        body = e.read().decode("utf-8") if e.fp else ""

Description-Behavior Mismatch

Critical
Category
Not specified by scanner
Confidence
99% confidence
Finding

This skill is advertised as image recognition/OCR, but the implementation performs account onboarding, SMS login, API-key issuance, plan discovery, and payment initiation. That mismatch is highly dangerous because it can trick users or higher-level orchestration into invoking credential and billing workflows under the guise of harmless image analysis.

Content

No source excerpt is available for this finding.

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The code allows API base URLs to be overridden via environment variables and then sends sensitive authentication material to those URLs using requests.post. Because the same flow carries SMS-login tokens, refresh tokens, and generated API keys, a malicious or compromised environment can redirect traffic to an attacker-controlled endpoint and exfiltrate credentials.

Content

Scanner excerpt · scripts/onboarding.py (reported line 196)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 244, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The gateway request target is derived from environment-controlled base URLs and then used in urllib.request.urlopen with the Authorization header populated from LINKFOX_AGENT_API_KEY. An attacker who can influence the runtime environment can redirect these requests to capture the API key and observe account, package, and order operations.

Content

Scanner excerpt · scripts/onboarding.py (reported line 246)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

Tainted flow: 'req' from os.environ.get (line 57, 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/upload_image.py (reported line 73)May include surrounding context.

python
)

    try:
        with urlopen(req, timeout=150) as response:
            result = json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""

Tainted flow: 'req' from os.environ.get (line 57, 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/upload_image.py (reported line 106)May include surrounding context.

python
)

    try:
        with urlopen(req, timeout=120) as response:
            if response.status not in (200, 201):
                print(f"Upload failed with status: {response.status}", file=sys.stderr)
                sys.exit(1)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill advertises multimodal image analysis, but also instructs uploading local images to object storage and obtaining public URLs. That changes the data flow from analysis to external publication/storage of user files, which is a materially different and more sensitive behavior than the description suggests.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill advertises multimodal image analysis, but also instructs uploading local images to object storage and obtaining public URLs. That changes the data flow from analysis to external publication/storage of user files, which is a materially different and more sensitive behavior than the description suggests.

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 86)May include surrounding context.

md
- **响应401或402状态码**
- **响应提示算力或余额不足**:消息含"算力余额不足/计费不足/余额不足/quota exceeded/insufficient balance/套餐到期/需充值/请充值",或类似含义的内容。

## Display Rules

1. **Show the analysis result clearly**: Present the returned text analysis in a readable format. Use bullet points or paragraphs as appropriate for the content.
2. **No fabrication**: Only relay information that the API actually returned. Do not add visual details that were not in the response.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The onboarding document directs the image-recognition skill to handle authentication troubleshooting, account registration, API key setup, and billing/payment flows that are outside the declared purpose of image analysis. This scope expansion creates a confused-deputy risk where a user invoking a benign visual-analysis skill may be prompted for credentials, phone numbers, or payment actions unrelated to the skill’s expected function.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documented behavior broadens the skill from image analysis into authentication recovery, account onboarding, and payment operations, which materially changes the trust boundary and expected user interaction. In the context of an image-recognition skill, this is especially dangerous because users are less likely to expect sensitive-data collection or transactional actions, increasing social-engineering and misuse risk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The instructions explicitly allow collecting a user's phone number, sending verification codes, logging in, listing plans, and creating payment orders, none of which are justified by an image-recognition feature. These actions enable unnecessary handling of personal data and commerce operations, expanding abuse impact from misclassification into privacy compromise and unauthorized financial workflows.

Content

No source excerpt is available for this finding.

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

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code includes subscription/package enumeration, order creation, and QR-based payment collection that are unrelated to the stated visual-analysis function. In the context of a multimodal image skill, embedding billing logic increases the risk of deceptive purchases and unauthorized financial actions.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The skill performs SMS authentication, token-based login, team discovery, and API-token generation, none of which are necessary for image recognition or OCR. In this context, the code appears designed to harvest or mint credentials under misleading packaging, making the mismatch especially dangerous.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill declares no explicit tool scope while its instructions clearly require environment-variable access, network calls, and local file writes. That mismatch weakens least-privilege controls and makes it harder for reviewers or runtime policy to constrain what the skill can do, increasing the chance of overbroad execution or abuse.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The trigger conditions are very broad and include implicit activation whenever a user provides an image URL and asks about visual content. Overbroad triggers can cause the skill to run in contexts the user did not clearly intend, leading to unnecessary network access, uploads, cost incurrence, or data disclosure.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs automatic persistence of full API responses into session-scoped project files and may print full results to stdout. Because the response can contain OCR text or sensitive image-derived content, this creates a durable data-exposure path through local storage, logs, and downstream tooling that may not be expected by the user.

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 49)May include surrounding context.

}

text

## curl 示例

```bash
curl -X POST https://tool-gateway.linkfox.com/multimodal/recognizeImage \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The documentation defines a feedback API that encourages sending free-form content including what the user said or intended and what actually happened, but it provides no warning to avoid personal, sensitive, or image-derived data. In this skill context, image analysis results may contain OCR text, personal details, or other sensitive content, so an agent could exfiltrate user data to a separate external endpoint without informed consent or minimization.

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 collection of a user's phone number and use of SMS-based registration/login commands without any warning about sensitive-data handling, retention, or verification of user consent. In a skill that users expect to analyze images, requesting phone numbers and codes is anomalous and increases the likelihood of privacy violations or social-engineering abuse.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The document tells users to persist an API key in shell startup files and environment settings without warning that this stores a long-lived secret on disk and may expose it to other processes, backups, support logs, or local users. This is risky operational guidance, particularly when embedded in an unrelated skill, because users may follow it without understanding the credential-security consequences.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill always stores full API responses and also caches results locally, which can include OCR output, image descriptions, or other sensitive derived content unrelated to the minimum needed runtime behavior. In an agent setting, persistent storage expands the data exposure window and may violate user expectations or least-retention principles, especially since the manifest describes image analysis rather than durable archival.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documentation says writing to /tmp is forbidden and that failure should occur if the current directory is not writable, but the implementation silently falls back to home and temporary directories. This mismatch is security-relevant because operators may rely on the documented no-/tmp behavior when assessing where sensitive recognition outputs can land, causing accidental storage in less controlled locations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code sends the provided JSON parameters to a remote HTTPS endpoint and also includes SESSION_ID, MESSAGE_ID, MODE_ID, and APP_NAME from the environment in request headers. While the module docstring explains output behavior, there is no user-facing disclosure in code near the transmission that input data and environment-derived metadata will be sent over the network.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.