Back to skill

Security audit

Gmail Enhanced

Security checks for vulnerabilities and agentic risk

Overview

This Gmail automation skill is mostly purpose-aligned, but it gives broad mailbox access and has unsafe attachment, token, and automation handling that warrants careful review before installation.

Install only if you are comfortable granting read, send, label, and modify access to Gmail. Use a dedicated Google OAuth client and least-privilege account where possible, keep token files private, review and limit any saved rules before running process_rules(), and avoid bulk attachment downloads until filename sanitization and overwrite protections are added.

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

Error
Location
gmail_enhanced.py:387
Finding

Path Traversal in Attachment Download Enables Arbitrary File Overwrite

Content
View full analysis

Vulnerability Details

File Location: gmail_enhanced.py, lines 387-393, with the file-write sink at lines 344-365
Vulnerability Type: Path traversal and arbitrary file overwrite
Risk Level: High

Vulnerable Code

python
def download_attachment(
    self,
    msg_id: str,
    attachment_id: str,
    save_path: str
) -> Dict:
    """Download attachment"""
    service = self._get_service()
    
    attachment = service.users().messages().attachments().get(
        userId='me',
        messageId=msg_id,
        id=attachment_id
    ).execute()
    
    # Decode
    data = base64.urlsafe_b64decode(attachment['data'])
    
    # Save
    with open(save_path, 'wb') as f:
        f.write(data)
    
    return {'saved_to': save_path, 'size': len(data)}
python
for att in attachments:
    if att['attachmentId']:
        save_path = os.path.join(save_dir, att['filename'])
        self.download_attachment(
            msg['id'],
            att['attachmentId'],
            save_path
        )

Technical Analysis

The attachment filename originates from untrusted MIME metadata supplied by the email sender. The application directly combines that filename with save_dir using os.path.join() without validating or normalizing it.

A filename containing traversal components such as ../../.bashrc can escape the intended download directory. If the attachment filename is an absolute path, os.path.join() can also discard save_dir entirely. The resulting path reaches open(save_path, 'wb'), which creates or truncates the destination file.

No resolved-path containment check, basename normalization, absolute-path rejection, symlink protection, or overwrite prevention is applied.

