Back to skill

Security audit

cc-sticky-notify

Security checks for vulnerabilities and agentic risk

Overview

This Mac notification skill is mostly coherent, but it adds persistent Claude hooks and under-disclosed window/app inspection with unsafe temporary state, so users should review it before installing.

Install only if you are comfortable granting a skill persistent Claude Code hook execution and local macOS GUI context access. Review the exact settings.json hook entries, consider removing or disabling focus-restoration/window-inspection behavior, and avoid installing on shared machines until the /tmp state handling is hardened.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/notify.sh:40
Finding

Undocumented Process and GUI Window Reconnaissance

Content
View full analysis
/dev/null | tr -d ' ') if [ -z "$_pid" ] || [ "$_pid" = "0" ] || [ "$_pid" = "1" ]; then break; fi _ancestors="${_ancestors:+$_ancestors,}$_pid" done # Detect parent GUI app and front window via System Events (synchronous single call) SOURCE_APP="" if [ -n "$_ancestors" ]; then _wf="${FOCUS_FILE%.focus}.window" _posf="${FOCUS_FILE%.focus}.pos" _result=$(osascript -e "tell application \"System Events\" set pids to {$_ancestors} repeat with p in pids try set proc to first application process whose unix id is p if (background only of proc) is false and (bundle identifier of proc) is not missing value then set _n to name of proc set _w to \"\" set _px to \"\" set _py to \"\" try set _w to name of front window of proc set _pos to position of front window of proc set _px to (item 1 of _pos) as text set _py to (item 2 of _pos) as text end try return _n & \"|\" & _w & \"|\" & _px & \",\" & _py end if end try end repeat end tell" 2>/dev/null) if [ -n "$_result" ]; then SOURCE_APP=$(printf '%s' "${_result%%|*}" | tr -d '\r\n') _rest="${_result#*|}" _win=$(printf '%s' "${_rest%%|*}" | tr -d '\r\n') _pos=$(printf '%s' "${_rest#*|}" | tr -d '\r\n') [ -n "$SOURCE_APP" ] && printf '%s\n' "$SOURCE_APP" > "$FOCUS_FILE" [ -n "$_win" ] && [ ! -f "$_wf" ] && printf '%s\n' "$_win" > "$_wf" [[ "$_pos" == *,* ]] && [ ! -f ...[truncated 4565 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/notify.sh:32
Finding

Predictable Shared Temporary Files Allow Symlink and State-Manipulation Attacks

Content
View full analysis
"$FOCUS_FILE" [ -n "$_win" ] && [ ! -f "$_wf" ] && printf '%s\n' "$_win" > "$_wf" [[ "$_pos" == *,* ]] && [ ! -f "$_posf" ] && printf '%s\n' "$_pos" > "$_posf" _widFile="$TMP_DIR/${SESSION_KEY}.wid" if [ ! -f "$_widFile" ]; then _wid=$(osascript -l JavaScript -e " ObjC.import('CoreGraphics');ObjC.import('Foundation'); var target='$SOURCE_APP'.toLowerCase(); var cfArr=\$.CGWindowListCopyWindowInfo(1,0); var nsArr=ObjC.castRefToObject(cfArr);var r=''; for(var i=0;i/dev/null) [ -n "$_wid" ] && printf '%s\n' "$_wid" > "$_widFile" fi ``` ```bash LAST_SIG_FILE="$TMP_DIR/${SESSION_KEY}.sig" if [ -f "$LAST_SIG_FILE" ]; then PREV_SIG=$(cat "$LAST_SIG_FILE" 2>/dev/null) if [ "$PREV_SIG" = "$SIG" ]; then exit 0 fi fi printf '%s' "$SIG" > "$LAST_SIG_FILE" printf '%s\n' "${LINES[@]}" > "$CONTENT_FILE" ``` The Swift application uses the same shared directory and trusts its entries: ```swift let tmpDir = URL(fileURLWith ...[truncated 3996 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (26)

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
95% confidence
Finding

This skill inspects and later influences the agent's persistent configuration under ~/.claude/settings.json, which is a sensitive control-plane file. Modifying or even programmatically steering changes to hook configuration can create lasting command execution behavior across future sessions, so compromise or mistakes here have amplified impact.

Content

Scanner excerpt · SKILL.md (reported line 69)May include surrounding context.

md
1. **Check Xcode CLT** — exits early with instructions if `xcode-select -p` fails.
2. **`chmod +x notify.sh`** — ensures the script is executable (git clone may strip the +x bit).
3. **Build `.app bundle`** — compiles `sticky-window.swift`, writes `Info.plist` + entitlements, signs with `codesign`. Skipped if the bundle already exists.
4. **Check hook configuration** — inspects `~/.claude/settings.json` for existing `cc-sticky-notify` entries and prints the required hook commands if none are found.
5. **Smoke test** — fires a test notification via `notify.sh`.

### Step 2 — Configure settings.json hooks

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 70)May include surrounding context.

md
5. **Smoke test** — fires a test notification via `notify.sh`.

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
98% confidence
Finding

The skill explicitly instructs appending command hooks into ~/.claude/settings.json, creating persistent automatic execution on Stop, Notification, and PostToolUse events. Persistent hooks are security-sensitive because any later alteration of the referenced script or path can turn routine agent events into a durable execution mechanism.

Content

Scanner excerpt · SKILL.md (reported line 74)May include surrounding context.

Step 2 — Configure settings.json hooks

