Back to skill

Security audit

worldcup-analyzer

Security checks for vulnerabilities and agentic risk

Overview

This football prediction skill is mostly coherent, but it needs review because an API base URL override can cause API keys to be sent to an arbitrary host.

Install only if you trust the publisher and the jiajielitong.com service. Do not set WORLDCUP_API_BASE to an untrusted host, and avoid using a permanent SOCCER_API_KEY with custom staging or local endpoints unless the client is hardened to validate the destination. Treat outputs as statistical references, not betting advice.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wc_client.py:93
Finding

API Key Disclosure Through an Unrestricted API Base URL Override

Content
View full analysis

Vulnerability Details

File Location: scripts/wc_client.py:93-103 and scripts/wc_client.py:213-245
Vulnerability Type: Arbitrary-origin transmission of authentication credentials
Risk Level: High

Vulnerable Code

python
def _api_key() -> str:
    key = os.environ.get("SOCCER_API_KEY")
    return key or request_agent_temp_key()["api_key"]


def _permanent_api_key_configured() -> bool:
    return bool(os.environ.get("SOCCER_API_KEY"))


def _base_url() -> str:
    return os.environ.get("WORLDCUP_API_BASE", DEFAULT_BASE_URL).rstrip("/")
python
def _request(
    method: str,
    path: str,
    payload: Optional[dict] = None,
    *,
    require_auth: bool = True,
    api_key: Optional[str] = None,
) -> Any:
    """Issue an HTTP request and map errors to WorldCupAPIError.

    Shared by GET (list_teams) and POST (predict_match). The application
    response envelope is identical across both endpoints.
    """
    url = f"{_base_url()}{path}"
    headers = {"Accept": "application/json"}
    if require_auth:
        headers["X-API-Key"] = api_key or _api_key()
    if method == "POST":
        headers["Content-Type"] = "application/json"

    try:
        if _BACKEND == "httpx":
            if method == "POST":
                resp = httpx.post(url, json=payload, headers=headers, timeout=DEFAULT_TIMEOUT)
            else:
                resp = httpx.get(url, params=payload, headers=headers, timeout=DEFAULT_TIMEOUT)
        else:  # requests fallback
            if method == "POST":
                resp = requests.post(url, json=payload, headers=headers, timeout=DEFAULT_TIMEOUT)
            else:
                resp = requests.get(url, params=payload, headers=headers, timeout=DEFAULT_TIMEOUT)

Technical Analysis

