Back to skill

Security audit

IDFM Journey (PRIM/Navitia)

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent Île-de-France transit helper, but one command option can send the user's IDFM API key to an arbitrary server.

Review this skill before installing. Use it only in an environment where the IDFM_PRIM_API_KEY is limited in scope, and do not pass --base-url unless you have audited and trust the destination. A safer version should remove that option or restrict it to the official prim.iledefrance-mobilites.fr Navitia endpoint before 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:251
Finding

Caller-Controlled Base URL Can Exfiltrate the IDFM API Key

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 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) ``` ```python p.add_argument( "--base-url", default=BASE_URL, help="override PRIM base URL (default: %(default)s)", ) sp = p.add_subparsers(dest="cmd", required=True) p_places = sp.add_parser("places", help="resolve places via /places") p_places.add_argument("query") p_places.add_argument("--count", type=int, default=5) p_j = sp.add_parser("journeys", help="plan a journey via /journeys") p_j.add_argument("--from", dest="from_query", required=True) p_j.add_ar ...[truncated 2907 chars]
Remediation
View remediation
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 (3)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill instructs the user to run a bundled Python script that reads an environment variable and makes external network requests, but the manifest declares no explicit tool scope or permissions. This creates an authorization and transparency gap: an agent or platform may not clearly signal that the skill needs environment-secret access and outbound network capability, increasing the risk of unintended secret exposure or unauthorized external calls.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The CLI exposes a user-controlled --base-url that overrides the expected IDFM/Navitia endpoint, which expands the script from a fixed transit-query tool into a generic authenticated HTTP client. In agent or automation contexts, this can enable SSRF-like behavior, unintended outbound requests, and possible leakage of the IDFM API key to attacker-controlled hosts because the apikey header is sent to whatever URL is provided.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

Several user-facing strings in journey section rendering are fixed in French, such as "attente" and "marche", and this continues in later printed output. The file does not offer a language choice or clearly document that the tool is intentionally French-only, which can violate locale policy requirements.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.