Back to skill

Security audit

Filechat

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent Google Drive RAG purpose, but it handles sensitive documents and credentials with several serious implementation and disclosure risks.

Review this skill carefully before installing. It can recursively read Google Drive folders, extract text including OCR, send content to embedding providers and optionally Qdrant, persist raw document chunks locally, and upload/download files. Do not use it with sensitive folders until folder ID validation, credential removal/rotation, safer subprocess calls, pinned tooling, explicit deletion confirmation, and clearer data-retention controls 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
sync.js:52
Finding
Shell Command Injection Through Unvalidated Google Drive Folder IDs<![CDATA[ ## Vulnerability Details **File Location**: `sync.js:52-54`; `sync-all.js:12-16` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code `sync.js:52-54`: ```js const query = `'${folderId}' in parents and trashed = false`; const escapedQuery = query.replace(/'/g, "'\\''"); const cmd = `${CERT_PREFIX}npx @googleworkspace/cli drive files list --params '{"q": "${escapedQuery}", "fields": "files(id, name, mimeType, modifiedTime, shortcutDetails)"}'`; try { const res = execSync(cmd, { encoding: 'utf-8', stdio: 'pipe' }); ``` `sync-all.js:12-16`: ```js for (let folder of folders) { console.log(`Starting sync for ${folder.name} (${folder.id})...`); try { execSync(`node sync.js ${folder.id}`, { stdio: 'inherit' }); } catch(e) { ``` ### Technical Analysis Both scripts construct shell command strings by interpolating folder identifiers into arguments passed to `execSync`. The folder identifier originates either from `process.argv[2]` or from the writable `folders.json` registry. The quoting transformation in `sync.js` is intended to escape single quotes inside the Google Drive query, but it does not provide a robust security boundary across JavaScript string construction, JSON encoding, and shell parsing. Other shell metacharacters may still alter how the command is interpreted. `sync-all.js` is more directly vulnerable because `folder.id` is inserted into a shell command without validation or quoting. An attacker-controlled registry value containing shell syntax can cause additional commands to run. The legitimate operation only requires passing a Drive folder ID to another executable. Invoking a shell is unnecessary and exceeds the minimum execution capability required for the declared functionality. ### Attack Path 1. An attacker convinces a user or Agent to register or sync a crafted folder identifier, or modifies `folders.json` through another available write primitive. 2. The crafted value is passed to ...[truncated 870 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace shell-based execution with an API that passes arguments without shell interpretation: ```js const { execFileSync } = require('child_process'); const params = JSON.stringify({ q: `'${folderId}' in parents and trashed = false`, fields: 'files(id, name, mimeType, modifiedTime, shortcutDetails)' }); const res = execFileSync( 'npx', ['@googleworkspace/cli', 'drive', 'files', 'list', '--params', params], { encoding: 'utf8', stdio: 'pipe', env: { ...process.env, ...(fs.existsSync('/workspace/cacert.pem') ? { SSL_CERT_FILE: '/workspace/cacert.pem' } : {}) } } ); ``` 2. Invoke `sync.js` without a shell: ```js execFileSync(process.execPath, ['sync.js', folder.id], { stdio: 'inherit', cwd: __dirname }); ``` 3. Validate every folder ID before storage and before use: ```js function validateFolderId(value) { if (typeof value !== 'string' || !/^[A-Za-z0-9_-]+$/.test(value)) { throw new Error('Invalid Google Drive folder ID'); } return value; } ``` 4. Validate all entries loaded from `folders.json`; do not assume locally stored configuration is trusted. 5. Add regression tests containing spaces, quotes, semicolons, command substitutions, pipes, and newline characters, and verify that none can result in command execution. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
tests/credentials.json:2
Finding
Google OAuth Client Secret and Gemini API Key Committed to Source<![CDATA[ ## Vulnerability Details **File Location**: `tests/credentials.json:2-9`; `tests/setup.js:8-16`; `tests/skill.test.js:36-39,59-62` **Vulnerability Type**: Hardcoded credentials and secret exposure **Risk Level**: High ### Vulnerable Code `tests/credentials.json:2-9` contains an OAuth client configuration with a literal client secret: ```json { "installed": { "client_id": "670517489366-e4ntqidm16icf860b68d0onnt65p1e0f.apps.googleusercontent.com", "project_id": "poised-charger-485513-d9", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "client_secret": "<REDACTED_COMMITTED_GOOGLE_OAUTH_CLIENT_SECRET>", "redirect_uris": ["http://localhost"] } } ``` `tests/setup.js:8-16` reads and propagates that secret and also contains a literal Gemini API key: ```js const credentialsStr = fs.readFileSync(credentialsPath, 'utf8'); const credentials = JSON.parse(credentialsStr); const env = { ...process.env, GOOGLE_WORKSPACE_CLI_CREDENTIALS_FILE: credentialsPath, GOOGLE_WORKSPACE_CLI_CLIENT_ID: credentials.installed.client_id, GOOGLE_WORKSPACE_CLI_CLIENT_SECRET: credentials.installed.client_secret, GEMINI_API_KEY: '<REDACTED_COMMITTED_GEMINI_API_KEY>' }; ``` The same Gemini key is repeated in `tests/skill.test.js`: ```js const env = { ...process.env, GEMINI_API_KEY: '<REDACTED_COMMITTED_GEMINI_API_KEY>' }; ``` The literal values are intentionally redacted from this report to avoid further credential disclosure. ### Technical Analysis Secrets stored in tracked project files become available to every person and system that receives the source package or repository history. Moving a committed secret to an environment variable at runtime does not protect it when the original value remains in source. The test setup passes the exposed credentials to subprocesses. Although the reviewe ...[truncated 1633 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the committed Gemini API key and OAuth client secret immediately. 2. Review usage and billing logs for unauthorized activity. 3. Remove the secrets from the current files and purge them from repository history and published artifacts. 4. Load test credentials exclusively from a secret manager or externally supplied environment variables: ```js const required = [ 'GOOGLE_WORKSPACE_CLI_CLIENT_ID', 'GOOGLE_WORKSPACE_CLI_CLIENT_SECRET', 'GEMINI_API_KEY' ]; for (const name of required) { if (!process.env[name]) { throw new Error(`Missing required test secret: ${name}`); } } const env = { ...process.env, GOOGLE_WORKSPACE_CLI_CLIENT_ID: process.env.GOOGLE_WORKSPACE_CLI_CLIENT_ID, GOOGLE_WORKSPACE_CLI_CLIENT_SECRET: process.env.GOOGLE_WORKSPACE_CLI_CLIENT_SECRET, GEMINI_API_KEY: process.env.GEMINI_API_KEY }; ``` 5. Replace `tests/credentials.json` with a non-secret example such as `credentials.example.json`. 6. Add `.env`, credential JSON files, and generated token files to `.gitignore`. 7. Use mocked API clients for ordinary unit tests and restrict live integration tests to isolated CI environments. 8. Apply API restrictions, quotas, least-privilege scopes, and allowed-service restrictions to replacement credentials. 9. Enable automated secret scanning in local hooks and CI. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
sync.js:207
Finding
Sensitive Drive Document Content Stored in Plaintext and Sent to Unvalidated Qdrant Endpoints<![CDATA[ ## Vulnerability Details **File Location**: `sync.js:13-18,207-214,220-222,263-269`; `query.js:13-18,102-119` **Vulnerability Type**: Insecure storage and unrestricted external data destination **Risk Level**: Medium ### Vulnerable Code Qdrant is enabled solely based on an environment-supplied URL: ```js const QDRANT_URL = process.env.QDRANT_URL; const QDRANT_API_KEY = process.env.QDRANT_API_KEY; // Keep Local DB as fallback if no Qdrant configured const USE_QDRANT = !!QDRANT_URL; const DB_PATH = path.join(__dirname, `vector_db_${ROOT_FOLDER_ID}.json`); ``` Extracted document chunks and metadata are retained in the local database or included in Qdrant payloads: ```js points.push({ id: pointId, vector: emb, payload: { fileId: targetId, filename: filePath, chunkIndex: i, text: c } }); ``` ```js db.push({ fileId: targetId, filename: filePath, chunkIndex: i, text: c, embedding: emb }); ``` The data is uploaded to the configured endpoint: ```js if (USE_QDRANT && points.length > 0) { await qdrant.upsert(collectionName, { wait: true, points }); } ``` The local database is written without explicit restrictive permissions or encryption: ```js if (!USE_QDRANT) { fs.writeFileSync(DB_PATH, JSON.stringify(db)); console.log(`Sync complete. Local database saved to disk with ${db.length} total chunks.`); } else { console.log(`Sync complete. Updated Qdrant database.`); } ``` Queries also trust the configured endpoint: ```js const qdrant = new QdrantClient({ url: QDRANT_URL, apiKey: QDRANT_API_KEY }); const collectionName = `filechat_${ROOT_FOLDER_ID}`; const searchResult = await qdrant.search(collectionName, { vector: queryEmbedding, limit: 3, with_payload: true }); ``` ### Technical Analysis The local vector database is not merely a set of embeddings. It contains raw extracted text, Google Drive file identifiers, filenames, and chunk indexes. These values can include medical, financial, legal, or other confidential informatio ...[truncated 2532 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit user consent before transmitting document contents to any cloud embedding, OCR, or vector-database provider. 2. Require HTTPS for remote Qdrant endpoints: ```js function validateQdrantUrl(value) { const url = new URL(value); const local = url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1'; if (!local && url.protocol !== 'https:') { throw new Error('Remote Qdrant endpoints must use HTTPS'); } return url.toString(); } ``` 3. Support a deployment-level allowlist of approved Qdrant hosts and reject all other destinations. 4. Do not send raw text in Qdrant payloads unless retrieval requires it and the user has approved remote storage. Consider storing only opaque record identifiers remotely and retaining encrypted content locally. 5. Encrypt local document content at rest with a key stored outside the project directory. 6. Create database and metadata files with owner-only permissions: ```js fs.writeFileSync(DB_PATH, JSON.stringify(db), { encoding: 'utf8', mode: 0o600 }); ``` 7. Verify and correct permissions for existing database files, since the creation mode does not repair overly broad permissions on an existing file. 8. Store generated data outside the Skill source directory in a dedicated private application-data directory. 9. Add configurable retention and secure-deletion controls. 10. Document every external recipient, the exact data sent, applicable retention policies, and whether provider-side training or logging may occur. 11. Avoid placing Gemini API keys in URL query parameters where possible; prefer authorization headers or an SDK that protects credentials from URL logging. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (67)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill actually executes arbitrary subprocesses, exports API keys, and deletes local files while presenting itself as a benign RAG utility, the mismatch materially increases operational risk. In security-sensitive agent ecosystems, inaccurate declarations can bypass scrutiny and lead to over-privileged deployment.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the skill actually executes arbitrary subprocesses, exports API keys, and deletes local files while presenting itself as a benign RAG utility, the mismatch materially increases operational risk. In security-sensitive agent ecosystems, inaccurate declarations can bypass scrutiny and lead to over-privileged deployment.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the skill actually executes arbitrary subprocesses, exports API keys, and deletes local files while presenting itself as a benign RAG utility, the mismatch materially increases operational risk. In security-sensitive agent ecosystems, inaccurate declarations can bypass scrutiny and lead to over-privileged deployment.

Credential Access

High
Category
Privilege Escalation
Content
Create the `.env` file like this:
```bash
echo "EMBEDDING_PROVIDER=gemini" > ./skills/filechat/.env
echo "GEMINI_API_KEY=your_key_here" >> ./skills/filechat/.env
```
Confidence
87% confidence
Finding
The skill instructs creation and use of a plaintext `.env` file for embedding provider configuration and API keys. Storing secrets in a workspace path used by an agent skill increases the likelihood of accidental disclosure through logs, file reads, packaging, or other tool access.

Credential Access

High
Category
Privilege Escalation
Content
Create the `.env` file like this:
```bash
echo "EMBEDDING_PROVIDER=gemini" > ./skills/filechat/.env
echo "GEMINI_API_KEY=your_key_here" >> ./skills/filechat/.env
```

**Google Workspace Authentication:**
Confidence
87% confidence
Finding
This line directly demonstrates writing a sensitive API key into a local `.env` file. In the context of a document-processing skill with shell access, that secret may be exposed to other commands, subprocesses, or accidental file exfiltration.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
*Note: Syncs are highly incremental and use a local cache! If a file hasn't been modified in Drive, the script will skip it instantly and output "0 chunks" embedded. This is NORMAL behavior. If you are debugging, testing, or the user specifically requests a hard flush, you MUST delete the cache files first:*
```bash
rm ./skills/filechat/meta_<FOLDER_ID>.json
rm ./skills/filechat/vector_db_<FOLDER_ID>.json
```
Confidence
95% confidence
Finding
The command deletes a path derived from `<FOLDER_ID>` without any visible sanitization or path restriction checks. If the folder identifier is attacker-controlled or malformed, this pattern can enable unintended file deletion or shell/path manipulation when used by an agent or copied into scripts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
*Note: Syncs are highly incremental and use a local cache! If a file hasn't been modified in Drive, the script will skip it instantly and output "0 chunks" embedded. This is NORMAL behavior. If you are debugging, testing, or the user specifically requests a hard flush, you MUST delete the cache files first:*
```bash
rm ./skills/filechat/meta_<FOLDER_ID>.json
rm ./skills/filechat/vector_db_<FOLDER_ID>.json
```

## How to Answer User Questions (RAG)
Confidence
95% confidence
Finding
This deletion command has the same unsafe parameterization issue for the vector database file. In a skill that handles persistent state and potentially sensitive document embeddings, unintended deletion can cause loss of data availability and integrity.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
*(If this fails, check folder permissions or GWS credentials.)*
3. **Force a Clean Sync:** Clear the cache for the test folder to guarantee a fresh run, then sync.
   ```bash
   rm -f ./skills/filechat/meta_<FOLDER_ID>.json ./skills/filechat/vector_db_<FOLDER_ID>.json
   node ./skills/filechat/sync.js <FOLDER_ID>
   ```
   *(You should see files being downloaded, OCR'd, and chunks being embedded. If it says "0 chunks", verify the folder isn't empty.)*
Confidence
96% confidence
Finding
Combining multiple `rm -f` targets with an interpolated `<FOLDER_ID>` inside a validation workflow amplifies the risk of destructive misuse. Because it is presented as a routine test step, an operator or agent may execute it with untrusted or mistaken input, causing broader file deletion than intended.

Known Vulnerable Dependency: axios==1.13.5 — 16 advisory(ies): CVE-2026-44494 (axios Vulnerable to Full Man-in-the-Middle via Prototype Pollution Gadget in `co); CVE-2026-44495 (axios Vulnerable to Credential Theft and Response Hijacking via Prototype Pollut); CVE-2025-62718 (Axios has a NO_PROXY Hostname Normalization Bypass that Leads to SSRF) +13 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: brace-expansion==5.0.2 — 5 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-45149 (brace-expansion: Large numeric range defeats documented `max` DoS protection) +2 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: form-data==4.0.5 — 1 advisory(ies): CVE-2026-12143 (form-data: CRLF injection in form-data via unescaped multipart field names and f)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: minimatch==10.2.0 — 3 advisory(ies): CVE-2026-27904 (minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regu); CVE-2026-26996 (minimatch has a ReDoS via repeated wildcards with non-matching literal in patter); CVE-2026-27903 (minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adja)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: brace-expansion==2.0.3 — 3 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro); CVE-2026-69152 (brace-expansion: DoS via unbounded intermediate arrays, bypassing the CVE-2026-1)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: browserslist==4.28.1 — 2 advisory(ies): CVE-2026-73088 (Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.); CVE-2026-73089 (Browserslist: Unbounded memory growth (no cache eviction) via distinct query res)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Credential Access

High
Category
Privilege Escalation
Content
const fs = require('fs');
const path = require('path');
const { QdrantClient } = require('@qdrant/js-client-rest');
require('dotenv').config({ path: path.join(__dirname, '.env') });

const folderArg = process.argv[2];
const query = process.argv[3];
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');
const { QdrantClient } = require('@qdrant/js-client-rest');
require('dotenv').config({ path: path.join(__dirname, '.env') });

const folderArg = process.argv[2];
const query = process.argv[3];
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');

describe('FileChat RAG Skill', () => {
  const credentialsPath = path.join(__dirname, 'credentials.json');
  
  beforeAll(() => {
    // Ensure we have the credentials to authenticate GWS CLI
Confidence
70% 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');

describe('FileChat RAG Skill', () => {
  const credentialsPath = path.join(__dirname, 'credentials.json');
  
  beforeAll(() => {
    // Ensure we have the credentials to authenticate GWS CLI
Confidence
70% 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
Injecting a hardcoded API key into the environment without any safeguards is a true credential exposure issue, not merely a documentation problem. The key is plainly recoverable from source and can be reused by anyone with repository access, making the skill more dangerous because it touches external services and may run in shared CI or developer environments.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README advertises recursive Google Drive syncing, OCR of images, embedding, and persistent local vector storage, but it does not warn users that potentially sensitive document contents will be copied, transformed, and retained locally. In this context, the omission is security-relevant because users may unknowingly index private files, including data reachable through subfolders and shortcuts, creating confidentiality and retention risks.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill declares no explicit tool scope or allowed-tools despite requiring environment access, package installation, network access, and Google Drive operations. This increases the chance of overbroad execution and makes it harder to constrain what the agent may do when handling sensitive documents and credentials.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The trigger conditions are broad enough that the skill may activate on generic user requests about files, saving, retrieval, or syncing. In a skill with Drive access, upload/download behavior, and indexing, accidental invocation could cause unintended document access, storage, or modification.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
Using `npx @googleworkspace/cli` without pinning a specific version causes runtime retrieval of whatever package version is current, creating a supply-chain risk. A malicious or compromised upstream release could execute arbitrary code in the agent environment with access to local files, OAuth tokens, and document data.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding
This invocation again relies on an unpinned `npx` package fetch for authentication flow. Because it is used during login, compromise here would be especially sensitive: the executed code could intercept tokens or manipulate OAuth behavior.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The instructions tell the agent to delete cache/index files as part of debugging or hard flush without requiring user confirmation or emphasizing data-loss consequences. In practice, this can destroy local indexing state and backups, causing availability loss and potentially forcing reprocessing of sensitive documents.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
sync-all.js:17

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
sync.js:50

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/setup.js:30

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
tests/skill.test.js:17

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
query.js:16

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
sync.js:17

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
tests/setup.js:14

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
tests/skill.test.js:39