T09 · Insecure Skill Coding Practices
- Location
scripts/shared.js:45- Finding
Account Label Path Traversal Enables Token File Access Outside the Token Directory
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly does what it claims, but it needs Review because it handles Google mail/calendar tokens and attachments with overbroad permissions and unsafe file path controls.
Review before installing. Use the --readonly authorization mode unless Calendar writes are truly needed, keep ~/.openclaw/google-workspace-byok owner-only, avoid shared machines, and be careful downloading email attachments because crafted filenames could overwrite files outside the target folder. Revoke the Google OAuth grant if you stop using the skill.
scripts/shared.js:45Account Label Path Traversal Enables Token File Access Outside the Token Directory
scripts/gmail.js:242Attachment Filename Path Traversal Enables Writes Outside the Selected Output Directory
scripts/shared.js:24OAuth Credentials and Refresh Tokens Are Stored Without Explicit Restrictive Permissions
scripts/shared.js:14Full Calendar Read/Write OAuth Scope Is Requested by Default Despite Read-Only Implementation
The code’s behavior is a subset of the declared description: it clearly supports calendar-related read actions and multi-account selection via an account label, which aligns with part of the description. However, the declared purpose prominently describes a combined Google Calendar and Gmail integration with substantial Gmail-related capabilities, while the provided code chunk contains only calendar access logic. There is no evidence here of email access, Gmail search, attachment download, or PDF text extraction. This is a description-versus-code mismatch for the supplied chunk because the declared functionality materially exceeds what this code actually does, even though the implemented calendar capabilities themselves are accurately described.
The skill instructs users to copy OAuth client credentials into a fixed local path under ~/.openclaw/google-workspace-byok/credentials.json. Storing sensitive OAuth material in a predictable location raises the risk of credential theft by other local processes, misconfigured permissions, backups, or accidental disclosure.
node {baseDir}/scripts/setup.js --credentials /path/to/downloaded-credentials.json
This copies your credentials to ~/.openclaw/google-workspace-byok/credentials.json.
This section continues the same insecure credential-handling pattern by directing persistence of OAuth credentials in a predictable file path. If compromised, these credentials can facilitate unauthorized OAuth flows and access to connected Google data.
node {baseDir}/scripts/setup.js --credentials /path/to/downloaded-credentials.json
This copies your credentials to `~/.openclaw/google-workspace-byok/credentials.json`.
### Step 5: Authorize Google Accounts
The documented file layout explicitly stores both OAuth client credentials and per-account tokens on disk under a fixed directory. Those token files likely contain refresh tokens, which can grant persistent access to Gmail and Calendar across multiple accounts if exfiltrated.
~/.openclaw/google-workspace-byok/
├── credentials.json # Your GCP OAuth credentials
└── tokens/
├── personal.json # Token for "personal" account
└── work.json # Token for "work" account
Referencing credentials.json here reinforces the insecure credential-storage model and discusses redirect URI configuration tied to that local credential file. While not a separate exploit by itself, it is part of the same pattern of handling sensitive OAuth material in a predictable local file.
Apps in "Testing" publishing status issue tokens that expire after 7 days. Publish your app to "Production" for long-lived tokens. For personal Gmail (External user type), you can skip verification and just accept the "unverified app" warning.
### `Error: redirect_uri_mismatch`
Your credentials.json doesn't include `http://localhost` as a redirect URI. Edit your OAuth client in GCP Console → **Authorized redirect URIs** → add `http://localhost`.
### `npm install` fails or `mupdf` won't install
`mupdf` requires a C++ build toolchain on some platforms. If it fails, you can still use all other features — PDF text extraction is the only feature that requires it. Try: `npm install --ignore-scripts` to skip native compilation, then install `mupdf` separately if needed.
brace-expansion 2.0.2 is flagged with multiple DoS-style advisories involving pathological brace patterns that can trigger excessive CPU or memory use. In this lockfile it is a transitive dependency of minimatch/glob/rimraf, so the risk depends on whether untrusted glob-like patterns can reach that code path; in a Gmail/Calendar skill, attacker-controlled email metadata, attachment names, or search-like inputs could increase exposure if reused in file or pattern matching.
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.
minimatch 9.0.5 is reported with multiple ReDoS/backtracking vulnerabilities, which can allow crafted glob patterns to consume excessive CPU. This is especially relevant in automation tooling because file selection, ignore lists, and cleanup logic often process user- or environment-influenced patterns; while the lockfile alone does not prove exploitability, the dependency is genuinely vulnerable.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
/**
* Setup script — copies Google OAuth credentials into the config directory.
* Usage: node setup.js --credentials /path/to/credentials.json
*/
const fs = require('fs');
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
/**
* Setup script — copies Google OAuth credentials into the config directory.
* Usage: node setup.js --credentials /path/to/credentials.json
*/
const fs = require('fs');
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
/**
* Setup script — copies Google OAuth credentials into the config directory.
* Usage: node setup.js --credentials /path/to/credentials.json
*/
const fs = require('fs');
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
/**
* Setup script — copies Google OAuth credentials into the config directory.
* Usage: node setup.js --credentials /path/to/credentials.json
*/
const fs = require('fs');
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
/**
* Setup script — copies Google OAuth credentials into the config directory.
* Usage: node setup.js --credentials /path/to/credentials.json
*/
const fs = require('fs');
Without declared permissions the skill's intent is opaque and cannot be validated.
The activation guidance is broad enough that the skill may be invoked for many generic email or calendar-related requests. In a high-sensitivity integration that can read mail, download attachments, and access multiple accounts, overly broad routing increases the chance of unnecessary access to private data.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
This installs `googleapis` (Google API client) and `mupdf` (PDF text extraction for email attachments).
### Step 2: Create a Google Cloud Project
1. Go to [Google Cloud Console](https://console.cloud.google.com) and create a new project (or use an existing one)
2. Enable the **Google Calendar API** and **Gmail API**:
The skill requests full calendar scope by default while presenting the integration in a largely read-oriented context. Overbroad OAuth scopes violate least privilege and increase damage if tokens are stolen or the skill is misused, because an attacker could modify or delete calendar data rather than only read it.
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.
The refresh token expired or was revoked. Re-run `node {baseDir}/scripts/auth.js --account <label>` to re-authorize.
### Tokens expire after 7 days
Apps in "Testing" publishing status issue tokens that expire after 7 days. Publish your app to "Production" for long-lived tokens. For personal Gmail (External user type), you can skip verification and just accept the "unverified app" warning.
### `Error: redirect_uri_mismatch`
Your credentials.json doesn't include `http://localhost` as a redirect URI. Edit your OAuth client in GCP Console → **Authorized redirect URIs** → add `http://localhost`.
This code performs network calls to Google Calendar and outputs detailed event data including descriptions, locations, organizer/creator emails, attendee lists, and links. There is no confirmation prompt or explicit warning in the file that these results may contain sensitive personal or organizational data.
The get-event path fetches a specific calendar event and prints complete creator, organizer, attendees, recurrence, reminders, and other event metadata. Because this is a code file handling potentially sensitive calendar content, the absence of an explicit warning or disclosure is a safety concern.
The attachment action writes Gmail attachment contents directly to a filesystem path derived from user-controlled inputs (--out-dir and potentially --filename) without any confirmation, restriction, or safety guard. In an agent/skill context, silent file writes can be abused to drop arbitrary content on disk, overwrite existing files, or stage follow-on attacks, especially because the data source is untrusted email content and the default behavior is to persist it locally.
The skill defines a full calendar read/write OAuth scope even though the described functionality in the metadata is primarily read-oriented (listing calendars, getting events, checking free/busy). This violates least privilege and, if this scope is ever requested or used, a compromised skill or downstream bug could modify or delete calendar data unnecessarily.
This code creates local directories for OAuth credentials and tokens, and later persists token data to disk. While the file has developer-oriented comments, it does not include any user-facing warning, confirmation, or disclosure that sensitive authentication material will be stored under the user's home directory.
OAuth tokens are persisted in plaintext JSON on disk under the user's home directory, and the code does not set restrictive file permissions or use an OS credential store. If another local user, process, or malware can read these files, the attacker may gain ongoing access to the victim's Gmail and Calendar data across linked accounts.
This action performs a Google Calendar API request using the selected calendar ID and returns availability data, which can still reveal sensitive scheduling patterns. The code does not provide a user-facing disclosure that calendar availability information will be queried and printed.
No suspicious patterns detected.