T09 · Insecure Skill Coding Practices
- 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]; ``` ]]>
