T09 · Insecure Skill Coding Practices
- 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: MediumVulnerable 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
--uidis 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
DELETEbefore reporting success.
The use of `startswith("UID:" ...[truncated 1786 chars]
- require the UID to use the Skill-owned
- Remediation
View remediation
Remediation Suggestions
- 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") - Parse the ICS resource and compare the complete UID for equality rather than using
startswith. - Restrict UID-based deletion to
prof["calendars"]["plans"]by default. Do not scan every accessible calendar. - 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.
- 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.
- Check the CalDAV
DELETEstatus and report success only for accepted success codes such as 200, 202, or 204. - Consider querying the exact plans-calendar resource through CalDAV filters instead of enumerating and downloading every event from every calendar.
- Require exact ownership validation before deletion:
