Back to skill

Security audit

Economic Calendar Pro

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its economic-calendar purpose, but it needs review because its credential and .env handling are broader than documented.

Review this before installing if you use .env files with sensitive values. Run it from a controlled directory, prefer a narrowly scoped TradingEconomics key, and be aware that recurring briefings create a remind-me cron and that the TradingEconomics credential may appear in provider or proxy URL logs.

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
src/service.py:27
Finding

API Credential Transmitted in a URL Query String

Content
View full analysis
str: """Build the TradingEconomics calendar URL for a date range.""" path = ( f"/calendar/country/{CALENDAR_COUNTRY_SCOPE}/" f"{start_date.isoformat()}/{end_date.isoformat()}" ) query = urlencode({"c": api_key, "f": "json"}) return f"{API_BASE_URL}{path}?{query}" ``` ### Technical Analysis The TradingEconomics API credential is inserted into the `c` URL query parameter. Although the destination is a hard-coded official HTTPS endpoint, query strings are commonly retained in reverse-proxy logs, server access logs, network monitoring products, debugging output, browser or client histories, and application-performance monitoring systems. TLS protects the complete request from passive network observers, but it does not prevent endpoint infrastructure or local instrumentation from recording the URL. Consequently, the credential may be exposed to parties that have access to logs but are not intended to possess API credentials. The outbound request is necessary for the Skill’s declared economic-calendar functionality and is not evidence of covert data exfiltration. However, transmitting a reusable secret in a URL creates more exposure than header-based authentication and does not follow least-disclosure principles. ### Attack Path 1. A user configures `TRADING_ECONOMICS_API_KEY` in the process environment or a loaded `.env` file. 2. `get_economic_calendar()` passes the credential to `fetch_tradingeconomics_payload()`. 3. `build_calendar_url()` embeds the complete credential in the URL query string. 4. The URL is processed by local monitoring infrastructure, an HTTPS proxy, the provider’s edge infrastructure, or provider-side access logging. 5. A party w ...[truncated 626 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
src/utils.py:87
Finding

Overbroad and Incorrectly Scoped Dotenv File Loading

Content
View full analysis
Path: """Return the repository root from this skill file.""" return Path(__file__).resolve().parents[3] def _strip_wrapping_quotes(value: str) -> str: """Remove matching wrapping quotes from a dotenv value.""" if len(value) >= 2 and value[0] == value[-1] and value[0] in {'"', "'"}: return value[1:-1] return value def load_env_file_if_present() -> bool: """Load a nearby .env file without overriding existing environment values.""" candidates = [] seen = set() for base in (Path.cwd(), repo_root()): for candidate in (base / ".env",): resolved = candidate.resolve() if resolved in seen: continue seen.add(resolved) candidates.append(candidate) loaded = False for candidate in candidates: if not candidate.is_file(): continue for raw_line in candidate.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) key = key.strip() value = _strip_wrapping_quotes(value.strip()) if key: os.environ.setdefault(key, value) loaded = True return loaded ``` ### Technical Analysis The loader searches for `.env` files in both the current working directory and the path returned by `repo_root()`. The latter uses `Path(__file__).resolve().parents[3]`, which does not identify the project root for the reviewed layout. For `artifact/src/utils.py`, the project root is reached with `parents[1]`; `parents[3]` resolves substantially higher in the directory hierarchy and may reference a sha ...[truncated 2253 chars]
Remediation
View remediation
Path: return Path(__file__).resolve().parents[1] ``` 2. Avoid searching the arbitrary current working directory unless explicitly requested by a trusted command-line option. 3. Parse only the configuration variable required by this Skill instead of importing every assignment: ```python if key == API_KEY_ENV_VAR and API_KEY_ENV_VAR not in os.environ: os.environ[API_KEY_ENV_VAR] = value ``` 4. Prefer returning the parsed credential directly rather than mutating the complete process environment. 5. Where the deployment model supports ownership checks: - Reject symbolic links for credential files. - Require the file to be owned by the executing user. - Reject group-writable or world-writable credential files. - Use restrictive permissions such as `0600`. 6. Report the exact trusted configuration path when diagnostic output is enabled, without printing the credential itself. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The documented behavior says the skill fetches calendar events, but analysis indicates additional locking, serialization, and timeout behavior via filesystem lock files. Even if not overtly malicious, undisclosed side effects in /tmp or configurable paths can affect other executions, enable denial-of-service through stale locks, or create unsafe file-interaction patterns if lock paths are controllable.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · src/service.py (reported line 426)May include surrounding context.

python
def load_env_file_if_present() -> bool:
    """Load a nearby .env file without overriding existing environment values."""
    candidates = []
    seen = set()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · src/utils.py (reported line 109)May include surrounding context.

python
def load_env_file_if_present() -> bool:
    """Load a nearby .env file without overriding existing environment values."""
    candidates = []
    seen = set()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · src/utils.py (reported line 114)May include surrounding context.

python
seen = set()

    for base in (Path.cwd(), repo_root()):
        for candidate in (base / ".env",):
            resolved = candidate.resolve()
            if resolved in seen:
                continue

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes a local Python script, reads environment variables and possibly a repo-root .env file, and makes outbound network requests, but it does not declare any explicit tool scope or permissions boundary in the skill manifest. This creates an authorization gap where reviewers and runtime policy may not clearly constrain or surface sensitive capabilities such as secret access and external data exfiltration.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 104)May include surrounding context.

"tz:"

text

> ⚠️ **Critical**: Channel, chatId, and timezone resolution follow the same rules as `remind-me` (see its SKILL.md). Auto-detect from session context. Never ask the user for these.

#### Step 3 — Confirm completion

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The constants set YAHOO_LANG to en-US and YAHOO_REGION to US, which imposes a specific language/locale configuration. For this rule set, forcing a locale without any documented user choice or opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The Yahoo fallback request explicitly sets an Accept-Language header using YAHOO_LANG and appends English as a preference fallback. This is a natural-language locale choice embedded in the code, and there is no indication here that the user can choose or opt into that locale behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.