Back to skill

Security audit

Agent Notify

Security checks for vulnerabilities and agentic risk

Overview

This is a real notification-hook installer, but it persistently changes agent settings and includes script argument-handling weaknesses that should be reviewed before use.

Review the generated settings.json hook changes before installing, especially if you use multiple coding agents. Install only if you are comfortable with persistent local command hooks for notifications, and prefer a version that fixes the Python argument interpolation and includes the missing Windows script or removes the Windows support claim.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/notify-linux.sh:11
Finding
Python Code Injection Through Unvalidated Script Arguments in Linux Notification Script<![CDATA[ ## Vulnerability Details **File Location**: `scripts/notify-linux.sh`, lines 11-18 **Vulnerability Type**: Python code injection caused by unsafe string interpolation **Risk Level**: Medium ### Vulnerable Code ```bash # Load config if provided if [ -n "$CONFIG_PATH" ] && [ -f "$CONFIG_PATH" ]; then if command -v jq &>/dev/null; then CUSTOM_SOUND=$(jq -r ".sounds.${TYPE} // empty" "$CONFIG_PATH" 2>/dev/null) elif command -v python3 &>/dev/null; then CUSTOM_SOUND=$(python3 -c "import json; c=json.load(open('$CONFIG_PATH')); print(c.get('sounds',{}).get('$TYPE',''))" 2>/dev/null) fi fi ``` ### Technical Analysis The script accepts `TYPE` and `CONFIG_PATH` as command-line arguments and directly interpolates both values into Python source passed to `python3 -c`. Shell quoting does not make this safe because the interpolated data is inserted inside Python string literals. An attacker can include quotes, parentheses, semicolons, and Python expressions in either argument to terminate the intended expression and append arbitrary Python statements. The vulnerable branch is reached when: 1. The supplied configuration path references an existing file. 2. `jq` is unavailable. 3. `python3` is available. The configured hooks normally pass fixed notification types, but the script is also documented for standalone invocation and does not enforce an allowlist for `TYPE`. ### Attack Path 1. The attacker causes the script to be invoked with an existing JSON configuration file. 2. The environment does not have `jq`, causing the Python fallback to run. 3. The attacker supplies a crafted `TYPE`, for example: ```bash bash scripts/notify-linux.sh \ "x','')); __import__('os').system('touch /tmp/agent-notify-pwned'); #" \ ./config/default.json ``` 4. The generated Python program is equivalent to an expression containing: ```python __import__('os').system('touch /tmp/agent-notify-pwned') ``` 5. Python executes the injected operating- ...[truncated 836 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Keep Python source constant and pass all dynamic values as positional arguments: ```bash CUSTOM_SOUND=$( python3 -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as handle: config = json.load(handle) print(config.get("sounds", {}).get(sys.argv[2], "")) ' "$CONFIG_PATH" "$TYPE" 2>/dev/null ) ``` Additionally: 1. Restrict `TYPE` to the supported values before processing it: ```bash case "$TYPE" in confirm|done|error|default) ;; *) echo "Unsupported notification type" >&2; exit 2 ;; esac ``` 2. Resolve and validate the configuration path if callers are not intended to select arbitrary files. 3. Fail safely when JSON parsing fails rather than continuing with partially initialized data. 4. Add regression tests containing quotes, semicolons, command substitutions, newlines, and Python syntax in both arguments. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/notify-macos.sh:13
Finding
Python Code Injection Through Unvalidated Script Arguments in macOS Notification Script<![CDATA[ ## Vulnerability Details **File Location**: `scripts/notify-macos.sh`, lines 13-23 **Vulnerability Type**: Python code injection caused by unsafe string interpolation **Risk Level**: Medium ### Vulnerable Code ```bash # Load config if provided if [ -n "$CONFIG_PATH" ] && [ -f "$CONFIG_PATH" ]; then if command -v jq &>/dev/null; then FLASH_COUNT=$(jq -r '.taskbar.flashCount // 5' "$CONFIG_PATH" 2>/dev/null) CUSTOM_SOUND=$(jq -r ".sounds.${TYPE} // empty" "$CONFIG_PATH" 2>/dev/null) elif command -v python3 &>/dev/null; then FLASH_COUNT=$(python3 -c "import json; c=json.load(open('$CONFIG_PATH')); print(c.get('taskbar',{}).get('flashCount',5))" 2>/dev/null || echo 5) CUSTOM_SOUND=$(python3 -c "import json; c=json.load(open('$CONFIG_PATH')); print(c.get('sounds',{}).get('$TYPE',''))" 2>/dev/null) fi fi ``` ### Technical Analysis Both `CONFIG_PATH` and `TYPE` are embedded directly into Python programs constructed with `python3 -c`. They are placed inside single-quoted Python string literals without Python-level escaping. An attacker-controlled value can terminate the string literal and append arbitrary Python statements. The shell's double quotes only govern shell parsing; they do not prevent the resulting value from changing the syntax of the dynamically generated Python program. The vulnerable fallback is used when `jq` is absent and `python3` is present. On macOS, Python may be installed through development tools or another package even where `jq` is unavailable. ### Attack Path 1. The attacker obtains control over arguments passed to `notify-macos.sh`, such as through a modified agent hook or direct standalone invocation. 2. The attacker supplies an existing JSON file as the configuration path. 3. The attacker supplies a crafted notification type: ```bash bash scripts/notify-macos.sh \ "x','')); __import__('os').system('touch /tmp/agent-notify-pwned'); #" \ ./config/default.json ``` 4. If `jq` i ...[truncated 926 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Do not construct Python source with shell-provided values. Pass the configuration path and notification type through `sys.argv`: ```bash FLASH_COUNT=$( python3 -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as handle: config = json.load(handle) print(config.get("taskbar", {}).get("flashCount", 5)) ' "$CONFIG_PATH" 2>/dev/null ) CUSTOM_SOUND=$( python3 -c ' import json import sys with open(sys.argv[1], encoding="utf-8") as handle: config = json.load(handle) print(config.get("sounds", {}).get(sys.argv[2], "")) ' "$CONFIG_PATH" "$TYPE" 2>/dev/null ) ``` Further hardening should include: 1. Allowlisting `confirm`, `done`, `error`, and `default` as the only valid notification types. 2. Validating that `FLASH_COUNT` is a bounded non-negative integer. 3. Restricting the configuration path to the expected agent configuration directory where appropriate. 4. Returning a clear error when configuration parsing fails. 5. Adding automated tests for malicious quotes, Python statements, newlines, and unusual file names. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (10)

Vague Triggers

High
Confidence
98% confidence
Finding
The description explicitly says to invoke the skill even for vague phrases like 'I want a sound' or 'how do I get notified'. Because the skill then proceeds into installation logic and command execution, overly broad activation materially raises the chance of accidental triggering and unintended host changes.

Ae1

High
Category
analysis-evasion
Content
found=$(find "$base" -name "skill.md" -path "*/agent-notify/*" 2>/dev/null | head -1)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
found=$(find "$base" -name "skill.md" -path "*/agent-notify/*" 2>/dev/null | head -1)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger list includes very generic terms such as 'notify', 'bell', and 'beep', which can cause the skill to activate during ordinary conversation rather than only on deliberate user intent. In an agent environment, unintended activation can lead to unsolicited configuration changes, hook installation, or execution of follow-on setup steps, increasing the attack surface and creating confusion or unsafe side effects.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
|-------|----------|
| No sound (Windows) | Check system volume & sound scheme |
| No sound (macOS) | Check volume & notification permissions |
| No sound (Linux) | `sudo apt install pulseaudio-utils libnotify-bin` |
| Hooks not working | Restart your agent after configuration |

---
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Vague Triggers

Medium
Confidence
94% confidence
Finding
Generic triggers such as 'bell', 'beep', 'notify', and similar everyday phrases are likely to collide with normal conversation. In the context of a skill that modifies settings and writes files, trigger collisions increase the chance that sensitive actions begin without clear user intent.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The skill directs the agent to run host shell and PowerShell commands to detect the OS and locate agent configuration directories. Even though the purpose is installation of notification hooks, this expands the skill from advisory behavior into direct system modification and command execution, which increases risk if triggered unexpectedly or adapted maliciously.

Skill Enumeration

Medium
Category
Agent Snooping
Content
Find the skill directory by searching for this skill.md file:
```bash
for base in ~/.claude/skills ~/.codex/skills ~/.openclaw/skills ~/.kiro/skills ~/.cursor/skills; do
  found=$(find "$base" -name "skill.md" -path "*/agent-notify/*" 2>/dev/null | head -1)
  [ -n "$found" ] && echo "$(dirname "$found")" && break
done
Confidence
85% confidence
Finding
The skill enumerates potential skills directories to locate its own installation path. While this is not inherently malicious, it reveals local environment structure and installed agent ecosystems, and it is unnecessary if the user can provide the path or the runtime can supply the current skill directory directly.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The script defines Chinese notification text alongside English messages in string literals, but provides no documented language choice or locale opt-in. This can violate a language/locale policy when a skill embeds a specific non-default language without allowing user selection or explaining the constraint.

Context-Inappropriate Capability

Low
Confidence
87% confidence
Finding
The skill performs filesystem discovery across multiple agent and skills directories using find/loop logic. This is broader than necessary for simple notification setup and can expose or enumerate installed tools and local paths, especially if the skill is invoked by vague triggers.

Static analysis

No suspicious patterns detected.