Back to skill

Security audit

macOS Calendar Assistant

Security checks for vulnerabilities and agentic risk

Overview

This calendar skill is mostly purpose-aligned, but it asks for real Calendar access and installs a cron job with unsafe scripting patterns that could lead to unintended calendar changes or user-level command execution from crafted configuration values.

Review carefully before installing. Only run this on a Mac account where you are comfortable granting Calendar access, inspect config.json yourself, avoid untrusted configuration files, and do not run the cron installer or regression test until the cron validation, AppleScript escaping, and live-calendar test isolation issues are fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/install.sh:13
Finding
Cron command injection through insufficient check_time validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh`, lines 13–24 and 37–40 **Vulnerability Type**: Persistent command injection through generated crontab content **Risk Level**: High ### Vulnerable Code ```sh CHECK_TIME=$(/usr/bin/python3 - <<'PY' "$CONFIG_PATH" import json,sys cfg=json.load(open(sys.argv[1])) print(cfg.get('check_time','22:20')) PY ) HOUR="${CHECK_TIME%%:*}" MIN="${CHECK_TIME##*:}" if [[ -z "$HOUR" || -z "$MIN" ]]; then echo "Invalid check_time in config.json, expected HH:MM" exit 1 fi ( crontab -l 2>/dev/null || true ) | sed '/calendar_clean_notify.sh/d' > "$TMPCRON" echo "$MIN $HOUR * * * CAL_SKILL_CONFIG=$CONFIG_PATH $SCRIPT_DIR/calendar_clean_notify.sh" >> "$TMPCRON" crontab "$TMPCRON" ``` ### Technical Analysis The installer reads `check_time` from `config.json` and inserts the resulting values directly into a crontab file. Validation only verifies that the derived hour and minute strings are nonempty. It does not: - Require numeric values. - Enforce hour and minute ranges. - Reject newline or carriage-return characters. - Reject shell or cron metacharacters. - Safely encode `CONFIG_PATH` or `SCRIPT_DIR` for execution by cron's shell. A crafted configuration value can therefore inject additional crontab records or alter the shell command installed by the Skill. Since crontab entries survive the installer process, successful exploitation results in persistent execution. The scheduled duplicate-check feature is documented and functionally relevant, but unsafe generation of the crontab entry exceeds what is necessary to implement it securely. ### Attack Path 1. An attacker gains the ability to modify or supply the project's `config.json`, including through an untrusted project archive or configuration automation. 2. The attacker places newline-delimited cron content or shell syntax in `check_time`. 3. The user or Agent runs `scripts/install.sh`. 4. The installer writes the malicious value into the tempor ...[truncated 639 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate `check_time` with a strict expression such as `^(?:[01][0-9]|2[0-3]):[0-5][0-9]$`. - Reject all control characters, including newlines and carriage returns. - Convert the validated hour and minute to integers before generating the schedule. - Safely quote the configuration and script paths for the shell used by cron. - Prefer installing a fixed wrapper script with no configuration-derived shell syntax. - Consider a macOS LaunchAgent whose `ProgramArguments` are represented as an argument array rather than a shell command. - Show the exact scheduled entry and require explicit confirmation before installation. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/calendar_clean_notify.sh:43
Finding
AppleScript injection through the configured notification title<![CDATA[ ## Vulnerability Details **File Location**: `scripts/calendar_clean_notify.sh`, lines 43–48 and 94–101 **Vulnerability Type**: AppleScript source injection **Risk Level**: High ### Vulnerable Code ```sh NOTIFY_TITLE=$(/usr/bin/python3 - <<'PY' "$CONFIG_PATH" import json,sys cfg=json.load(open(sys.argv[1])) notify=cfg.get('notification',{}) if isinstance(cfg.get('notification',{}),dict) else {} print(str(notify.get('title','MemoryXBOT 日程提醒'))) PY ) if [[ "$CANDIDATES" -gt 0 ]]; then MSG="Calendar发现 ${CANDIDATES} 条重复候选,建议清理" echo "$(date '+%Y-%m-%d %H:%M:%S') $MSG" > "$ALERT" if [[ "$NOTIFY_ENABLED" == "true" ]]; then /usr/bin/osascript -e "display notification \"$MSG\" with title \"$NOTIFY_TITLE\"" fi fi ``` ### Technical Analysis `notification.title` is controlled by `config.json` and is embedded directly into AppleScript source passed to `osascript -e`. The value is not escaped for AppleScript string-literal syntax. Quotes, backslashes, and line breaks can consequently terminate the intended string and introduce additional AppleScript statements. AppleScript can invoke local commands through functionality such as `do shell script`. This turns a notification customization field into a potential arbitrary-code execution channel. Because the vulnerable script is installed as a daily cron job, malicious configuration can remain dormant until duplicate candidates are found and the notification branch is reached. ### Attack Path 1. An attacker modifies `config.json` and places AppleScript syntax in `notification.title`. 2. The user installs or has already installed the daily duplicate-check cron job. 3. The scheduled script runs and detects one or more duplicate candidates. 4. The script constructs an `osascript -e` program containing the malicious title. 5. The title terminates the intended string literal and inserts attacker-controlled AppleScript. 6. The injected statements execute under the user's account. ### Impact Assessment Su ...[truncated 382 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Keep the AppleScript program static and pass the title and message through `osascript` positional arguments. - Access those values through an `on run argv` handler rather than interpolating them into source code. - If source interpolation cannot be removed, correctly escape backslashes, quotes, and control characters according to AppleScript string rules. - Reject line breaks and other control characters in notification titles. - Add regression tests containing quotes, backslashes, Unicode characters, and newline attempts. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/set_alarm.py:10
Finding
AppleScript injection through incompletely escaped event UID<![CDATA[ ## Vulnerability Details **File Location**: `scripts/set_alarm.py`, lines 10–15 and 47–49 **Vulnerability Type**: AppleScript source injection **Risk Level**: High ### Vulnerable Code ```python ap = argparse.ArgumentParser() ap.add_argument("--uid", required=True) ap.add_argument("--alarm-minutes", type=int, required=True) args = ap.parse_args() uid = args.uid.replace('"', '\\"') minutes_before = int(args.alarm_minutes) APPLESCRIPT = f''' set eventUid to "{uid}" set minutesBefore to {minutes_before} tell application "Calendar" set foundEvent to missing value set foundCal to missing value repeat with c in calendars try set matches to (every event of c whose uid is eventUid) if (count of matches) > 0 then set foundEvent to item 1 of matches set foundCal to c exit repeat end if end try end repeat if foundEvent is missing value then error "Event not found: " & eventUid try repeat with a in (every display alarm of foundEvent) delete a end repeat end try make new display alarm at end of display alarms of foundEvent with properties {trigger interval:(-minutesBefore * minutes)} return "OK alarm set: " & minutesBefore & " min before (calendar=" & (name of foundCal) & ")" end tell ''' p = subprocess.run(["osascript"], input=APPLESCRIPT, capture_output=True, text=True) ``` ### Technical Analysis The UID is embedded into executable AppleScript source. The code escapes double quotes but does not first escape existing backslashes. A crafted sequence containing backslashes and quotes can interfere with the intended escaping and terminate the AppleScript string literal. The command-line interface accepts arbitrary UID text. In an Agent workflow, an attacker may be able to persuade the Agent to pass untrusted text as a UID, making this flaw reachable from chat or other untrusted scheduling input. The alarm value is parsed as an integer and is not affected by the same issue. ...[truncated 766 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use a static AppleScript and pass the UID through `osascript` arguments. - Retrieve the UID from `argv` in an `on run argv` handler. - If interpolation must remain, escape backslashes before escaping quotes and reject control characters. - Apply a conservative UID format and length validation where compatible with Calendar identifiers. - Do not pass values extracted from untrusted chat content to this utility without validation. - Add tests for backslash-quote combinations and multiline values. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/regression_test.py:31
Finding
Regression test can modify and delete real user calendar data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/regression_test.py`, lines 31–136 **Vulnerability Type**: Destructive integration test without isolation **Risk Level**: Medium ### Vulnerable Code ```python now = dt.datetime.now(TZ).replace(minute=0, second=0, microsecond=0) + dt.timedelta(days=3) start = now end = now + dt.timedelta(minutes=30) title = f"[回归测试]{uuid.uuid4().hex[:8]}" cal = "产品" # 1) create p1 = run([ "python3", str(SCRIPT_DIR / "upsert_event.py"), "--title", title, "--start", iso(start), "--end", iso(end), "--calendar", cal, "--notes", "regression-create", "--alarm-minutes", "5", ]) # 4) create a deliberate duplicate via legacy add_event run([ "python3", str(SCRIPT_DIR / "add_event.py"), "--title", title, "--start", iso(start), "--end", iso(end + dt.timedelta(minutes=15)), "--calendar", cal, "--notes", "regression-duplicate", ]) range_start = iso(start - dt.timedelta(hours=1)) range_end = iso(end + dt.timedelta(hours=2)) # 6) apply with confirm run([ "python3", str(SCRIPT_DIR / "calendar_clean.py"), "--start", range_start, "--end", range_end, "--apply", "--confirm", "yes", ]) # If no candidates, remove exact by title/start as a fallback. if parse_candidates(cleanup.stdout) == 0: swift_code = f'''import EventKit import Foundation let store = EKEventStore() let fmt = ISO8601DateFormatter() let sem = DispatchSemaphore(value: 0) store.requestAccess(to: .event) {{ granted, _ in guard granted else {{ sem.signal(); return }} guard let start = fmt.date(from: "{iso(start)}") else {{ sem.signal(); return }} guard let cal = store.calendars(for: .event).first(where: {{$0.title == "{cal}"}}) else {{ sem.signal(); return }} let pred = store.predicateForEvents(withStart: start, end: start.addingTimeInterval(60), calendars: [cal]) let events = store.events(matching: pred).filter {{$0.title == "{title}"}} for e in events {{ try? store.remove(e, span: .thisEv ...[truncated 1909 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require an explicitly configured disposable test calendar and abort if it does not exist. - Never fall back to an arbitrary writable calendar during tests. - Record the exact UIDs of events created by the regression test. - Delete only events whose UIDs were created during the current test run. - Do not invoke the general duplicate cleaner with deletion enabled as part of a regression test. - Put destructive integration tests behind an explicit flag such as `--allow-live-calendar-test`. - Add a prominent warning to the README and SKILL.md that the integration test modifies Calendar data. - Prefer mocks or an abstraction around EventKit for non-destructive regression coverage. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/uninstall.sh:7
Finding
Installer and uninstaller remove unrelated cron entries by broad substring matching<![CDATA[ ## Vulnerability Details **File Location**: `scripts/install.sh`, line 37; `scripts/uninstall.sh`, lines 7–8 **Vulnerability Type**: Overbroad scheduled-task removal **Risk Level**: Low ### Vulnerable Code From `scripts/install.sh`: ```sh ( crontab -l 2>/dev/null || true ) | sed '/calendar_clean_notify.sh/d' > "$TMPCRON" ``` From `scripts/uninstall.sh`: ```sh ( crontab -l 2>/dev/null || true ) | sed '/calendar_clean_notify.sh/d' > "$TMPCRON" crontab "$TMPCRON" ``` ### Technical Analysis Both scripts delete every crontab line containing the substring `calendar_clean_notify.sh`. The removal is not tied to: - The exact project installation path. - A unique job identifier. - A managed begin/end marker. - The exact cron expression originally installed by this project. As a result, an unrelated cron task with a script of the same name, a different copy of this Skill, or even the substring in an argument or comment will be removed. The project’s scheduled task is disclosed and has a legitimate daily-notification purpose. The security concern is the overly broad modification of the user's existing crontab rather than the mere presence of persistence. ### Attack Path 1. The user already has another cron entry containing `calendar_clean_notify.sh`, potentially from a separate installation or unrelated project. 2. The user runs this project's installer or uninstaller. 3. `sed` removes every matching line from the current crontab. 4. The modified crontab replaces the original one. 5. The unrelated scheduled task is silently lost. ### Impact Assessment The vulnerability can disable legitimate scheduled jobs under the current user account. It does not directly grant additional privileges or execute attacker-controlled code, but it can disrupt monitoring, backups, notifications, or other user tasks whose lines happen to match the substring. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Surround the managed cron record with unique project-specific begin and end markers. - Remove only the exact marked block created by this installation. - Include a stable installation identifier or canonical absolute project path. - Before replacing the crontab, compare the proposed changes and warn if multiple records match. - Preserve a backup of the previous crontab so accidental removals can be restored. - Consider using a uniquely labeled macOS LaunchAgent instead of editing the user's aggregate crontab. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (34)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill claims richer parsing, rescheduling, conflict handling, review sync, and duplicate cleanup than the provided command set substantiates, and the idempotency model appears limited. In a calendar-management context, such gaps are dangerous because agents may rely on nonexistent safeguards and unintentionally create duplicate, conflicting, or incorrectly modified events.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The README advertises very broad natural-language examples such as 'add this event', 'extend to 14:00', and 'move to Friday night' for use inside IM conversations. Without a clearly scoped invocation prefix, confirmation gate, or target-event disambiguation, ordinary chat messages or forwarded content could unintentionally trigger calendar writes or edits, especially in a system designed to parse screenshots and conversational context.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The Chinese section repeats similarly broad trigger phrases like '加个日程', '延长到14:00', and '改到周五晚上', again implying that casual IM language can directly cause calendar mutations. Because the skill is intended for high-frequency chat workflows and screenshot-driven extraction, this increases the chance of accidental invocation, social-engineering-induced edits, or unintended scheduling changes from ambiguous messages.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares executable shell, file read, and file write behaviors but does not define any tool scope such as allowed-tools or permissions. This creates an authorization ambiguity where an agent may invoke broader capabilities than users or reviewers expect, especially given the presence of scripts that can modify calendar data and system cron state.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The activation text is broad and likely to match many ordinary calendar-related requests, which can cause the skill to be selected in situations beyond its safest or narrowest intended use. In this skill's context, that matters because the available commands include write, delete, and persistent scheduler actions, increasing the chance of overbroad invocation and unintended system changes.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This script directly creates calendar events via osascript with no built-in confirmation, dry-run mode, or user-visible warning before modifying local data. In an IM-first assistant context, ambiguous or adversarial chat content could be translated into unintended calendar changes, making silent state-changing behavior materially riskier.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def list_events(start_iso: str, end_iso: str):
    list_events_swift = str(Path(__file__).resolve().parent / "list_events.swift")
    cmd = ["swift", list_events_swift, start_iso, end_iso]
    r = subprocess.run(cmd, capture_output=True, text=True, check=True)
    return json.loads(r.stdout).get("events", [])
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
}}
sem.wait()
'''
    p = subprocess.run(["swift", "-"], input=code, capture_output=True, text=True)
    if p.returncode != 0:
        raise SystemExit(p.stderr or p.stdout)
    return p.stdout.strip()
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
`NOTIFY_TITLE` and later `MSG` are fixed in Chinese, which imposes a specific language on notifications regardless of user preference. The file does not provide any opt-in, locale selection, or justification for restricting notifications to Chinese.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The message `Calendar发现 ${CANDIDATES} 条重复候选,建议清理` is always shown in Chinese when notifications are enabled. This is a natural-language locale policy issue because the script forces one language and does not let the user choose another.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def _system_timezone() -> str | None:
    """Detect macOS system timezone via systemsetup."""
    try:
        out = subprocess.check_output(
            ["/usr/sbin/systemsetup", "-gettimezone"],
            text=True, timeout=5, stderr=subprocess.DEVNULL,
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The code defaults to "Asia/Shanghai" in both get_timezone_name and get_zoneinfo when no user configuration is present. This imposes a specific locale/timezone choice by default, which is a natural-language policy concern because users are not offered an explicit opt-in or alternative.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run(cmd):
    return subprocess.run(cmd, capture_output=True, text=True)


def main():
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def main():
    script = Path(__file__).resolve().parent / "list_calendars.swift"
    p = subprocess.run(["swift", str(script)], capture_output=True, text=True)
    if p.returncode != 0:
        print(p.stderr or p.stdout, file=sys.stderr)
        sys.exit(1)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This script exports all matching calendar events, including sensitive fields such as titles, locations, and especially notes, directly to stdout once calendar permission is granted. In the context of an IM-driven assistant that may relay results to chat systems or other downstream tools, this can expose private calendar contents without data minimization, per-field consent, or a user-facing disclosure.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run(cmd, check=True):
    p = subprocess.run(cmd, capture_output=True, text=True)
    if check and p.returncode != 0:
        raise RuntimeError(f"cmd failed: {' '.join(cmd)}\n{p.stdout}\n{p.stderr}")
    return p
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# If no candidates, remove exact by title/start as a fallback.
    if parse_candidates(cleanup.stdout) == 0:
        swift_code = f'''import EventKit\nimport Foundation\nlet store = EKEventStore()\nlet fmt = ISO8601DateFormatter()\nlet sem = DispatchSemaphore(value: 0)\nstore.requestAccess(to: .event) {{ granted, _ in\n  guard granted else {{ sem.signal(); return }}\n  guard let start = fmt.date(from: "{iso(start)}") else {{ sem.signal(); return }}\n  guard let cal = store.calendars(for: .event).first(where: {{$0.title == "{cal}"}}) else {{ sem.signal(); return }}\n  let pred = store.predicateForEvents(withStart: start, end: start.addingTimeInterval(60), calendars: [cal])\n  let events = store.events(matching: pred).filter {{$0.title == "{title}"}}\n  for e in events {{ try? store.remove(e, span: .thisEvent) }}\n  sem.signal()\n}}\nsem.wait()\n'''
        subprocess.run(["swift", "-"], input=swift_code, capture_output=True, text=True)

    print(json.dumps({
        "ok": True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
end tell
'''

    p = subprocess.run(["osascript"], input=APPLESCRIPT, capture_output=True, text=True)
    if p.returncode != 0:
        raise SystemExit(p.stderr.strip() or p.stdout.strip() or f"osascript failed: {p.returncode}")
    print(p.stdout.strip())
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.