Back to skill

Security audit

Synero

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to perform its stated Synero AI-council function, but it sends user prompts and an API key over the network and allows an environment variable to redirect that authenticated request to an arbitrary endpoint.

Review before installing. Use this only when you intend to send the prompt to Synero, avoid secrets or regulated/confidential data unless approved, keep SYNERO_API_KEY narrowly scoped and revocable, and do not set SYNERO_API_URL unless you fully trust the exact HTTPS endpoint receiving the bearer token.

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
scripts/synero-council.py:19
Finding
Configurable API Endpoint Allows Bearer Credential and Prompt Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/synero-council.py`, lines 19 and 143–159 **Vulnerability Type**: Unvalidated destination for sensitive network transmission **Risk Level**: Medium ### Technical Analysis The script accepts the complete API destination from the `SYNERO_API_URL` environment variable without validating its scheme or hostname: ```python API_URL = os.environ.get("SYNERO_API_URL", "https://synero.ai/api/query") ``` It subsequently sends the user-supplied prompt and bearer API key to that destination: ```python payload = json.dumps(build_payload(args), ensure_ascii=False).encode("utf-8") req = urllib.request.Request(API_URL, data=payload, method="POST") req.add_header("Content-Type", "application/json") req.add_header("Accept", "text/event-stream") req.add_header( "User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/126.0.0.0 Safari/537.36", ) req.add_header("Authorization", f"Bearer {api_key}") events: dict[str, Any] = {"synthesis": "", "advisor": {}, "complete": None} try: with urllib.request.urlopen(req, timeout=REQUEST_TIMEOUT_SECONDS) as resp: ``` Network transmission is necessary for the Skill’s declared council-query functionality, and the default destination uses HTTPS at `synero.ai`. However, allowing an unrestricted environment variable to replace the entire destination means the same sensitive request can be sent to an arbitrary host or over plaintext HTTP. The request body includes the prompt and may also include thread identifiers, parent query identifiers, and model configuration. The `Authorization` header contains the `SYNERO_API_KEY`. No scheme restriction, hostname allowlist, or explicit warning is applied before these values are transmitted. This is a least-privilege concern because endpoint customization implicitly grants the configured destination access to both the user’s submitted information and a reusable ...[truncated 2001 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Require HTTPS** - Parse the configured URL with `urllib.parse.urlparse`. - Reject URLs whose scheme is not exactly `https`. - Reject malformed URLs, embedded credentials, fragments, and missing hostnames. 2. **Restrict credential destinations** - By default, permit bearer-token transmission only to `synero.ai` or a documented set of trusted Synero API hosts. - If custom enterprise endpoints are required, introduce an explicit allowlist such as `SYNERO_ALLOWED_API_HOSTS`. - Compare normalized hostnames rather than using substring or suffix checks that could accept domains such as `synero.ai.attacker.example`. 3. **Separate endpoint customization from credential forwarding** - Do not automatically attach `SYNERO_API_KEY` to arbitrary custom destinations. - Require a separate, explicit opt-in before forwarding credentials to a non-default host. - Prefer host-specific credential variables when multiple providers or self-hosted deployments are supported. 4. **Prevent unsafe redirects** - Ensure redirects cannot forward the `Authorization` header to an untrusted origin. - Disable redirects for authenticated requests or validate every redirect target before following it. 5. **Warn users about data handling** - Clearly state that prompts, identifiers, model selections, and the API key are transmitted to the configured endpoint. - Warn users not to include secrets or regulated data unless the destination and its retention policy are trusted. 6. **Reduce credential impact** - Use narrowly scoped, revocable, and short-lived API credentials where supported. - Apply server-side rate limits and usage alerts. - Rotate the key immediately if an untrusted endpoint may have received it. A hardened implementation should validate the URL before constructing the authenticated request and terminate with a clear error whenever the destination is not an approved HTTPS origin. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Tainted flow: 'req' from os.environ.get (line 149, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
events: dict[str, Any] = {"synthesis": "", "advisor": {}, "complete": None}

    try:
        with urllib.request.urlopen(req, timeout=REQUEST_TIMEOUT_SECONDS) as resp:
            if resp.status != 200:
                body = resp.read().decode("utf-8", errors="replace")
                raise RuntimeError(f"HTTP {resp.status}: {body[:400]}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares environment and network capabilities through metadata but does not define an explicit permission or allowed-tools scope. That creates an authorization/consent gap: a caller may not realize the skill can transmit user prompts and environment-derived secrets to an external service, increasing the risk of unintended data exposure or overbroad execution in permissive runtimes.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The description is broad enough to match many common 'strategy' or 'research' requests, which can cause the skill to auto-activate in contexts where the user did not intend to send content to a third-party service. In this skill's context, overbroad activation is more dangerous because activation triggers external network transmission of potentially sensitive prompts.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The markdown explains that prompts are sent to Synero, but it does not provide a clear, prominent user warning that user input will be transmitted to an external API service. For a judgment-oriented assistant skill, users may include proprietary plans, hiring discussions, architecture details, or other sensitive data; without an explicit warning and consent cue, this creates a meaningful data leakage risk.

Missing User Warnings

Low
Confidence
88% confidence
Finding
This script transmits the user prompt, optional thread ID, optional parent query ID, and API key to a remote service without an explicit runtime notice. In a terminal tool used for strategy, research, hiring, or architecture questions, users may paste sensitive business or personal data, so the lack of a clear disclosure increases the chance of unintended data exposure to the third-party service.

Static analysis

No suspicious patterns detected.