Back to skill

Security audit

IDFM Journey

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent IDFM transit helper, but it exposes an option that can send the user's IDFM API key to an arbitrary server.

Install only if you trust users and agents invoking it not to use --base-url with anything except the official IDFM PRIM/Navitia endpoint. Prefer a version that removes that option or validates it against https://prim.iledefrance-mobilites.fr/marketplace/v2/navitia before loading or sending the API key.

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

Error
Location
scripts/idfm.py:238
Finding

Unrestricted API Base URL Allows IDFM Credential Exfiltration

Content
View full analysis
dict: req = urllib.request.Request(url) req.add_header("apikey", api_key) req.add_header("accept", "application/json") try: with urllib.request.urlopen(req, timeout=timeout_s) as resp: raw = resp.read().decode("utf-8") return json.loads(raw) except urllib.error.HTTPError as e: body = None try: body = e.read().decode("utf-8", errors="replace") except Exception: pass raise PrimError(f"HTTP {e.code} {e.reason}: {body or ''}".strip()) from e except urllib.error.URLError as e: raise PrimError(f"Network error: {e}") from e ``` The client obtains the sensitive credential from the environment and builds requests using an arbitrary base URL: ```python class PrimClient: def __init__(self, api_key: str | None = None, base_url: str = BASE_URL): self.api_key = api_key or os.environ.get("IDFM_PRIM_API_KEY") if not self.api_key: raise PrimError("Missing IDFM_PRIM_API_KEY env var") self.base_url = base_url.rstrip("/") def get(self, path: str, params: dict | None = None) -> dict: base = f"{self.base_url}/{path.lstrip('/')}" qs = urllib.parse.urlencode(params or {}, doseq=True) url = f"{base}?{qs}" if qs else base return _http_get_json(url, self.api_key) ``` The command-line interface exposes the destination without validation: ```python p.add_argument( "--base-url", default=BASE_URL, help="override PRIM base URL (default: %(default)s)", ) ``` The unvalidated value is ...[truncated 2496 chars]
Remediation
View remediation
str: parsed = urlsplit(value) if ( parsed.scheme != ALLOWED_SCHEME or parsed.hostname != ALLOWED_HOST or parsed.port not in ALLOWED_PORTS or parsed.username is not None or parsed.password is not None or parsed.query or parsed.fragment or parsed.path.rstrip("/") != ALLOWED_PATH ): raise PrimError("Untrusted PRIM base URL") return value.rstrip("/") ``` 3. **Validate the destination before retrieving the API key.** This reduces the chance that future code paths accidentally expose the credential to an untrusted endpoint. 4. **Do not send production credentials to test servers.** If endpoint substitution is needed for automated tests, inject a separate dummy credential and restrict the feature to an explicit test mode. 5. **Require HTTPS and reject redirects to untrusted origins.** Ensure every redirect remains on the exact approved HTTPS origin before forwarding the authentication header. 6. **Rotate the API key if the vulnerable option has been used with any untrusted destination.** Review relevant API usage and quota logs for unauthorized requests. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill invokes a bundled Python script that uses environment variables and makes outbound network requests to the IDFM PRIM/Navitia API, but the manifest does not declare any explicit tool scope such as permissions or allowed-tools. This creates a transparency and policy-enforcement gap: an agent or reviewer cannot easily determine that the skill can access secrets and exfiltrate data over the network, which increases the risk of unintended secret exposure or misuse if the skill is triggered in a broader environment.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The CLI exposes a user-controlled --base-url that is passed directly into PrimClient and then used for all outbound requests, while still attaching the IDFM API key as an HTTP header. This enables server-side requests to arbitrary hosts and can leak the API key to attacker-controlled infrastructure, expanding the skill beyond its stated purpose of querying IDFM/Navitia only.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.