Back to skill

Security audit

Apple Calendar Pal

Security checks for vulnerabilities and agentic risk

Overview

This calendar skill mostly does what it claims, but one deletion path can remove unrelated Apple Calendar events despite documentation saying cancellations are limited to skill-created events.

Review before installing. Use a revocable Apple app-specific password, keep `data/profile.json` private, prefer a dedicated plans calendar, and avoid using `cancel --uid` until the script enforces exact `calpal-` ownership and calendar scoping. Expect destination and coordinate data to be sent to weather/geocoding/routing services when trip planning is used.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cal.py:428
Finding

Unrestricted UID-Based Deletion Can Remove Unrelated Calendar Events

Content
View full analysis

Vulnerability Details

File Location: scripts/cal.py, lines 428–449
Vulnerability Type: Missing ownership and calendar-scope validation
Risk Level: Medium

Vulnerable Code

python
if args.get("uid"):
    # 按 UID 精确删:扫全部日历找到那条资源的 URL
    for name, url in calendars.items():
        s, c = cli.dav("PROPFIND", url,
                       '<?xml version="1.0" encoding="UTF-8"?>'
                       '<d:propfind xmlns:d="DAV:"><d:prop><d:getetag/></d:prop>'
                       "</d:propfind>", depth=1)
        for resp in tags(ET.fromstring(c), "response"):
            href = first_text(resp, "href")
            if not href or not href.rstrip("/").endswith(".ics"):
                continue
            item = urllib.parse.urljoin(url, href)
            s, body = cli._raw("GET", item)
            if s == 200 and any(l.startswith("UID:" + args["uid"])
                                for l in unfold(body.decode("utf-8", "replace"))):
                cli.dav("DELETE", item)
                print("已删除 %s(%s)" % (args["uid"], name))
                return
    die("所有日历里都没找到 UID=%s" % args["uid"])

Technical Analysis

The date-based cancellation path limits deletion to events whose UID begins with calpal- and protects the commitments calendar unless an override is supplied. The UID-based path does not enforce either control.

When --uid is supplied, the script scans every calendar available to the configured Apple account. It retrieves each ICS resource and deletes the first resource containing a UID line that starts with the supplied value. It does not:

  • require the UID to use the Skill-owned calpal- prefix;
  • restrict the search to the configured plans calendar;
  • exclude the protected commitments calendar;
  • enforce the chat confirmation required by SKILL.md; or
  • verify the result of the CalDAV DELETE before reporting success.

The use of `startswith("UID:" ...[truncated 1786 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require exact ownership validation before deletion:
    python
    requested_uid = args["uid"]
    if not requested_uid.startswith(UID_PREFIX):
        die("Refusing to delete an event not owned by this Skill")
    
  2. Parse the ICS resource and compare the complete UID for equality rather than using startswith.
  3. Restrict UID-based deletion to prof["calendars"]["plans"] by default. Do not scan every accessible calendar.
  4. Refuse deletion from the commitments calendar and unrelated calendars. If administrative override is genuinely required, implement a separate explicit mode with exact calendar selection and stronger confirmation.
  5. Add a machine-verifiable confirmation mechanism, such as a confirmation token bound to the previously displayed UID, title, date, time, and calendar. Do not rely solely on documentation instructing the Agent to confirm.
  6. Check the CalDAV DELETE status and report success only for accepted success codes such as 200, 202, or 204.
  7. Consider querying the exact plans-calendar resource through CalDAV filters instead of enumerating and downloading every event from every calendar.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill claims sensitive calendar read/write/delete and trip-planning behavior, while the implementation contract is inconsistent and includes undeclared external API usage. This mismatch is dangerous because users and the host agent may trust the skill with highly sensitive scheduling data under false assumptions about what capabilities exist and where data is sent, enabling privacy violations and unsafe automation.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The module-level safety claim says cancellation only affects this skill’s own calpal- events, but the cancel --uid path scans all calendars and deletes any event whose ICS content contains the supplied UID, without enforcing the calpal- prefix or restricting deletion to skill-owned resources. In a calendar-writing skill, this breaks an explicit safety boundary and can delete unrelated user events if a UID is known or guessed from prior queries, logs, or other tooling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README describes importing schedules and '直写 iPhone 日历' but does not prominently warn users that the skill can directly create, modify, or cancel events in their iCloud calendar. Because this skill has real write access to personal calendar data, insufficient disclosure can mislead users about the sensitivity of invoking it and increase the risk of unintended or overly trusted actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README advertises activation using broad natural-language phrases like '想去哪/想做什么' and '帮我看看周四有什么安排', which can cause the skill to trigger on ordinary conversation without strong user intent verification. In a skill that can read and modify Apple Calendar data, over-broad triggering raises the chance of unintended calendar queries or writes, especially when the surrounding platform performs automatic skill routing.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs the agent to use network-capable scripts for calendar access, geocoding, weather, and routing, but does not declare any tool scope or allowed tools. That creates an authorization and transparency gap: the agent may send user-provided locations, schedules, and home coordinates to external services without explicit permission boundaries.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The activation rules are extremely broad and can trigger on ordinary conversation about plans, availability, or destinations. In this skill's context, that is risky because activation grants access to sensitive profile data, commitment files, calendar contents, and network lookups involving home coordinates and travel destinations, increasing the chance of unnecessary data access or unintended calendar modifications.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The configuration hard-codes city_code as 010 and timezone as Asia/Shanghai, which indicates a fixed locale assumption in natural-language-facing behavior. Under the policy, locale constraints should be optional, user-selectable, or clearly justified as region-specific; this file provides neither.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The file presents the tool entirely in Chinese and also forces Chinese locale parameters in external requests, indicating the skill is designed to operate in a specific language without offering a user choice. This matches the policy category for language or locale constraints that are not explicitly optional or justified as region-specific compliance behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/env_tools.py (reported line 161)May include surrounding context.

python
# ---------------------------------------------------------------- 日出日落 / 天气
def sun_times(lat, lon, date):
    d = http_json("https://api.open-meteo.com/v1/forecast", {
        "latitude": lat, "longitude": lon,
        "daily": "sunrise,sunset,daylight_duration",
        "timezone": "Asia/Shanghai", "start_date": date, "end_date": date,

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/env_tools.py (reported line 174)May include surrounding context.

python
# ---------------------------------------------------------------- 日出日落 / 天气
def sun_times(lat, lon, date):
    d = http_json("https://api.open-meteo.com/v1/forecast", {
        "latitude": lat, "longitude": lon,
        "daily": "sunrise,sunset,daylight_duration",
        "timezone": "Asia/Shanghai", "start_date": date, "end_date": date,

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README content is written as a directive in Chinese ('Put your commitment spec JSON here'), which imposes a specific language/locale in the skill text without offering any user choice or opt-in. The policy for SQP-3 applies to all file types and covers language/locale restrictions expressed in natural language.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.