The client reads WORLDCUP_API_BASE directly from the environment and uses it to construct every API URL without validating the destination scheme, hostname, port, or trust level. For authenticated endpoints, ...[truncated 2061 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse the base URL with urllib.parse.urlparse and reject malformed URLs, embedded credentials, fragments, and unexpected schemes.
  2. Require HTTPS for all non-loopback destinations.
  3. Allowlist www.jiajielitong.com for normal production operation.
  4. Place staging and local endpoint overrides behind an explicit development-mode setting that is disabled by default.
  5. Do not attach SOCCER_API_KEY when the configured origin differs from the approved production origin.
  6. Use a separate, explicitly configured development credential for staging endpoints.
  7. If localhost support is required, restrict it to loopback addresses and refuse to forward production credentials.
  8. Consider refusing redirects or validating every redirect destination before retaining the authentication header.
  9. Add tests confirming that credentials are not sent to HTTP endpoints, arbitrary hosts, redirect targets, or custom ports.
  10. Document the trust boundary and warn operators that endpoint overrides must never receive production credentials.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unbounded Third-Party Dependency Versions

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-6
Vulnerability Type: Unpinned dependency and supply-chain exposure
Risk Level: Low

Vulnerable Code

text
# Either backend works; the client prefers httpx and falls back to requests
# if httpx isn't importable.
httpx>=0.24
# requests is optional but kept as a hard fallback for environments without
# httpx. Comment out if you want to enforce httpx-only.
requests>=2.28

Technical Analysis

Both dependencies specify only minimum versions. There are no exact pins, upper bounds, lock file entries, or package hashes. A future installation can therefore resolve to any newer release available from the configured package index.

This does not establish that either named package is currently malicious. The risk is that builds are not reproducible and may automatically consume an unsafe, compromised, or incompatible future release without a deliberate review. The exposure also depends on the package index and installation environment.

The project installs both HTTP libraries even though the runtime prefers httpx and treats requests as a fallback. Making both hard dependencies increases the supply-chain surface beyond what is necessary when httpx is available.

Attack Path

  1. A future dependency release is compromised, malicious, or contains a security regression, or an installation environment uses an untrusted package index.
  2. An operator executes pip install -r requirements.txt.
  3. The unconstrained requirement resolves to the affected newer package version.
  4. Package installation or subsequent import executes code from that release.
  5. The compromised dependency runs with the privileges of the user or service account performing installation or running the Skill.

Impact Assessment

The maximum impact is bounded by the privileges of the installation or runtime account. A compromised dependency could access process environment variables, including `SO ...[truncated 326 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin reviewed dependency versions exactly in a lock file or generated requirements file.
  2. Generate and verify package hashes, such as with pip-compile --generate-hashes.
  3. Install with hash enforcement where practical.
  4. Review dependency updates through a controlled pull-request and testing process.
  5. Use a trusted package index and restrict unexpected extra indexes.
  6. Run vulnerability scanning against the resolved dependency set.
  7. Consider making requests an optional development or compatibility extra rather than installing both HTTP clients by default.
  8. Periodically update pins so security fixes are adopted intentionally rather than allowing unrestricted automatic upgrades.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (25)

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
85% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · evals/evals.json (reported line 62)May include surrounding context.

json
"Refuse politely and briefly, citing that this skill is for statistical analysis only and not betting advice.",
        "Offer to share the model's projected outcome and expected goal difference for any specific matchup the user names.",
        "Do not list bookmaker odds, do not rank 'best picks', do not suggest stake sizes.",
        "Do not lecture with legal text — short friendly redirect per references/compliance.md."
      ],
      "must_include": ["statistical"],
      "must_not_include": ["recommended bet", "best bet", "sure win", "lock", "tips", "pick of the day", "stake", "wager", "odds"]

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
85% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · scripts/wc_client.py (reported line 838)May include surrounding context.

python
in that case we never warn. Returns None if no warning is needed.

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
80% confidence
Finding

Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Content

Scanner excerpt · scripts/wc_client.py (reported line 847)May include surrounding context.

python
if not isinstance(used, (int, float)) or not isinstance(limit, (int, float)):
        return None
    if limit == -1 or limit == 0:
        return None  # unlimited or unknown — no warning
    if used >= limit:
        if _is_zh(language):
            return (

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README says the client will automatically request a temporary API key from a remote third-party service, but it does not clearly warn users that invoking the skill may trigger network requests and transmit query data to an external domain. In an agent-skill context, hidden or insufficiently disclosed outbound requests can undermine informed consent, surprise users in restricted environments, and expose match queries or metadata to the remote provider.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The helper descriptions show first_use_message(language="zh") and quota_warning(data, language="zh"), which suggests the skill defaults important user-facing messages to Chinese. That is a language/locale policy concern because the file does not indicate that users are asked to opt in to that locale before those messages are shown.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill appears to require network access, environment-variable access, and file reads, but it does not explicitly declare a restrictive tool scope such as permissions or allowed-tools. That creates an authorization gap where the runtime may grant broader capabilities than reviewers or operators expect, increasing the risk of unintended outbound requests, access to secrets like SOCCER_API_KEY, or misuse of local files by future code changes.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The refusal template nominally disclaims betting advice, but immediately encourages the user to provide a matchup and 'interpret the numbers however you like.' That wording weakens the refusal boundary and can be reasonably understood as facilitating wagering decisions despite the stated restriction. In this skill context, which is explicitly about match outcome prediction, the mixed message is more dangerous because users seeking betting guidance can easily treat the output as de facto betting advice.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/api.md (reported line 69)May include surrounding context.

Example:

bash
curl -X POST 'https://www.jiajielitong.com/matches/epl/simulate/' \
  -H 'X-API-Key: your_key' \
  -H 'Content-Type: application/json' \
  -d '{

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/api.md (reported line 108)May include surrounding context.

Example:

bash
curl -X POST 'https://www.jiajielitong.com/matches/simulate/' \
  -H 'X-API-Key: your_key' \
  -H 'Content-Type: application/json' \
  -d '{

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/wc_client.py (reported line 142)May include surrounding context.

python
if isinstance(cached, dict) and cached.get("api_key"):
        return cached

    data = _request("POST", AGENT_TEMP_KEY_PATH, require_auth=False)
    payload = data.get("data") if isinstance(data.get("data"), dict) else data
    if not isinstance(payload, dict):
        raise WorldCupAPIError(f"Malformed temp-key response: {data!r}")

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/wc_client.py (reported line 441)May include surrounding context.

python
if isinstance(cached, dict) and cached.get("api_key"):
        return cached

    data = _request("POST", AGENT_TEMP_KEY_PATH, require_auth=False)
    payload = data.get("data") if isinstance(data.get("data"), dict) else data
    if not isinstance(payload, dict):
        raise WorldCupAPIError(f"Malformed temp-key response: {data!r}")

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/wc_client.py (reported line 142)May include surrounding context.

python
if isinstance(cached, dict) and cached.get("api_key"):
        return cached

    data = _request("POST", AGENT_TEMP_KEY_PATH, require_auth=False)
    payload = data.get("data") if isinstance(data.get("data"), dict) else data
    if not isinstance(payload, dict):
        raise WorldCupAPIError(f"Malformed temp-key response: {data!r}")

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/wc_client.py (reported line 441)May include surrounding context.

python
if isinstance(cached, dict) and cached.get("api_key"):
        return cached

    data = _request("POST", AGENT_TEMP_KEY_PATH, require_auth=False)
    payload = data.get("data") if isinstance(data.get("data"), dict) else data
    if not isinstance(payload, dict):
        raise WorldCupAPIError(f"Malformed temp-key response: {data!r}")

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/wc_client.py (reported line 243)May include surrounding context.

python
resp = httpx.get(url, params=payload, headers=headers, timeout=DEFAULT_TIMEOUT)
        else:  # requests fallback
            if method == "POST":
                resp = requests.post(url, json=payload, headers=headers, timeout=DEFAULT_TIMEOUT)
            else:
                resp = requests.get(url, params=payload, headers=headers, timeout=DEFAULT_TIMEOUT)
    except Exception as e:

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The onboarding strings state that the same home/away fixture repeated within 3 days will not consume credits. However, the actual prediction cache TTL is defined as 6 hours (L076-L079) and used by both world cup and EPL prediction paths (L364-L365, L401-L403), so the implementation does not support the documented 3-day behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This JSON manifest-like file contains trigger-like user prompts such as "Predict Arsenal vs Chelsea in the Premier League." without any explicit boundary on when the skill should or should not activate. Because SQP-1 applies to manifest files, broad everyday prediction requests that lack exclusion conditions can count as ambiguous trigger scope.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The instruction "Respond in Chinese (matching the user's working language)" enforces a locale/language policy based on inference rather than an explicit user choice. SQP-3 covers language policy violations, and there is no statement here offering an alternative language or explicit opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The line requires rendering output in Chinese when language='zh', but does not mention any user override or opt-in mechanism. While language matching can be useful, the policy asks to flag forced language behavior when no explicit choice is offered.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This markdown file documents a production HTTPS endpoint, an authentication header, and a flow where the client requests a temporary API key from a remote service. Although the mechanics are explained, there is no explicit warning that using the skill sends requests and authentication material to an external service, which is the kind of disclosure SQP-2 expects in markdown for privacy/system-impacting behavior.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The World Cup guidance explicitly requires using the English Wikipedia page and a Baidu Baike English fallback. This imposes a language choice in the skill instructions without offering the user a language/locale option or documenting why English is required.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is specified with only a lower bound (httpx>=0.24), so builds may resolve to different versions over time. This weakens supply-chain control and makes it harder to ensure a tested, non-vulnerable version is installed, especially because known advisories exist for some httpx releases.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
# Either backend works; the client prefers httpx and falls back to requests
# if httpx isn't importable.
httpx>=0.24
# requests is optional but kept as a hard fallback for environments without
# httpx. Comment out if you want to enforce httpx-only.
requests>=2.28

Unverifiable Dependency: httpx has 2 known advisory(ies) (CVE-2021-41945 (Improper Input Validation in httpx); CVE-2021-41945 (Encode OSS httpx <=1.0.0.beta0 is affected by improper input validation in `http)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
83% confidence
Finding

Because httpx is not pinned, it is impossible to verify from this manifest alone whether deployment will use a version affected by known advisories. In isolation this is not proof of an exploitable package version, but it is a real supply-chain hygiene issue because the manifest cannot guarantee avoidance of vulnerable releases.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency is unpinned (requests>=2.28), allowing environment-dependent resolution to newer or older acceptable versions. This creates reproducibility and supply-chain risk, and can inadvertently pull a vulnerable release if dependency resolution is not tightly controlled.

Content

Scanner excerpt · requirements.txt (reported line 6)May include surrounding context.

text
httpx>=0.24
# requests is optional but kept as a hard fallback for environments without
# httpx. Comment out if you want to enforce httpx-only.
requests>=2.28

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The manifest does not pin requests, so there is no assurance that the installed version will avoid known vulnerable releases. Given the number of historical advisories for Requests, leaving the version open increases uncertainty and weakens control over exposure, even though the exact vulnerable version cannot be confirmed from this file alone.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

Several user-facing formatting helpers accept a language parameter but default to English when none is provided, and format_prediction emits English text unless the caller explicitly supplies a Chinese locale. This creates a natural-language locale preference in the skill behavior without an explicit user choice being offered in this file.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.