Back to skill

Security audit

Gmail No Send

Security checks for vulnerabilities and agentic risk

Overview

This Gmail skill has no send command in its code, but it requests powerful Gmail permissions and stores a reusable local token, so its read-only and cannot-send framing needs careful review.

Install only if you are comfortable granting Gmail read, compose, and modify permissions to this tool. Treat the local token as a sensitive credential, restrict its filesystem permissions, revoke it from your Google account if exposed, and prefer a reviewed pinned release or local source review over the mutable Git install. Do not rely on the name as a true platform-level no-send guarantee; it is no-send only in the current application code path.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/gmail_no_send/auth.py:30
Finding

OAuth Credentials and Audit Logs Are Stored Without Enforced Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/gmail_no_send/auth.py:30-32; scripts/gmail_no_send/utils.py:18-30
Vulnerability Type: Insecure local storage of sensitive credentials and audit metadata
Risk Level: Medium

Vulnerable Code

scripts/gmail_no_send/auth.py:30-32:

python
def save_credentials(creds: Credentials):
    ensure_config_dir()
    token_path().write_text(creds.to_json())

scripts/gmail_no_send/utils.py:18-30:

python
def ensure_config_dir():
    config_dir().mkdir(parents=True, exist_ok=True)


def audit_log(action: str, payload: dict | None = None):
    ensure_config_dir()
    entry = {
        "ts": int(time.time()),
        "action": action,
        "payload": payload or {},
    }
    with audit_log_path().open("a", encoding="utf-8") as f:
        f.write(json.dumps(entry) + "\n")

Technical Analysis

The application writes a refresh-capable OAuth token to token.json using Path.write_text() and appends operational metadata to audit.log using the default filesystem creation mode. It also creates the configuration directory without explicitly applying mode 0700.

Effective permissions therefore depend on the process umask and any pre-existing directory or files. Under a permissive environment, another local account or process may be able to read these files. The token is particularly sensitive because its scopes include Gmail read, modify, and compose authorization. Although this application has no send endpoint, a party that obtains the token can reuse it directly with the Gmail API, where the compose scope can permit sending.

The audit log can contain account labels, Gmail search queries, recipient addresses, message subjects, message IDs, and draft IDs. Exposure may reveal sensitive correspondence metadata even when the OAuth token itself is not compromised.

