Back to skill

Security audit

HabitChat

Security checks for vulnerabilities and agentic risk

Overview

HabitChat is a coherent local habit tracker, but its reminder feature creates executable scheduled-reminder scripts in a way that can turn habit names into shell commands.

Install only if you are comfortable with a local habit tracker storing personal routine data in ~/.habitchat. Avoid enabling system reminders until the shell-script generation is fixed, and do not add generated reminder scripts to cron if habit names could contain quotes, semicolons, backticks, dollar signs, or other shell characters.

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

Error
Location
scripts/reminder.py:99
Finding
Shell Command Injection Through Unescaped Habit Names in Reminder Scripts<![CDATA[ ## Vulnerability Details **File Location**: `scripts/reminder.py:99-143` **Vulnerability Type**: Shell command injection in generated executable scripts and cron instructions **Risk Level**: High ### Vulnerable Code ```python if plat == "macos": # Create a launchd plist or use osascript-based reminder script_content = f'''#!/bin/bash osascript -e 'display notification "Time for: {habit["name"]}" with title "HabitChat Reminder"' echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] {habit["name"]}: Reminder fired" >> "{REMINDERS_LOG}" ''' script_path = DATA_DIR / f"reminder_{habit['id']}.sh" script_path.write_text(script_content) os.chmod(script_path, 0o755) result["method"] = "macos-notification" result["script"] = str(script_path) result["instructions"] = ( f"Reminder script created at {script_path}. " f"To activate, add a cron job: crontab -e and add:\n" f"{minute} {hour} * * * {script_path}" ) result["cron_line"] = f"{minute} {hour} * * * {script_path}" elif plat == "linux-desktop": script_content = f'''#!/bin/bash notify-send "HabitChat" "Time for: {habit["name"]}" --icon=dialog-information echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] {habit["name"]}: Reminder fired" >> "{REMINDERS_LOG}" ''' script_path = DATA_DIR / f"reminder_{habit['id']}.sh" script_path.write_text(script_content) os.chmod(script_path, 0o755) result["method"] = "linux-notify-send" result["script"] = str(script_path) result["instructions"] = ( f"Reminder script created at {script_path}. " f"To activate, add a cron job: crontab -e and add:\n" f"{minute} {hour} * * * DISPLAY=:0 {script_path}" ) result["cron_line"] = f"{minute} {hour} * * * DISPLAY=:0 {script_path}" else: # Headless / unknown - log file only result["method"] = "log-file" result["instructions"] = ( f"No desktop notification available. Reminders will be logged to {REMINDERS_LOG}. " ...[truncated 3225 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Do not generate shell source from habit names.** Implement notification and logging operations directly in Python. For Linux notifications, use an argument array without a shell: ```python subprocess.run( ["notify-send", "HabitChat", f"Time for: {habit['name']}", "--icon=dialog-information"], check=False, shell=False, ) ``` Append reminder records using Python file operations rather than an `echo` command. 2. **Use a fixed helper program for scheduled execution.** The cron entry should contain only a trusted Python executable, a fixed script path, and a validated habit ID. The helper should load the display name from JSON at runtime and must never interpret it as shell code. 3. **Validate habit identifiers and reminder times.** - Require habit IDs to match their expected hexadecimal format. - Parse the reminder time using `datetime.strptime(value, "%H:%M")`. - Convert the parsed hour and minute back to integers before using them in scheduling instructions. - Reject control characters, including carriage returns and newlines, in values used in configuration or generated instructions. 4. **If shell generation cannot be removed, quote every dynamic shell argument.** Use `shlex.quote()` for each separately interpolated argument. Do not interpolate untrusted values inside pre-existing single- or double-quoted shell strings. This is a secondary defense and is less robust than eliminating generated shell code. 5. **Create scripts with restrictive permissions.** If executable helper files remain necessary, use mode `0700` rather than `0755` and ensure `~/.habitchat` is not writable by other users. 6. **Make scheduling lifecycle management explicit.** Record the exact cron entry, provide an unambiguous removal procedure, and ensure disabling a reminder removes or disables the corresponding scheduled entry where feasible and only with explicit user authorization. 7. **Add re ...[truncated 244 chars]
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill also sets up OS-level reminders and notification behavior, including platform-specific mechanisms and local reminder logs, but this is not reflected in the declared purpose. Hidden scheduling or notification side effects expand the trust boundary beyond habit coaching and can surprise users by creating background artifacts or system integrations they did not expect.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill also sets up OS-level reminders and notification behavior, including platform-specific mechanisms and local reminder logs, but this is not reflected in the declared purpose. Hidden scheduling or notification side effects expand the trust boundary beyond habit coaching and can surprise users by creating background artifacts or system integrations they did not expect.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
Be like a supportive friend, not a drill sergeant:
- Celebrate wins enthusiastically but authentically
- Acknowledge struggles without judgment
- Offer practical suggestions, not platitudes
- Reference their actual data: "You've nailed this 6 out of 7 days this week"
- Use habit science concepts from the references (cue-routine-reward, implementation intentions, temptation bundling)
Confidence
80% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
# Habit Science Reference

Use these evidence-based concepts when coaching users. Reference them naturally in conversation - don't lecture.

## Core Concepts
Confidence
85% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill instructs the agent to execute shell commands and write persistent data under ~/.habitchat, but it does not declare any explicit tool scope such as allowed-tools or permissions. This creates an authorization and transparency gap: a user or platform reviewer cannot easily see that the skill needs shell execution and filesystem write access, increasing the risk of overbroad tool use or accidental misuse.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The delete command permanently removes a habit from the persisted habits data and also removes its streak record, but there is no confirmation prompt or explicit user-facing warning that this operation is destructive. Although the command prints a result afterward, that disclosure comes only after the irreversible write has already occurred.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The manifest presents the skill as a habit coach but omits that it performs local system automation by writing reminder scripts and guiding the user to install cron jobs. That mismatch is security-relevant because users may grant trust to a coaching tool without expecting file creation, executable content, or persistence-related setup steps.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The code creates executable shell scripts in the user's home directory and tells the user to persist them via cron, which is local system automation beyond what the skill description says. Even though this appears intended to implement reminders, undisclosed creation of executables and persistence mechanisms increases attack surface and could normalize risky behavior for users.

Session Persistence

Medium
Category
Rogue Agent
Content
}

    if plat == "macos":
        # Create a launchd plist or use osascript-based reminder
        script_content = f'''#!/bin/bash
osascript -e 'display notification "Time for: {habit["name"]}" with title "HabitChat Reminder"'
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] {habit["name"]}: Reminder fired" >> "{REMINDERS_LOG}"
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.

Session Persistence

Medium
Category
Rogue Agent
Content
result["script"] = str(script_path)
        result["instructions"] = (
            f"Reminder script created at {script_path}. "
            f"To activate, add a cron job: crontab -e and add:\n"
            f"{minute} {hour} * * * {script_path}"
        )
        result["cron_line"] = f"{minute} {hour} * * * {script_path}"
Confidence
85% confidence
Finding
The code explicitly instructs the user to add a cron entry that will execute a generated shell script on a schedule, which is a persistence mechanism. In a habit-coaching context this may be functionally related to reminders, but persistence guidance should still be treated carefully because it creates recurring code execution outside the application's immediate runtime.

Session Persistence

Medium
Category
Rogue Agent
Content
result["script"] = str(script_path)
        result["instructions"] = (
            f"Reminder script created at {script_path}. "
            f"To activate, add a cron job: crontab -e and add:\n"
            f"{minute} {hour} * * * DISPLAY=:0 {script_path}"
        )
        result["cron_line"] = f"{minute} {hour} * * * DISPLAY=:0 {script_path}"
Confidence
85% confidence
Finding
This Linux path instructs the user to register a recurring cron job that launches the generated script daily, constituting scheduled persistence. Although intended for reminders, recurring execution of locally written scripts is a security-sensitive capability that should be transparently disclosed and minimized.

Session Persistence

Medium
Category
Rogue Agent
Content
print(json.dumps({
        "status": "ok",
        "message": f"Reminder disabled for '{habit['name']}'. "
                   f"Remember to also remove the cron job if you added one: crontab -e",
    }))
Confidence
85% 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.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The CLI exposes a --user-data option for the insights command, but the implementation ignores that argument and always reads from fixed files under the user's home directory. This can cause the tool to analyze unintended data, violate caller expectations, and leak or misuse the local user's habit data in contexts where a different dataset was explicitly requested.

Missing User Warnings

Low
Confidence
82% confidence
Finding
The tool stores habit, log, streak, and config data under ~/.habitchat and performs multiple file writes, but the module docstring only describes functionality and does not disclose that personal activity data will be persisted on disk. For a tracking tool handling potentially sensitive behavioral information, a clear notice about local storage would help users understand the privacy impact.

Missing User Warnings

Low
Confidence
83% confidence
Finding
The reminder setup path writes an executable script and saves persistent state without any explicit warning or confirmation step before doing so. While the behavior is not inherently malicious, silent file creation of executable content reduces user awareness and makes misuse or misunderstanding more likely.

Missing User Warnings

Low
Confidence
85% confidence
Finding
The disable command unlinks the generated reminder shell script if it exists, which is a file deletion operation. While the command prints a success message afterward, there is no prior disclosure in the code comments, CLI help, or user-facing output that disabling may delete a file from ~/.habitchat.

Static analysis

No suspicious patterns detected.