Back to skill

Security audit

题庄中小学真题题库

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its question-bank purpose, but it can send local API keys or account tokens to an unrestricted service URL configured by the environment.

Review this skill before installing if you will configure QUESTION_SERVICE_LICENSE or QUESTION_SERVICE_ACCOUNT_TOKEN. Only use the default trusted service URL or a clearly trusted HTTPS endpoint, and avoid setting QUESTION_SERVICE_URL in shared or untrusted environments. Treat public share links as visible to anyone with the URL, and use answer-including or no-watermark options only when you intentionally want that disclosure.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/question_bank.py:16
Finding

Environment-Controlled API Base URL Can Exfiltrate Credentials

Content
View full analysis

Vulnerability Details

File Location: scripts/question_bank.py:16-18, scripts/question_bank.py:55-77, scripts/question_bank.py:145-151, scripts/question_bank.py:171-181
Vulnerability Type: Unvalidated credential destination
Risk Level: High

Vulnerable Code

python
BASE_URL = os.getenv(
    "QUESTION_SERVICE_URL", "https://tizhuang.qcscience.cc/api"
).rstrip("/")
python
def _http_json(
    path: str,
    params: dict | None = None,
    *,
    headers: dict[str, str] | None = None,
    method: str = "GET",
    json_body: dict | None = None,
):
    query = urllib.parse.urlencode(
        {key: value for key, value in (params or {}).items() if value is not None},
        doseq=True,
    )
    url = f"{BASE_URL}{path}" + (f"?{query}" if query else "")
    request_headers = dict(headers or {})
    body = None
    if json_body is not None:
        body = json.dumps(json_body, ensure_ascii=False).encode("utf-8")
        request_headers["Content-Type"] = "application/json"
    api_request = urllib.request.Request(
        url, data=body, headers=request_headers, method=method
    )
    try:
        with urllib.request.urlopen(api_request, timeout=30) as response:
            return json.load(response)
python
license_key = os.getenv("QUESTION_SERVICE_LICENSE")
if license_key:
    return _http_json(
        registered_path,
        params,
        headers={"X-API-Key": license_key},
        method=method,
        json_body=json_body,
    )
python
token = os.getenv("QUESTION_SERVICE_ACCOUNT_TOKEN")
if not token:
    raise SystemExit(
        "This command needs a signed-in website account. Open the account URL "
        "from the onboarding command, then configure QUESTION_SERVICE_ACCOUNT_TOKEN "
        "locally. Never paste the token into chat."
    )
