Back to skill

Security audit

Gmail Attachment Downloader

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to perform the advertised Gmail attachment download workflow, but its Gmail credential handling and local file-write behavior need review before installation.

Review before installing. Use a dedicated Gmail app password, avoid passing it with `--gmail-pass` where possible, prefer letting the script read it from the environment or a safer secret mechanism, run `--dry-run` first, choose a private destination folder, and revoke the app password when finished.

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
SKILL.md:45
Finding

Gmail App Password Exposed Through Process Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:45-55; supporting implementation at scripts/download_gmail_attachments.py:47
Vulnerability Type: Sensitive credential exposure through process arguments
Risk Level: Medium

Vulnerable Code

bash
python3 scripts/download_gmail_attachments.py \
  --gmail-user "$GMAIL_USER" \
  --gmail-pass "$GMAIL_PASS" \
  --sender "auth@shove.xforceplus.com" \
  --subject "发票" \
  --since "01-Jan-2025" \
  --extensions ".pdf,.ofd" \
  --save-folder "~/Documents/walmart-invoices" \
  --max-results 200

The script explicitly accepts the credential as a command-line argument:

python
parser.add_argument("--gmail-pass", default=os.environ.get("GMAIL_PASS", ""))

Technical Analysis

The documented execution command expands GMAIL_PASS and passes the resulting Gmail app password in the --gmail-pass command-line argument. Although the secret is initially stored in an environment variable, shell expansion places its plaintext value in the Python process argument vector.

Process arguments can be captured by local process-monitoring facilities, orchestration and observability systems, Agent tool-call logs, command histories generated by wrappers, diagnostic reports, or other software that records executed commands. The degree of local visibility depends on the operating-system configuration, but credentials should not be placed in arguments because command lines are commonly treated as non-secret operational metadata.

The network authentication itself is expected: the implementation sends the username and app password over an SSL connection to the fixed official endpoint imap.gmail.com. The vulnerability is the local handling of the credential before authentication, not transmission to Gmail.

Attack Path

  1. The user provides a Gmail app password or exports it as GMAIL_PASS.
  2. The Skill follows its documented invocation and expands the ...[truncated 1072 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove --gmail-pass from all documented command examples.
  2. Prefer a protected secret-input mechanism such as:
    • getpass.getpass() for interactive use;
    • an operating-system credential store or keyring;
    • a narrowly scoped inherited file descriptor;
    • a secret manager that does not inject the value into the command line.
  3. If environment-variable support must remain for automation, read GMAIL_PASS only inside the process and invoke the script without expanding it into an argument:
    bash
    GMAIL_USER="user@gmail.com" GMAIL_PASS="secret" \
      python3 scripts/download_gmail_attachments.py \
      --save-folder "~/Documents/gmail-attachments"
    
  4. Deprecate and then remove the --gmail-pass parser option to prevent accidental exposure.
  5. Ensure Agent execution logs, telemetry, crash reports, and error output redact Gmail credentials.
  6. Advise affected users to revoke and regenerate any app password that may already have appeared in process or execution logs.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/download_gmail_attachments.py:184
Finding

Attachment and Summary Writes Can Follow Symbolic Links

Content
View full analysis

Vulnerability Details

File Location: scripts/download_gmail_attachments.py:184-191, scripts/download_gmail_attachments.py:226-234, and scripts/download_gmail_attachments.py:297-301
Vulnerability Type: Symlink-following file write and time-of-check/time-of-use race
Risk Level: Medium

Vulnerable Code

Destination selection checks whether a path exists but does not atomically reserve it:

python
def unique_destination(folder: Path, filename: str, date_str: str) -> Path:
    base_name, ext = os.path.splitext(filename)
    candidate = f"{base_name}_{date_str}{ext}" if date_str else filename
    path = folder / candidate
    counter = 1
    while path.exists():
        suffix = f"{base_name}_{date_str}_{counter}{ext}" if date_str else f"{base_name}_{counter}{ext}"
        path = folder / suffix
        counter += 1
    return path

Summary output follows the resolved destination path without rejecting symbolic links:

python
def write_summary(path: str, summary: dict[str, object]) -> None:
    if not path:
        return
    summary_path = Path(os.path.expanduser(path)).resolve()
    summary_path.parent.mkdir(parents=True, exist_ok=True)
    summary_path.write_text(
        json.dumps(summary, ensure_ascii=False, indent=2) + "\n",
        encoding="utf-8",
    )

Attachment data is subsequently written using a non-exclusive operation:

python
destination = unique_destination(config.save_folder, filename, date_str)
matched_files.append(destination.name)
if config.dry_run:
    print(f"Matched: {destination.name}")
    continue
destination.write_bytes(payload)
downloaded += 1
print(f"Downloaded: {destination.name}")

Technical Analysis

unique_destination() separates the existence check from the eventual write. No file descriptor is opened during destination selection, so another process can create or replace the chosen path after the ...[truncated 2573 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create each output file atomically with exclusive creation rather than checking and writing separately.
  2. On supported platforms, use os.open() with O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW, then write through the resulting file descriptor.
  3. If the candidate already exists, generate the next filename and repeat the exclusive-open operation instead of calling Path.exists().
  4. Verify with fstat() that the opened object is a regular file before writing.
  5. Apply equivalent no-follow and atomic-write controls to --summary-json.
  6. Create download directories with restrictive permissions and reject destinations that are group- or world-writable unless the user explicitly confirms the risk.
  7. Consider writing to a securely created temporary file in the same trusted directory and atomically renaming it, while ensuring the final destination cannot overwrite or follow an existing symlink.
  8. Document that destination folders should be private to the user and should not be shared with untrusted local accounts or processes.
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 (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill invokes a script that uses network access to Gmail, reads secrets from arguments or environment variables, and writes attachments to the local filesystem, but it declares no explicit tool scope or permissions. That mismatch can cause an agent or reviewer to underestimate the skill's capabilities, increasing the chance of unintended credential handling, mailbox access, and local file writes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This skill requests a Gmail app password and downloads email attachments to a local folder, but the input and execution guidance does not prominently warn that this grants broad mailbox access and may copy sensitive documents onto disk. Without clear privacy and handling warnings, users may expose credentials or export regulated content to insecure locations unintentionally.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.