Back to skill

Security audit

Gmail Bridge

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly transparent about connecting to Google Workspace, but it exposes sensitive account data and write actions through a loosely scoped local bridge.

Review this before installing if your Google Workspace account contains sensitive mail, files, sheets, or calendar data. Only use it with a trusted local bridge, avoid setting GMAIL_BRIDGE_URL to non-local destinations, rotate BRIDGE_SECRET if it may have been exposed, and require explicit user approval before running write or create operations.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
run.sh:4
Finding
Bridge secret and Workspace data can be transmitted to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `run.sh:4-9` **Vulnerability Type**: Unvalidated service endpoint with automatic credential forwarding **Risk Level**: Medium ### Complete Code Snippet ```bash BASE_URL="${GMAIL_BRIDGE_URL:-http://127.0.0.1:8787}" SECRET="${BRIDGE_SECRET:-}" # optional; if your bridge enforces x-bridge-secret hdrs=() if [[ -n "${SECRET}" ]]; then hdrs=(-H "x-bridge-secret: ${SECRET}") fi ``` ### Technical Analysis The script allows `GMAIL_BRIDGE_URL` to override the documented loopback endpoint without validating the destination host, scheme, or port. When `BRIDGE_SECRET` is present, the script automatically adds it to every request as the `x-bridge-secret` header. Consequently, a party capable of influencing the script's environment can redirect requests to an arbitrary HTTP or HTTPS server. This exposes both the bridge secret and request content to that server. Depending on the invoked operation, exposed information can include Gmail queries and message identifiers, Drive queries and file identifiers, spreadsheet identifiers and values, or calendar details. The default URL uses unencrypted HTTP. While loopback HTTP does not traverse an external network under normal conditions, an overridden URL may also use plaintext HTTP because the script does not enforce transport security. ### Attack Path 1. The attacker gains the ability to control or inject environment variables for the process invoking `run.sh`, such as through a wrapper, task configuration, inherited environment, or compromised launcher. 2. The attacker sets `GMAIL_BRIDGE_URL` to an attacker-controlled endpoint, for example `http://attacker.example:8787`. 3. The legitimate environment contains `BRIDGE_SECRET`, or the user exports it according to the skill documentation. 4. The user or agent invokes any supported operation. 5. `curl` sends the `x-bridge-secret` header and operation-specific request data to the attacker-controlled server. 6. If the capt ...[truncated 774 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `GMAIL_BRIDGE_URL` override if remote bridge endpoints are not a required feature. 2. If configurability is required, parse the URL and allow only explicitly approved loopback hosts such as `127.0.0.1`, `[::1]`, or a strictly controlled allowlist. 3. Reject URLs containing user information, fragments, unexpected ports, or unsupported schemes. 4. Require HTTPS for every non-loopback destination. 5. Attach `x-bridge-secret` only after validating that the destination is trusted. 6. Consider using a local Unix-domain socket to avoid network destination ambiguity. 7. Configure `curl` to reject redirects, or ensure credentials are never forwarded across redirects. Explicitly use options such as `--proto '=https'` for approved remote endpoints and an appropriate redirect policy. 8. Store the secret with the narrowest possible permissions and rotate it immediately if it may have been transmitted to an untrusted endpoint. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
run.sh:91
Finding
Unescaped arguments permit JSON structure injection in Sheets write requests<![CDATA[ ## Vulnerability Details **File Location**: `run.sh:91-100` **Vulnerability Type**: Unsafe manual JSON construction **Risk Level**: Medium ### Complete Code Snippet ```bash sheets-set) spreadsheetId="${1:-}" range="${2:-}" values_json="${3:-}" if [[ -z "${spreadsheetId}" || -z "${range}" || -z "${values_json}" ]]; then usage; exit 2; fi curl -sS "${hdrs[@]}" -X POST "${BASE_URL}/sheets/set" \ -H "Content-Type: application/json" \ -d "{\"spreadsheetId\":\"${spreadsheetId}\",\"range\":\"${range}\",\"values\":${values_json},\"valueInputOption\":\"USER_ENTERED\"}" \ | eval "${jq_pretty}" ;; ``` ### Technical Analysis The `spreadsheetId` and `range` arguments are interpolated directly into a JSON string without JSON escaping. An argument containing a quotation mark, backslash, control character, or additional JSON syntax can terminate its intended string and alter the structure of the request body. The `values_json` argument is deliberately inserted as raw JSON but is not validated before transmission. It can therefore make the entire request malformed or introduce an unexpected JSON type or structure. The ultimate result depends on the bridge's JSON parser, duplicate-key handling, and server-side validation. Shell quoting prevents these arguments from becoming shell commands in this specific statement, so this is not shell command injection. The issue is injection into the JSON request consumed by the privileged local bridge. ### Attack Path 1. An attacker influences an argument passed to `run.sh sheets-set`, directly or through untrusted data incorporated by an agent or wrapper. 2. The attacker supplies a crafted `spreadsheetId` or `range` containing JSON delimiters, or supplies an unexpected raw JSON value through `values_json`. 3. The script concatenates that data into the request body without escaping or schema validation. 4. The resulting body contains attacker-controlled JSON structure rather than ...[truncated 815 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Construct the complete request body with `jq` rather than string concatenation: ```bash if ! printf '%s' "${values_json}" | jq -e 'type == "array"' >/dev/null; then echo "values_json must be a valid JSON array" >&2 exit 2 fi request_body="$( jq -nc \ --arg spreadsheetId "${spreadsheetId}" \ --arg range "${range}" \ --argjson values "${values_json}" \ '{ spreadsheetId: $spreadsheetId, range: $range, values: $values, valueInputOption: "USER_ENTERED" }' )" curl -sS "${hdrs[@]}" -X POST "${BASE_URL}/sheets/set" \ -H "Content-Type: application/json" \ --data-binary "${request_body}" | jq -C . ``` Additionally: 1. Validate spreadsheet identifiers against the expected identifier syntax. 2. Validate range expressions against an accepted A1-notation format or allowlist. 3. Require `values_json` to be an array of arrays and enforce reasonable row, column, and payload-size limits. 4. Enforce an equivalent strict schema on the bridge server; client-side validation must not be the only security boundary. 5. Consider whether `RAW` should be used instead of `USER_ENTERED` when formula interpretation is not explicitly required. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill exposes shell-based access to a local API that can read emails, search Drive, read/write Sheets, create calendar events, and forward email, but it declares no explicit tool scope or permission boundary. Without an allowlist or permission declaration, an agent may invoke these commands more broadly than intended, increasing the risk of unauthorized access, data exfiltration, or state-changing actions on the user's Google Workspace data.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill documents state-changing operations like forwarding email, writing spreadsheet ranges, and creating calendar events without any explicit warning or confirmation requirement. In an agent setting, this can lead to accidental data modification or exfiltration if a user request is ambiguous or if the agent over-executes a task using the provided shell commands.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The sheets-set command performs a write operation to a Google Sheet via POST, which can modify user data, but the script provides no confirmation prompt, warning message, or user-facing disclosure at the point of execution. In this file, the usage text describes the command syntactically but does not warn that it changes remote data.

External Transmission

Medium
Category
Data Exfiltration
Content
range="${2:-}"
    values_json="${3:-}"
    if [[ -z "${spreadsheetId}" || -z "${range}" || -z "${values_json}" ]]; then usage; exit 2; fi
    curl -sS "${hdrs[@]}" -X POST "${BASE_URL}/sheets/set" \
      -H "Content-Type: application/json" \
      -d "{\"spreadsheetId\":\"${spreadsheetId}\",\"range\":\"${range}\",\"values\":${values_json},\"valueInputOption\":\"USER_ENTERED\"}" \
      | eval "${jq_pretty}"
Confidence
86% confidence
Finding
The script transmits spreadsheet modification data to a local HTTP service and constructs the JSON body by directly interpolating unescaped shell variables such as spreadsheetId and range. If those values contain quotes or crafted content, the request body can be malformed or manipulated, potentially causing unintended writes or parameter injection into the bridge API; additionally, the bridge uses plaintext HTTP and an optional secret, so any local compromise or rogue local service could abuse these high-privilege operations.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The cal-create command issues a POST request that creates a calendar event, which changes user data in an external system, but the script contains no confirmation prompt or explicit warning before performing the action. The operation is not accompanied by a visible disclosure in this file that it will create a persistent calendar entry.

External Transmission

Medium
Category
Data Exfiltration
Content
if [[ -z "${summary}" || -z "${startISO}" || -z "${endISO}" ]]; then usage; exit 2; fi

    curl -sS "${hdrs[@]}" -X POST "${BASE_URL}/calendar/create" \
      -H "Content-Type: application/json" \
      -d "$(jq -nc \
        --arg summary "${summary}" \
Confidence
72% confidence
Finding
This command sends calendar event creation data to a local bridge that can modify a user's Google Workspace data, which is sensitive capability exposure. Although the JSON body is safely assembled with jq, the operation still relies on a local HTTP endpoint with optional authentication, so a misconfigured or spoofed local service could receive or perform unintended privileged actions.

Static analysis

No suspicious patterns detected.