Back to skill

Security audit

System Watchdog

Security checks for vulnerabilities and agentic risk

Overview

The skill does useful local resource monitoring, but it persistently records full process command lines, which can expose secrets passed as command arguments.

Install only if you are comfortable with a local watchdog seeing and retaining command lines for your running processes. Avoid running it with elevated privileges, consider setting SYSTEM_WATCHDOG_STATE to a protected location, and remove or redact the command field before regular use on systems where command arguments may contain credentials.

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

T09 · Insecure Skill Coding Practices

Warning
Location
check.sh:233
Finding
Full Process Command Lines Are Exposed and Persisted Without Redaction## Vulnerability Details **File Location**: `check.sh`, lines 233–258, 260, 384–400, and 404–430 **Vulnerability Type**: Sensitive information exposure through process enumeration and persistent state storage **Risk Level**: Medium ### Vulnerable Code ```python ps_out = run(["ps", "axo", "pid=,ppid=,pcpu=,rss=,etime=,comm=,args="]) processes = [] for line in ps_out.strip().split('\n'): parts = line.split(None, 6) if len(parts) < 7: continue pid, ppid, cpu, rss, elapsed, comm, args = parts pid = int(pid) ppid = int(ppid) cpu = float(cpu) rss_kb = int(rss) elapsed_secs = parse_elapsed(elapsed) command = args.strip() first_arg = command.split()[0] if command else "" name = os.path.basename(comm).strip() or os.path.basename(first_arg).strip() or comm.strip() or "unknown" # macOS sometimes truncates comm to 2 chars — fall back to first arg if len(name) <= 2 and first_arg: name = os.path.basename(first_arg).strip() or name processes.append({ "pid": pid, "ppid": ppid, "name": name, "command": command, "cpu_pct": round(cpu, 1), "mem_mb": rss_kb // 1024, "elapsed_secs": elapsed_secs, "elapsed": human_elapsed(elapsed_secs), }) top_processes = sorted(processes, key=lambda p: (p["mem_mb"], p["cpu_pct"]), reverse=True)[:10] ``` The collected command lines are returned in the JSON output: ```python result = { "suspicious": len(issues) > 0, "verdict": verdict, "os": platform.system(), "summary": summary, "issues": issues, "top_processes": top_processes, "ignored_normals": ignored, } print(json.dumps(result, indent=2)) ``` They are also stored for every enumerated process: ```python state = { "timestamp": int(time.time()), "system": { "swap_used_mb": swap_used_mb, ...[truncated 3998 chars]
Remediation
## Remediation Suggestions 1. Stop requesting or retaining full process arguments. Use `comm` for executable identification and collect only PID, PPID, CPU, RSS, and elapsed time. ```python ps_out = run(["ps", "axo", "pid=,ppid=,pcpu=,rss=,etime=,comm="]) ``` 2. Remove the `command` field from `processes`, `top_processes`, anomaly details, and persistent state. Persist only the values required for cross-run comparisons: ```python "processes": { str(p["pid"]): { "mem_mb": p["mem_mb"], "cpu_pct": p["cpu_pct"], "name": p["name"], } for p in processes } ``` 3. If arguments are operationally necessary, make their collection opt-in and redact credential-bearing options, URLs, authorization headers, and environment-like values before output or storage. Prefer an allowlist of explicitly safe arguments over pattern-based removal alone. 4. Create the state directory and file with restrictive permissions. Use directory mode `0700`, write through a temporary file opened with mode `0600`, flush it, and atomically replace the destination. 5. Correct permissions on existing installations and delete previously generated state or log files that may contain secrets. 6. Document that monitoring output must not be sent to untrusted logs or consumers, and run the watchdog with the least-privileged account capable of collecting the required metrics.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill invokes a shell script, reads environment variables, and documents writing persistent state, but the manifest declares no explicit tool scope or allowed-tools constraints. That increases the attack surface because an agent may grant broader shell/file capabilities than necessary, making unintended command execution or filesystem access easier if the skill or its dependencies are modified or misused.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script persists a state file under the user's home directory and later stores per-process metadata including full command lines. Command-line arguments frequently contain sensitive material such as file paths, internal hostnames, access tokens, API keys, or credentials, so retaining them on disk creates an avoidable privacy and secret-exposure risk. In this monitoring context, the risk is more credible because the skill scans all processes and records them across runs, expanding visibility into unrelated workloads.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The skill states that it persists monitoring state under the user's home directory but does not explicitly warn the user that local data will be stored across runs. While the stored data appears operational rather than highly sensitive, silent persistence can create privacy and transparency issues and may retain process/resource history longer than a user expects.

Static analysis

No suspicious patterns detected.