Back to skill

Security audit

apple-calpal

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real calendar-planning tool, but it has enough calendar-deletion and consent-control risk that users should review it carefully before installing.

Install only if you are comfortable giving the skill an Apple app-specific calendar password and allowing it to contact iCloud plus mapping/weather services. Review every proposed calendar write/delete before applying it, avoid using cancel --uid until the UID-prefix and calendar-scope checks are fixed, and keep data/profile.json private.

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:408
Finding

Calendar mutation commands do not enforce confirmation and UID deletion bypasses calendar protections

Content
View full analysis

Vulnerability Details

File Location: scripts/cal.py:408-416, 433-478; documented security requirement in SKILL.md:119-131
Vulnerability Type: Missing authorization gate and inconsistent deletion scope enforcement
Risk Level: Medium

Technical Analysis

SKILL.md requires the agent to display complete event details and obtain user confirmation before writing or deleting calendar events. However, the executable entry point does not require a confirmation token, interactive approval, or other verifiable proof of consent.

For event creation, --dry-run is optional. If it is omitted, the command immediately authenticates to iCloud and performs CalDAV PUT requests:

python
if args.get("dry_run"):
    print("dry-run:将写入 %d 条到「%s」:" % (len(todo), cal_name))
    for uid, _lines, e in todo:
        print("  %s  %s-%s  %s%s" % (e["date"], e["start"], e["end"], e["title"],
              " @%s" % e["location"] if e.get("location") else ""))
    return

cli, home, calendars = connect(prof)
target = get_calendar(prof, cli, home, calendars, cal_name)
ok = 0
for uid, lines, e in todo:
    url = target.rstrip("/") + "/" + urllib.parse.quote(uid, safe="") + ".ics"
    s, body = cli.dav("PUT", url, calendar_body(lines), ctype="text/calendar; charset=utf-8")
    if s not in (200, 201, 204):
        die("写入失败 %s(%d)" % (uid, s), body.decode("utf-8", "replace")[:300])

The UID-based cancellation path is broader than the date-based cancellation path. It searches every calendar and deletes a matching resource without requiring the UID to use the calpal- prefix, without restricting the operation to the configured plans calendar, and without applying the commitments-calendar protection:

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:
...[truncated 3361 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require a machine-verifiable confirmation artifact for every non-dry-run mutation. For example, generate a short-lived confirmation token from the normalized operation details during preview and require that token for PUT or DELETE.
  2. Make preview the default behavior. Require an explicit mutation option such as --apply --confirmation-token <token> before contacting CalDAV with a state-changing method.
  3. Restrict cancel --uid to UIDs beginning with calpal- and to the configured plans calendar by default.
  4. If deletion of foreign or commitments-calendar events is genuinely required, place it behind a separate explicit mode with a fresh confirmation tied to the exact calendar, UID, title, date, and time.
  5. Apply the same calendar and ownership checks to UID-based and date-based deletion paths through a shared authorization function.
  6. Validate the response status from every DELETE request and report failure rather than printing a successful deletion unconditionally.
  7. Add regression tests proving that:
    • mutation fails without a valid confirmation token;
    • foreign UIDs cannot be deleted through the normal cancellation command;
    • the commitments calendar remains protected in both date- and UID-based paths;
    • expired or operation-mismatched confirmation tokens are rejected.
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 (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill description claims direct Apple Calendar/iCloud/CalDAV querying and modification as a core function, while the analyzed behavior indicates those capabilities are absent or unverified and that external third-party lookups are performed instead. This mismatch is dangerous because it can mislead users and orchestrators about what the skill really does, what data leaves the environment, and whether sensitive calendar actions are actually supported or safely implemented.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module-level safety claim says cancellation only affects this skill’s own calpal- events, but the --uid path does not enforce that restriction. It scans all calendars and deletes any event whose ICS content contains a matching UID, so a caller who can supply an arbitrary UID can delete non-calpal events, violating the documented safety boundary and enabling unintended calendar data loss.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README explicitly recommends broad natural-language triggers such as everyday scheduling and outing phrases, which can easily overlap with ordinary conversation. In an agent environment, this increases the chance of unintended invocation, causing the skill to access calendar data or create/modify events when the user did not clearly intend to use this skill.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill instructs the agent to use network-capable scripts for geocoding, weather, sunset, routing, and CalDAV-style calendar operations, but it declares no explicit tool scope or permissions. That creates an authorization and transparency gap: a user or platform may not realize the skill can make outbound requests or touch external calendar services, increasing the risk of unintended data exposure or overbroad tool use.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation guidance is very broad, triggering on common conversational phrases about plans, availability, or what someone wants to do. In context, that can cause the skill to engage in calendar reads, planning queries, or network lookups when the user did not clearly intend to invoke a calendar-integrated tool, increasing the chance of unnecessary access to personal schedule data or external services.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill hard-codes Chinese language/locale parameters in multiple API requests, including language=zh, accept-language=zh, and later a fixed Shanghai timezone. This enforces a specific locale behavior rather than offering the user a choice, which matches the policy category for language/locale violations.

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

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The docstring instructs the agent to explicitly tell the user specific Chinese-language wording, which imposes a language/locale behavior. Because no opt-in or alternative language handling is offered, this is a natural-language policy violation under the language-choice rule.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code sends precise origin/destination coordinates and the Amap API key to a third-party service, which creates a real privacy and data-sharing risk because itinerary and location data may reveal sensitive movements or habits. In this skill’s context, users are asking about availability, travel timing, and calendar-related plans, so the coordinates can be closely tied to personal routines; however, the transmission is over HTTPS to the intended mapping provider rather than to an obviously malicious endpoint, which lowers severity from high to medium.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The natural-language instructions and usage examples assume Chinese as the interaction language, but the README does not mention whether other languages are supported or whether Chinese-only behavior is intentional. This can be a language-policy issue when a skill effectively forces one language without user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instruction text is written only in Chinese, telling the user to place their commitment spec JSON here. This imposes a specific language/locale without offering a choice or documenting a region-specific reason, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The configuration sets the timezone to "Asia/Shanghai", which imposes a specific locale assumption in natural-language-visible configuration. There is no indication here that the user can choose a different locale or that the constraint is justified as region-specific.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The manifest describes querying, writing, and canceling iCloud calendar events, which suggests operating within existing calendars. The implementation goes further: when a target non-commitments calendar does not exist, get_calendar silently creates a new remote calendar via MKCALENDAR. That is a broader remote modification than the user-facing description explicitly conveys.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.