The documented threat model recommends applying chmod 600 to the token ...[truncated 1649 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the configuration directory with mode 0700 and verify its owner and permissions before every use.
  2. Create OAuth token and audit files with mode 0600, rather than relying on the process umask.
  3. Write credentials atomically to a newly created private file, flush and synchronize it, and then replace the destination.
  4. Reject symbolic links and files not owned by the current user to reduce symlink and pre-created-file attacks.
  5. Validate permissions and ownership when loading an existing token; fail closed if the path is unsafe.
  6. Consider storing OAuth credentials in an operating-system credential manager or encrypted secret store.
  7. Minimize sensitive audit fields. Hash, redact, or omit search queries, recipient addresses, and message subjects where they are not required.
  8. Provide documented token revocation and rotation procedures for suspected exposure.
  9. Add automated tests verifying directory mode 0700, file mode 0600, ownership validation, and rejection of symlinked paths.

T08 · Insecure Dependencies

Warning
Location
scripts/pyproject.toml:9
Finding

Unpinned Dependencies and Mutable Git Installation Create Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: scripts/pyproject.toml:9-12; scripts/requirements.txt:1-3; SKILL.md:20-22
Vulnerability Type: Non-reproducible installation from mutable and unconstrained upstream sources
Risk Level: Medium

Vulnerable Code

scripts/pyproject.toml:9-12:

toml
dependencies = [
  "google-api-python-client",
  "google-auth-httplib2",
  "google-auth-oauthlib",
]

scripts/requirements.txt:1-3:

text
google-api-python-client
google-auth-httplib2
google-auth-oauthlib

SKILL.md:20-22:

bash
pip install git+https://github.com/meimakes/gmail-no-send.git

Technical Analysis

The dependency declarations contain no version constraints or integrity hashes. A future installation may therefore resolve to package versions and transitive dependencies that were not included in this audit.

The documented Git installation command also omits a tag or commit identifier. It follows the repository's current default branch, making the installed code mutable relative to the reviewed artifact. Consequently, reviewing this project once does not establish the integrity of code installed later with the documented command.

Python package installation can execute build backend logic, and installed dependencies run in the application's process when imported. A compromised upstream repository, malicious future release, or compromised transitive dependency could consequently execute with the installing user's privileges and access locally stored Gmail credentials.

No evidence was found that the currently reviewed Google packages or project source are malicious. The vulnerability is the absence of reproducible version and integrity controls, which exposes future installations to upstream changes.

Attack Path

  1. An upstream repository, package release process, maintainer account, or transitive dependency is compromised or publishes an unsafe update.
  2. A ...[truncated 1588 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin direct and transitive dependencies to reviewed versions through a generated lock file.
  2. Use hashes for all resolved distributions and install with hash verification enabled.
  3. Replace the mutable Git installation example with an immutable reviewed commit, signed tag, or versioned release artifact.
  4. Publish checksums or signatures for release artifacts and document how users should verify them.
  5. Use an automated dependency update process that runs security scans and tests before accepting new versions.
  6. Generate a software bill of materials for each release.
  7. Build and publish packages through a controlled, reproducible release pipeline with protected maintainer credentials.
  8. Avoid installing as an administrator or root; use an isolated virtual environment with least privilege.
  9. Regularly scan pinned dependencies for known vulnerabilities while retaining explicit review before upgrades.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill repeatedly claims it is 'read-only' and that sending is impossible, but it also describes draft creation, draft update, archive operations, and acknowledges use of the Gmail compose scope. Because compose-scoped OAuth credentials are inherently capable of send-adjacent or send-capable API use outside this application's narrow code path, the security claim is overstated and could mislead operators into granting stronger access than they realize.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: gmail-no-send
description: Read-only Gmail CLI that cannot send email by design. Search, read, create drafts, update drafts, and archive messages — with zero send capability in the codebase. Use when the agent needs Gmail access for reading email, drafting responses, searching inbox, or archiving messages, but sending must be impossible. Ideal for AI agents that should never autonomously send email. Requires Google Cloud OAuth credentials (user provides their own client_secret.json).
metadata: {"openclaw":{"security":"No send endpoints exist in code. OAuth scope includes compose (Gmail has no drafts-only scope), but send is enforced absent at the application layer. All operations are audit-logged.","author":"Mei Park (@meimakes)","homepage":"https://github.com/meimakes/gmail-no-send"}}
---

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 32)May include surrounding context.

md
---
name: gmail-no-send
description: Read-only Gmail CLI that cannot send email by design. Search, read, create drafts, update drafts, and archive messages — with zero send capability in the codebase. Use when the agent needs Gmail access for reading email, drafting responses, searching inbox, or archiving messages, but sending must be impossible. Ideal for AI agents that should never autonomously send email. Requires Google Cloud OAuth credentials (user provides their own client_secret.json).
metadata: {"openclaw":{"security":"No send endpoints exist in code. OAuth scope includes compose (Gmail has no drafts-only scope), but send is enforced absent at the application layer. All operations are audit-logged.","author":"Mei Park (@meimakes)","homepage":"https://github.com/meimakes/gmail-no-send"}}
---

YARA rule 'agent_skill_remote_bootstrap_execution': Remote script or code download followed by execution/bootstrap installation [agent_skills]

High
Category
YARA Match
Confidence
89% confidence
Finding

The skill recommends pip install git+https://github.com/..., which performs remote code retrieval and installation directly from a live repository. That creates a supply-chain risk: users may install unreviewed or changed code at execution time, and the broad claims of 'cannot send' could cause them to trust the package more than they should.

Content

Scanner excerpt · SKILL.md (reported line 22)May include surrounding context.

yer. All operations are audit-logged.","author":"Mei Park (@meimakes)","homepage":"https://github.com/meimakes/gmail-no-send"}}

gmail-no-send

Gmail CLI that cannot send email. Not "won't" — can't. There is no send function in the codebase.

Install

Requires Python 3.9+.

bash
cd <skill-dir>/scripts/gmail-no-send
pip install -e .

Or install from the GitHub repo:

bash
pip install git+https://github.com/meimakes/gmail-no-send.git

First-Time Auth

Each user needs their own Google Cloud OAuth credentials:

  1. Create a project at console.cloud.google.com
  2. Enable the Gmail API
  3. Create OAuth 2.0 credentials (Desktop app type)
  4. Download client_secret.json

Then authenticate:

bash
gmail-no-send auth --client-secret /path/to/client_secret.json --account myname

This opens a browser for Google OAuth consent. Token is saved to ~/.config/gmail-no-send/token.json and auto-refreshes.

Commands

All commands require --account <name> (matches

Credential Access

High
Category
Privilege Escalation
Confidence
72% confidence
Finding

The documented auth flow stores a long-lived OAuth token in ~/.config/gmail-no-send/token.json with auto-refresh, creating a reusable local credential that can persist access to Gmail. If local filesystem protections are weak, another process, user, or compromised agent environment could reuse that token to access or potentially send mail within the granted OAuth scope.

Content

Scanner excerpt · SKILL.md (reported line 36)May include surrounding context.

Then authenticate:

bash
gmail-no-send auth --client-secret /path/to/client_secret.json --account myname

This opens a browser for Google OAuth consent. Token is saved to ~/.config/gmail-no-send/token.json and auto-refreshes.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill is marketed as a read-only Gmail CLI that should never send email, but it requests gmail.compose and gmail.modify in addition to gmail.readonly. Those scopes materially exceed a read-only trust boundary and enable draft creation and mailbox changes, so a compromised or extended tool could act beyond what users were led to authorize.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill exposes sensitive capabilities implied by its workflow—network access, local file read/write, and environment interaction—yet does not declare any explicit tool scope or permission boundaries. That omission makes it harder for a host agent platform to constrain execution and increases the chance the skill is run with broader privileges than intended.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: gmail-no-send
description: Read-only Gmail CLI that cannot send email by design. Search, read, create drafts, update drafts, and archive messages — with zero send capability in the codebase. Use when the agent needs Gmail access for reading email, drafting responses, searching inbox, or archiving messages, but sending must be impossible. Ideal for AI agents that should never autonomously send email. Requires Google Cloud OAuth credentials (user provides their own client_secret.json).
metadata: {"openclaw":{"security":"No send endpoints exist in code. OAuth scope includes compose (Gmail has no drafts-only scope), but send is enforced absent at the application layer. All operations are audit-logged.","author":"Mei Park (@meimakes)","homepage":"https://github.com/meimakes/gmail-no-send"}}
---

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 3)May include surrounding context.

md
---
name: gmail-no-send
description: Read-only Gmail CLI that cannot send email by design. Search, read, create drafts, update drafts, and archive messages — with zero send capability in the codebase. Use when the agent needs Gmail access for reading email, drafting responses, searching inbox, or archiving messages, but sending must be impossible. Ideal for AI agents that should never autonomously send email. Requires Google Cloud OAuth credentials (user provides their own client_secret.json).
metadata: {"openclaw":{"security":"No send endpoints exist in code. OAuth scope includes compose (Gmail has no drafts-only scope), but send is enforced absent at the application layer. All operations are audit-logged.","author":"Mei Park (@meimakes)","homepage":"https://github.com/meimakes/gmail-no-send"}}
---

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
80% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/threat-model.md (reported line 11)May include surrounding context.

md
## What It Does NOT Protect Against

1. **Token reuse.** If an attacker obtains `~/.config/gmail-no-send/token.json`, they can use it with the Gmail API directly (the token has `compose` scope). Mitigate: restrict file permissions (`chmod 600`), encrypt at rest.

2. **Malicious code modification.** Someone could add a send function to the codebase. Mitigate: pin to a known version, verify checksums, review source.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The inline comment states that no send endpoints are used, but the requested gmail.compose scope grants send-adjacent capability and broad draft access. Misleading comments around security-sensitive permissions increase the risk of unsafe review assumptions, user deception, and future code changes that leverage already-granted privileges.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This code persists OAuth credentials to disk via token_path().write_text(creds.to_json()), which affects sensitive user data. Although there is audit logging elsewhere, there is no visible user-facing prompt, print/log disclosure, or inline comment/docstring warning that tokens will be stored locally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
79% confidence
Finding

Creating an InstalledAppFlow and running the local-server OAuth flow initiates network communication with Google and transmits authentication-related user/system data. The file includes audit logs but no user-facing disclosure, prompt, or explanatory comment warning that external authentication and data exchange will occur.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The create_draft method sends recipient, subject, and body data to Google's Gmail service via a network API call. Although the action is audit-logged, there is no user-facing confirmation, print/log disclosure, or explanatory comment/docstring in this file warning that message content will be transmitted to an external service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The update_draft method transmits recipient, subject, and body content over the network to update a Gmail draft. The file contains audit logging, but no visible user-facing warning, confirmation, or explanatory comment/docstring disclosing that private email content is being sent to an external provider.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

The dependency is specified without a version pin, so installations may resolve to different releases over time. This increases supply-chain risk because a future compromised, breaking, or vulnerable upstream release could be pulled into the environment without review; in a Gmail-integrated skill, that could affect OAuth handling or API interactions.

Content

Scanner excerpt · scripts/requirements.txt (reported line 1)May include surrounding context.

text
google-api-python-client
google-auth-httplib2
google-auth-oauthlib

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

This package is unpinned, which makes builds non-reproducible and exposes the skill to accidental or malicious upstream changes. Because the skill depends on Google authentication libraries, an unsafe future version could impact token exchange, credential storage, or authenticated API access.

Content

Scanner excerpt · scripts/requirements.txt (reported line 2)May include surrounding context.

text
google-api-python-client
google-auth-httplib2
google-auth-oauthlib

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
95% confidence
Finding

Leaving this OAuth-related package unpinned means the environment may install an unexpected version at deployment time. That creates avoidable supply-chain exposure and operational instability, especially in a skill that handles Gmail OAuth flows and user-granted mailbox access.

Content

Scanner excerpt · scripts/requirements.txt (reported line 3)May include surrounding context.

text
google-api-python-client
google-auth-httplib2
google-auth-oauthlib