Back to skill

Security audit

GrowthLoop – Plan & Habit Tracker

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent habit tracker, but it automatically checks and mutates personal habit data across conversations and includes under-described external/scheduled reminder paths that users should review before installing.

Install only if you are comfortable with a Chinese-first habit tracker that stores habit goals, notes, reminders, and some coaching conversation history locally in plaintext. Review or disable the every-conversation heartbeat behavior if you do not want unrelated chats to access or update habit reminder state, and avoid enabling cron/launchd/GitHub Actions reminders unless you understand the scheduling and endpoint exposure. Use a private data directory with restrictive permissions.

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
store.py:30
Finding
Habit and Conversation Data Stored Without Explicitly Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `store.py:30` and `store.py:52-60` **Vulnerability Type**: Plaintext sensitive-data storage with permissions dependent on the process umask **Risk Level**: Medium ### Vulnerable Code ```python os.makedirs(self.data_dir, exist_ok=True) ``` ```python def save(self, user_data: UserData) -> None: """保存用户数据,原子写入 + 文件锁""" tmp_file = self._data_file + ".tmp" try: data = user_data.to_dict() with open(tmp_file, "w", encoding="utf-8") as f: fcntl.flock(f.fileno(), fcntl.LOCK_EX) try: json.dump(data, f, ensure_ascii=False, indent=2) finally: fcntl.flock(f.fileno(), fcntl.LOCK_UN) # 原子替换 os.replace(tmp_file, self._data_file) ``` The persisted data includes user-controlled habit goals, check-in notes, coaching preferences, reminder information, and rationalization conversation history. For example, the conversation history is populated in `agent.py:112-117`: ```python if ai_message: rat.conversation.append(ConversationTurn(role="ai", content=ai_message)) if user_response: rat.conversation.append(ConversationTurn(role="user", content=user_response)) rat.round_count += 1 ``` ### Technical Analysis The data directory is created without an explicit mode, and the temporary JSON file is opened without setting or verifying restrictive permissions. Consequently: - Directory permissions are derived from the default creation mode and the process umask. - The temporary file is normally created from mode `0666`, modified only by the process umask. - `os.replace()` preserves the permissions of the temporary file when it becomes `habits.json`. - Corrupted and manually created backups use similarly inherited permissions. - All records are stored as unencrypted JSON. With a typical restrictive umask, practical exposure may be reduced. However, the code does not enforce that assumption. In environments wi ...[truncated 2004 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the data and backup directories with owner-only permissions: ```python os.makedirs(self.data_dir, mode=0o700, exist_ok=True) os.chmod(self.data_dir, 0o700) ``` Apply equivalent protection to the backup directory. 2. Create temporary files atomically with mode `0600`, rather than relying on the process umask: ```python fd = os.open( tmp_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) ``` 3. After `os.replace()`, verify or enforce owner-only permissions: ```python os.replace(tmp_file, self._data_file) os.chmod(self._data_file, 0o600) ``` 4. Reject or warn about user-supplied data directories that are symlinks, shared, group-writable, world-writable, or owned by another user. 5. Apply mode `0600` to backup and error-log files. Ensure backup directories use mode `0700`. 6. Document that habit records and conversation history are stored locally in plaintext. Provide retention controls and a command to securely delete stored records and backups. 7. If the Skill is expected to store particularly sensitive health or behavioral information, encrypt records at rest using a key held outside the data directory. 8. Add automated tests that run under permissive umasks and verify that directories are `0700` and all data, temporary, backup, and log files are `0600`. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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 (25)

Vague Triggers

High
Confidence
96% confidence
Finding
The skill instructs a heartbeat check to run on every conversation and to blend reminders into dialogue whenever pending tasks exist. This is risky because it creates cross-context activation and background tool execution regardless of the user's current topic, potentially exposing stored personal routine data or causing unsolicited behavioral nudging outside the intended session scope.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill declares executable behavior that reads environment variables, reads and writes files, and invokes shell commands, but it does not declare any explicit tool scope or permission boundaries. This creates an authorization gap where a user or orchestrator may invoke a seemingly harmless coaching skill that can persist data and execute local commands without transparent consent or least-privilege controls.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The manifest description is entirely in Chinese and presents the skill behavior as Chinese-first, and the operational instructions also prescribe Chinese phrasing for user interaction. The file does not offer a language choice or indicate that the locale restriction is intentional and limited to a region-specific use case.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases include very common conversational language such as check-ins, progress updates, and plan adjustments, which can cause the skill to activate in unrelated conversations. Over-broad activation increases the chance of unintended access to habit data and accidental execution of reminder or persistence logic when the user did not intend to invoke this skill.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The code hard-codes "Asia/Shanghai" as the default timezone in user-facing date calculations, which imposes a specific locale assumption across the skill. The file also contains Chinese-only natural-language descriptions and messages, but does not offer a language or locale choice or explain why the locale restriction is required.

Context-Inappropriate Capability

