Back to skill

Security audit

Jetlag Planner

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says, but it uses broad Google Calendar access, automatically writes many calendar events, and gives unsafe credential setup guidance.

Review this carefully before installing. Use fresh Google OAuth credentials created specifically for this skill, do not ask another bot to print secrets, and expect the tool to read your primary calendar and automatically add multiple events. Prefer running it manually first, reviewing what it creates, restricting token file permissions, and revoking the Google authorization if you stop using it.

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

T09 · Insecure Skill Coding Practices

Warning
Location
README.md:13
Finding
OAuth Client Secret Disclosure Through Agent Conversation<![CDATA[ ## Vulnerability Details **File Location**: `README.md:13-15` **Vulnerability Type**: Sensitive credential exposure through conversation history **Risk Level**: Medium ### Vulnerable Code or Instruction ```markdown You already have Google OAuth credentials from your OpenClaw setup. Ask your Claw bot: > "What is your Google Client ID and Secret from your config?" ``` ### Technical Analysis The setup procedure explicitly instructs the user to ask an AI agent to retrieve and disclose an existing Google OAuth client secret. This moves credential material from its original configuration boundary into the conversation channel. Conversation content may be retained in local logs, remote service telemetry, message history, debugging traces, or screenshots. The disclosure is unnecessary: users can create dedicated OAuth credentials or transfer credentials directly into a protected configuration file without exposing them to the agent conversation. A desktop OAuth client secret is not generally treated as a sufficient standalone authenticator, so possession of this value alone does not automatically grant Calendar access. Nevertheless, disclosure can facilitate OAuth client impersonation, abuse of the associated project, or a broader compromise when combined with authorization codes, redirect manipulation, or other leaked OAuth material. ### Attack Path 1. The user follows the README and asks the agent to print the Google Client ID and client secret. 2. The agent retrieves the values from its configuration and includes them in a response. 3. The response is retained in conversation history, logs, telemetry, or another accessible storage location. 4. An attacker with access to that location extracts the OAuth client credentials. 5. The attacker uses the credentials to impersonate the OAuth client or combines them with separately obtained OAuth authorization material. 6. If a usable authorization code or token is also obtained, the attacker can access reso ...[truncated 630 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove all instructions that ask an agent to print or relay secrets through chat. - Require users to create dedicated OAuth credentials for this Skill rather than reusing credentials from another OpenClaw configuration. - Direct users to enter credentials locally into a protected file or operating-system secret store. - Ensure `.env` is excluded from version control and created with owner-only permissions such as `0600`. - Add explicit documentation warning users never to paste OAuth secrets, authorization codes, or tokens into conversations. - Prefer OAuth flows suitable for installed applications that do not rely on treating a desktop client secret as a confidential secret. - Rotate previously disclosed credentials when conversation or logging systems may have retained them. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:42
Finding
OAuth Tokens Stored Without Explicit Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `index.js:42-59` **Vulnerability Type**: Plaintext sensitive token storage with unsafe default permissions **Risk Level**: Medium ### Vulnerable Code ```js if (fs.existsSync(TOKEN_PATH)) { const token = JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8')); auth.setCredentials(token); // Refresh if expired if (token.expiry_date && Date.now() > token.expiry_date - 60_000) { const { credentials } = await auth.refreshAccessToken(); fs.writeFileSync(TOKEN_PATH, JSON.stringify(credentials)); auth.setCredentials(credentials); } return auth; } const authUrl = auth.generateAuthUrl({ access_type: 'offline', scope: SCOPES }); console.log('\n► Open this URL in your browser:\n'); console.log(' ' + authUrl + '\n'); const opened = await open(authUrl).then(() => true).catch(() => false); if (opened) { console.log('(Browser window opened)'); } else { console.log('(Could not open browser automatically — paste the URL above into your browser manually)'); } console.log('\nAfter you click "Allow", Google will show you a short code.'); const code = await promptLine('Paste the code here and press Enter: '); const { tokens } = await auth.getToken(code.trim()); fs.writeFileSync(TOKEN_PATH, JSON.stringify(tokens)); ``` ### Technical Analysis The application stores Google OAuth credentials, potentially including an offline refresh token, as plaintext JSON in `.oauth-token.json`. Both initial creation and refresh use `fs.writeFileSync` without an explicit owner-only mode. For a newly created file, Node.js uses a default mode subject to the process umask. Depending on the environment, this can leave the file readable by other local users or processes. When updating an existing file, the application also does not verify whether its current ownership and permissions are safe. This violates least-privilege handling for reusable authentication material. The risk is particularly significant because `access_type: ...[truncated 1368 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the token file with explicit owner-only permissions: ```js fs.writeFileSync(TOKEN_PATH, JSON.stringify(tokens), { encoding: 'utf8', mode: 0o600, }); ``` - After writing, enforce permissions with `fs.chmodSync(TOKEN_PATH, 0o600)`. - Before reading an existing token file, verify that it is a regular file, owned by the expected user, and not accessible by group or other users. - Reject symbolic links or use safe file-opening flags to reduce symlink and file-replacement risks. - Store refresh tokens in an operating-system credential store or encrypted secret-management service where practical. - Add `.oauth-token.json` and `.env` to `.gitignore`. - Document token revocation and rotation procedures. - Consider using narrower Calendar scopes if the workflow can be redesigned to avoid unrestricted modification of the primary calendar. ]]>

T08 · Insecure Dependencies

Note
Location
package.json:10
Finding
Non-Reproducible Dependency Resolution Due to Mutable Version Ranges and Missing Lockfile<![CDATA[ ## Vulnerability Details **File Location**: `package.json:10-14` **Vulnerability Type**: Unlocked third-party dependency supply chain **Risk Level**: Low ### Vulnerable Code ```json "dependencies": { "dotenv": "^16.4.5", "googleapis": "^144.0.0", "luxon": "^3.5.0", "open": "^10.1.0" } ``` ### Technical Analysis The project declares dependencies using caret ranges and does not include a package lockfile in the audited directory. Consequently, `npm install` can resolve versions and transitive dependency trees that differ from those reviewed during this audit. No evidence indicates that the named packages are malicious. The weakness is that future installation behavior is not reproducible: a compromised, withdrawn, or unexpectedly changed direct or transitive package version could be installed without a corresponding reviewed source change in this repository. Installed dependencies execute in the same Node.js process as the Skill. Runtime dependencies therefore have access to the process environment, loaded Google client credentials, OAuth tokens read by the application, local files permitted to the user, and outbound network connectivity. ### Attack Path 1. A maintainer, package account, release process, or transitive dependency is compromised, or an unsafe release is published within an allowed version range. 2. A user follows the documented setup and runs `npm install`. 3. npm resolves the newly published version because no reviewed lockfile fixes the dependency graph. 4. Malicious package lifecycle or runtime code executes with the privileges of the installing or running user. 5. The package reads environment variables, `.env`, `.oauth-token.json`, or Calendar data handled by the application. 6. The package sends the information to an attacker-controlled endpoint or modifies local behavior. ### Impact Assessment Successful supply-chain exploitation would execute code with the privileges of the user running the Skill. Depending on loc ...[truncated 348 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Generate, review, and commit `package-lock.json`. - Document and use `npm ci` for reproducible installations. - Pin direct dependency versions where appropriate and review all version changes. - Enable automated dependency monitoring and review security advisories before merging updates. - Run `npm audit` as part of continuous integration while manually validating findings. - Review package lifecycle scripts and consider installation controls such as `--ignore-scripts` when compatible with the dependency set. - Use a trusted registry configuration and integrity verification supplied by the lockfile. - Periodically review transitive dependencies and remove unnecessary packages to reduce supply-chain exposure. ]]>
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 (30)

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The README instructs users to ask another bot to reveal its configured Google Client ID and Client Secret, which normalizes exfiltrating secrets from a separate system. Even if these are OAuth client credentials rather than end-user tokens, directing a user to retrieve and reuse secrets from another agent breaks credential-boundary expectations and can expose credentials beyond their intended scope.

Ssd 3

High
Confidence
99% confidence
Finding
Telling users to ask another bot to disclose its stored Google OAuth client ID and secret is an explicit instruction to retrieve sensitive credentials from a different application context. This encourages unsafe secret disclosure patterns and could train users to bypass access controls or reuse credentials across unrelated components.

Credential Access

High
Category
Privilege Escalation
Content
git clone https://github.com/chadholdorf/openclaw-jetlag.git
cd openclaw-jetlag
npm install
cp .env.example .env
```

Open `.env` and paste in the values your Claw bot gave you:
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
git clone https://github.com/chadholdorf/openclaw-jetlag.git
cd openclaw-jetlag
npm install
cp .env.example .env
```

Open `.env` and paste in the values your Claw bot gave you:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill's declared description understates its actual behavior: it checks for credentials, triggers an OAuth authorization flow, and causes a local Node program to access and modify Google Calendar data. This mismatch undermines informed consent and hides sensitive data access plus write actions behind a seemingly simple travel-planning description.

Credential Access

High
Category
Privilege Escalation
Content
Run the jetlag planner by following these steps exactly.

## Step 1 — Check for .env

Check whether the file `~/openclaw-jetlag/.env` exists.
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
Run the jetlag planner by following these steps exactly.

## Step 1 — Check for .env

Check whether the file `~/openclaw-jetlag/.env` exists.
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
Run the jetlag planner by following these steps exactly.

## Step 1 — Check for .env

Check whether the file `~/openclaw-jetlag/.env` exists.
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
Run the jetlag planner by following these steps exactly.

## Step 1 — Check for .env

Check whether the file `~/openclaw-jetlag/.env` exists.
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
Run the jetlag planner by following these steps exactly.

## Step 1 — Check for .env

Check whether the file `~/openclaw-jetlag/.env` exists.
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
function getOAuthClient() {
  const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI } = process.env;
  if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
    console.error('Missing GOOGLE_CLIENT_ID or GOOGLE_CLIENT_SECRET in .env');
    process.exit(1);
  }
  return new google.auth.OAuth2(
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
function getOAuthClient() {
  const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI } = process.env;
  if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
    console.error('Missing GOOGLE_CLIENT_ID or GOOGLE_CLIENT_SECRET in .env');
    process.exit(1);
  }
  return new google.auth.OAuth2(
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
function getOAuthClient() {
  const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, GOOGLE_REDIRECT_URI } = process.env;
  if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
    console.error('Missing GOOGLE_CLIENT_ID or GOOGLE_CLIENT_SECRET in .env');
    process.exit(1);
  }
  return new google.auth.OAuth2(
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README emphasizes automatic detection and automatic writing of 14+ events to the user's calendar without a prominent warning that the skill will modify calendar data. In a skill context, silent or under-disclosed write behavior is risky because users may invoke it expecting analysis only, not persistent changes.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger phrase examples are broad and action-oriented, increasing the chance the skill is invoked unintentionally in normal conversation. Because the skill performs write actions against a Google Calendar, accidental invocation can lead to unwanted calendar modifications without clear confirmation.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README describes scanning the primary Google Calendar via OAuth but does not provide a clear privacy notice about what data is accessed, how long tokens are stored, and what is sent or retained. This matters more here because the skill processes personal travel itinerary data and persists authorization in a local token file.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill instructs the agent to inspect local files and run local code, but it declares no explicit tool scope or allowed tools. This creates an undeclared capability boundary where a user may invoke filesystem and execution behavior without clear permissioning or review, increasing the risk of unintended local access and side effects.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrases are broad enough to match ordinary travel-related conversation, which raises the chance of accidental invocation. Because invocation leads to credential checks and local code execution with calendar access, an unintended trigger could cause privacy-impacting actions without deliberate user intent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The description does not clearly warn that the skill will inspect Google Calendar-related setup, use stored OAuth credentials, and write new events back to the user's calendar. In a skill that processes private travel data and modifies a personal calendar, missing disclosure materially increases consent and privacy risk.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill automatically inserts multiple calendar events after detecting possible flights, without any explicit confirmation, preview, or per-flight approval from the user. Because it uses broad calendar write access and heuristic flight detection, a mistaken match or unexpected parse can silently modify the user's primary calendar and create unwanted reminders or clutter at scale.

Vague Triggers

Medium
Confidence
89% confidence
Finding
The package description advertises automatic scanning of Google Calendar and writing data back to it, but it does not mention any invocation limits, explicit confirmation, or scope restrictions. In a skill context that handles personal calendar data, broad read/write behavior increases the risk of over-collection or unintended modification if the implementation follows the description literally.

Context-Inappropriate Capability

Low
Confidence
85% confidence
Finding
The manifest describes a calendar-scanning and plan-writing skill, but does not indicate any need to read local environment secrets or guide the user through credential bootstrap. While OAuth is a plausible implementation detail for Google Calendar access, directly consuming local client credentials from .env adds a capability outside the user-facing purpose of jetlag planning itself.

Natural-Language Policy Violations

Low
Confidence
71% confidence
Finding
The natural-language description is platform-specific and presumes interaction with Google Calendar as the default behavior. If organizational policy requires avoiding forced platform or locale constraints without user opt-in, this wording can be read as mandating a specific service rather than offering a choice.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"start": "node index.js"
  },
  "dependencies": {
    "dotenv": "^16.4.5",
    "googleapis": "^144.0.0",
    "luxon": "^3.5.0",
    "open": "^10.1.0"
Confidence
83% confidence
Finding
Using a caret range for dotenv allows future compatible releases to be installed without explicit review, which weakens supply-chain integrity and reproducibility. While dotenv is not especially high risk here, unpinned dependencies can still introduce vulnerable or unexpected code into the skill.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "dotenv": "^16.4.5",
    "googleapis": "^144.0.0",
    "luxon": "^3.5.0",
    "open": "^10.1.0"
  },
Confidence
88% confidence
Finding
The googleapis dependency is not pinned, so builds may resolve to different releases over time, increasing supply-chain risk for a package that likely handles OAuth tokens and calendar read/write access. Because this skill accesses sensitive personal data and may modify calendar entries, dependency drift is more dangerous than in a non-privileged utility.

Static analysis

No suspicious patterns detected.