T09 · Insecure Skill Coding Practices
- Location
scripts/server.py:450- Finding
Unauthenticated LAN Exposure and Modification of Claude Monitor State
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is a disclosed Claude status monitor, but it installs persistent hooks and exposes unauthenticated Claude activity data and state controls on the local network.
Review this before installing on any machine used for private or client work. Only use it on a trusted LAN, avoid router port forwarding, consider adding authentication or binding the event receiver to localhost, and delete ~/.claude/kindle-monitor/events.jsonl if you uninstall and do not want hook history retained.
scripts/server.py:450Unauthenticated LAN Exposure and Modification of Claude Monitor State
scripts/server.py:240Excessive Plaintext Collection and Indefinite Retention of Complete Claude Hook Payloads
The README states that installation appends seven hooks to ~/.claude/settings.json, which modifies the agent's config to ensure ongoing interception of Claude events. In this skill's context that behavior is core functionality rather than obviously malicious, but it is still security-relevant because it changes trusted agent behavior and creates a persistent data-forwarding path from future sessions.
1. 拷 `server.py` / `notify.sh` 到 `~/.claude/kindle-monitor/`
2. 渲染并装 launchd plist 到 `~/Library/LaunchAgents/com.user.kindle-monitor.plist`
3. 启动 server(绑定 0.0.0.0:8787)
4. 在 `~/.claude/settings.json` 的 `hooks` 里 append 7 个 kindle 钩子(不动既有 hooks)
5. 检测 macOS 防火墙隐身模式,提示用户手动关
6. 报告 Mac 局域网 IP,告诉你 Kindle 上输入哪个 URL
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.
| 现象 | 修法 |
|---|---|
| Kindle 转圈连不上 | macOS stealth mode 把 LAN 包丢了 → `sudo socketfilterfw --setstealthmode off` |
| 浏览器都连不上 | server 没起 → `launchctl list \| grep kindle-monitor`;看 `~/.claude/kindle-monitor/launchd.err.log` |
| Mac 端能开 localhost 但 Kindle 连不上 | Mac IP 变了 → `ipconfig getifaddr en0` 拿新 IP;路由器后台给 Mac 设静态 DHCP 一劳永逸 |
| 一直卡在「请确认」但实际没在等 | 浏览器开 `/reset`,或等 5 分钟超时 |
| 短命令偶发误判为 waiting | 调大 `KINDLE_MONITOR_TOOL_STALL`(默认 20s) |
Editing ~/.claude/settings.json to append seven hooks changes core agent behavior and affects future sessions, which is sensitive because hook configuration can execute logic on multiple lifecycle events. In the context of an agent skill, modifying the main config directory is particularly dangerous because a mistake, abuse, or later compromise of the installed scripts could impact all subsequent agent runs.
`install.sh` 会做这些事:
- 把 `server.py` / `notify.sh` 拷到 `~/.claude/kindle-monitor/`
- 把 launchd plist 写到 `~/Library/LaunchAgents/` 并启动
- 在 `~/.claude/settings.json` 的 hooks 里 append 7 个 kindle 钩子(不动既有 hooks)
- 处理 macOS 防火墙隐身模式(如有需要会提示用户用 sudo 关闭)
- 报告 Mac 局域网 IP,告诉你 Kindle 上输入什么 URL
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.
| 现象 | 原因 | 修法 |
|---|---|---|
| Kindle 转圈连不上 | macOS stealth mode 把 LAN 包丢了 | `sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode off` |
| 浏览器都连不上 | server 没起来 | `launchctl list \| grep kindle-monitor` 看是否 running,看 `~/.claude/kindle-monitor/launchd.err.log` |
| Mac 端能开 localhost 但 Kindle 连不上 | Mac IP 变了 | `ipconfig getifaddr en0` 拿新 IP,路由器后台给 Mac 设静态 DHCP 租约一劳永逸 |
| 显示一直停在「请确认」但没在等 | 虚拟 session 锁死 | 浏览器开 `http://<Mac-IP>:8787/reset`,或等 5 分钟 |
| 事件流里看到 `~PreToolUse` | 来自其他 session 的事件被锁住时只追加不抢 banner | 这是设计行为,不是 bug |
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.
| 现象 | 原因 | 修法 |
|---|---|---|
| Kindle 转圈连不上 | macOS stealth mode 把 LAN 包丢了 | `sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode off` |
| 浏览器都连不上 | server 没起来 | `launchctl list \| grep kindle-monitor` 看是否 running,看 `~/.claude/kindle-monitor/launchd.err.log` |
| Mac 端能开 localhost 但 Kindle 连不上 | Mac IP 变了 | `ipconfig getifaddr en0` 拿新 IP,路由器后台给 Mac 设静态 DHCP 租约一劳永逸 |
| 显示一直停在「请确认」但没在等 | 虚拟 session 锁死 | 浏览器开 `http://<Mac-IP>:8787/reset`,或等 5 分钟 |
| 事件流里看到 `~PreToolUse` | 来自其他 session 的事件被锁住时只追加不抢 banner | 这是设计行为,不是 bug |
The script explicitly targets ~/.claude/settings.json and comments that it will append hooks there, indicating direct modification of the agent's configuration directory. Access to agent config is high sensitivity because it enables persistent behavioral changes, automatic command execution on future events, and potential collection of user/session data through hook chaining.
#!/bin/bash
# Kindle Claude Monitor — install script
# 把 server.py / notify.sh 部署到 ~/.claude/kindle-monitor/,注册 launchd,
# 在 ~/.claude/settings.json append 7 个 hook(不动既有 hooks)。
set -e
Defining SETTINGS as $HOME/.claude/settings.json confirms intentional access to the agent configuration store. In this skill context, that is more dangerous because the monitor is not just displaying data; it is wiring itself into Claude's future event pipeline via persistent hooks, expanding from visualization into control-plane modification.
RUNTIME_DIR="$HOME/.claude/kindle-monitor"
LAUNCH_AGENTS="$HOME/Library/LaunchAgents"
PLIST_DST="$LAUNCH_AGENTS/com.user.kindle-monitor.plist"
SETTINGS="$HOME/.claude/settings.json"
# ---- 1. 检查依赖 ----
echo "==> 检查 python3..."
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.
RUNTIME_DIR="$HOME/.claude/kindle-monitor"
PLIST_DST="$HOME/Library/LaunchAgents/com.user.kindle-monitor.plist"
SETTINGS="$HOME/.claude/settings.json"
echo "==> 停止 launchd 服务"
launchctl unload "$PLIST_DST" 2>/dev/null || true
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
fi
echo "==> 删除运行时文件"
rm -f "$RUNTIME_DIR/server.py" "$RUNTIME_DIR/notify.sh"
echo " 保留: $RUNTIME_DIR/events.jsonl (历史日志)"
cat <<EOF
The README explicitly says Claude hook events are forwarded to a local HTTP server that is intended to be reachable from other devices on the LAN. Even if the payload is only 'status', hook events can contain sensitive project names, tool usage context, and workflow timing metadata, and the documentation does not warn users about data exposure, access control, or network trust assumptions.
Installing a launchd plist creates persistence so the monitor server starts automatically, which extends the lifetime of the network-exposed service and any associated logging or event collection. Persistence is expected for a monitor, but it increases risk if the service is misconfigured, exposed to the LAN, or later forgotten by the user.
`install.sh` 做这几件事:
1. 拷 `server.py` / `notify.sh` 到 `~/.claude/kindle-monitor/`
2. 渲染并装 launchd plist 到 `~/Library/LaunchAgents/com.user.kindle-monitor.plist`
3. 启动 server(绑定 0.0.0.0:8787)
4. 在 `~/.claude/settings.json` 的 `hooks` 里 append 7 个 kindle 钩子(不动既有 hooks)
5. 检测 macOS 防火墙隐身模式,提示用户手动关
The README instructs users to disable macOS stealth mode to make the service reachable, but does not clearly warn that this increases the host's network visibility and potentially broadens exposure of the monitor service. In the context of a server bound to 0.0.0.0, this materially lowers a network protection barrier and may make reconnaissance and access by other LAN actors easier.
The README includes a sudo command that changes a system firewall setting. Although the command itself is not arbitrary code execution, encouraging elevated execution to weaken a security control is risky because users may run it without understanding the broader exposure it creates.
如果 Kindle 显示"无法连接",多半是 macOS 防火墙隐身模式:
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode off
The troubleshooting table repeats sudo-based instructions to disable firewall stealth mode, reinforcing a privileged action that reduces host hardening. Repetition in troubleshooting increases the chance that users treat the change as routine and permanent rather than as a risky exception.
| 现象 | 修法 |
|---|---|
| Kindle 转圈连不上 | macOS stealth mode 把 LAN 包丢了 → `sudo socketfilterfw --setstealthmode off` |
| 浏览器都连不上 | server 没起 → `launchctl list \| grep kindle-monitor`;看 `~/.claude/kindle-monitor/launchd.err.log` |
| Mac 端能开 localhost 但 Kindle 连不上 | Mac IP 变了 → `ipconfig getifaddr en0` 拿新 IP;路由器后台给 Mac 设静态 DHCP 一劳永逸 |
| 一直卡在「请确认」但实际没在等 | 浏览器开 `/reset`,或等 5 分钟超时 |
The skill describes shell execution, file writes, environment-variable use, and persistent service setup, but does not declare an explicit tool scope such as permissions or allowed-tools. That mismatch increases the chance the skill will run with broader capabilities than a user expects, especially because it modifies hooks and agent configuration. The issue is primarily inadequate least-privilege declaration rather than overtly malicious behavior.
Using the broad trigger term "monitor" can cause unintended invocation during unrelated conversations about monitoring, dashboards, or status displays. Accidental activation is risky here because the skill is documented to install files, alter hooks, and start a local service, so an overly broad trigger materially raises the chance of unplanned system changes.
The skill documents modifying user files, appending hooks to ~/.claude/settings.json, writing a LaunchAgent plist, and potentially changing firewall settings, but does not present an explicit security warning or consent boundary before those actions. Users may trigger the skill expecting a display setup and not realize it introduces persistence, network listening behavior, and configuration changes.
Installing a launchd plist with KeepAlive creates persistence across logins and automatic restarts, which is a classic persistence mechanism. While legitimate for a monitor service, persistence is security-relevant because it keeps code running in the background and can preserve compromise or unwanted behavior after the initial session ends.
`install.sh` 会做这些事:
- 把 `server.py` / `notify.sh` 拷到 `~/.claude/kindle-monitor/`
- 把 launchd plist 写到 `~/Library/LaunchAgents/` 并启动
- 在 `~/.claude/settings.json` 的 hooks 里 append 7 个 kindle 钩子(不动既有 hooks)
- 处理 macOS 防火墙隐身模式(如有需要会提示用户用 sudo 关闭)
- 报告 Mac 局域网 IP,告诉你 Kindle 上输入什么 URL
The skill may prompt the user to use sudo to disable macOS stealth mode, which weakens host network hardening and broadens reachability from the LAN. Even though the text says "if needed," introducing privileged changes for convenience increases attack surface and may normalize risky administrative actions.
- 把 `server.py` / `notify.sh` 拷到 `~/.claude/kindle-monitor/`
- 把 launchd plist 写到 `~/Library/LaunchAgents/` 并启动
- 在 `~/.claude/settings.json` 的 hooks 里 append 7 个 kindle 钩子(不动既有 hooks)
- 处理 macOS 防火墙隐身模式(如有需要会提示用户用 sudo 关闭)
- 报告 Mac 局域网 IP,告诉你 Kindle 上输入什么 URL
## Kindle 端配置
Mentioning launchctl load/unload further indicates long-lived session persistence. The risk is limited compared with stealthy persistence because it is openly documented, but it still affects the host beyond one-time skill execution.
| `KINDLE_MONITOR_REFRESH` | 3 | meta refresh 秒数 |
| `KINDLE_MONITOR_WAITING_TIMEOUT` | 300 | waiting 锁超时秒数 |
改了之后改 plist 里的 EnvironmentVariables 块,然后 `launchctl unload && launchctl load` 一次。
## 卸载
Mentioning launchctl load/unload further indicates long-lived session persistence. The risk is limited compared with stealthy persistence because it is openly documented, but it still affects the host beyond one-time skill execution.
| `KINDLE_MONITOR_REFRESH` | 3 | meta refresh 秒数 |
| `KINDLE_MONITOR_WAITING_TIMEOUT` | 300 | waiting 锁超时秒数 |
改了之后改 plist 里的 EnvironmentVariables 块,然后 `launchctl unload && launchctl load` 一次。
## 卸载
The uninstall section confirms a LaunchAgents plist is created and later removed, validating persistent installation behavior. Transparent uninstall helps, but the persistence itself remains security-relevant because it introduces an always-on local service.
bash ~/.claude/skills/kindle-claude-monitor/uninstall.sh
会停 launchd、删 LaunchAgents plist、从 settings.json 移除 7 个 kindle 钩子(保留其他不动)。
## 故障排查
The troubleshooting advice explicitly suggests a sudo command to disable stealth mode system-wide. In context, this is more dangerous because the skill already exposes a local HTTP service to the network, so reducing firewall stealth further increases discoverability and exposure of that service.
| 现象 | 原因 | 修法 |
|---|---|---|
| Kindle 转圈连不上 | macOS stealth mode 把 LAN 包丢了 | `sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode off` |
| 浏览器都连不上 | server 没起来 | `launchctl list \| grep kindle-monitor` 看是否 running,看 `~/.claude/kindle-monitor/launchd.err.log` |
| Mac 端能开 localhost 但 Kindle 连不上 | Mac IP 变了 | `ipconfig getifaddr en0` 拿新 IP,路由器后台给 Mac 设静态 DHCP 租约一劳永逸 |
| 显示一直停在「请确认」但没在等 | 虚拟 session 锁死 | 浏览器开 `http://<Mac-IP>:8787/reset`,或等 5 分钟 |
This duplicate plist-related indicator points to the same persistence mechanism: storage of a LaunchAgent plist in the user's LaunchAgents directory. In context, persistence is somewhat expected for a monitor, but still dangerous because it increases runtime exposure and survives beyond the installing shell session.
SKILL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
RUNTIME_DIR="$HOME/.claude/kindle-monitor"
LAUNCH_AGENTS="$HOME/Library/LaunchAgents"
PLIST_DST="$LAUNCH_AGENTS/com.user.kindle-monitor.plist"
SETTINGS="$HOME/.claude/settings.json"
# ---- 1. 检查依赖 ----
This duplicate plist-related indicator points to the same persistence mechanism: storage of a LaunchAgent plist in the user's LaunchAgents directory. In context, persistence is somewhat expected for a monitor, but still dangerous because it increases runtime exposure and survives beyond the installing shell session.
SKILL_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
RUNTIME_DIR="$HOME/.claude/kindle-monitor"
LAUNCH_AGENTS="$HOME/Library/LaunchAgents"
PLIST_DST="$LAUNCH_AGENTS/com.user.kindle-monitor.plist"
SETTINGS="$HOME/.claude/settings.json"
# ---- 1. 检查依赖 ----