Medium
Confidence
88% confidence
Finding
The manifest describes goal setting, plan breakdown, daily check-ins, dynamic difficulty adjustment, and progress visualization for up to 5 habits. This file additionally implements a generic external data reporting interface that accepts arbitrary source/payload input and converts it into check-ins, which is a broader integration capability not declared in the skill description.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The docstring says the external reporting path is only a reserved interface, but the implementation actively writes user progress into persistent state. That mismatch is dangerous because reviewers or integrators may assume the path is inert, while untrusted callers can cause state changes, falsify habit history, and influence summaries, streaks, reminders, and phase adjustments.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This code file contains natural-language text in Chinese in the module docstring and comments, indicating the skill is oriented toward a single language/locale. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation when no alternative or opt-in is provided.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The guidance to 'default inherit' a specific OpenClaw persona/context means the skill may impose a predefined communication style or identity framing without explicit user consent. In a coaching skill, this can override user preferences for language, tone, or boundaries, creating confusing or manipulative interactions and weakening user control over how the agent presents itself.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The file is entirely written as a mandatory Chinese-language rule set, including output and generation instructions, with no indication that users may choose another language or that Chinese is an optional locale. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly justified.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This markdown guidance forces a specific language/locale in its natural-language instructions without stating that the user can choose their preferred language. The policy explicitly calls for flagging language or locale constraints unless the skill offers a choice or clearly justifies the constraint.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The instruction string explicitly directs the AI to generate the reminder message in Chinese ("请基于以上数据生成一条友好的提醒消息"). There is no indication in this file that the language is configurable or limited to a justified region-specific context, so it may violate language/locale policy expectations.

Session Persistence

Medium
Category
Rogue Agent
Content
```

## 方式二:macOS launchd
创建 ~/Library/LaunchAgents/com.habit-tracker.reminder.plist

## 方式三:GitHub Actions(免费、稳定)
在你的 repo 中创建 .github/workflows/reminder.yml:
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.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The module docstring and inline documentation are entirely in Chinese, which indicates a fixed language choice in the skill's natural-language surface. The file does not provide any opt-in, alternative locale, or justification that this skill is intentionally limited to a Chinese-speaking context.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The module docstring and usage text are written entirely in Chinese, and the rest of the script continues to present user-facing prompts and status messages only in Chinese. This creates a language/locale policy issue because the skill does not provide user opt-in, fallback, or any indication that it is intentionally limited to a Chinese-speaking audience.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The script's runtime interaction—including step descriptions, progress logs, and the cleanup prompt—is consistently hard-coded in Chinese. For a generally usable skill, this enforces a single language without user opt-in and therefore violates the natural-language locale policy described in SQP-3.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# ============================================================
        print_step(13, "通过 CLI 执行 list 命令")
        import subprocess
        cli_result = subprocess.run(
            [sys.executable, "agent.py", "list", "--data-dir", test_data_dir],
            capture_output=True, text=True, cwd=os.path.dirname(os.path.abspath(__file__))
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
check("CLI 列出所有习惯", False)

        print_step("13b", "CLI summary 命令")
        cli_result = subprocess.run(
            [sys.executable, "agent.py", "summary", "--data-dir", test_data_dir],
            capture_output=True, text=True, cwd=os.path.dirname(os.path.abspath(__file__))
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
check("CLI summary 执行成功", cli_result.returncode == 0)

        print_step("13c", "CLI visualize 命令")
        cli_result = subprocess.run(
            [sys.executable, "agent.py", "visualize", "--format", "text", "--data-dir", test_data_dir],
            capture_output=True, text=True, cwd=os.path.dirname(os.path.abspath(__file__))
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
check("CLI visualize 执行成功", cli_result.returncode == 0)

        print_step("13d", "CLI remind 命令")
        cli_result = subprocess.run(
            [sys.executable, "agent.py", "remind", "--data-dir", test_data_dir],
            capture_output=True, text=True, cwd=os.path.dirname(os.path.abspath(__file__))
        )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
This code uses Chinese natural-language strings throughout docstrings and all user-facing output, such as titles, status labels, and empty-state messages, with no indication that the user can choose another language. The policy explicitly flags language/locale constraints that force a specific language without user opt-in, and no justification for a Chinese-only locale is documented in this file.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The guidance states a default timezone of Asia/Shanghai and frames first-use confirmation around whether the user is in Beijing time, which can bias the skill toward a fixed locale by default. The policy allows locale constraints when users are given a choice or explicit opt-in, but here the initial behavior is preset rather than neutral.

Missing User Warnings

Low
Confidence
87% confidence
Finding
The trigger_reminder path creates a PendingReminder, appends it to persistent data, prunes prior reminders, and saves the updated store. In this file, that state-changing write is not accompanied by any confirmation, logging, or comment/docstring warning that user reminder data will be modified when the scheduled trigger runs.

Context-Inappropriate Capability

Low
Confidence
83% confidence
Finding
The manifest describes a habit supervision skill focused on goal setting, check-ins, adjustment, and progress tracking. In addition to local reminder logic, this file provides explicit instructions to expose or invoke a reminder endpoint via GitHub Actions and curl, which adds an externally triggered network capability not justified by the stated user-facing purpose.

Context-Inappropriate Capability

Low
Confidence
88% confidence
Finding
The manifest describes a habit supervision skill for planning, check-ins, adjustment, and visualization. In this file, the skill test script imports subprocess and launches separate Python processes to invoke CLI commands, which is a host-level execution capability not justified by the end-user habit-tracking purpose itself.

Static analysis

No suspicious patterns detected.