T08 · Insecure Dependencies
- Location
sync.js:31- Finding
Unpinned Runtime Package Retrieval and Execution
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Google Drive document-search skill is coherent, but it needs review because it broadly indexes Drive contents, sends content to Gemini or OpenAI, and stores raw document text and API keys locally with weak controls.
Install only if you are comfortable letting this skill recursively read the selected Google Drive folder, including subfolders and shortcuts, send document text/images and search queries to the configured AI provider, and keep a local plaintext index. Avoid using it for regulated or highly confidential files unless you add stronger consent, scoping, local-only processing, secret handling, dependency pinning, and deletion controls.
sync.js:31Unpinned Runtime Package Retrieval and Execution
sync.js:58Sensitive Documents, Images, and Search Queries Are Sent to External AI Providers Without Adequate Data-Handling Controls
sync.js:157Plaintext Document Database and Predictable Temporary Files Expose Sensitive Drive Content
The declared purpose centers on Google Drive-backed document management and retrieval with semantic search and persistent vector indexing. The supplied code does none of that. Instead, it implements a single OCR-like helper: it reads an image file locally, encodes it, submits it to a Gemini model with a prompt to transcribe visible text, and returns the text. This is a materially different primary purpose and introduces an undeclared capability (image text extraction) while lacking the declared Google Drive, retrieval, indexing, and RAG behaviors.
The skill instructs the agent to collect API keys from the user and write them directly into a plaintext .env file under the workspace. Storing credentials this way exposes them to other tools, processes, logs, backups, or later workspace inspection, creating a straightforward secret-handling risk.
If they are missing, STOP and ask the user to provide them.
Create the .env file like this:
echo "EMBEDDING_PROVIDER=gemini" > ./skills/filechat/.env
echo "GEMINI_API_KEY=your_key_here" >> ./skills/filechat/.env
The example command appends a live API key into a .env file using shell echo, which can leak the secret through shell history, command logs, transcript retention, or file exposure. Because this is presented as the normal setup path, it operationalizes insecure credential storage and handling.
Create the .env file like this:
echo "EMBEDDING_PROVIDER=gemini" > ./skills/filechat/.env
echo "GEMINI_API_KEY=your_key_here" >> ./skills/filechat/.env
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const folderArg = process.argv[2];
const query = process.argv[3];
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') });
const folderArg = process.argv[2];
const query = process.argv[3];
The README describes recursively downloading all files from a Google Drive folder, extracting text including OCR from images, generating embeddings, and storing the results in a persistent local vector database, but it does not clearly warn users about the privacy, retention, and sensitive-data exposure implications. This is dangerous because users may authorize indexing of confidential documents without understanding that broad content copies and semantic representations will remain on local storage and may be queried later.
The skill declares installation and execution steps that use environment variables, external CLIs, and networked services, but it does not define any explicit tool scope or permissions boundaries. That makes the agent's operational authority ambiguous and increases the chance of unintended access to local secrets, remote services, or user data during execution.
The invocation guidance is broad enough to activate the skill for generic requests about saving, retrieving, or asking about files. In context, that can cause the agent to engage Google Drive sync, indexing, or file download flows without sufficiently narrowing user intent, increasing the risk of over-collection or unintended document access.
The sync instructions direct the agent to download, chunk, embed, and locally index all new or changed files from an entire Google Drive folder, including subfolders, without an up-front warning about the scope of data collection. This is dangerous because users may expect a narrow action but instead authorize bulk ingestion of potentially sensitive folder contents into a persistent local vector store.
The instructions tell the agent to download a Drive file into the workspace and send it back to the user, but they omit an explicit warning or confirmation step that a local copy will be created and transmitted. For sensitive documents, this can lead to unnecessary local persistence and accidental disclosure if the wrong file is selected or the workspace is accessible to other processes.
This function sends raw image contents from a local file to Google's Gemini API for OCR/transcription without any visible consent, notice, or policy enforcement in the code path. In the context of a document-storage and retrieval skill that processes user files from Google Drive, images may contain sensitive personal, financial, or corporate data, so undisclosed transfer to a third-party AI service creates a real privacy and data-handling risk.
The code sends the user's raw search query to third-party embedding providers (OpenAI or Google Gemini) to generate embeddings. In a document-search skill, queries may contain sensitive business, personal, or regulated information, and there is no consent, minimization, redaction, or disclosure mechanism shown before transmitting that data externally.
This fetch call transmits user-supplied query text to OpenAI's embeddings API. In the context of a RAG skill for private Google Drive content, external transmission materially increases confidentiality risk because users may search for sensitive internal document contents and assume the search stays within the local skill boundary.
async function getEmbedding(text) {
if (EMBEDDING_PROVIDER === "openai") {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
"Content-Type": "application/json",
This fetch call transmits user-supplied query text to OpenAI's embeddings API. In the context of a RAG skill for private Google Drive content, external transmission materially increases confidentiality risk because users may search for sensitive internal document contents and assume the search stays within the local skill boundary.
async function getEmbedding(text) {
if (EMBEDDING_PROVIDER === "openai") {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
"Content-Type": "application/json",
Using execSync to shell out to external commands adds unnecessary subprocess capability and expands the attack surface compared with direct API/library calls. Although the immediate interpolation points appear constrained, this design still inherits shell parsing risks, dependency execution risks, and weaker control over error handling and permissions.
The code invokes npx @googleworkspace/cli without pinning an exact version, so each sync may resolve a newer package from the registry. That creates a supply-chain risk where a compromised or breaking upstream release could execute arbitrary code in the skill's environment with access to downloaded files and API keys.
This second npx @googleworkspace/cli call has the same unpinned dependency problem during file download. A malicious or altered upstream package could run arbitrary code when syncing and directly access document contents being retrieved from Drive.
The sync process sends extracted document text to OpenAI or Gemini embedding APIs, which is an external transmission of potentially sensitive file contents. That is materially broader than a purely local indexing expectation and increases confidentiality risk for any documents stored in Drive, especially because the skill markets the data as securely stored and isolated.
The code transmits document text to external embedding providers without any in-code disclosure, consent gate, or visible warning to the user. In a document-storage and retrieval skill, hidden third-party transmission is especially sensitive because users are likely to assume their files remain within Drive and the local indexer.
The hardcoded OpenAI endpoint confirms that file-derived content is transmitted to an external service boundary. This is risky because the skill processes arbitrary user documents, and the transmission is not limited to metadata but includes substantive extracted text.
async function getEmbedding(text) {
if (EMBEDDING_PROVIDER === "openai") {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
"Content-Type": "application/json",
The hardcoded OpenAI endpoint confirms that file-derived content is transmitted to an external service boundary. This is risky because the skill processes arbitrary user documents, and the transmission is not limited to metadata but includes substantive extracted text.
async function getEmbedding(text) {
if (EMBEDDING_PROVIDER === "openai") {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
"Content-Type": "application/json",
Image files are sent in full to a generative model for OCR, which exposes the raw image contents—not just derived chunks—to an external provider. This broadens data exposure beyond simple local chunk/embed behavior and may leak screenshots, scans, IDs, or other highly sensitive visual data.
The code persists a JSON vector database that includes raw text chunks from documents, not just embeddings and metadata. Storing extracted content unencrypted on disk creates a local confidentiality risk if the host is shared, compromised, or backed up to less trusted locations.
The dependency is specified with a caret range, which permits automatic installation of newer compatible versions. This weakens supply-chain reproducibility and can unexpectedly pull in a compromised or breaking release if the upstream package or registry is abused.
"query": "node query.js"
},
"dependencies": {
"@google/generative-ai": "^0.2.1",
"chromadb": "^1.8.1",
"pdf-parse": "^1.1.1",
"dotenv": "^16.4.5"
Using a caret version for chromadb allows semver-compatible updates to be fetched implicitly, which increases supply-chain risk and reduces build determinism. If an upstream release is malicious or vulnerable, environments may ingest it without an explicit review.
},
"dependencies": {
"@google/generative-ai": "^0.2.1",
"chromadb": "^1.8.1",
"pdf-parse": "^1.1.1",
"dotenv": "^16.4.5"
}