Back to skill

Security audit

EODHD API

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward EODHD financial-data helper, but users should treat its API-token handling as sensitive.

Install only if you are comfortable providing an EODHD API token. Prefer using a temporary token or environment-based secret handling if possible, restrict config.json permissions if you persist it, and avoid sharing full error objects because they may include the token.

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

Warning
Location
scripts/eodhd_client.py:37
Finding
API Token Disclosed in Error Return Values## Vulnerability Details **File Location**: `scripts/eodhd_client.py`, lines 37-48 **Vulnerability Type**: Sensitive credential exposure through error handling **Risk Level**: Medium **Complete Code Snippet**: ```python def _get_request(self, endpoint, params=None): '''Helper function to make a GET request to the API.''' if params is None: params = {} params['api_token'] = self.api_token params['fmt'] = 'json' url = f"{self.base_url}/{endpoint}" try: response = requests.get(url, params=params) response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx) return response.json() except requests.exceptions.RequestException as e: return {"error": str(e), "url": url, "params": params} ``` ### Technical Analysis The method inserts the EODHD API token into the mutable `params` dictionary and returns that entire dictionary when a network or HTTP error occurs. Consequently, every returned error object contains the plaintext token under `params["api_token"]`. The Skill instructions direct callers to check for an `error` field and report errors to the user. A caller that serializes, prints, logs, or otherwise exposes the complete error object can therefore disclose the credential. Authentication is necessary for the API request, but including the credential in diagnostic output is not necessary and exceeds minimum disclosure requirements. ### Attack Path 1. A valid EODHD token is loaded from configuration or passed to `EODHDClient`. 2. A request fails because of an HTTP error, connectivity problem, TLS error, timeout, or deliberately invalid request. 3. The exception handler returns `params`, which contains the plaintext `api_token`. 4. Agent code, application logging, telemetry, debugging output, or a user-facing response serializes the returned dictionary. 5. A party with access to that output obtains the token an ...[truncated 492 chars]
Remediation
## Remediation Suggestions - Never return authentication parameters in error objects. - Construct a sanitized diagnostic dictionary and replace the token with a fixed value such as `[REDACTED]`. - Prefer returning only an error type, a concise message, and an HTTP status code. - Ensure application logs and Agent responses apply credential redaction as a defense-in-depth measure. - Avoid mutating a caller-supplied parameter dictionary when adding authentication data. Example: ```python request_params = dict(params or {}) request_params["api_token"] = self.api_token request_params["fmt"] = "json" try: response = requests.get(url, params=request_params, timeout=30) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: return { "error": str(e), "url": url, "params": { key: ("[REDACTED]" if key == "api_token" else value) for key, value in request_params.items() }, } ```

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:29
Finding
API Token Stored Persistently in a Plaintext Configuration File## Vulnerability Details **File Location**: `SKILL.md`, lines 29-38; supporting implementation in `scripts/eodhd_client.py`, lines 18-24 **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Low **Complete Code Snippet**: ```markdown 1. **Ask the user** for their EODHD API token. 2. **Write the token** to the configuration file using the `file` tool: ```python default_api.file( action="write", path="/home/ubuntu/skills/eodhd-api/config.json", text=f"{{\"api_token\": \"{user_provided_token}\"}}" ) ``` ``` The corresponding credential-loading implementation is: ```python if api_token: self.api_token = api_token else: config_path = os.path.join(os.path.dirname(__file__), '..', 'config.json') if not os.path.exists(config_path): raise FileNotFoundError("config.json not found. Please create it with your API token.") with open(config_path, 'r') as f: config = json.load(f) self.api_token = config.get("api_token") ``` The distributed `config.json` contains only the placeholder `YOUR_API_TOKEN`; no live hardcoded credential was found. ### Technical Analysis The documented setup procedure directs the Agent to place a personal API credential in a predictable plaintext file inside the Skill directory. It does not require restrictive filesystem permissions, exclude the file from version control, or recommend a secret-management mechanism. Persisting a credential is not inherently malicious, but unrestricted plaintext storage creates unnecessary exposure to other local users, unrelated processes with filesystem access, backup systems, packaging operations, and accidental source-control commits. The API client already accepts an `api_token` argument, so persistent package-local storage is not strictly required for its declared functionality. ### Attack Path 1. The user supplies a valid EODHD token. 2 ...[truncated 907 chars]
Remediation
## Remediation Suggestions - Prefer an environment variable or operating-system secret store instead of a package-local configuration file. - Allow callers to supply the token at runtime without persisting it. - If file storage is unavoidable, create the file with owner-only permissions such as `0600`. - Store credentials outside the Skill source directory. - Add `config.json` and equivalent secret-bearing files to `.gitignore` and packaging exclusion rules. - Retain a separate example file containing only a placeholder, such as `config.example.json`. - Document token rotation and immediate revocation procedures for suspected disclosure. - Ensure logs, exceptions, backups, and generated artifacts do not include the credential.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill describes code-driven behavior that reads local configuration and performs outbound API requests, but it does not declare an explicit tool scope such as allowed tools or permissions. That increases the risk of overbroad agent execution, because a runtime may permit file or network access not clearly constrained by the skill contract, making review and policy enforcement harder.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill instructs the agent to persist a user-provided API token directly into a local config file without any warning, consent flow, or handling guidance for secrets. Storing credentials in plaintext can expose them to other tools, logs, future prompts, or unintended file reads, leading to credential theft and unauthorized API usage.

Static analysis

No suspicious patterns detected.