Attack Path

  1. An attacker sends an email containing an attachment whose MIME filename is a traversal path, such as ../../.bashrc.
  2. The victim invokes `search_att ...[truncated 1254 chars]
Remediation
View remediation

Remediation Suggestions

Treat every attachment filename as untrusted input:

  1. Reject absolute paths and filenames containing traversal components.
  2. Reduce the supplied filename to a safe basename and replace unsafe characters.
  3. Resolve both the download directory and destination path, then verify that the destination remains inside the download directory.
  4. Generate a server-controlled, collision-resistant filename rather than relying on MIME metadata.
  5. Use exclusive file creation where replacement is not required.
  6. Reject symlink destinations and consider opening files with platform-supported no-follow protections.
  7. Preserve the original filename only as display metadata.

Example hardening pattern:

python
from pathlib import Path
import re
import uuid

base_dir = Path(save_dir).resolve()
base_dir.mkdir(parents=True, exist_ok=True)

supplied_name = Path(att['filename']).name
safe_name = re.sub(r'[^A-Za-z0-9._-]', '_', supplied_name)
safe_name = safe_name or f"attachment-{uuid.uuid4().hex}"

destination = (base_dir / safe_name).resolve()
if destination.parent != base_dir:
    raise ValueError("Unsafe attachment path")

with open(destination, 'xb') as f:
    f.write(data)

If subdirectories are intentionally supported, use destination.is_relative_to(base_dir) or an equivalent containment check rather than requiring the immediate parent to equal base_dir.

T09 · Insecure Skill Coding Practices

Warning
Location
gmail_enhanced.py:78
Finding

OAuth Refresh Token Stored Without Enforced Restrictive Permissions

Content
View full analysis

Vulnerability Details

File Location: gmail_enhanced.py, lines 78-80
Vulnerability Type: Insecure storage of sensitive authentication credentials
Risk Level: Medium

Vulnerable Code

python
# Save token
Path(self.token_path).parent.mkdir(parents=True, exist_ok=True)
with open(self.token_path, 'w') as f:
    f.write(creds.to_json())

Technical Analysis

The application stores Gmail OAuth credentials, potentially including a reusable refresh token, in a plaintext JSON file. File and directory permissions are inherited from the process umask because the code does not explicitly create the directory with mode 0700 or the token file with mode 0600.

On a shared system or a system configured with a permissive umask, other local users may be able to read the token. The code also does not verify that the token path is not a symbolic link before writing, which can increase exposure in environments where another local user can manipulate the token directory.

Although plaintext token files are sometimes required by client applications, they must be protected as bearer credentials through restrictive permissions and safe file creation.

Attack Path

  1. A victim authenticates through the OAuth flow.
  2. The application serializes the resulting credentials to self.token_path.
  3. The token file receives permissions determined solely by the environment's umask.
  4. On a permissively configured or shared host, another local user reads the JSON token.
  5. The attacker extracts and reuses the access or refresh token.
  6. The attacker accesses Gmail using the permissions granted to the OAuth client until the token is revoked or otherwise becomes unusable.

Impact Assessment

Successful exploitation requires local access sufficient to read an inadequately protected token file. The attacker may obtain Gmail access within the OAuth scopes granted by this application, which include:

  • Reading ema ...[truncated 444 chars]
Remediation
View remediation

Remediation Suggestions

Protect the token as a sensitive bearer credential:

  1. Create the parent credentials directory with mode 0700.
  2. Atomically create or replace the token file with mode 0600.
  3. Explicitly correct permissions on an existing token file.
  4. Reject token paths that are symbolic links.
  5. Write through a securely created temporary file in the same directory and atomically rename it.
  6. Prefer an operating-system credential store or secrets manager where available.
  7. Document that token revocation is required after suspected exposure.

Example hardening pattern:

python
token_path = Path(self.token_path)
token_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
os.chmod(token_path.parent, 0o700)

if token_path.is_symlink():
    raise RuntimeError("Token path must not be a symbolic link")

flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
if hasattr(os, "O_NOFOLLOW"):
    flags |= os.O_NOFOLLOW

fd = os.open(token_path, flags, 0o600)
try:
    os.fchmod(fd, 0o600)
    with os.fdopen(fd, "w") as f:
        fd = None
        f.write(creds.to_json())
finally:
    if fd is not None:
        os.close(fd)
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

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 56)May include surrounding context.

md
):
        self.credentials_path = credentials_path or os.getenv(
            "GMAIL_CREDENTIALS_PATH",
            "~/.credentials/gmail-credentials.json"
        )
        self.token_path = token_path or os.getenv(
            "GMAIL_TOKEN_PATH",

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 62)May include surrounding context.

md
):
        self.credentials_path = credentials_path or os.getenv(
            "GMAIL_CREDENTIALS_PATH",
            "~/.credentials/gmail-credentials.json"
        )
        self.token_path = token_path or os.getenv(
            "GMAIL_TOKEN_PATH",

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 67)May include surrounding context.

md
):
        self.credentials_path = credentials_path or os.getenv(
            "GMAIL_CREDENTIALS_PATH",
            "~/.credentials/gmail-credentials.json"
        )
        self.token_path = token_path or os.getenv(
            "GMAIL_TOKEN_PATH",

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 · gmail_enhanced.py (reported line 40)May include surrounding context.

python
):
        self.credentials_path = credentials_path or os.getenv(
            "GMAIL_CREDENTIALS_PATH",
            "~/.credentials/gmail-credentials.json"
        )
        self.token_path = token_path or os.getenv(
            "GMAIL_TOKEN_PATH",

Missing User Warnings

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The rule processor performs automated state-changing actions on mailbox contents, including labeling, marking read, archiving, and sending auto-replies, based solely on stored rules and matching messages. In an agent context this is high risk because a misconfigured or malicious rule can silently alter evidence, trigger data leakage through replies, or perform large-scale mailbox changes without any per-action review.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documents capabilities that inherently require access to environment variables, local files, and networked Gmail APIs, but it does not declare an explicit tool scope or permission boundary. That omission can cause an agent platform or reviewer to underestimate the skill's operational reach, increasing the chance of overbroad execution and unintended access to credentials, mail content, and attachments.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill promotes downloading attachments, extracting them to cloud storage, auto-processing emails, and parsing structured data without any privacy, consent, retention, or sensitive-data handling guidance. In an email skill, this is dangerous because inbox contents and attachments commonly contain personal, financial, or corporate secrets, so automation can silently expand exposure.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
74% confidence
Finding

The documentation explicitly directs users to generate a token file for reuse, creating persistent session material on disk. In the context of a Gmail automation skill with read/send/modify scopes, a stolen or overexposed token can grant ongoing mailbox access without repeated user interaction.

Content

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

md
## Prerequisites

1. Enable Gmail API in Google Cloud Console
2. Create OAuth 2.0 credentials
3. Download credentials.json
4. Generate tokens.json (run once with authentication)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The send method performs a Gmail API call that transmits user-provided body content, recipients, and optional attachments, but there is no confirmation prompt, logging, or visible user disclosure around the outbound network action. The short docstring alone does not warn about data transmission or attachment sending, so users invoking this code may not be clearly informed that content is being sent externally.

Content

No source excerpt is available for this finding.

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 · gmail_enhanced.py (reported line 303)May include surrounding context.

python
message_list_visibility: str = "show",
        color: str = None
    ) -> Dict:
        """Create label"""
        service = self._get_service()
        
        body = {

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The attachment download function writes arbitrary email attachment content directly to a caller-supplied filesystem path without validation or safety controls. In an agent setting, this can enable unsafe persistence of untrusted files, overwriting local files, or dropping malicious content to disk where later components or users may execute or trust it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This bulk download workflow automatically creates a directory and saves all matching attachments from email search results with no user confirmation, filename sanitization, or conflict handling. In context, that is more dangerous because email attachments are untrusted input and a broad query can cause mass download of malicious or sensitive files onto the host.

Content

No source excerpt is available for this finding.