Back to skill

Security audit

投机选品专家

Security checks for vulnerabilities and agentic risk

Overview

The product-scouting purpose is coherent, but the package includes under-scoped credential, scheduling, arbitrary-script, and persistent agent-instruction capabilities that should be reviewed before use.

Install only if you trust the LinkFox publisher and runtime environment. Review or remove the CLAUDE.md patch script and arbitrary script runner, avoid environment endpoint overrides outside controlled development, treat stdout/logs as sensitive during onboarding, and confirm scheduled tasks and public file uploads before allowing them to run.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T02 · Agent Memory Poisoning

Error
Location
skills/amazon-asin-dynamic-scoring/scripts/patch_scoring_to_agent.py:143
Finding

Undisclosed persistent modification of Agent instruction files

Content
View full analysis
bool: """Patch a CLAUDE.md file to add scoring module. Returns True if changes made.""" path = Path(filepath) if not path.exists(): print(f"ERROR: File not found: {filepath}") return False content = path.read_text(encoding="utf-8") # Check if already has the full scoring step (not just the reference line) if "二次评分(动态评分引擎)" in content: print(f"SKIP: {filepath} already has scoring step") return False # Check if it uses amazon-product-scout-agent if "amazon-product-scout-agent" not in content: print(f"SKIP: {filepath} does not use amazon-product-scout-agent") return False original = content changes = [] # 1. Add skill reference line after amazon-product-scout-agent line pattern_ref = r"(主脚本:call skill `amazon-product-scout-agent`[^\n]*)" if re.search(pattern_ref, content) and SKILL_REF_LINE not in content: content = re.sub( pattern_ref, r"\1\n" + SKILL_REF_LINE, content ) changes.append("skill 引用行") # 2. Add --json-output to product_scout_agent.py command if "--json-output" not in content: content = re.sub( r"(python3 /scripts/product_scout_agent\.py[^`]*?--sort-desc\s+\S+)\s*\n```", r"\1 \\\n --json-output\n```", content ) changes.append("--json-output 标志") # 3. Add "评分" trigger to intent recognition trigger_line = '- 用户说"评分""打分""二次筛选""精排" → 进入二次评分流程' if trigger_line not in content: intent_section = re.search( r"(## Step 1[^#]*?)(\n##\s)", content, re.DOTALL ) if in ...[truncated 4076 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
skills/amazon-product-scout-agent/scripts/response_io.py:59
Finding

Arbitrary Python script execution with inherited credentials

Content
View full analysis
Path: p = Path(script_arg).expanduser() if not p.is_absolute(): # Resolve relative to the current working directory the agent invoked from. p = (Path.cwd() / p).resolve() else: p = p.resolve() if not p.is_file(): _err(f"--script path not found: {p}") return p ``` ```python def cmd_run(args: argparse.Namespace) -> int: main_script = _resolve_script(args.script) skill_name = _resolve_skill_name(main_script) params_str = _resolve_params(args) out_dir = Path(args.out_dir).expanduser().resolve() try: out_dir.mkdir(parents=True, exist_ok=True) except OSError as e: _err(f"Failed to create --out-dir {out_dir}: {e}") if not os.access(out_dir, os.W_OK): _err(f"--out-dir is not writable: {out_dir}") timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") rand = secrets.token_hex(3) safe_label = _sanitize_label(args.label) if args.label else "" label_part = f"__{safe_label}" if safe_label else "" out_file = out_dir / f"{skill_name}__{timestamp}_{rand}{label_part}.json" # Force the child process to emit UTF-8 regardless of the host console # encoding. child_env = os.environ.copy() child_env["PYTHONIOENCODING"] = "utf-8" timed_out = False try: proc = subprocess.run( [sys.executable, str(main_script), params_str], capture_output=True, text=True, encoding="utf-8", errors="replace", env=child_env, timeout=args ...[truncated 3306 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
skills/linkfox-aigc-textgen/scripts/onboarding.py:69
Finding

