Back to skill

Security audit

AgentSports - AI Agents Sports Competition Platform

Security checks for vulnerabilities and agentic risk

Overview

The skill appears aligned with its sports-prediction purpose, but it needs review because it can submit wager-like predictions and stores reusable account credentials in plaintext.

Review before installing. Use a dedicated low-value account, avoid passing real passwords in command lines or agent transcripts, set ASP_MAX_STAKE to a strict limit, avoid real-money rooms unless you confirm each prediction yourself, and delete or protect ~/.asp/ because it can contain plaintext credentials and session cookies. Do not set ASP_BASE_URL except for trusted local development with separate credentials.

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

T09 · Insecure Skill Coding Practices

Error
Location
src/asp/api/state.py:75
Finding
Plaintext persistence of account passwords and session secrets<![CDATA[ ## Vulnerability Details **File Location**: `src/asp/api/state.py:75-115`; credential persistence is invoked from `src/asp/api/auth.py:26-33` and `src/asp/api/auth.py:54-70` **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```python def _save_cookies(self, cookies: httpx.Cookies) -> None: jar_list = [] for cookie in cookies.jar: jar_list.append({ "name": cookie.name, "value": cookie.value, "domain": cookie.domain, "path": cookie.path, }) self.cookie_file.write_text(json.dumps(jar_list, ensure_ascii=False, indent=2)) # ── metadata ─────────────────────────────────────────────────────── def _load_meta(self) -> dict[str, Any]: if not self.state_file.exists(): return {} try: return json.loads(self.state_file.read_text()) except Exception: return {} def _save_meta(self, meta: dict[str, Any]) -> None: self.state_file.write_text(json.dumps(meta, ensure_ascii=False, indent=2)) # ── credentials ─────────────────────────────────────────────────── def load_credentials(self) -> dict[str, str] | None: if not self.credentials_file.exists(): return None try: data = json.loads(self.credentials_file.read_text()) if data.get("email") and data.get("password"): return data except Exception: pass return None def save_credentials(self, email: str, password: str) -> None: self.credentials_file.write_text( json.dumps({"email": email, "password": password}, indent=2) ) ``` Successful authentication automatically invokes this storage behavior: ```python if result.get("authenticated"): self.state.save_credentials(email, password) ``` ### Technical Analysis The application stores account passwords as unencrypted JSON in `credentials.json`. It also writes reusable HTTP cookies and CSRF-related metadata to JSON files. No OS-bac ...[truncated 1979 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Stop saving raw passwords by default. Retain only the service-issued session token when persistence is necessary. 2. If automatic reauthentication is required, store credentials in an OS keyring or another encrypted secret manager. 3. Require explicit, informed user consent before enabling credential persistence, with a `--save-credentials` option disabled by default. 4. Create `~/.asp/` with mode `0700` and secret-bearing files with mode `0600`, independent of the process umask. 5. Use atomic writes through a securely created temporary file followed by `os.replace()`. 6. Separate low-sensitivity application state from passwords and session secrets. 7. Provide commands to remove saved credentials and invalidate active sessions without deleting unrelated configuration. 8. After remediation, advise existing users to delete plaintext credentials, rotate their passwords, and revoke stored sessions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/asp/api/client.py:35
Finding
Configurable API origin can redirect credentials, PII, and session data to an untrusted or plaintext endpoint<![CDATA[ ## Vulnerability Details **File Location**: `src/asp/api/client.py:35-39`, `src/asp/api/client.py:65-89`, and `src/asp/api/client.py:99-126` **Vulnerability Type**: Unrestricted sensitive-data destination and missing HTTPS enforcement **Risk Level**: High ### Vulnerable Code ```python def __init__(self, data_dir: str = "~/.asp/", base_url: str | None = None): self.state = StateManager(data_dir) self._base_url = ( base_url or os.environ.get("ASP_BASE_URL", DEFAULT_BASE_URL) ).rstrip("/") self._max_stake = self._parse_max_stake() ``` The selected origin receives persisted cookies and arbitrary request bodies: ```python with self.state.lock(): cookies, meta = self.state.load() csrf = meta.get("csrf_token", "") headers = kwargs.pop("headers", {}) headers.setdefault("Accept", "application/json") if csrf: headers["X-CSRF-TOKEN"] = csrf with httpx.Client( base_url=self._base_url, cookies=cookies, timeout=_TIMEOUT, follow_redirects=True, ) as http: resp = http.request(method, path, headers=headers, **kwargs) self._extract_csrf(resp, meta) if resp.status_code == 401 and allow_relogin: resp = self._try_relogin(http, method, path, headers, meta, resp, kwargs) if clear_csrf: meta["csrf_token"] = "" self.state.save(http.cookies, meta) ``` A `401` response can trigger transmission of saved credentials to that same configurable origin: ```python creds = self.state.load_credentials() if not creds: return orig_resp login_resp = http.post( "/api/login", json=creds, headers={"Accept": "application/json", "Content-Type": "application/json"}, ) if login_resp.status_code != 200: return orig_resp login_data = self._parse_response(login_resp) if not login_data.get("authenticated"): return orig_resp self._extract_csrf(login_resp, meta) csrf = meta.get("csrf_token", "") if csrf: hea ...[truncated 2313 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https://agentsports.io` in normal production mode. 2. Parse the URL and reject non-HTTPS schemes, embedded credentials, malformed hosts, and unexpected ports. 3. If custom endpoints are needed for development, require an explicit option such as `ASP_ALLOW_INSECURE_DEV_ENDPOINT=1`. 4. Restrict insecure development endpoints to loopback addresses and display a prominent warning. 5. Use separate state directories for each origin so production cookies and credentials are never loaded for a development server. 6. Bind saved credentials to the exact trusted origin and disable automatic relogin whenever the current origin differs. 7. Reject HTTPS-to-HTTP redirects and validate the final redirect origin before forwarding authentication state. 8. Avoid attaching cookies until their domain and origin have been validated against the intended service. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/asp/cli/main.py:78
Finding
CLI accepts and documents account passwords as command-line arguments<![CDATA[ ## Vulnerability Details **File Location**: `src/asp/cli/main.py:78-85` and `src/asp/cli/main.py:94-113`; the behavior is encouraged by `SKILL.md:45-49` and `SKILL.md:95-99` **Vulnerability Type**: Sensitive information exposure through process arguments and command history **Risk Level**: Medium ### Vulnerable Code ```python @cli.command("login") @click.option("--email", default="", help="Account email") @click.option("--password", default="", help="Account password") @click.pass_context def login(ctx: click.Context, email: str, password: str) -> None: """Log in. Omit both flags to reuse saved credentials.""" _run(ctx.obj["client"].login, email or None, password or None) ``` Registration has the same exposure: ```python @cli.command("register") @click.option("--username", required=True) @click.option("--email", required=True) @click.option("--password", required=True) @click.option("--first-name", required=True) @click.option("--last-name", required=True) @click.option("--birth-date", required=True, help="DD/MM/YYYY") @click.option("--phone", required=True) @click.option("--country-code", default="US") @click.option("--city", default="") @click.option("--address", default="") @click.option("--zip-code", default="") @click.option("--sex", default="male", type=click.Choice(["male", "female"])) @click.pass_context def register(ctx: click.Context, **kwargs: Any) -> None: """Register a new account.""" _run(ctx.obj["client"].register, **kwargs) ``` The Skill instructions explicitly direct agents to use this channel: ```text asp login --email ... --password ... ``` ### Technical Analysis Command-line arguments are not a secure secret-input channel. Depending on the operating system and runtime environment, they may be visible in process listings, shell history, terminal scrollback, agent tool-call transcripts, observability platforms, CI logs, crash reports, and endpoint monitoring. The exposure is amplified in an agent Skill becau ...[truncated 1159 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove password values from normal CLI arguments. 2. Use `click.prompt("Password", hide_input=True)` for interactive sessions. 3. For automation, accept the secret through a protected file descriptor, OS keyring, or secret-manager reference. 4. If environment-based input is retained as a compatibility mechanism, document that it may still leak through process environments and prefer a secret store. 5. Ensure application logs, MCP traces, and agent telemetry redact password fields. 6. Update `SKILL.md` and `README.md` so examples never place real passwords in command lines. 7. For MCP, prefer host-provided secret references or an out-of-band authentication flow instead of ordinary tool arguments. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:5
Finding
Skill installation retrieves code from an unpinned mutable Git repository<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:5` **Vulnerability Type**: Unpinned VCS dependency and mutable installation source **Risk Level**: Medium ### Vulnerable Code ```yaml metadata: {"openclaw": {"requires": {"bins": ["asp"], "config_paths": ["~/.asp/"]}, "homepage": "https://agentsports.io", "install": [{"id": "uv", "kind": "uv", "package": "agentsports", "args": ["--from", "git+https://github.com/elesingp2/agentsports-connect.git"], "bins": ["asp"], "label": "Install agentsports via uv", "env": {"UV_CACHE_DIR": "/workspace/.uv-cache"}}, {"id": "path", "kind": "shell", "command": "export PATH=\"$HOME/.local/bin:$PATH\"", "label": "Add bin dir to PATH"}]}} ``` ### Technical Analysis The installation source is a Git repository URL without a commit hash, signed tag, immutable release artifact, or verified digest. The effective code installed in the future can therefore differ from the code reviewed during this audit. This is a supply-chain weakness rather than evidence that the currently reviewed repository contains an embedded malicious payload. If the upstream repository, maintainer account, default branch, DNS path, or release process is compromised, subsequent installations can retrieve and execute altered package code. The installation also resolves transitive Python dependencies from broad minimum-version constraints in `pyproject.toml`, increasing reproducibility risk, although no malicious dependency was confirmed during this static audit. ### Attack Path 1. An attacker compromises the upstream GitHub repository or obtains permission to modify its default branch. 2. The attacker adds malicious installation or runtime code to the repository. 3. A user installs the Skill using the declared `git+https://github.com/elesingp2/agentsports-connect.git` source. 4. The installer retrieves the current mutable branch rather than the version that was audited. 5. The malicious package code runs during installation, command execution, ...[truncated 525 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the VCS installation URL to a reviewed full commit SHA, for example `git+https://...git@<full-commit-sha>`. 2. Prefer a versioned package from a trusted registry with cryptographic hashes. 3. Publish signed releases and verify signatures or attestations during installation. 4. Use a lockfile or constraints file that records exact transitive dependency versions and hashes. 5. Add automated dependency scanning, provenance verification, and reproducible-build checks. 6. Re-audit the package whenever the pinned commit or dependency lockfile changes. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (30)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
Local disk persistence of cookies and user credentials is a meaningful security concern, especially when not prominently disclosed in the declared purpose. In shared or agent-run environments, stored authentication artifacts can be reused by other processes, users, or subsequent tasks, leading to account takeover or unauthorized wagering/account actions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local disk persistence of cookies and user credentials is a meaningful security concern, especially when not prominently disclosed in the declared purpose. In shared or agent-run environments, stored authentication artifacts can be reused by other processes, users, or subsequent tasks, leading to account takeover or unauthorized wagering/account actions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local disk persistence of cookies and user credentials is a meaningful security concern, especially when not prominently disclosed in the declared purpose. In shared or agent-run environments, stored authentication artifacts can be reused by other processes, users, or subsequent tasks, leading to account takeover or unauthorized wagering/account actions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
Local disk persistence of cookies and user credentials is a meaningful security concern, especially when not prominently disclosed in the declared purpose. In shared or agent-run environments, stored authentication artifacts can be reused by other processes, users, or subsequent tasks, leading to account takeover or unauthorized wagering/account actions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Local disk persistence of cookies and user credentials is a meaningful security concern, especially when not prominently disclosed in the declared purpose. In shared or agent-run environments, stored authentication artifacts can be reused by other processes, users, or subsequent tasks, leading to account takeover or unauthorized wagering/account actions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Local disk persistence of cookies and user credentials is a meaningful security concern, especially when not prominently disclosed in the declared purpose. In shared or agent-run environments, stored authentication artifacts can be reused by other processes, users, or subsequent tasks, leading to account takeover or unauthorized wagering/account actions.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Credentials & Data

Session cookies and credentials are auto-saved to `~/.asp/`. Wipe: `rm -rf ~/.asp/`.

## Exit Codes (CLI)
Confidence
90% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Credentials & Data

Session cookies and credentials are auto-saved to `~/.asp/`. Wipe: `rm -rf ~/.asp/`.

## Exit Codes (CLI)
Confidence
85% 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).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
## Credentials & Data

Session cookies and credentials are auto-saved to `~/.asp/`. Wipe: `rm -rf ~/.asp/`.

## Exit Codes (CLI)
Confidence
90% 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).

Credential Access

High
Category
Privilege Escalation
Content
.lock            — filelock (automatic)
        cookies.json     — HTTP cookies (list of {name, value, domain, path})
        state.json       — CSRF token, last login timestamp, username
        credentials.json — email + password (if user allowed saving)
    """

    def __init__(self, data_dir: str = DEFAULT_DATA_DIR):
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
.lock            — filelock (automatic)
        cookies.json     — HTTP cookies (list of {name, value, domain, path})
        state.json       — CSRF token, last login timestamp, username
        credentials.json — email + password (if user allowed saving)
    """

    def __init__(self, data_dir: str = DEFAULT_DATA_DIR):
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README instructs users to pass a password directly on the command line (`asp login --email ... --password s3cret`), which can expose credentials through shell history, process listings, terminal logging, and agent/tool transcripts. In this skill's context, the risk is elevated because it targets autonomous agents and MCP/CLI workflows, where command invocations may be logged or echoed by orchestration systems, increasing the chance of credential leakage for an account tied to real-money activity.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares no explicit tool scope or permission boundaries even though it clearly contemplates network access, credential persistence, and filesystem interaction. In an agent setting, missing scope declarations increase the chance that an agent can invoke sensitive capabilities without clear user visibility or policy enforcement.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documented autonomous workflow allows the agent to place predictions with stake values without per-action confirmation, and the skill explicitly markets real-money use. In a financial or wagering context, this can cause unauthorized transactions, loss of funds, or regulatory/compliance issues if the user did not knowingly approve each stake.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
Although the skill mentions asking which autonomy mode the user prefers, the 'Fully autonomous play' section normalizes agent-driven wagering behavior and does not establish a strong opt-in gate. For sensitive financial actions, ambiguity around consent is dangerous because an agent may infer permission too broadly and act without informed authorization.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This code persists credentials locally immediately after login without any visible notice or consent in the flow shown here. Silent storage of reusable credentials increases the chance of credential theft from local files, developer tooling, backups, or multi-tenant agent environments.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The login flow saves the user's email and password locally after successful authentication, which creates unnecessary secret persistence risk. If the local state store is readable by other users, malware, logs, backups, or agent tooling, account credentials can be recovered and reused for account takeover.

Description-Behavior Mismatch

Medium
Confidence
80% confidence
Finding
The registration flow collects substantial personal data, including full identity and contact fields, beyond what is apparent from the brief skill description. While some of this may be legitimate for a real-money platform, the mismatch increases privacy and compliance risk because users may not expect this scope of data collection from the advertised functionality.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
After registration succeeds, the code stores the newly created account's email and password locally, again preserving reusable credentials without any visible warning. This creates a durable compromise point for a financial or real-money-associated service, making downstream account abuse more serious.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The `_raw_get` method performs a GET to any caller-supplied URL with no allowlist or origin validation. Because it reuses the persisted session cookie jar, an attacker who can influence the URL could trigger server-side requests to arbitrary hosts and potentially leak cookies or perform unintended authenticated actions against third-party or internal endpoints, depending on cookie scope and deployment context.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The docstring states credentials are persisted with file locking, but the credential load/save methods do not actually acquire the lock. This can lead to race conditions, partial writes, or inconsistent credential state, which is especially risky for authentication material and may indirectly cause credential corruption or unintended disclosure through concurrent access patterns.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
The module stores a user's email and password in a local JSON file, which creates a real credential-exposure risk if the host is multi-user, backed up insecurely, compromised by malware, or the file permissions are too broad. In the context of a consumer-facing skill that says no API key is required but handles real-money activity, persisting raw login credentials increases the blast radius beyond normal session persistence.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The method persists email and password to credentials.json, which is a safety-critical file write involving sensitive credentials. While the class docstring mentions that credentials may be saved, there is no confirmation prompt, user-facing log/print, or explicit warning at the point of the operation in this code file.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The login command accepts email and password via command-line options, which can expose credentials through shell history, process listings, job logs, and other local observability mechanisms. In a skill handling real-money accounts, this increases the chance of credential theft even if the downstream authentication logic is otherwise correct.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The register command accepts multiple sensitive fields including full name, birth date, phone, address, and email, and then submits them via the client. While the command name implies account creation, the code provides no user-facing disclosure in this file that this personally identifiable information will be transmitted or stored.

Static analysis

No suspicious patterns detected.