Back to skill

Security audit

OpenMandate

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for an existing OpenMandate account, but it can perform consequential account actions and may send the API bearer key to an arbitrary configured base URL.

Install only for an existing OpenMandate account whose API key you control. Avoid setting OPENMANDATE_BASE_URL unless you fully trust the endpoint, and require explicit human confirmation before closing mandates, declining matches, or deleting contacts because the helper itself will execute those actions directly.

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/openmandate.py:37
Finding
Unvalidated API Base URL Can Disclose the OpenMandate Bearer Credential<![CDATA[ ## Vulnerability Details **File Location**: `scripts/openmandate.py`, lines 37–62 **Vulnerability Type**: Unvalidated credential destination / sensitive credential disclosure **Risk Level**: High ### Vulnerable Code ```python def _get_base_url() -> str: return os.environ.get(BASE_URL_ENV, DEFAULT_BASE_URL).rstrip("/") def _die(message: str) -> None: print(f"Error: {message}", file=sys.stderr) sys.exit(1) def _request(method: str, path: str, params: dict | None = None) -> dict: """Make an HTTP request to the OpenMandate API and return parsed JSON.""" base = _get_base_url() url = f"{base}{path}" if params: query = urllib.parse.urlencode({key: value for key, value in params.items() if value is not None}) if query: url = f"{url}?{query}" req = urllib.request.Request(url, method=method) req.add_header("Authorization", f"Bearer {_get_api_key()}") req.add_header("Content-Type", "application/json") req.add_header("Accept", "application/json") req.add_header("User-Agent", USER_AGENT) ``` ### Technical Analysis The helper obtains the request origin directly from the configurable `OPENMANDATE_BASE_URL` environment variable. It does not validate the URL scheme, hostname, port, path, or user-information component before attaching the secret from `OPENMANDATE_API_KEY` as a bearer credential. Consequently, the authorization header is not restricted to the legitimate `https://api.openmandate.ai` origin. A manipulated configuration can direct an authenticated request to an attacker-controlled HTTP or HTTPS endpoint. Allowing plain HTTP also permits interception of the credential by a network-positioned attacker. The HTTP client additionally relies on default redirect handling without explicitly enforcing a same-origin redirect policy. Security-sensitive authorization headers should never be forwarded to a different origin during redirects. ### Attack Path 1. An attacker obtains th ...[truncated 1653 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `OPENMANDATE_BASE_URL` support from production builds if endpoint customization is unnecessary. 2. Otherwise, parse the configured URL with `urllib.parse.urlsplit()` and enforce all of the following: - The scheme must be `https`. - The normalized hostname must exactly match an explicit allowlist, preferably only `api.openmandate.ai`. - The port must be absent or explicitly approved. - User information, fragments, query strings, and unexpected base paths must be rejected. 3. Construct API URLs from validated components rather than concatenating an arbitrary string with a path. 4. Implement an explicit redirect policy that either disables redirects or permits only same-scheme, same-host, and same-port redirects. 5. Ensure the `Authorization` header is removed before following any cross-origin redirect. 6. Do not send credentials over plain HTTP, including in development environments. 7. Add automated tests covering malicious schemes, lookalike domains, embedded credentials, unexpected ports, path confusion, and cross-origin redirects. 8. Rotate the API key if the helper has previously run with an untrusted `OPENMANDATE_BASE_URL`. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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 (6)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```text
POST   /v1/mandates/{mandate_id}/close
POST   /v1/matches/{match_id}/decline
DELETE /v1/contacts/{contact_id}
```

Confirm the exact target with the user before invoking a withdrawal action.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Missing User Warnings

High
Confidence
97% confidence
Finding
The delete-contact command issues a DELETE request directly with no warning, confirmation, or friction. This is especially risky because it affects contact records, which may contain sensitive business or personal information, and accidental or adversarial agent execution could lead to data loss and operational impact.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises use of environment variables and a hosted MCP endpoint but does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization gap where the runtime may permit broader network or environment access than intended, making it harder to constrain secret exposure or outbound requests. The skill context increases concern because it uses an API key and external service access, so least-privilege boundaries matter.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The close command performs a state-changing POST immediately, with no confirmation, dry-run, or safeguard against accidental invocation. In an agent context, a prompt injection, tool misuse, or simple misunderstanding could close a mandate irreversibly or prematurely, disrupting retained work.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The decline command submits a match-decline action immediately with no user-facing warning or secondary approval. Because declining a match can affect business opportunities and workflow outcomes, an unintended or manipulated invocation could cause material operational harm.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest says the skill is for accessing historical mandates and matches or closing retained work, but the CLI also exposes contact-listing and contact-deletion operations. This scope expansion increases the available attack surface and may let an agent perform actions on personal/contact data that users and integrators would not reasonably expect from the declared capability set.

Static analysis

No suspicious patterns detected.