Back to skill

Security audit

Check and book Tennis and Pickleball Courts at Bay Club Gateway

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a real Bay Club court-booking bot, but it can automatically make bookings, accept page checkboxes, choose partners, and write calendar events with too little user confirmation or scoping.

Review this skill carefully before installing. It should only be used if you are comfortable giving it Bay Club credentials and allowing it to make real reservations. Avoid using the booking action until it adds an explicit final confirmation step, stops auto-selecting partners, stops blanket-clicking checkboxes, and narrows Google Calendar credential handling and OAuth scope.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
BayClubBot.ts:458
Finding
Booking Flow Silently Selects a Hardcoded or Arbitrary Third-Party Partner<![CDATA[ ## Vulnerability Details **File Location**: `BayClubBot.ts:458-491` **Vulnerability Type**: Unauthorized third-party selection and violation of least privilege **Risk Level**: High ### Complete Code Snippet ```ts // Select buddy/partner - look for Samuel Wang or Emma Campbell console.log('Looking for buddy selection...'); try { await this.page.waitForTimeout(3000); // Try to find and click Samuel Wang or Emma Campbell by name const buddySelected = await this.page.evaluate(() => { // Find all elements that might contain buddy/person info const allElements = Array.from(document.querySelectorAll('*')); // Look for Samuel Wang or Emma Campbell const samuelOrEmma = allElements.filter(el => { const text = el.textContent || ''; return text.includes('Samuel Wang') || text.includes('Emma Campbell'); }); // Click the most specific (smallest) element if (samuelOrEmma.length > 0) { // Sort by text length to get most specific samuelOrEmma.sort((a, b) => (a.textContent?.length || 999) - (b.textContent?.length || 999) ); const buddy = samuelOrEmma[0] as HTMLElement; buddy.scrollIntoView({ block: 'center' }); buddy.click(); console.log('Selected buddy:', buddy.textContent?.trim().substring(0, 50)); return true; } // Fallback: click any app-racquet-sports-person element const personElements = Array.from(document.querySelectorAll('app-racquet-sports-person')); if (personElements.length > 0) { (personElements[0] as HTMLElement).click(); console.log('Selected first person element'); return true; } console.log('No buddy elements found'); return false; }); ``` ### Technical Analysis The public booking operation accepts only `sport`, `day`, and `time`. It does not accept a partner identity or record user approval to associate another person with the reservation. Despite this, the browser autom ...[truncated 1772 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all hardcoded person names from the booking flow. 2. Add an optional, explicit `buddy` parameter to the booking API and CLI. 3. Require exact matching against a stable identifier or a normalized full name approved by the user. 4. Never fall back to selecting the first available person. 5. If the website requires a partner and none was supplied, stop before confirmation and return a structured error requesting user input. 6. Display the selected partner in a final booking summary and require explicit confirmation before submission. 7. Add tests ensuring that no partner element is clicked when the parameter is absent or ambiguous. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
BayClubBot.ts:501
Finding
Booking Flow Automatically Accepts Every Checkbox Before Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `BayClubBot.ts:501-508` **Vulnerability Type**: Indiscriminate consent and policy acceptance **Risk Level**: High ### Complete Code Snippet ```ts // Step before confirming: Check for any required checkboxes (terms, policies, etc.) console.log('Checking for required checkboxes...'); await this.page.evaluate(() => { const checkboxes = Array.from(document.querySelectorAll('input[type="checkbox"]')); checkboxes.forEach(cb => { if (!(cb as HTMLInputElement).checked) { (cb as HTMLInputElement).click(); console.log('Checked a checkbox'); } }); }); ``` ### Technical Analysis The code selects every unchecked checkbox present on the booking page. It does not: - Determine whether a checkbox is required. - Inspect or present its associated label. - Distinguish legal terms and waivers from optional marketing consent. - Restrict selection to known controls. - Obtain explicit user approval. The comment acknowledges that these controls may represent terms or policies, but the implementation accepts them indiscriminately. Any new checkbox introduced by the website would also be selected automatically, creating an unsafe fail-open behavior. ### Attack Path 1. A user asks the Skill to book a court. 2. The automation reaches the final booking page. 3. The page contains one or more unchecked controls, such as a waiver, policy acknowledgment, optional communication preference, or another consent. 4. The script queries all checkbox inputs. 5. It clicks every unchecked checkbox without identifying its purpose. 6. The script then clicks the final “CONFIRM BOOKING” control. 7. The booking is submitted with all displayed acknowledgments or options enabled. A malicious or compromised target page could exploit this behavior by adding a consequential checkbox to the booking form, which the automation would select automatically. ### Impact Assessment The user may unknowingly: - Accept legal terms, waiv ...[truncated 382 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the loop that clicks every checkbox. 2. Create an allowlist of stable selectors for controls that are strictly necessary for booking. 3. Extract and display the complete label or policy text associated with each required control. 4. Require explicit user approval before accepting legal terms, waivers, or materially changed policies. 5. Treat unknown required checkboxes as a blocking condition and abort safely. 6. Never enable optional marketing, communication, or unrelated consent controls automatically. 7. Record which approved control was selected without logging sensitive page contents. 8. Add regression tests confirming that unknown and optional checkboxes remain unchecked. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
GoogleCalendarService.js:37
Finding
Google Calendar Integration Requests Broader OAuth Access Than Required<![CDATA[ ## Vulnerability Details **File Location**: `GoogleCalendarService.js:37-42` **Vulnerability Type**: Excessive OAuth scope **Risk Level**: Medium ### Complete Code Snippet ```js const auth = new JWT({ email: credentials.client_email, key: credentials.private_key, scopes: ['https://www.googleapis.com/auth/calendar'], }); this.calendar = google.calendar({ version: 'v3', auth }); ``` ### Technical Analysis The service requests the full Google Calendar OAuth scope: ```text https://www.googleapis.com/auth/calendar ``` The implementation’s declared and observed purpose is to insert a court-booking event through `calendar.events.insert()`. Full Calendar access is broader than necessary for that operation and violates least-privilege principles. The credentials are loaded from either `GOOGLE_CALENDAR_CREDENTIALS` or a local service-account JSON file. If the private key or runtime is compromised, the broad scope can increase the operations available against calendars shared with that service account. ### Attack Path 1. The user creates a Google service account and shares a calendar with it using write privileges. 2. The Skill loads the service-account email and private key. 3. It authenticates using the full Calendar scope. 4. An attacker who obtains the credential file, environment value, or control of the running process authenticates as the service account. 5. The attacker can exercise the broader Calendar API permissions permitted by both the OAuth scope and the calendars shared with the account. The reviewed code does not itself exfiltrate the service-account key. This finding concerns the unnecessary impact available after a separate credential or runtime compromise. ### Impact Assessment Depending on Google Calendar sharing configuration, compromised credentials may permit broader reading or modification of calendar resources than the Skill’s event-creation function requires. Potential effects include: - Reading event details availabl ...[truncated 348 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the full Calendar scope with the narrower event scope: ```js scopes: ['https://www.googleapis.com/auth/calendar.events'] ``` 2. Create a dedicated calendar used only for court-booking events. 3. Share only that dedicated calendar with the service account. 4. Grant the service account only the minimum calendar-level permission required to create events. 5. Keep the service-account key outside the project directory and restrict filesystem permissions. 6. Prefer a managed secret store or protected environment injection over a plaintext credential file. 7. Rotate the service-account key periodically and immediately after suspected exposure. 8. Ensure `GOOGLE_CALENDAR_ID` references only the dedicated booking calendar. ]]>
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (41)

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The code iterates over every unchecked checkbox and clicks it blindly before confirmation. On a booking site, checkboxes may represent terms of service, waivers, billing consent, marketing consent, or policy acknowledgements, so mass-selecting them can cause the user to accept legal or operational obligations they never reviewed.

Missing User Warnings

High
Confidence
99% confidence
Finding
Automatically checking undisclosed checkboxes immediately before booking is a silent consent bypass. Even if technically needed to complete the flow, it suppresses meaningful user review of contractual or policy text and compounds the risk of the subsequent automatic confirmation.

Missing User Warnings

High
Confidence
96% confidence
Finding
The bot proceeds to click the final 'CONFIRM BOOKING' action automatically, with no user-facing review or approval immediately before the irreversible reservation step. In this skill context, that can create real bookings, consume scarce court inventory, trigger penalties or fees, and commit the user to selections they may not have intended.

Credential Access

High
Category
Privilege Escalation
Content
try {
      // Check for credentials file or environment variable
      const credentialsPath = process.env.GOOGLE_CALENDAR_CREDENTIALS_PATH 
        || resolve(__dirname, 'google-calendar-credentials.json');
      
      const credentialsJson = process.env.GOOGLE_CALENDAR_CREDENTIALS;
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
Copy the JSON to the bot directory:

```bash
cp ~/Downloads/your-key.json ~/.openclaw/workspace/skills/bayclub_manager/google-calendar-credentials.json
```

Set your calendar ID:
Confidence
96% confidence
Finding
The instructions tell users to copy a Google service-account JSON key into the bot workspace, which is a sensitive credential containing private key material. In an agent framework that can read local files, storing long-lived credentials in an easily accessible project directory materially increases the risk of credential theft, unauthorized calendar access, and lateral use of the service account.

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
ke one called "Court Bookings")
- Settings → Share with specific people
- Add the service account email (it's in the JSON: `client_email`)
- Give it "Make changes to events" permission

**3. Add credentials**

Copy the JSON to the bot directory:

```bash
cp ~/Downloads/your-key.json ~/.openclaw/workspace/skills/bayclub_manager/google-calendar-credentials.json
```

Set your calendar ID:

```bash
echo 'export GOOGLE_CALENDAR_ID="your-email@gmail.com"' >> ~/.bashrc
source ~/.bashrc
```

That's it. Future bookings will show up in your calendar automatically.

## Usage

### Via WhatsApp (Natural Language)

Just text your OpenClaw agent:

- "Check tennis courts for Sunday"
- "Book pickleball Saturday at 10am"
- "What's available tomorrow?"

### Via Command Line

```bash
# Check what's open
NODE_ENV=development STAGEHAND_ENV=LOCAL HEADLESS=true npx ts-node cli.ts check tennis saturday

# Book a slot
NODE_ENV=development STAGEHAND_ENV=LOCAL HEADLESS=true npx ts-node cli.ts book pickleball su
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description omits access to Google Calendar via service-account credentials and creation of external calendar events. Hidden use of external credentials and third-party resource modification expands the trust boundary significantly and can lead to unauthorized data access or side effects outside the Bay Club booking context.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description omits access to Google Calendar via service-account credentials and creation of external calendar events. Hidden use of external credentials and third-party resource modification expands the trust boundary significantly and can lead to unauthorized data access or side effects outside the Bay Club booking context.

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
96% confidence
Finding
form-data 4.0.5 is flagged for CRLF injection in multipart field names/filenames, which can enable request smuggling or manipulation of downstream multipart parsing when attacker-controlled field metadata is embedded into outbound requests. This is more concerning in an automation/integration skill because it may interact with external web services and APIs, increasing the chance that untrusted input could be relayed into multipart requests.

Possible Typosquatting: 'gaxios' resembles popular package 'axios'

High
Category
Supply Chain
Confidence
70% confidence
Finding
Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Known Vulnerable Dependency: jsondiffpatch==0.6.0 — 2 advisory(ies): CVE-2025-9910 (jsondiffpatch is vulnerable to Cross-site Scripting (XSS) via HtmlFormatter::nod); CVE-2026-8657 ( jsondiffpatch patch APIs are vulnerable to prototype pollution)

High
Category
Supply Chain
Confidence
87% confidence
Finding
jsondiffpatch 0.6.0 is reported vulnerable to XSS in HTML formatting and prototype pollution in patch APIs. Because this file only shows the dependency and not actual use of HtmlFormatter or patch application on untrusted objects, exploitability is contextual; however, inclusion of a package with two high-severity classes of bugs is still a valid vulnerability finding. In this backend-oriented booking skill, XSS risk is likely lower, but prototype pollution could still matter if untrusted diffs are processed anywhere in the stack.

Known Vulnerable Dependency: nanoid==3.3.11 — 3 advisory(ies): CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero); CVE-2026-73086 (nanoid: Integer Overflow or Wraparound)

High
Category
Supply Chain
Confidence
84% confidence
Finding
nanoid 3.3.11 is flagged for edge-case denial-of-service and integer handling issues in non-secure/custom generator paths. This is a real vulnerability in the package version, though practical exposure depends on whether the skill or its dependencies use the affected APIs with attacker-controlled sizes or custom generators; many consumers only use the safe default path.

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
95% confidence
Finding
ws 8.19.0 is flagged for memory disclosure and memory-exhaustion denial of service. This is particularly relevant in a browser automation skill because WebSocket transport is commonly used by Playwright, browser-control tooling, and AI streaming integrations, so an exposed or attacker-reachable WS channel could have meaningful confidentiality and availability impact.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The bot auto-selects specific named playing partners ('Samuel Wang' or 'Emma Campbell') without deriving that choice from explicit user input. This exceeds the stated purpose of court booking by making a consequential selection on behalf of the user, and it can create unauthorized reservations, privacy issues, or social/accountability problems for the named people.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Event creation always uses the 'America/Los_Angeles' time zone, regardless of the user's actual locale or preference. This is a natural-language policy concern because it imposes a fixed locale setting without opt-in or an explicit documented regional constraint.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code sends booking details, including summary, location, description, and buddy name, to Google Calendar without any consent, notice, or confirmation mechanism in this component. In a booking skill, that creates a real privacy risk because personal scheduling data is transmitted to a third party and persisted externally, potentially without the user's explicit awareness at the point of action.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The README advertises booking courts and modifying Google Calendar via WhatsApp/text, but it does not clearly warn users that natural-language messages can trigger actions against external accounts. In an agentic environment, this increases the risk of unintended bookings, account changes, or social-engineering-driven misuse because users may not realize that messages cause real-world side effects.

Session Persistence

Medium
Category
Rogue Agent
Content
### Option 1: DigitalOcean (Easiest)

1. Go to the [OpenClaw marketplace page](https://marketplace.digitalocean.com/apps/openclaw)
2. Click "Create OpenClaw Droplet"
3. Pick the $21/month plan
4. Wait ~2 minutes for it to boot
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.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README instructs users to store Bay Club credentials and Google service-account material without discussing secret handling, file permissions, or the sensitivity of calendar access. This can lead to accidental credential exposure, especially in shared home directories, repos, backups, or agent-accessible workspaces where secrets may be read or exfiltrated.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
```bash
# Check what's open
NODE_ENV=development STAGEHAND_ENV=LOCAL HEADLESS=true npx ts-node cli.ts check tennis saturday

# Book a slot
NODE_ENV=development STAGEHAND_ENV=LOCAL HEADLESS=true npx ts-node cli.ts book pickleball sunday "10:00 AM - 11:00 AM"
Confidence
60% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Unsafe Defaults

Medium
Category
Tool Misuse
Content
```bash
# Check what's open
NODE_ENV=development STAGEHAND_ENV=LOCAL HEADLESS=true npx ts-node cli.ts check tennis saturday

# Book a slot
NODE_ENV=development STAGEHAND_ENV=LOCAL HEADLESS=true npx ts-node cli.ts book pickleball sunday "10:00 AM - 11:00 AM"
Confidence
60% confidence
Finding
Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill invokes a shell command and relies on environment variables, but the manifest does not declare an explicit tool scope or allowed-tools boundary. That omission weakens least-privilege controls and makes it easier for the skill to gain broader execution capability than users or reviewers would expect.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
Using `npx ts-node` without a pinned version allows resolution of whatever package version is available at runtime, which can change unexpectedly or be influenced by supply-chain compromise. Because this command is executed through the shell, an attacker controlling dependencies or package resolution could achieve arbitrary code execution in the agent environment.

Static analysis

No suspicious patterns detected.