Credential-bearing requests accept unrestricted environment-controlled destinations

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 {"_er ...[truncated 5408 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
skills/linkfox-aigc-textgen/scripts/onboarding.py:470
Finding

Generated API key is emitted in plaintext to standard output

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), } ``` ```python def _emit(obj: dict) -> None: print(json.dumps(obj, ensure_ascii=False, indent=2)) def _cmd_login(args) -> int: r = login_and_get_key(args.phone.strip(), args.code.strip(), args.channel) _emit(r) if "api_key" in r: print(f"{TAG} 成功获取 API key(来源: {r['source']})", file=sys.stderr) return 0 return 1 ``` ### Technical Analysis After completing SMS authentication and generating or retrieving an API key, `login_and_get_key()` places the com ...[truncated 1535 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
skills/amazon-product-scout-agent/scripts/linkfox_paths.py:230
Finding

OSS upload destination is accepted without hostname or TLS validation

Content
View full analysis
tuple[str, str]: """Normalize OSS endpoint, supporting values with or without a scheme.""" ep = (endpoint or "").strip().rstrip("/") if ep.startswith("https://"): return ep, ep[len("https://"):] if ep.startswith("http://"): return ep, ep[len("http://"):] return f"https://{ep}", ep ``` ```python def upload_file( local_path: str, *, slug: Optional[str] = None, ts: Optional[float] = None, voucher: Optional[dict] = None, ) -> dict: """Upload a local file to Alibaba Cloud OSS and register it in session metadata.""" try: import oss2 except ImportError: raise RuntimeError("Missing oss2 dependency") import uuid as _uuid if ts is None: ts = time.time() if slug is None: slug = os.path.splitext(os.path.basename(local_path))[0] ext = os.path.splitext(local_path)[1].lstrip(".").lower() if not ext: ext = "bin" if voucher is None: voucher = get_sts_voucher() file_size = os.path.getsize(local_path) max_size = voucher.get("maxFileSize") if isinstance(max_size, int) and max_size > 0 and file_size > max_size: raise RuntimeError( f"File size {file_size} exceeds OSS limit {max_size}: {local_path}" ) supported = voucher.get("supportedTypes") if isinstance(supported, str) and supported.strip(): allowed = {t.strip().lower() for t in supported.split(",") if t.strip()} if allowed and ext not in allowed: raise RuntimeErr ...[truncated 2509 chars]
Remediation
View remediation
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (136)

Tainted flow: 'req' from os.environ.get (line 274, 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 · skills/amazon-product-scout-agent/scripts/linkfox_paths.py (reported line 504)May include surrounding context.

python
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
    try:
        with urlopen(req, timeout=timeout) as resp:
            # 从 Content-Type 进一步修正扩展名
            if guessed_ext == "bin":
                ct = resp.headers.get("Content-Type", "")

Tainted flow: 'req' from os.environ.get (line 335, 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 · skills/linkfox-aigc-textgen/scripts/aigc_textgen.py (reported line 237)May include surrounding context.

python
req = _lf_Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
    try:
        with _lf_urlopen(req, timeout=timeout) as resp:
            if guessed_ext == "bin":
                ct = resp.headers.get("Content-Type", "")
                if "mp4" in ct:

Tainted flow: 'req' from os.environ.get (line 335, 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 · skills/linkfox-aigc-textgen/scripts/aigc_textgen.py (reported line 337)May include surrounding context.

python
}
    req = Request(url, data=data, headers=headers, method="POST")
    try:
        with urlopen(req, timeout=HTTP_TIMEOUT) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""

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

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The code builds request URLs from environment-controlled base endpoints and then sends sensitive data to them via requests.post. Because this script handles phone numbers, SMS codes, access tokens, refresh tokens, and generated API keys, an attacker who can influence environment variables can redirect these secrets to an attacker-controlled server, causing credential exfiltration and account compromise.

Content

Scanner excerpt · skills/linkfox-aigc-textgen/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
96% confidence
Finding

The gateway URL is also derived from environment variables and used in urlopen with the Authorization header carrying the API key. If an attacker can control the environment, requests can be redirected to a malicious endpoint and the API key, order actions, and account metadata can be exposed or abused.

Content

Scanner excerpt · skills/linkfox-aigc-textgen/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 274, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

download_media() accepts an arbitrary url parameter and performs a server-side HTTP(S) fetch with urlopen, then writes the response into local session storage. Even though non-HTTP schemes are blocked, this still enables SSRF-style access to internal services, metadata endpoints, or other network-reachable resources if an attacker can influence the URL supplied to the skill.

Content

Scanner excerpt · skills/linkfox-file-upload/scripts/linkfox_paths.py (reported line 524)May include surrounding context.

python
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
    try:
        with urlopen(req, timeout=timeout) as resp:
            # 从 Content-Type 进一步修正扩展名
            if guessed_ext == "bin":
                ct = resp.headers.get("Content-Type", "")

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

download_media() performs arbitrary outbound HTTP(S) requests to a caller-supplied URL with no allowlist, host validation, or private-network blocking. This creates a server-side request forgery surface that can be used to reach internal services, cloud metadata endpoints, or other otherwise inaccessible network targets, and the downloaded response is then stored locally and registered in session metadata.

Content

Scanner excerpt · skills/linkfox-report-generator/scripts/linkfox_paths.py (reported line 524)May include surrounding context.

python
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
    try:
        with urlopen(req, timeout=timeout) as resp:
            # 从 Content-Type 进一步修正扩展名
            if guessed_ext == "bin":
                ct = resp.headers.get("Content-Type", "")

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The POST destination is derived from environment-controlled base URLs, and the request can carry sensitive material including SMS login data, access tokens, refresh tokens, uid/tid headers, and API-token generation flows. If an attacker can influence environment variables, they can redirect these requests to attacker-controlled infrastructure and exfiltrate credentials or session secrets.

Content

Scanner excerpt · skills/linkfox-sellersprite-product-search/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
96% confidence
Finding

The gateway URL is also environment-derived and used for authenticated urllib requests with the LINKFOX_AGENT_API_KEY in the Authorization header. An attacker who can set the environment can redirect package, account, and order operations to a malicious server and capture API keys or manipulate responses to influence payments and account state.

Content

Scanner excerpt · skills/linkfox-sellersprite-product-search/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 73, 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 · skills/linkfox-sellersprite-product-search/scripts/sellersprite_product_search.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 ""

Tainted flow: 'req' from os.environ.get (line 274, 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 · skills/amazon-product-scout-agent/scripts/linkfox_paths.py (reported line 281)May include surrounding context.

python
headers={"Content-Type": "application/json", "Authorization": api_token},
        )
        try:
            with urlopen(req, timeout=30) as resp:
                body = json.loads(resp.read().decode())
            break
        except urllib.error.HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 274, 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 · skills/linkfox-file-upload/scripts/linkfox_paths.py (reported line 281)May include surrounding context.

python
headers={"Content-Type": "application/json", "Authorization": api_token},
        )
        try:
            with urlopen(req, timeout=30) as resp:
                body = json.loads(resp.read().decode())
            break
        except urllib.error.HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 274, 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 · skills/linkfox-report-generator/scripts/linkfox_paths.py (reported line 281)May include surrounding context.

python
headers={"Content-Type": "application/json", "Authorization": api_token},
        )
        try:
            with urlopen(req, timeout=30) as resp:
                body = json.loads(resp.read().decode())
            break
        except urllib.error.HTTPError as e:

Tainted flow: 'req' from os.environ.get (line 274, 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 · skills/linkfox-task-scheduler/scripts/linkfox_paths.py (reported line 281)May include surrounding context.

python
headers={"Content-Type": "application/json", "Authorization": api_token},
        )
        try:
            with urlopen(req, timeout=30) as resp:
                body = json.loads(resp.read().decode())
            break
        except urllib.error.HTTPError as e:

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

download_media fetches an attacker-controlled URL over the network with only a scheme check and no host allowlist, private-address blocking, or redirect validation. In an agent/runtime context this can be abused for SSRF to reach internal services or retrieve sensitive resources, and the downloaded content is then persisted into the session workspace.

Content

Scanner excerpt · skills/linkfox-task-scheduler/scripts/linkfox_paths.py (reported line 524)May include surrounding context.

python
req = Request(url, headers={"User-Agent": "LinkFox-Skill/2.0"})
    try:
        with urlopen(req, timeout=timeout) as resp:
            # 从 Content-Type 进一步修正扩展名
            if guessed_ext == "bin":
                ct = resp.headers.get("Content-Type", "")

Tainted flow: 'req' from os.environ.get (line 249, 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 · skills/linkfox-task-scheduler/scripts/task_scheduler.py (reported line 264)May include surrounding context.

python
method="POST",
    )
    try:
        with urlopen(req, timeout=150) as response:
            body = response.read().decode("utf-8")
            if not body.strip():
                # delete 等接口可能无返回体

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

Scheduled task creation/update/deletion via an external API, reminder creation, use of authorization keys, local writeback of API responses, and task-context detection add automation and persistence capabilities beyond a simple scouting assistant. In context, this can be abused to establish recurring execution or silent background actions without clear disclosure.

Content

No source excerpt is available for this finding.