Back to skill

Security audit

Google Drive based RAG

Security checks for vulnerabilities and agentic risk

Overview

FileChat has a coherent document-search purpose, but it broadly copies and indexes Drive documents, sends document and query content to Gemini, and keeps sensitive text locally with weak containment.

Review this skill before installing. Only point it at a Drive folder whose contents you are comfortable copying into a local plaintext index and sending to Google Gemini for embeddings and image OCR. Protect the .env file and vector_db.json, avoid highly sensitive folders unless you accept that data flow, and prefer a version with explicit confirmation, scoped sync controls, pinned dependencies, safer command execution, and a documented delete/flush process.

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

T09 · Insecure Skill Coding Practices

Error
Location
sync.js:16
Finding
Shell Command Injection Through Interpolated Drive Identifiers<![CDATA[ ## Vulnerability Details **File Location**: `sync.js:16-36` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```js function getGWSFiles(folderId) { const query = `'${folderId}' in parents and trashed = false`; const escapedQuery = query.replace(/'/g, "'\\''"); const cmd = `SSL_CERT_FILE=/workspace/cacert.pem npx @googleworkspace/cli drive files list --params '{"q": "${escapedQuery}", "fields": "files(id, name, mimeType, shortcutDetails)"}'`; try { const res = execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' }); const jsonStart = res.indexOf('{'); const cleanRes = res.substring(jsonStart); return JSON.parse(cleanRes).files || []; } catch(e) { console.error("Error fetching files from GWS:", e.message); return []; } } function downloadFile(fileId, dest) { const cmd = `SSL_CERT_FILE=/workspace/cacert.pem npx @googleworkspace/cli drive files get --params '{"fileId": "${fileId}", "alt": "media"}' --output "${dest}"`; try { execSync(cmd, { stdio: 'pipe' }); return true; } catch(e) { return false; } } ``` ### Technical Analysis The implementation constructs shell command strings by interpolating `folderId`, `fileId`, and `dest`, then passes the resulting strings to `execSync()`. String-based `execSync()` invokes a shell, so shell metacharacters and command substitutions are interpreted before the Google Workspace CLI receives its arguments. The attempted escaping in `getGWSFiles()` only transforms apostrophes. It does not provide correct shell escaping for all surrounding quoting contexts and does not neutralize constructs such as command substitution, backticks, shell expansions, double quotes, or other metacharacters. The initial folder identifier is loaded from `FILECHAT_DRIVE_FOLDER_ID`. Recursive identifiers are obtained from Google Drive metadata, including shortcut target identifiers. Although legitimate Google-generated IDs normally have a restricted f ...[truncated 1332 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace string-based `execSync()` with `execFileSync()` or `spawnSync()` and pass each argument as a separate array element. - Do not invoke a shell to set `SSL_CERT_FILE`; pass it through the child process `env` option instead. - Validate Drive IDs using a strict allowlist appropriate for Google-generated identifiers. - Use absolute, internally generated output paths rather than interpolating caller-controlled paths. - Reject identifiers that do not meet the expected format before invoking the CLI. - Avoid attempting to implement shell escaping manually. A safer pattern is: ```js const { execFileSync } = require('child_process'); const output = execFileSync( gwsBinary, [ 'drive', 'files', 'list', '--params', JSON.stringify({ q: `'${folderId}' in parents and trashed = false`, fields: 'files(id, name, mimeType, shortcutDetails)' }) ], { encoding: 'utf8', stdio: 'pipe', env: { ...process.env, SSL_CERT_FILE: '/workspace/cacert.pem' } } ); ``` ]]>

T08 · Insecure Dependencies

Warning
Location
sync.js:20
Finding
Unpinned Runtime Package Retrieval and Execution<![CDATA[ ## Vulnerability Details **File Location**: `sync.js:20` and `sync.js:35`; `package.json:9-14` **Vulnerability Type**: Unsafe dependency resolution and runtime package execution **Risk Level**: Medium ### Vulnerable Code ```js const cmd = `SSL_CERT_FILE=/workspace/cacert.pem npx @googleworkspace/cli drive files list --params '{"q": "${escapedQuery}", "fields": "files(id, name, mimeType, shortcutDetails)"}'`; ``` ```js const cmd = `SSL_CERT_FILE=/workspace/cacert.pem npx @googleworkspace/cli drive files get --params '{"fileId": "${fileId}", "alt": "media"}' --output "${dest}"`; ``` The package manifest also uses mutable version ranges: ```json "dependencies": { "@google/generative-ai": "^0.2.1", "chromadb": "^1.8.1", "pdf-parse": "^1.1.1", "dotenv": "^16.4.5" } ``` No dependency lockfile was present in the audited project. ### Technical Analysis `npx @googleworkspace/cli` may resolve, download, and execute a package at runtime when an appropriate local installation is unavailable. No version is specified in these invocations. Consequently, the executable used by a reviewed version of the Skill can change independently of the reviewed source code. The remaining npm dependencies use caret ranges, and the project does not include a lockfile. A later installation can therefore select versions different from those originally tested. npm packages can execute code through lifecycle hooks during installation, while the CLI package invoked through `npx` executes directly with the Skill's privileges. The Google Workspace CLI is a legitimate dependency required by the declared functionality. The risk arises from mutable resolution and runtime retrieval rather than evidence that the named package is currently malicious. ### Attack Path 1. The Google Workspace CLI is unavailable in the expected local installation, or the project is installed again without a lockfile. 2. `npx` or `npm install` queries the configured npm registry and resolves a c ...[truncated 748 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Add `@googleworkspace/cli` as an explicit dependency at a reviewed, exact version. - Replace caret ranges with exact versions where operationally appropriate. - Generate and commit a verified `package-lock.json`. - Use `npm ci` in deployment so installation follows the lockfile. - Invoke the locally installed CLI directly rather than allowing `npx` to retrieve packages at runtime. - Use `npx --no-install` if `npx` must remain part of the invocation. - Review transitive dependencies and lifecycle scripts before upgrades. - Introduce automated dependency scanning and controlled update review. - Consider installation controls such as `--ignore-scripts` where compatible with the selected packages. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
sync.js:112
Finding
Predictable Temporary Files Permit Data Exposure and Path Manipulation<![CDATA[ ## Vulnerability Details **File Location**: `sync.js:112-138` **Vulnerability Type**: Insecure temporary-file handling **Risk Level**: Medium ### Vulnerable Code ```js const tmpFile = `./filechat_${targetId}`; if(downloadFile(targetId, tmpFile)) { let text = ""; try { if (mimeType === 'application/pdf') { const dataBuffer = fs.readFileSync(tmpFile); const data = await pdf(dataBuffer); text = data.text; } else if (mimeType.includes('image')) { text = await extractTextFromImage(tmpFile, mimeType); } else { text = fs.readFileSync(tmpFile, 'utf8'); } if(text.trim()) { const chunks = chunkText(text); for(let i=0; i<chunks.length; i++) { const c = chunks[i]; const emb = await getEmbedding(c); db.push({ fileId: targetId, filename: filePath, chunkIndex: i, text: c, embedding: emb }); } console.log(`Embedded ${chunks.length} chunks for ${item.name}`); } } catch(e) { console.error(`Failed to parse/embed ${item.name}: ${e.message}`); } if(fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile); } ``` ### Technical Analysis Downloaded documents are written to a predictable path under the current working directory using the Drive file ID. The code does not create a private temporary directory, request exclusive creation, verify that the path is not a symbolic link, or explicitly enforce restrictive permissions. Cleanup is not placed in a `finally` block and only occurs after `downloadFile()` reports success. Process termination, a crash, or exceptional control flow can leave sensitive files behind. Because the location is relative, the actual storage directory also depends on the process working directory rather than a fixed trusted location. If another local principal can write to the working directory, it may pre-create the expected path or a symbolic link. Whether a particular redirection s ...[truncated 1186 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create a private temporary directory with `fs.mkdtempSync()` under `os.tmpdir()`. - Ensure the temporary directory is created with permissions accessible only to the current user. - Generate unpredictable filenames instead of deriving them directly from Drive IDs. - Create files with exclusive semantics and mode `0600`. - Use absolute paths and verify that each path remains inside the private temporary directory. - Do not follow pre-existing symbolic links. - Place cleanup in a `finally` block so it occurs after both successful and failed processing. - Remove the complete private temporary directory recursively when synchronization ends. - Consider processing downloads in memory when file size and CLI support make that practical. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
sync.js:127
Finding
Sensitive Document Contents and Drive Identifiers Stored in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `sync.js:127-134` and `sync.js:158` **Vulnerability Type**: Insecure storage of sensitive information **Risk Level**: Medium ### Vulnerable Code ```js db.push({ fileId: targetId, filename: filePath, chunkIndex: i, text: c, embedding: emb }); ``` ```js fs.writeFileSync(DB_PATH, JSON.stringify(db)); ``` ### Technical Analysis The persistent database contains extracted document text, filenames, folder paths, Google Drive file identifiers, and embeddings. It is written as unencrypted JSON to `vector_db.json`. No explicit restrictive file mode is supplied to `writeFileSync()`, so access permissions depend on the process umask and any permissions already assigned to the file. The Skill is explicitly intended to index potentially sensitive material, including examples such as medical documents, making the plaintext content security-relevant. Storing text locally is necessary for the declared retrieval functionality, but storing it without explicit access controls exceeds the minimum safe implementation required for that purpose. The documentation also does not clearly identify `vector_db.json` as a sensitive data store requiring protection and secure deletion. ### Attack Path 1. The user configures FileChat to synchronize a Drive folder containing confidential documents. 2. `sync.js` extracts the documents into text chunks. 3. The Skill writes all chunks, filenames, and Drive identifiers to `vector_db.json`. 4. Another local account, process, backup service, or workspace consumer with read access to the Skill directory obtains the file. 5. The reader recovers the indexed document text and associated metadata without needing Drive or Gemini credentials. ### Impact Assessment Exposure can reveal the full indexed content of medical, financial, legal, business, or personal documents, together with filenames and Drive IDs. The scope is the complete synchronized library represented in the latest dat ...[truncated 195 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Store the database in a dedicated user-private data directory rather than alongside Skill source files. - Create and update the database with mode `0600`. - Verify that the parent directory is not accessible to unrelated users. - Write updates atomically through a private temporary file and rename it into place. - Consider encryption at rest using a key sourced from an operating-system credential store. - Minimize persisted data where possible, including omitting unnecessary metadata. - Document that the database contains plaintext copies of indexed documents. - Provide a secure flush operation that removes the database and residual temporary files. - Avoid including `vector_db.json` in source control, shared artifacts, logs, or broad backup scopes. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (31)

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill description advertises recursive download of all files, OCR of image contents, embedding with Gemini, and storage in a persistent local vector database, but it does not clearly warn users that sensitive document contents may be extensively copied, processed, retained locally, and partially transmitted to an external AI provider. In a document-ingestion skill, this omission is especially dangerous because users may trigger bulk processing of private files without understanding the scope of access or data handling.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
If only sync/indexing is implemented while search, retrieval, and storage semantics differ from the description, the skill is overclaiming functionality and underdisclosing processing details such as Gemini multimodal/OCR use. In a document-handling skill, undeclared content extraction and alternate persistence mechanisms materially increase privacy and compliance risk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If only sync/indexing is implemented while search, retrieval, and storage semantics differ from the description, the skill is overclaiming functionality and underdisclosing processing details such as Gemini multimodal/OCR use. In a document-handling skill, undeclared content extraction and alternate persistence mechanisms materially increase privacy and compliance risk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
If only sync/indexing is implemented while search, retrieval, and storage semantics differ from the description, the skill is overclaiming functionality and underdisclosing processing details such as Gemini multimodal/OCR use. In a document-handling skill, undeclared content extraction and alternate persistence mechanisms materially increase privacy and compliance risk.

Credential Access

High
Category
Privilege Escalation
Content
Create the `.env` file like this:
```bash
echo "GEMINI_API_KEY=your_key_here" > ./skills/filechat/.env
echo "FILECHAT_DRIVE_FOLDER_ID=your_folder_id_here" >> ./skills/filechat/.env
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Create the `.env` file like this:
```bash
echo "GEMINI_API_KEY=your_key_here" > ./skills/filechat/.env
echo "FILECHAT_DRIVE_FOLDER_ID=your_folder_id_here" >> ./skills/filechat/.env
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This code adds image OCR by sending image content to Gemini, which is outside the manifest’s stated purpose of securely searching and chatting with documents stored in a designated Google Drive folder using local RAG. That capability expansion matters because images may contain sensitive user data, and transmitting them to an external model service introduces a new data flow and privacy boundary not disclosed by the skill description.

Credential Access

High
Category
Privilege Escalation
Content
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });

const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
const DB_PATH = path.join(__dirname, 'vector_db.json');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });

const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
const DB_PATH = path.join(__dirname, 'vector_db.json');
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const DB_PATH = path.join(__dirname, 'vector_db.json');

if (!GEMINI_API_KEY) {
  console.error("Missing GEMINI_API_KEY in .env");
  process.exit(1);
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
const DB_PATH = path.join(__dirname, 'vector_db.json');

if (!GEMINI_API_KEY) {
  console.error("Missing GEMINI_API_KEY in .env");
  process.exit(1);
}
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
98% confidence
Finding
Downloaded document contents are transmitted to Google's embedding APIs without any explicit warning, consent flow, or prominent disclosure. In a file-search skill handling potentially sensitive user documents, silent third-party transfer materially increases privacy and compliance risk and could expose regulated or confidential content.

Missing User Warnings

High
Confidence
99% confidence
Finding
Image files are base64-encoded and sent to a generative AI service for OCR without explicit disclosure. This is especially sensitive because images may contain scanned IDs, signatures, medical records, or other high-risk content that users may not expect to leave the storage environment.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The README instructs users to simply tell the agent to 'sync', which is an extremely generic trigger for a high-impact operation. In this skill's context, 'sync' causes recursive download, OCR, embedding, and persistent local indexing of an entire Google Drive folder, so an ambiguous activation phrase increases the chance of accidental or contextually unintended execution.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill declares shell commands that read environment variables, perform networked Google Drive and Gemini operations, and write local files, but it does not declare any explicit tool scope or allowed-tools restrictions. This weakens least-privilege controls and increases the chance the agent can invoke broader capabilities than users expect.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The sync flow downloads and locally indexes all files from the configured Google Drive folder, but the instructions do not prominently warn the user that broad local replication and embedding/index creation will occur. In a knowledge skill operating on possibly sensitive documents, this is a meaningful privacy risk because users may assume remote-only search.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill instructs downloading a Drive file to /workspace and then sending it back to the user, but it does not require a clear user-facing warning that a local copy will be created. For sensitive documents, silent local persistence increases exposure through residual files, backups, or later unintended access by other tasks.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The skill description emphasizes local persistent ChromaDB indexing for Google Drive documents, but this file initializes an external Gemini API client using an environment API key. That creates an unjustified outbound dependency and potential exfiltration path for document-derived content, weakening the expectation that processing is local and contained.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
At this call site, image content is sent to an external AI service for transcription with no visible warning, consent flow, or disclosure. In the context of a file storage and retrieval skill, users may reasonably expect their stored files to remain within the documented Drive/local RAG boundary, so silent transmission of image contents increases privacy and compliance risk.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill sends the user's raw query text to Google's Gemini embedding API, which is an external network service, without any visible notice, consent flow, or minimization step. In a document-search skill, user queries may contain sensitive document names, personal data, or confidential business content, so silent transmission to a third party creates a real privacy and data-handling risk.

Context-Inappropriate Capability

Medium
Confidence
82% confidence
Finding
The stated purpose is document storage, retrieval, and semantic search over a designated Drive folder. Implementing this by spawning shell commands introduces a broader execution capability than is necessary for that purpose and is not declared in the manifest.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The code loads and accesses GEMINI_API_KEY from a local .env file, which is a sensitive credential operation covered by the warning rule. While it errors when variables are missing, it does not include comments or user guidance about the sensitivity of these credentials or safe handling expectations.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest states that the skill indexes documents into a local persistent ChromaDB instance. This code instead accumulates embeddings in memory and writes them to vector_db.json, which is a different storage mechanism than the one described.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The script executes `npx @googleworkspace/cli` without pinning an exact package version, so each run may fetch whatever version is currently published. That creates a supply-chain risk: a compromised or breaking upstream release could execute unintended code with the script's privileges and access to Google Drive data.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This second invocation has the same issue: unpinned `npx` execution allows an upstream package change or compromise to affect production behavior at runtime. Because this path downloads file contents, exploitation could expose sensitive documents or run attacker-controlled code.

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
sync.js:21