Back to skill

Security audit

Calendar Crab

Security checks for vulnerabilities and agentic risk

Overview

This calendar skill is coherent and not deceptive, but it can change or delete Google Calendar events immediately using stored OAuth credentials and has weak safeguards around ambiguous event selection and local token storage.

Review this skill before installing. Use event IDs for move and delete operations whenever possible, list events first, and avoid time-based deletes when more than one event could match. Store the OAuth files in a private directory with restrictive permissions and revoke the Google refresh token if those files are ever shared or exposed.

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

T09 · Insecure Skill Coding Practices

Warning
Location
calendar-crab.js:214
Finding

Ambiguous Time-Based Event Selection Can Modify or Delete the Wrong Event

Content
View full analysis
{ const d = eventStartToDate(e); const hh = String(d.getHours()).padStart(2, '0'); const mm = String(d.getMinutes()).padStart(2, '0'); return `${hh}:${mm}` === time; }); if (!target) { throw new Error(`No event found on ${date} at ${time}`); } return target; ``` The selected event is subsequently used by destructive or state-changing operations: ```js } else if (args.date && args.time) { event = await findEventByTime(args.date, args.time); } await gcal( `/calendars/${encodeURIComponent(CALENDAR_ID)}/events/${encodeURIComponent(event.id)}?sendUpdates=all`, 'DELETE' ); ``` ### Technical Analysis The time-based event lookup uses `Array.find()`, which returns the first event whose local hour and minute match the requested time. It does not determine whether multiple events share that date and time. Concurrent calendar events are valid and common. Google Calendar API ordering therefore determines which matching event is returned. This behavior contradicts the documented execution rule that a target should be verified as unique before it is moved or deleted. The affected operations execute immediately and specify `sendUpdates=all`, so selecting the wrong event can both change calendar state and notify its attendees. An attacker who can create calendar events, or an accidental duplicate event, can introduce ambiguity into time-based selection. ### Attack Path 1. Two or more events exist on the target calendar at the same local date and time. 2. A user or agent invokes a time-based operation such as: - `delete --date=YYYY-MM-DD --time=HH:MM` - `move --date=YYYY-MM-DD --from=HH:MM --to=HH:MM` 3. `findEventByTime()` retrieves the day's events and returns only the first matching entry. 4. The comman ...[truncated 695 chars]
Remediation
View remediation
{ const date = eventStartToDate(event); const hh = String(date.getHours()).padStart(2, '0'); const mm = String(date.getMinutes()).padStart(2, '0'); return `${hh}:${mm}` === time; }); if (matches.length === 0) { throw new Error(`No event found on ${requestedDate} at ${time}`); } if (matches.length > 1) { const candidates = matches .map((event) => `${event.summary || '(no title)'}: ${event.id}`) .join('\n'); throw new Error( `Multiple events found on ${requestedDate} at ${time}. Retry with --id:\n${candidates}` ); } return matches[0]; ``` ]]>

T09 · Insecure Skill Coding Practices

Note
Location
README.md:34
Finding

OAuth Secret Files Are Documented Without Restrictive Permission Requirements

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 62)May include surrounding context.

}

text

The script auto-refreshes access tokens using the refresh token.

## Commands

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 88)May include surrounding context.

"obtained_at": "" }

text
The script auto-refreshes the access token using the refresh token.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill description says an AI agent can manage the user's calendar, including deleting and moving events, but it does not warn that these actions can alter or remove existing calendar data. For a markdown file, destructive or data-affecting behavior should be clearly disclosed so users understand the risk before use.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · README.md (reported line 19)May include surrounding context.

md
# List next 7 days
node calendar-crab.js list

# Create an event
node calendar-crab.js create --title="Coffee" --date=2026-03-20 --time=09:00 --duration=30

# Move an event to a new time

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README instructs users to save OAuth client secrets and refresh tokens locally, and states that access tokens are auto-refreshed, but it does not warn that these files grant ongoing access to the user's Google Calendar. For markdown guidance covering credential handling and remote API access, users should be explicitly told that these secrets are sensitive and must be protected.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill documents access to sensitive local credential material via environment-driven secret paths but does not declare an explicit tool scope or permissions boundary. In agent environments, missing scope declarations can allow broader-than-expected access to host environment data, increasing the chance that secrets are read or exposed unintentionally.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill advertises event deletion commands without an explicit confirmation requirement or strong warning about destructive effects. In an agent-assisted workflow, ambiguous event targeting or mistaken execution could permanently remove calendar entries and notify attendees, causing operational disruption.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · SKILL.md (reported line 69)May include surrounding context.

md
## Setup

1. Go to [Google Cloud Console](https://console.cloud.google.com/) and create a project.
2. Enable the **Google Calendar API**.
3. Create OAuth 2.0 credentials (Desktop app type).
4. Save the credentials as `~/.openclaw/secrets/google-calendar-oauth.json`:

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · calendar-crab.js (reported line 8)May include surrounding context.

js
*
 * Usage:
 *   node calendar-crab.js list --days=3 --max=20
 *   node calendar-crab.js create --title="Lunch" --date=2026-03-20 --time=12:00
 *   node calendar-crab.js move --date=2026-03-01 --from=19:00 --to=16:00
 *   node calendar-crab.js move --id="EVENT_ID" --to="2026-03-01T16:00:00-07:00"
 *   node calendar-crab.js delete --id="EVENT_ID"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code accesses sensitive credentials from google-calendar-token.json and google-calendar-oauth.json to obtain API access. Although the header shows usage examples, it does not disclose that the tool reads and uses stored OAuth credentials, which is a sensitive operation under the warning criteria for code files.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

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.

Content

Scanner excerpt · calendar-crab.js (reported line 305)May include surrounding context.

js
Commands:
  list   --days=7 --max=20
  create --title="..." --date=YYYY-MM-DD --time=HH:MM [--duration=60] [--location="..."] [--attendees="a@b,c@d"] [--description="..."] [--tz=America/Los_Angeles]
  move   --date=YYYY-MM-DD --from=HH:MM --to=HH:MM [--tz=...]
  move   --id=EVENT_ID --to=2026-03-01T16:00:00-07:00
  delete --id=EVENT_ID

Static analysis

No suspicious patterns detected.