T09 · Insecure Skill Coding Practices
- Location
skills/linkfox-aigc-textgen/scripts/aigc_textgen.py:74- Finding
Credential-Bearing Requests Can Be Redirected to Arbitrary Endpoints
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill fits its Amazon competitor-analysis purpose, but it bundles credentialed network calls, public file upload, and active HTML report generation with weak scoping that users should review before installing.
Install only if you trust LinkFox and can run it in a controlled environment. Use a least-privilege API key, avoid setting gateway URL environment variables to non-LinkFox hosts, prefer a proper secret store over shell-profile exports, do not allow untrusted prompts to choose upload paths, and treat generated HTML reports as active content before opening or sharing them.
skills/linkfox-aigc-textgen/scripts/aigc_textgen.py:74Credential-Bearing Requests Can Be Redirected to Arbitrary Endpoints
skills/linkfox-aigc-textgen/scripts/aigc_textgen.py:82Unvalidated Session Identifiers Permit Filesystem Path Traversal
skills/linkfox-report-generator/scripts/inject_report.py:157Report Generator Allows Active HTML and JavaScript Injection
skills/linkfox-file-upload/scripts/file_upload.py:31File Upload Skill Can Publish Arbitrary Readable Local Files
skills/linkfox-file-upload/SKILL.md:32Documentation Recommends Unpinned Runtime Dependency Installation
skills/linkfox-aigc-textgen/references/onboarding.md:11Onboarding Instructions Persist API Keys in Plaintext Shell Profiles
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}
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 ""
The code builds request destinations from environment-controlled base URLs and then sends sensitive authentication material, including SMS-login tokens, API keys, and user identifiers, to those endpoints via requests.post. If an attacker can influence environment variables in the skill runtime, they can redirect these calls to attacker-controlled infrastructure and exfiltrate credentials or session tokens.
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 = ""
The gateway helper constructs outbound urllib requests using a base URL sourced from environment variables and attaches the API key in the Authorization header. A compromised or manipulated runtime environment could redirect billing, account, or package-management traffic to an attacker endpoint, exposing secrets and enabling fraudulent account actions.
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
download_media() accepts an arbitrary caller-supplied URL and fetches it with urlopen, only checking for the http/https scheme. This creates an SSRF-style primitive that can be abused to make the agent reach internal services, cloud metadata endpoints, or other unintended network locations, then persist the response locally. In a shared utility used by multiple skills, the risk is amplified because unrelated workflows may inherit this network access path.
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", "")
The script derives base URLs from environment variables and sends sensitive authentication material, including SMS login data and bearer tokens, to those endpoints via requests.post. If an attacker can influence the runtime environment, they can redirect these calls to attacker-controlled infrastructure and exfiltrate credentials, API keys, or payment/session data. The skill context increases risk because this file handles account onboarding, token minting, and login flows rather than mere public ASIN analysis.
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 = ""
The gateway URL is also sourced from environment variables and used to build urllib requests that include the LinkFox API key in the Authorization header. A malicious environment configuration can therefore redirect authenticated requests to an attacker endpoint and leak the API key, account metadata, order identifiers, and related billing information. This is especially dangerous because the script supports plan purchase and order creation, so redirected requests affect both secrets and payment workflows.
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
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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 ""
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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 ""
The code builds destination URLs from environment variables such as LINKFOX_LOGIN_API_URL and LINKFOX_AGENT_USER_API_URL, then sends login credentials, SMS codes, access tokens, refresh tokens, and API-key related requests to those endpoints via requests.post. If an attacker can influence the runtime environment, they can redirect sensitive authentication traffic to an attacker-controlled server, causing credential/token exfiltration and account compromise.
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 = ""
The gateway URL is derived from environment variables and then used in urllib.request.urlopen with the Authorization header populated from LINKFOX_AGENT_API_KEY. An attacker who controls the environment can redirect these requests to an arbitrary host and capture the API key and account/order data, enabling unauthorized API use and possible billing abuse.
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
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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:
download_media fetches arbitrary attacker-controlled http/https URLs and writes the response into the session workspace. That creates an SSRF primitive against internal services or cloud metadata endpoints, and can also be abused to make the agent download untrusted large content into local storage.
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", "")
The script builds destination URLs from environment variables and then sends sensitive authentication material to those endpoints via requests.post. In this file, those requests carry SMS-login data, access tokens, refresh tokens, and headers used for token issuance, so a malicious or misconfigured environment can redirect secrets to an attacker-controlled host.
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 = ""
The gateway URL is also derived from environment variables and used in urlopen with the Authorization header populated from the user's API key. This creates an exfiltration path where anyone controlling the runtime environment can force the tool to send API credentials and account/order requests to an attacker-controlled server.
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
The script lets environment-controlled values influence the outbound request destination and transmitted metadata. LINKFOX_TOOL_GATEWAY can redirect requests to an attacker-controlled host, while SESSION_ID, MESSAGE_ID, MODE_ID, and APP_NAME are sent as headers, enabling credential and task-metadata exfiltration if the environment is tampered with. In an agent skill context, environment variables are a trust boundary and should not be treated as inherently safe for network sinks.
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 ""
The POST target is derived from environment-controlled base URLs, and the function sends highly sensitive data including SMS login credentials, access tokens, refresh tokens, and generated API tokens to those endpoints. If an attacker can influence environment variables in the skill runtime, they can redirect authentication traffic to an attacker-controlled server and exfiltrate credentials or session material.
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 = ""
The gateway request URL is also built from environment-controlled base URLs and automatically includes the API key in the Authorization header. An attacker who can set the environment can redirect requests to a hostile endpoint and capture the user's API key, order details, and account metadata.
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
The request sends multiple environment-derived values, including an API key and session/message metadata, to a network endpoint whose base URL can also be overridden by the LINKFOX_TOOL_GATEWAY environment variable. If an attacker can influence that environment variable, the script can be redirected to an attacker-controlled server and leak credentials and potentially sensitive request data. In an agent/automation context, trusting environment-configured destinations without validation materially increases SSRF-style redirection and secret exfiltration risk.
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 ""
The script builds request destinations from environment-controlled base URLs and then sends sensitive data such as phone numbers, SMS codes, access tokens, refresh tokens, API keys, and payment/order data to those endpoints. If an attacker can influence the environment, they can redirect traffic to attacker-controlled hosts and exfiltrate credentials or session material; because this file is explicitly an onboarding/login flow, the skill context makes the sink far more sensitive than an ordinary HTTP client.
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 = ""
The gateway client uses environment-derived base URLs and transmits the LinkFox API key in the Authorization header via urlopen. An attacker who can set LINKFOX_AGENT_API_URL or related variables can redirect billing, account, and order operations to an attacker-controlled endpoint and capture API credentials or manipulate responses; this is especially dangerous because the same file also supports ordering and payment-related actions.
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
The script builds a network request using environment-controlled values, including the base URL via LINKFOX_TOOL_GATEWAY and multiple metadata headers, then sends the request with urlopen. If an attacker can influence the environment, they can redirect requests to an arbitrary host and exfiltrate the API key, request parameters, and session metadata; in this agent-skill context, environment variables are a meaningful trust boundary and should not be treated as inherently safe.
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 ""
The skill appears to behave more like a single-interface data fetch/cache tool than the claimed four-step, multi-source deep-analysis system. This is a genuine trust and governance issue because security review, user expectations, and permissioning depend on accurate capability disclosure.
The skill appears to behave more like a single-interface data fetch/cache tool than the claimed four-step, multi-source deep-analysis system. This is a genuine trust and governance issue because security review, user expectations, and permissioning depend on accurate capability disclosure.