Back to skill

Security audit

Nordpool Fi

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it claims: fetches Finnish electricity prices from a public API and calculates charging windows, with some reliability caveats.

Install only if you are comfortable with the skill making outbound requests to api.porssisahko.net when used. Treat the charging-window output as advisory because the timezone handling and API error handling should be improved before relying on it for automated or high-cost scheduling.

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
bin/nordpool-fi.py:12
Finding
Unbounded Network Wait and Remote Response Size## Vulnerability Details **File Location**: `bin/nordpool-fi.py`, lines 12–13 **Vulnerability Type**: Unbounded external API request and response processing **Risk Level**: Medium ### Vulnerable Code ```python with urllib.request.urlopen(req) as response: return json.loads(response.read().decode()) ``` ### Technical Analysis The external HTTPS request does not specify a timeout. Consequently, an unavailable, slow, or malicious upstream service may leave the process waiting indefinitely. The code also calls `response.read()` without imposing a maximum response size. The complete response is loaded into memory before JSON decoding and parsing. If the API endpoint or its delivery path returns an excessively large body, the process may consume substantial memory or terminate due to resource exhaustion. TLS reduces the likelihood of an arbitrary network attacker modifying the response, but it does not protect against upstream compromise, service malfunction, DNS or trust-store compromise, or unexpectedly large legitimate responses. The code also does not validate the response content type or schema before processing it. ### Attack Path 1. An attacker compromises or gains control over the configured API endpoint or a trusted component in its delivery path. 2. When the skill requests `https://api.porssisahko.net/v2/latest-prices.json`, the attacker either delays the response indefinitely or returns an excessively large body. 3. Because no timeout is configured, a delayed response can block the skill process. 4. Because no response-size limit is enforced, an oversized response is loaded into memory in full. 5. The process experiences prolonged blocking, excessive memory consumption, or termination. ### Impact Assessment The issue can cause denial of service within the privileges and resource limits of the skill process. It does not directly grant code execution, elevated privileges, credential access, or persistence. Its scope is limited to process avai ...[truncated 109 chars]
Remediation
## Remediation Suggestions - Configure a finite timeout when opening the URL, appropriate to the expected API latency. - Read the response incrementally and enforce a strict maximum byte count before decoding or parsing it. - Reject responses whose declared `Content-Length` exceeds the limit, while still enforcing the limit during streaming because that header may be absent or inaccurate. - Validate that the response has the expected JSON content type. - Validate the parsed object against an explicit schema, including the expected `prices` collection and required field types. - Handle timeout, size-limit, decoding, and schema-validation failures separately and return a controlled error. - Consider applying runtime memory and execution-time limits as an additional containment measure. A hardened implementation should follow this pattern: ```python MAX_RESPONSE_BYTES = 2 * 1024 * 1024 REQUEST_TIMEOUT_SECONDS = 10 with urllib.request.urlopen( req, timeout=REQUEST_TIMEOUT_SECONDS ) as response: content_type = response.headers.get_content_type() if content_type != "application/json": raise ValueError("Unexpected response content type") body = response.read(MAX_RESPONSE_BYTES + 1) if len(body) > MAX_RESPONSE_BYTES: raise ValueError("API response exceeds the permitted size") data = json.loads(body.decode("utf-8")) ```
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 (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill invokes a network-capable script but the manifest does not declare any tool scope such as permissions or allowed-tools. This weakens least-privilege controls and makes network access implicit rather than reviewable, which can hide unexpected outbound requests or future capability creep.

External Transmission

Medium
Category
Data Exfiltration
Content
from datetime import datetime, timedelta, timezone

def get_data():
    url = "https://api.porssisahko.net/v2/latest-prices.json"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The code comments claim Finland winter/summer handling, but the implementation always applies a fixed UTC+2 offset. During daylight saving time Finland uses UTC+3, so hourly grouping, current-hour matching, and charging-window calculations can be shifted by one hour, producing incorrect recommendations that could cause mistimed charging or misleading price statistics.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The description and keywords explicitly constrain the skill to Finland ("Hourly electricity prices for Finland", keyword "finland") without any natural-language indication that this locale restriction is optional or user-selected. Under the policy rule for language/locale constraints, this can be treated as a locale-specific limitation that is not justified in the file itself.

Static analysis

No suspicious patterns detected.