return _http_json(
    path,
    headers={"Authorization
...[truncated 2401 chars]
Remediation
View remediation

Remediation Suggestions

  1. Allow credentials to be sent only to the declared production origin, such as https://tizhuang.qcscience.cc/api.
  2. Parse the configured URL and reject:
    • Schemes other than HTTPS.
    • User-information components.
    • Unexpected hostnames or ports.
    • Malformed or ambiguous hostnames.
  3. If custom service endpoints are required for development, place them behind an explicit development-only option and maintain an administrator-controlled hostname allowlist.
  4. Do not forward production API keys or account bearer tokens to development or custom origins.
  5. Bind each credential to its expected origin and fail closed if the request destination differs.
  6. Consider separating public, trial, licensed, and account clients so that account credentials cannot accidentally be attached to unrelated destinations.
  7. Add automated tests confirming that HTTP URLs, unapproved hosts, URL user information, and unexpected ports are rejected before any request is sent.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/question_bank.py:115
Finding

Trial Credentials Are Cached Without Explicit Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/question_bank.py:115-128
Vulnerability Type: Insecure plaintext credential storage
Risk Level: Low

Vulnerable Code

python
CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
CACHE_FILE.write_text(
    json.dumps(
        {
            "base_url": BASE_URL,
            "trial_token": trial["trial_token"],
            "expires_at": trial["expires_at"],
            "anonymous_client_id": client_id,
        },
        ensure_ascii=False,
        indent=2,
    ),
    encoding="utf-8",
)

Technical Analysis

The script stores an active trial token and stable anonymous client identifier in plaintext. The directory and file are created without explicitly requesting owner-only permissions, so their effective access permissions depend on the process umask and platform defaults.

On a permissively configured multi-user system, another local user may be able to read the cache. The file is also written directly rather than through an atomic, securely permissioned temporary file, which complicates safe permission enforcement.

Local caching is necessary to preserve the intended 24-hour trial identity and prevent quota evasion. The weakness is not the cache itself, but the absence of explicit access controls around credential-bearing data.

Attack Path

  1. The Skill creates ~/.question-bank/trial.json while running under a permissive umask or equivalent platform configuration.
  2. Another local account obtains read access to the user's home subdirectory or cache file.
  3. The attacker reads trial_token and anonymous_client_id from the JSON document.
  4. The attacker reuses the active trial token or client identifier when communicating with the question service.
  5. The victim's temporary quota may be consumed or their anonymous activity may be correlated.

Impact Assessment

Exploitation is limited to attackers who already ha ...[truncated 465 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the cache directory with owner-only permissions, such as 0700 on POSIX systems.
  2. Create the credential file with mode 0600 rather than relying on the current umask.
  3. Write updates atomically:
    • Create a temporary file in the same directory using secure exclusive creation.
    • Set restrictive permissions before writing sensitive data.
    • Flush and replace the destination atomically.
  4. On startup, inspect existing file permissions and reject or repair files accessible by other users.
  5. Avoid following symbolic links when creating or replacing the cache file.
  6. Use an operating-system credential store where available, while retaining a secure file-based fallback for portability.
  7. Delete expired trial tokens when they are no longer required.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (13)

Tainted flow: 'api_request' from os.getenv (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 · scripts/question_bank.py (reported line 77)May include surrounding context.

python
url, data=body, headers=request_headers, method=method
    )
    try:
        with urllib.request.urlopen(api_request, timeout=30) as response:
            return json.load(response)
    except urllib.error.HTTPError as error:
        try:

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · references/api.md (reported line 116)May include surrounding context.

md
With `include_answers: true`, the snapshot may expose only the stored standard
  `answer`/`answer_html` values, recursively including compound subquestions;
  analyses, explanations, and solution fields remain private.
- `DELETE /v1/account/papers/{paper_id}/shares/{share_id}`: revoke a link.
- `POST /v1/account/papers/{paper_id}/shares/{share_id}/rotate`: invalidate the
  old token and create a recoverable token for the exact same immutable
  snapshot and the same answer/watermark settings. Rotation does not copy later

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill instructs the agent to use environment variables, local file access, and networked Python scripts, but it does not declare any explicit tool scope or permission boundaries. That increases the risk of over-privileged execution, accidental credential exposure, and misuse of local or network capabilities beyond what users would reasonably expect from a question-bank skill.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The description contains very broad trigger terms such as answers, explanations, exercises, tests, and papers, which are common in ordinary educational requests. This can cause the skill to activate unexpectedly and route unrelated user queries into a workflow that uses tools, external services, and cached credentials without sufficiently specific user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest contains a natural-language instruction in English that directs behavior as 'Use $question-bank...' while the skill metadata is otherwise Chinese. This creates a language-policy concern because the skill appears to enforce English behavior without offering the user a language choice or documenting a justified locale constraint.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/api.md (reported line 102)May include surrounding context.

md
or account mutations.

Persistent paper routes require an account session bearer token, not the Agent
License. Store it only in `QUESTION_SERVICE_ACCOUNT_TOKEN`; never ask the user
to paste it into chat or include it in output. `GET /v1/account/papers` lists
the account's papers. Share lifecycle routes are:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The code persists the trial token and anonymous client ID to disk in a predictable per-user location without enforcing restrictive file permissions. On multi-user systems or environments with permissive umasks, other local users or processes may be able to read the token and reuse the trial identity, exposing account-like state and enabling quota theft or session misuse.

Content

No source excerpt is available for this finding.

Tainted flow: 'CACHE_FILE' from os.getenv (line 19, credential/environment) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/question_bank.py (reported line 117)May include surrounding context.

python
headers={"X-Anonymous-Client-ID": client_id},
    )
    CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
    CACHE_FILE.write_text(
        json.dumps(
            {
                "base_url": BASE_URL,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The instructions require the agent to say the exact Chinese phrase “欢迎入庄” after registration, with no indication that the user may choose another language. This is a natural-language locale policy issue because it forces a specific language output rather than offering a language choice or documenting a justified region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file instructs the agent to ask an exact question in Chinese: “想怎么做?1. 在聊天里答 2. 打开练习页 3. 到网站组卷”. Because this applies as a fixed output template with no opt-in or alternative locale handling, it forces a specific language regardless of user preference.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The instruction mandates saying “欢迎入庄” as part of post-registration copy without offering a language fallback or user preference check. That is a natural-language policy concern because it imposes a single locale-specific phrase rather than adapting to the user's language.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The API reference embeds a specific Chinese phrase as the presented user experience text, which indicates a fixed-language interaction rather than offering locale choice. Because this is general API documentation rather than a clearly region-scoped tool, the language constraint appears to violate the policy against forcing a specific language without opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

This line states that registration reveals a fixed Chinese message, implying the skill or product presents a specific language to users by default. The file does not indicate that the message is localized, optional, or limited to a justified region-specific workflow.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.