Back to skill

Security audit

Football Tracker Match & News AI

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent football tracker, but it packages and stores API keys in plaintext, so it needs Review before installation.

Install only if you are comfortable with a football skill that uses external sports and news services and stores your football-data.org key locally in plaintext. The bundled API key should be treated as exposed and revoked or rotated before release, and users should prefer a version that uses a proper secret store and ships without user_config.json.

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
storage/user_config.json:1
Finding

Live API Credential and User Identifier Bundled in the Project

Content
View full analysis

Vulnerability Details

File Location: storage/user_config.json:1-5
Vulnerability Type: Hardcoded secret and sensitive identifier exposure
Risk Level: High

Vulnerable Code

json
{
  "918573385": {
    "api_key": "[REDACTED_EXPOSED_API_KEY]"
  }
}

The API key value has been redacted from this report to avoid further credential disclosure. The audited file contains the complete plaintext value.

Technical Analysis

The distributed project contains a plaintext football-data.org API key associated with a user identifier. Anyone who can download the project, inspect its source archive, access a repository clone, or read a backup can recover the credential without invoking the Skill.

The application sends this credential as an X-Auth-Token header to https://api.football-data.org/v4. Although that transmission is necessary for the declared club lookup functionality and uses HTTPS, embedding a live credential in the distributable package is unnecessary and violates secret-management best practices.

This finding does not provide operating-system privileges. Its scope is the external API account and the exposed user identifier.

Attack Path

  1. An attacker obtains the Skill package, repository contents, build artifact, or a backup containing it.
  2. The attacker opens storage/user_config.json.
  3. The attacker extracts the plaintext API key and associated user identifier.
  4. The attacker submits requests to football-data.org with the stolen key in the X-Auth-Token header.
  5. The requests consume the victim's API quota and are attributed to the victim's account until the credential is revoked.

No code execution or local privilege escalation is required.

Impact Assessment

An attacker may:

  • Make unauthorized requests under the affected football-data.org account.
  • Exhaust API quotas or trigger service rate limits.
  • Cause account abuse or operational disruption.
  • C ...[truncated 308 chars]
Remediation
View remediation

Remediation Suggestions

  1. Revoke and rotate the exposed football-data.org API key immediately.
  2. Remove storage/user_config.json from all release packages and repository history.
  3. Add the runtime credential file to .gitignore and packaging exclusion rules.
  4. Distribute only a secret-free example such as user_config.example.json.
  5. Store production credentials in an OS keyring, managed secret store, or deployment-specific credential service.
  6. Review API account logs and quota usage for unauthorized activity.
  7. Add automated secret scanning to commits, pull requests, and release builds.
  8. Avoid including persistent user identifiers in distributable artifacts.

T09 · Insecure Skill Coding Practices

Warning
Location
storage/user_config.py:17
Finding

User-Supplied API Keys Persisted in an Unprotected Plaintext File

Content
View full analysis

Vulnerability Details

File Locations: main.py:67-76; storage/user_config.py:17-20,31-40
Vulnerability Type: Insecure plaintext credential storage
Risk Level: Medium

Vulnerable Code

From main.py:67-76:

python
if text.startswith("set_api_key") or text.startswith("API_KEY:"):

    if text.startswith("set_api_key"):
        key = text.replace("set_api_key", "").strip()
    else:
        key = text.replace("API_KEY:", "").strip()

    set_api_key(user_id, key)

    return "✅ API_KEY configured successfully!" if locale == "en" else "✅ API_KEY configurada com sucesso!"

From storage/user_config.py:17-20,31-40:

python
def save_config(data):

    with open(CONFIG_PATH, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)


def set_api_key(user_id, key):

    config = load_config()

    if user_id not in config:
        config[user_id] = {}

    config[user_id]["api_key"] = key

    save_config(config)

Technical Analysis

The setup command accepts an API key through user-controlled message text and writes it directly into the predictable path storage/user_config.json. The value is serialized as plaintext JSON. The implementation does not use an OS credential store, encryption, explicit owner-only permissions, or a storage path isolated from the distributable project.

The file is opened with the process's default permission behavior, which depends on the runtime environment and its umask. Consequently, the code does not guarantee that only the Skill process owner can read the credential. The same file may also be captured by source archives, backups, support bundles, or release packaging, as demonstrated by the bundled credential found during this audit.

