Back to skill

Security audit

生肖查询 - 即刻数据

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it can send its API key to an environment-selected endpoint that is not documented or restricted.

Install only if you are comfortable sending zodiac query terms and a Jike AppKey to Jike's API. Before use, make sure JIKE_API_BASE_URL is unset or locked to https://api.jikeapi.cn, and prefer a scoped or rotatable AppKey because the script places the key in the request URL.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/chinese_zodiac_query.py:22
Finding

AppKey Disclosure Through an Unvalidated API Endpoint Override

Content
View full analysis
dict[str, Any]: """ 功能说明: 1. 调用即刻数据开放接口。 2. 自动追加 `appkey` 参数。 3. 返回接口 JSON;网络异常时返回统一错误结构。 @param params 接口业务参数 @param appkey 即刻数据 AppKey @return dict 接口返回或错误结构 """ url = f"{API_BASE_URL}{API_PATH}?{urllib.parse.urlencode({**params, 'appkey': appkey})}" try: with urllib.request.urlopen(url, timeout=15) as response: return json.loads(response.read().decode("utf-8")) except urllib.error.HTTPError as exc: return {"code": exc.code, "message": f"接口请求失败: HTTP {exc.code}", "data": ""} except urllib.error.URLError as exc: return {"code": 500, "message": f"网络请求失败: {exc.reason}", "data": ""} except Exception as exc: return {"code": 500, "message": f"请求异常: {exc}", "data": ""} ``` ### Technical Analysis The script permits the API base URL to be replaced through the undocumented `JIKE_API_BASE_URL` environment variable. It does not validate the resulting URL's scheme, hostname, port, or destination before appending the AppKey and issuing the request. Consequently, any party able to influence the process environment can redirect the request from the intended `https://api.jikeapi.cn` service to an arbitrary endpoint. The configured AppKey is included in the query string as `appkey`, so the substituted server receives the credential directly. Query-string credentials may additionally be retained in HTTP server logs, reverse-proxy logs, monitoring systems, or diagnostic records. The override also permits non-HTTPS destinations because no scheme restriction is enforced. If a ...[truncated 1603 chars]
Remediation
View remediation
str: parsed = urlparse(value) if parsed.scheme != "https": raise ValueError("The API endpoint must use HTTPS") if parsed.hostname not in ALLOWED_API_HOSTS: raise ValueError("The API endpoint host is not allowed") if parsed.username or parsed.password or parsed.query or parsed.fragment: raise ValueError("The API base URL contains unsupported components") if parsed.port not in (None, 443): raise ValueError("The API endpoint port is not allowed") return value.rstrip("/") ``` 3. Keep production and test endpoint selection in trusted configuration rather than inheriting an unrestricted environment value. If test endpoints are necessary, use an explicit development-only mode and a separate non-production credential. 4. Prefer sending the AppKey in an authorization header rather than in the URL query string if the API provider supports it. This reduces exposure through URL logs and monitoring systems. 5. Ensure diagnostic output, exceptions, proxy logs, and HTTP access logs do not record credentials. Rotate the AppKey if the Skill may previously have run with an untrusted endpoint configuration. 6. Add automated tests confirming that non-HTTPS URLs, unapproved hostnames, embedded credentials, unexpected ports, redirects to unapproved hosts, and malformed URLs are rejected before any credential-bearing request is sent. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (6)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The outbound request target is derived from JIKE_API_BASE_URL, an environment variable, and the URL includes the appkey in the query string. If an attacker or untrusted runtime can influence this environment variable, they can redirect requests to an arbitrary host and capture the credential, creating an SSRF-style exfiltration path. In a skill context, this is more dangerous because skills often run in shared or orchestrated environments where env vars may be configurable outside the script author's control.

Content

Scanner excerpt · scripts/chinese_zodiac_query.py (reported line 141)May include surrounding context.

python
"""
    url = f"{API_BASE_URL}{API_PATH}?{urllib.parse.urlencode({**params, 'appkey': appkey})}"
    try:
        with urllib.request.urlopen(url, timeout=15) as response:
            return json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as exc:
        return {"code": exc.code, "message": f"接口请求失败: HTTP {exc.code}", "data": ""}

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/chinese_zodiac_query.py (reported line 54)May include surrounding context.

python
env_value = os.environ.get(env_name, "").strip()
        if env_value:
            return env_value
    env_file = Path(__file__).parent / ".env"
    if not env_file.exists():
        return ""
    for line in env_file.read_text(encoding="utf-8").splitlines():

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes a Python script, reads API keys from environment variables, and contacts an external API, but it does not declare any explicit tool scope or permissions boundaries. This creates an unnecessary trust gap: an agent runtime may grant broader file, env, or network capabilities than are actually needed, increasing the blast radius if the skill or its script is modified or misused.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
83% confidence
Finding

The skill is designed to send user-supplied zodiac query data and an appkey to a third-party service at api.jikeapi.cn. While this is expected functionality, it is still an external data transmission path that can expose user queries and credentials to a remote provider, so it is a real security/privacy concern rather than a pure false positive.

Content

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

直接调用 API:

text
GET https://api.jikeapi.cn/v1/chinese_zodiac?name=牛&appkey=YOUR_APPKEY

AI 使用步骤

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · _meta.json (reported line 8)May include surrounding context.

json
"title": "生肖查询 - 即刻数据",
  "description": "生肖查询。输入生肖名称,查询五行、本命佛、出生年份、幸运数字、幸运花、性格、事业、爱情和运势。",
  "env": "JIKE_CHINESE_ZODIAC_QUERY_KEY",
  "api_url": "https://api.jikeapi.cn/v1/chinese_zodiac",
  "homepage": "https://www.jikeapi.cn/"
}

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/chinese_zodiac_query.py (reported line 121)May include surrounding context.

python
"""
    params = {}
    for param_name, _ in REQUIRED_PARAMS + OPTIONAL_PARAMS:
        value = getattr(args, param_name, "")
        value = validate_date_param(param_name, str(value))
        if value:
            params[param_name] = value

Static analysis

No suspicious patterns detected.