Back to skill

Security audit

Google Drive Skill

Security checks for vulnerabilities and agentic risk

Overview

This Google Drive skill largely matches its stated purpose, but it needs Review because a download path can overwrite local files and read-only commands may still use full Drive credentials.

Review before installing. Use a tightly scoped service account, prefer an API key for public read-only access, avoid directory downloads from untrusted Drive files unless the filename handling is hardened, and be careful with --public, writer roles, and --permanent --yes.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/download_file.py:23
Finding
Remote Drive Filename Can Escape the Destination Directory and Overwrite Local Files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/download_file.py:23-41` **Vulnerability Type**: Untrusted path traversal and arbitrary local file overwrite **Risk Level**: High ### Vulnerable Code ```python def download_file(drive, file_id: str, dest_path: str) -> None: # Get filename if dest_path is a directory if os.path.isdir(dest_path): meta = ( drive.files() .get(fileId=file_id, fields="name", supportsAllDrives=True) .execute() ) dest_path = os.path.join(dest_path, meta["name"]) request = drive.files().get_media(fileId=file_id, supportsAllDrives=True) with open(dest_path, "wb") as fh: downloader = MediaIoBaseDownload(fh, request) done = False while not done: status, done = downloader.next_chunk() pct = int(status.progress() * 100) print(f"\rDownloading... {pct}%", end="", flush=True) print(f"\nSaved to: {dest_path}") ``` ### Technical Analysis When the destination supplied through `--dest` is a directory, the script retrieves the file name from Google Drive metadata and passes it directly to `os.path.join`. The resulting path is then opened in `wb` mode without normalization, containment validation, or overwrite protection. The Drive filename is remote data and must therefore be treated as untrusted. A name containing path traversal components can cause the resolved destination to escape the selected download directory. On platforms where an attacker-controlled name is interpreted as an absolute path, `os.path.join` can also discard the intended directory entirely. Opening the resulting path with `open(dest_path, "wb")` creates a new file or truncates an existing file. The script does not check whether the target already exists, whether it is a symbolic link, or whether its resolved path remains under the requested directory. ### Attack Path 1. An attacker creates or controls a Google Drive fil ...[truncated 1558 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Treat the remote filename as untrusted and enforce destination containment: 1. Reject absolute filenames and names containing path separators. 2. Reduce the remote value to a safe filename using `os.path.basename`, while also accounting for separators used by other operating systems. 3. Resolve both the destination directory and candidate output path with `pathlib.Path.resolve()`. 4. Verify that the candidate is a child of the resolved destination directory. 5. Refuse to follow symbolic links or overwrite an existing file by default. 6. Require an explicit `--overwrite` option before truncating an existing destination. 7. Consider generating a local safe filename and retaining the Drive name only as display metadata. Example hardening pattern: ```python from pathlib import Path, PurePath dest_dir = Path(dest_path).resolve() remote_name = meta["name"] if ( not remote_name or PurePath(remote_name).is_absolute() or "/" in remote_name or "\\" in remote_name ): raise ValueError("Unsafe Drive filename") safe_name = Path(remote_name).name target = (dest_dir / safe_name).resolve() if dest_dir not in target.parents: raise ValueError("Download path escapes destination directory") if target.exists(): raise FileExistsError(f"Refusing to overwrite existing file: {target}") with target.open("xb") as fh: downloader = MediaIoBaseDownload(fh, request) ``` For stronger protection against symbolic-link races, use platform-supported descriptor-relative file creation with exclusive and no-follow flags where available. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/drive_client.py:25
Finding
Read-Only Operations Request Full Google Drive Access<![CDATA[ ## Vulnerability Details **File Location**: `scripts/drive_client.py:25-55`; invoked by `scripts/list_files.py:55` and `scripts/download_file.py:52` **Vulnerability Type**: Excessive OAuth scope and violation of least privilege **Risk Level**: Medium ### Vulnerable Code ```python SCOPES = ["https://www.googleapis.com/auth/drive"] def build_drive(readonly: bool = False): """ Return an authenticated Drive API v3 service object. Auth resolution order: 1. GOOGLE_SERVICE_ACCOUNT_JSON → service account credentials (read+write) 2. GOOGLE_API_KEY → API key (read-only public files only) """ sa_path = os.environ.get("GOOGLE_SERVICE_ACCOUNT_JSON") api_key = os.environ.get("GOOGLE_API_KEY") if sa_path: if not os.path.isfile(sa_path): sys.exit(f"Service account file not found: {sa_path}") creds = service_account.Credentials.from_service_account_file( sa_path, scopes=SCOPES ) return build("drive", "v3", credentials=creds) if api_key and readonly: return build("drive", "v3", developerKey=api_key) ``` Read-only callers use this factory as follows: ```python # scripts/list_files.py drive = build_drive(readonly=True) ``` ```python # scripts/download_file.py drive = build_drive(readonly=True) ``` ### Technical Analysis The client factory defines only the full Google Drive scope: ```text https://www.googleapis.com/auth/drive ``` Although the factory accepts `readonly=True`, that argument only permits fallback to API-key authentication. If `GOOGLE_SERVICE_ACCOUNT_JSON` is present, service-account authentication takes priority and the full Drive scope is requested even for listing and downloading. The full Drive scope permits reading, creating, modifying, and deleting accessible Drive resources. Read-only operations should instead use: ```text https://www.googleapis.com/auth/drive.readonly ``` This creates a mismatch between the operation ...[truncated 1521 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Select the OAuth scope according to the requested operation: ```python READONLY_SCOPES = ["https://www.googleapis.com/auth/drive.readonly"] WRITE_SCOPES = ["https://www.googleapis.com/auth/drive"] scopes = READONLY_SCOPES if readonly else WRITE_SCOPES creds = service_account.Credentials.from_service_account_file( sa_path, scopes=scopes, ) ``` Additional hardening measures: 1. Separate read-only and write client constructors to make privilege boundaries explicit. 2. Prefer API-key authentication for public read operations when both an API key and service-account credential are configured. 3. Use separate service accounts for read-only and write workflows. 4. Assign the service account only the minimum resource-level permissions required. 5. Avoid broad shared-drive roles such as organizer unless operationally necessary. 6. Document the exact scope used by each command. 7. Add automated tests asserting that `readonly=True` never produces a credential with the full Drive scope. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:64
Finding
Third-Party Python Dependencies Are Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:64-68`; duplicated in `README.md:42-46` and `scripts/drive_client.py:18-22` **Vulnerability Type**: Unpinned dependency installation and supply-chain exposure **Risk Level**: Low ### Vulnerable Code ```markdown ### 1. Install dependencies ```bash pip install google-api-python-client google-auth google-auth-httplib2 ``` ``` The runtime error repeats the same mutable installation command: ```python except ImportError: sys.exit( "Missing dependencies. Run:\n" " pip install google-api-python-client google-auth google-auth-httplib2" ) ``` ### Technical Analysis The installation instructions request package names without exact versions, a lockfile, or cryptographic hashes. Each installation therefore resolves whichever releases the configured package index considers current at that time. The package names are consistent with the Skill's declared Google Drive functionality, and no typosquatted or obviously unrelated dependency was identified. However, the mutable resolution process creates two security concerns: - A future compromised upstream release could introduce malicious installation or runtime behavior. - Users may receive dependency versions that were never tested with this Skill. The project contains no requirements lockfile or hash manifest that would allow users to verify package integrity and reproduce the reviewed dependency set. ### Attack Path 1. A user follows the documented setup command. 2. `pip` contacts the configured package index or mirror. 3. Because no exact versions or hashes are specified, the resolver selects currently available compatible releases. 4. A compromised upstream release, compromised package index, or maliciously altered mirror supplies an unsafe artifact. 5. The package is installed into the user's environment. 6. Malicious package code may execute during installation or when the Skill imports the dependency. This path depends ...[truncated 859 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Provide a reproducible, integrity-checked dependency specification: 1. Create a requirements or lock file containing exact tested versions. 2. Include SHA-256 hashes for every direct and transitive artifact. 3. Install with hash enforcement: ```bash python -m pip install --require-hashes -r requirements.txt ``` 4. Generate and review lockfile updates through a controlled dependency-update process. 5. Run vulnerability and provenance checks in continuous integration. 6. Prefer isolated virtual environments rather than global installation. 7. Update `SKILL.md`, `README.md`, and the import-error message to reference the locked requirements file. 8. If exact transitive locking is not practical, specify bounded compatible versions at minimum and document the versions used during security review. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Credential Access

High
Category
Privilege Escalation
Content
- Never hardcode API keys or service account credentials in source code. Use environment variables or secret managers.
- Restrict API key to the Drive API scope in the Google Cloud Console.
- Service account credentials (`service_account.json`) must be in `.gitignore`.
- When sharing files, prefer time-limited access tokens over permanent public links where possible.
- `"type": "anyone"` with `"role": "writer"` on a production folder is dangerous — audit permissions regularly.

---
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README documents commands to make files public and share them with others, but it does not clearly warn that these actions can expose sensitive data to anyone with access or to the internet at large. In a skill specifically designed for Drive CRUD and permission management, omission of an explicit disclosure warning increases the chance of accidental oversharing by users who copy commands verbatim.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes environment-dependent behavior by instructing users to read API keys and service account paths from environment variables, but it does not declare an explicit tool scope such as permissions or allowed-tools. In an agent setting, undeclared access to environment data can expand the skill's effective privileges and make secret exposure or unauthorized use more likely.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The description says to use the skill whenever the user wants to interact with a public Google Drive, including listing, reading, creating, updating, or deleting files and folders. This is very broad and lacks explicit trigger phrases, scope constraints, or negative examples beyond a couple of excluded API areas, which increases the chance of unintended invocation for common Drive-related requests.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# Permanently delete (irreversible):
    python delete_file.py --file-id <FILE_ID> --permanent

    # Skip confirmation prompt:
    python delete_file.py --file-id <FILE_ID> --permanent --yes

Warning:
Confidence
85% 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.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
# Permanently delete (irreversible):
    python delete_file.py --file-id <FILE_ID> --permanent

    # Skip confirmation prompt:
    python delete_file.py --file-id <FILE_ID> --permanent --yes

Warning:
Confidence
85% 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.

Static analysis

No suspicious patterns detected.