Read ~/.claude/settings.json and append one sticky-notify entry to each of the following four locations in the hooks field (skip if already present).

Stop — append to Stop[0].hooks:

json

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · SKILL.md (reported line 147)May include surrounding context.

md
## Troubleshooting

**No system notification either**
- Verify the hooks configuration is correctly written in `~/.claude/settings.json`
- Path should be `$HOME/.claude/skills/cc-sticky-notify/scripts/notify.sh`

**`xcrun: error: invalid active developer path` during compilation**

Agent Config Directory Access

High
Category
Agent Snooping
Confidence
90% confidence
Finding

Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Content

Scanner excerpt · install.sh (reported line 7)May include surrounding context.

sh
SKILL_DIR="$(cd "$(dirname "$0")" && pwd)"
SCRIPTS_DIR="$SKILL_DIR/scripts"
SETTINGS="$HOME/.claude/settings.json"

echo "==> cc-sticky-notify installation started"
echo "    Skill dir: $SKILL_DIR"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill clearly instructs the agent to run shell commands, compile Swift code, sign an app bundle, and modify Claude hook configuration, but it declares no tool scope or permission boundaries. That mismatch can cause the skill to be invoked without adequate user visibility into the commands and files it will touch, increasing the risk of overbroad execution.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The description uses broad trigger language like 'etc.' and 'used for updating scripts or troubleshooting notification failures,' which can make the skill eligible in situations beyond a narrowly defined notification install/fix workflow. Overly broad invocation scope increases the chance that an agent selects a skill that edits configuration or runs shell commands when a simpler, safer action would suffice.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 22)May include surrounding context.

├── sticky-window.swift ← Swift source (compiled by install.sh on first install) └── sticky-notify.app/ ← .app bundle (built automatically on first use) └── Contents/ ├── Info.plist └── MacOS/ └── sticky-notify-app

text

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 68)May include surrounding context.

├── sticky-window.swift ← Swift source (compiled by install.sh on first install) └── sticky-notify.app/ ← .app bundle (built automatically on first use) └── Contents/ ├── Info.plist └── MacOS/ └── sticky-notify-app

text

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 35)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 37)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 38)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 55)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 61)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 62)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 69)May include surrounding context.

sh
├── sticky-window.swift      ← Swift source (compiled by install.sh on first install)
    └── sticky-notify.app/       ← .app bundle (built automatically on first use)
        └── Contents/
            ├── Info.plist
            └── MacOS/
                └── sticky-notify-app
```

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
86% confidence
Finding

The troubleshooting instructions advise a sudo command, which introduces privilege escalation into the workflow. Even though the command shown is a legitimate system repair step, embedding privileged operations in a skill raises the risk that an agent or user normalizes running elevated commands during installation or debugging, increasing the blast radius of mistakes or future tampering.

Content

Scanner excerpt · SKILL.md (reported line 152)May include surrounding context.

md
**`xcrun: error: invalid active developer path` during compilation**
- Xcode Command Line Tools path is broken (common after macOS upgrade or Xcode reinstall)
- Fix: `sudo xcode-select --reset`
- If that doesn't work, reinstall: `xcode-select --install`
- The floating sticky window will be disabled if swiftc can't compile, but system notifications still work

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 35)May include surrounding context.

sh
mkdir -p "$APP_BUNDLE/Contents/MacOS"
    swiftc "$SCRIPTS_DIR/sticky-window.swift" -o "$APP_BINARY"

    cat > "$APP_BUNDLE/Contents/Info.plist" << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 37)May include surrounding context.

sh
mkdir -p "$APP_BUNDLE/Contents/MacOS"
    swiftc "$SCRIPTS_DIR/sticky-window.swift" -o "$APP_BINARY"

    cat > "$APP_BUNDLE/Contents/Info.plist" << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 56)May include surrounding context.

sh
mkdir -p "$APP_BUNDLE/Contents/MacOS"
    swiftc "$SCRIPTS_DIR/sticky-window.swift" -o "$APP_BINARY"

    cat > "$APP_BUNDLE/Contents/Info.plist" << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · install.sh (reported line 61)May include surrounding context.

sh
mkdir -p "$APP_BUNDLE/Contents/MacOS"
    swiftc "$SCRIPTS_DIR/sticky-window.swift" -o "$APP_BINARY"

    cat > "$APP_BUNDLE/Contents/Info.plist" << 'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The installer creates entitlements granting Apple Events automation to the bundled app even though the described purpose is only local sticky-note and system notifications. Apple Events permission expands the app's ability to control other macOS applications, which is unnecessary here and increases post-install attack surface if the app or its source is modified.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill stores active application/window context per session in /tmp files (.focus, .window, .pos, .wid), but the manifest describes only a notification utility and does not disclose this tracking behavior. Undisclosed context capture increases privacy risk because users may not expect the skill to record which app/window they were using when Claude Code events occur.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script traverses ancestor PIDs, queries System Events for the foreground application and front window title/position, and then uses CoreGraphics to capture a CGWindowID. That is substantially broader GUI introspection than is required to show a notification and creates unnecessary collection of user context that may expose sensitive app names, document/window titles, and screen-position metadata.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/notify.sh (reported line 131)May include surrounding context.

sh
# No running instance — launch new floating sticky note
if [ -f "$BINARY" ]; then
    "$BINARY" "$CONTENT_FILE" </dev/null >/dev/null 2>&1 &
    disown $!
fi