Back to skill

Security audit

football-value-bets

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for football betting analysis, but it should be reviewed because it automatically stores betting history and has a date path-validation bug that could let result updates touch JSON files outside its betting folder.

Review this skill before installing. It requires a football-data.org API key, may use web searches for betting context, and stores bet slips and betting statistics locally. Only use canonical YYYY-MM-DD dates when recording results, and prefer a version that validates dates, confines file paths to its data folder, and asks before saving betting history.

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/bet_tracker.py:36
Finding
Path Traversal Through Unvalidated Bet Date<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bet_tracker.py`, lines 36-48; user-controlled value reaches these functions at lines 173 and 281 **Vulnerability Type**: Path traversal and unintended file access **Risk Level**: Medium ### Vulnerable Code ```python def _load_bet_file(date_str: str) -> Dict[str, Any]: path = BETS_DIR / f"{date_str}.json" if not path.exists(): return {"date": date_str, "slips": []} with open(path, "r", encoding="utf-8") as fh: return json.load(fh) def _save_bet_file(date_str: str, data: Dict[str, Any]): _ensure_dirs() path = BETS_DIR / f"{date_str}.json" with open(path, "w", encoding="utf-8") as fh: json.dump(data, fh, indent=2, ensure_ascii=False) ``` The unvalidated value reaches the vulnerable functions through the result-recording flow: ```python bet_data = _load_bet_file(date_str) ... _save_bet_file(date_str, bet_data) ``` It originates from an unrestricted command-line argument: ```python parser.add_argument("--date", type=str, help="Date (YYYY-MM-DD) for result mode") ... output = mark_result(args.date, args.slip_idx, args.pick_idx, args.result) ``` ### Technical Analysis Although `--date` is documented as using the `YYYY-MM-DD` format, the program does not enforce that format. The supplied string is interpolated directly into a path under `BETS_DIR`. A value containing traversal components, such as `../../some/target`, produces a path equivalent to: ```text data/bets/../../some/target.json ``` No canonicalization or containment check ensures that the resolved path remains inside `data/bets`. Consequently, the operating system resolves traversal components before the file is opened. In `mark_result`, a selected file is parsed as JSON, accessed through its expected `slips` structure, modified, and written back to the same attacker-selected path. Exploitation therefore requires the targeted file to exist, be readable and writable by the Skill process, ...[truncated 1561 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Strictly parse and normalize the date before using it in a filename: ```python from datetime import datetime def _validate_date(date_str: str) -> str: try: parsed = datetime.strptime(date_str, "%Y-%m-%d").date() except ValueError as exc: raise ValueError("Date must use the YYYY-MM-DD format") from exc normalized = parsed.isoformat() if normalized != date_str: raise ValueError("Date must be a canonical YYYY-MM-DD value") return normalized ``` 2. Construct file paths only from the normalized value and enforce directory containment: ```python def _bet_path(date_str: str) -> Path: normalized = _validate_date(date_str) root = BETS_DIR.resolve() path = (root / f"{normalized}.json").resolve() if path.parent != root: raise ValueError("Bet file path is outside the permitted directory") return path ``` 3. Use the centralized safe path function for both reads and writes: ```python def _load_bet_file(date_str: str) -> Dict[str, Any]: path = _bet_path(date_str) ... def _save_bet_file(date_str: str, data: Dict[str, Any]): _ensure_dirs() path = _bet_path(date_str) ... ``` 4. Validate the loaded JSON schema before mutation. Require `slips` to be a list and validate the expected fields and types for every selected record. 5. Use atomic writes through a temporary file created inside `data/bets`, followed by `os.replace`, to reduce corruption risk. 6. Create local data files with restrictive permissions where supported, such as owner-only read and write access. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description emphasizes an analysis/generation skill that creates football bet slips based on sporting data and value assessment. The supplied code does not analyze matches, retrieve football data, evaluate form/H2H/injuries/standings, or generate picks autonomously. Instead, it accepts already-formed bet slip JSON, stores it, updates outcomes, and calculates tracking statistics such as hitrate, ROI, monthly budget usage, and history. While the description mentions result tracking and ROI, that is only one subset of the implementation; the primary advertised capability—data-driven bet analysis and tip generation—is absent from this code chunk. Therefore the description does not accurately represent the actual behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description promises a betting-analysis skill that produces data-driven football bets and tracks betting performance. The supplied code only fetches match-related data from an external football API: fixtures, standings with form, and limited head-to-head records. While some fetched inputs overlap with the description (form, standings, H2H), the core advertised capabilities are missing: there is no betting logic, no odds or bookmaker/value analysis, no injury handling, no slip construction, and no result-tracking metrics like hit rate or ROI. Therefore the actual code materially underdelivers relative to the declared purpose, making this a description-behavior mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares access to environment variables and instructs execution of Python scripts that fetch network data and save tracking data, but it does not define any explicit tool scope or allowed-tools boundary. That creates an over-privileged execution model where a caller or downstream runtime may permit broader file, network, and write actions than users would reasonably expect from the manifest.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The manifest description uses generic trigger terms like bet slip, tips, or picks without clearly delimiting when the skill should be invoked. In an agent ecosystem, vague invocation guidance can route ordinary user requests into a skill that performs network calls and file writes, leading to unintended processing or storage.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The activation criteria are broad enough to trigger on common sports or betting-related queries, which can cause the skill to run in contexts the user did not clearly intend. Because the skill can invoke scripts, use network data, and persist bet history, overbroad activation increases the chance of unnecessary data access and unintended side effects.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The workflow says to automatically save the generated bet slip for tracking, but it does not require explicit user consent or provide a user-facing warning at the point of collection. This creates a privacy and autonomy issue because user betting preferences and activity may be written to local storage without clear notice or opt-in.

External Transmission

Medium
Category
Data Exfiltration
Content
}))
    sys.exit(1)

BASE_URL = "https://api.football-data.org/v4"
CONFIG_PATH = Path(__file__).resolve().parents[1] / "config" / "settings.json"

COMPETITION_CODES = ["PL", "BL1", "SA", "PD", "FL1", "DED", "CL"]
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Low
Confidence
98% confidence
Finding
The tone and style section mandates "Output in English" as a fixed requirement. This is a natural-language locale policy constraint and there is no user choice, opt-in, or stated regional justification in the file.

Static analysis

No suspicious patterns detected.