The code also lacks validation for empty or malformed keys and does not use atomic file replacement. Those weaknesses can cause configuration corruption or misleading setup success messages, alth ...[truncated 1582 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace JSON credential storage with an OS keyring or managed secret store.
  2. Store only a secret reference or credential identifier in ordinary configuration files.
  3. If file-based storage is unavoidable:
    • Keep the file outside the project and release directories.
    • Create it with owner-only permissions such as 0600.
    • Verify ownership and permissions before every read and write.
    • Encrypt stored values with a key managed separately from the file.
    • Use an atomic write-and-rename operation to prevent partial configuration files.
  4. Ensure the setup message containing the key is not retained in chat logs, telemetry, exception reports, or command histories; delete or redact it where the host platform permits.
  5. Validate that submitted keys are non-empty and conform to the expected format before storing them.
  6. Return setup success only after secure persistence succeeds.
  7. Separate credentials by tenant or user so compromise of one storage object does not expose all users.
  8. Add packaging checks that fail builds when runtime credential files are present.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

Persisting per-user API keys in local files while the skill advertises only football summaries is a meaningful undisclosed secret-handling behavior. Hidden credential storage increases the risk of accidental exposure, misuse by other skills/components, or insecure retention practices if file paths and access boundaries are not tightly controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

Persisting per-user API keys in local files while the skill advertises only football summaries is a meaningful undisclosed secret-handling behavior. Hidden credential storage increases the risk of accidental exposure, misuse by other skills/components, or insecure retention practices if file paths and access boundaries are not tightly controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Persisting per-user API keys in local files while the skill advertises only football summaries is a meaningful undisclosed secret-handling behavior. Hidden credential storage increases the risk of accidental exposure, misuse by other skills/components, or insecure retention practices if file paths and access boundaries are not tightly controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

Persisting per-user API keys in local files while the skill advertises only football summaries is a meaningful undisclosed secret-handling behavior. Hidden credential storage increases the risk of accidental exposure, misuse by other skills/components, or insecure retention practices if file paths and access boundaries are not tightly controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

Persisting per-user API keys in local files while the skill advertises only football summaries is a meaningful undisclosed secret-handling behavior. Hidden credential storage increases the risk of accidental exposure, misuse by other skills/components, or insecure retention practices if file paths and access boundaries are not tightly controlled.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill declares no explicit tool scope even though its documented behavior requires network access and local file reads/writes for configuration. In an agent environment, undeclared capabilities reduce transparency and can lead to over-privileged execution or unexpected access to user data such as stored API keys.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Examples that trigger on bare team names like 'Brazil' or 'Chelsea' create an overly broad activation surface and can cause the skill to run unintentionally during ordinary conversation. In agent systems, ambiguous triggers may result in unexpected network requests, use of stored credentials, or disclosure of contextual information when the user did not clearly intend to invoke the skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

setup_message defaults locale to "pt", and the locale detection logic later falls back to Portuguese whenever English markers do not outnumber Portuguese markers. This imposes a language preference by default rather than asking the user or remaining neutral, which is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The setup message explicitly tells users they can send an API key in chat for the agent to configure, but it provides no warning that secrets should not be shared over ordinary conversation channels. This increases the chance users disclose credentials into transcripts, logs, or interfaces not designed for secret handling.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The setup text directly encourages users to provide their API key to the agent, which normalizes sharing secrets through chat. In the context of an otherwise simple football-tracking skill, this is more dangerous because users would not expect secret-management behavior and may not realize chats can be stored or observed.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code accepts and stores user-supplied API credentials, a capability broader than simple sports lookup. Even if intended for convenience, handling secrets in-chat introduces confidentiality risk if storage, logging, or downstream components are exposed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

When a user sends the set_api_key command, the skill stores the provided credential immediately with no confirmation, disclosure, or preview of what will be retained. This can lead to accidental secret submission and persistence without informed consent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The locale detector returns Portuguese whenever English markers are not more numerous, including cases with zero markers or mixed ambiguous input. This means users can receive Portuguese output without opting in, rather than being asked to choose a language.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The helper sends the provided API key in the X-Auth-Token header to an external football-data.org service, which is a network transmission of credential material. In this file there is no confirmation prompt, logging, comment, or docstring disclosing that the skill will send authentication data to a third-party API.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
79% confidence
Finding

get_matches defaults locale to "pt", and the file consistently uses Portuguese-oriented strings and formatting unless the caller explicitly switches to English. A default forced language without user choice or documented justification is a natural-language policy concern under the locale rule.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest description explicitly says the skill returns 'recent news,' but this file's news-related logic is limited to scraping Google News RSS and linked pages to extract broadcaster names. No function here returns headlines, summaries, or news content to the caller, so the implemented behavior falls short of the stated output.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The skill issues third-party requests to Google News and then derives additional outbound fetches from returned links without clear necessity or disclosure. In a user-facing agent context, this broadens data transmission and creates hidden network interactions with external services beyond the primary football API.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The search string uses Portuguese phrasing ("onde assistir transmissão") and the request pins Google News parameters to pt-BR/BR, forcing a specific locale behavior. The file does not offer locale selection for this feature or explain that it is intentionally Brazil-specific.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill follows links from Google News results and fetches arbitrary third-party pages, expanding its network reach beyond the football API into open-ended web retrieval. This can expose the runtime to SSRF-like behavior, unexpected content fetching, privacy leakage through outbound requests, and dependency on untrusted pages that are not necessary for a compact match-summary skill.

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 · services/football_api.py (reported line 13)May include surrounding context.

python
from services.worldcup_2026 import get_team as get_worldcup_team


BASE_URL = "https://api.football-data.org/v4"
BASE_DIR = Path(__file__).resolve().parent.parent
TEAMS_PATH = BASE_DIR / "storage" / "teams.json"
COMPETITION_HINTS = {

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 · services/team_resolver.py (reported line 11)May include surrounding context.

python
from services.worldcup_2026 import get_team as get_worldcup_team


BASE_URL = "https://api.football-data.org/v4"
BASE_DIR = Path(__file__).resolve().parent.parent
TEAMS_PATH = BASE_DIR / "storage" / "teams.json"
COMPETITION_HINTS = {

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The main user-facing function build_team_summary defaults locale to "pt", causing responses to be generated in Portuguese unless the caller explicitly overrides it. This is a natural-language policy concern because the skill imposes a specific language by default rather than offering or negotiating a language choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

_local_timezone() preferentially returns America/Sao_Paulo, which makes displayed kickoff times localized to São Paulo regardless of the actual user's region. This enforces a locale-specific behavior without user opt-in and can conflict with language/locale policy expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code persists user API keys directly to a local JSON file in plaintext without any visible encryption, access control, or disclosure mechanism. If the host, workspace, backups, or logs are accessible to other users or processes, stored credentials can be recovered and abused to access third-party services on behalf of users.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

These functions retrieve and store per-user API keys from a shared JSON configuration file, but there is no visible user consent flow, secrecy boundary, validation of storage safety, or protection against local disclosure. In a multi-user or agent-hosted environment, one compromised process, exposed filesystem, or accidental file sharing could leak all users' stored credentials.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.