Back to skill

Security audit

Gamified Habits

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a disclosed local habit tracker, but it has real review concerns around user-data privacy and unsafe file scoping.

Review this skill carefully before installing on a shared or multi-user system. It stores habit history and generated diaries locally in plaintext, includes existing user data in the package, and allows manual user selection that can bypass the claimed data isolation. It should be fixed to remove packaged user data, validate and contain all storage paths, and prevent ordinary users from selecting another user's records.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
src/storage.js:21
Finding

Path Traversal Through Unvalidated User Identifiers

Content
View full analysis
arg.startsWith('--user=')); if (userArg) { return userArg.split('=')[1]; } const channel = process.env.OPENCLAW_CHANNEL || process.env.CHANNEL; const accountId = process.env.OPENCLAW_ACCOUNT_ID || process.env.ACCOUNT_ID; if (channel && accountId) { return `${channel}-${accountId}`; } if (process.env.GAMIFIED_HABITS_USER) { return process.env.GAMIFIED_HABITS_USER; } return 'default'; } const DATA_PATH = path.join(DATA_DIR, `${getUserId()}.json`); function loadData() { migrateOldData(); try { if (fs.existsSync(DATA_PATH)) { const data = fs.readFileSync(DATA_PATH, 'utf8'); return JSON.parse(data); } } catch (error) { console.error('Failed to read data:', error.message); } const defaultData = getDefaultData(); saveData(defaultData); return defaultData; } function saveData(data) { try { if (!fs.existsSync(DATA_DIR)) { fs.mkdirSync(DATA_DIR, { recursive: true }); } fs.writeFileSync(DATA_PATH, JSON.stringify(data, null, 2), 'utf8'); return true; } catch (error) { console.error('Failed to save data:', error.message); return false; } } ``` The same identifier is used in diary paths: ```js function saveDiary(userId, diaryContent) { const dataDir = storage.DATA_DIR; const diaryDir = path.join(dataDir, 'diaries', userId); if (!fs.existsSync(diaryDir)) { fs.mkdirSync(diaryDir, { recursive: true }); } const today = storage.getTodayStr(); const filePath = path.join(diaryDir, `${today}.md`); try { fs.writeFileSync(filePath, diaryContent, 'utf8'); return { succ ...[truncated 2797 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/storage.js:21
Finding

Arbitrary User Selection Bypasses Multi-User Data Isolation

Content
View full analysis
arg.startsWith('--user=')); if (userArg) { return userArg.split('=')[1]; } // OpenClaw context const channel = process.env.OPENCLAW_CHANNEL || process.env.CHANNEL; const accountId = process.env.OPENCLAW_ACCOUNT_ID || process.env.ACCOUNT_ID; if (channel && accountId) { return `${channel}-${accountId}`; } // Environment-variable override if (process.env.GAMIFIED_HABITS_USER) { return process.env.GAMIFIED_HABITS_USER; } return 'default'; } const DATA_PATH = path.join(DATA_DIR, `${getUserId()}.json`); ``` The CLI explicitly advertises the override: ```js Multi-user: Automatic identification: User data is managed according to the OpenClaw channel and account Manual selection: habits status --user=wanwan Environment variable: export GAMIFIED_HABITS_USER=wanwan ``` ### Technical Analysis The application treats possession of a user identifier as sufficient authority to access that user's data. A caller-provided `--user` option has higher priority than the OpenClaw identity context, and no authentication or authorization check confirms that the invoking user owns the selected identifier. Consequently, the storage filename acts as both an identifier and an authorization mechanism. User IDs disclosed through logs, documentation, filenames, shared directories, or predictable channel/account formats can be supplied directly to select another user's record. This contradicts the documented assertion that user data is completely isolated. ### Attack Path 1. The attacker learns or guesses another user's identif ...[truncated 1467 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
data/dingtalk-0124046821484330.json:1
Finding

Identifiable User Activity and Diary Data Included in the Distributed Artifact

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/xp-system.js:347
Finding

Boss Battle Reward Can Be Claimed Repeatedly

Content
View full analysis
c.date === today); const completedHabitNames = todayCheckins.map(c => c.habitName); const allHabits = data.habits; const allCompleted = allHabits.length > 0 && allHabits.every(h => completedHabitNames.includes(h.name)); if (allCompleted) { const bossData = storyGenerator.generateBossStory( allHabits, allHabits.length, data.user.level ); if (bossData.victory && bossData.bonusXp > 0) { data.user.xp += bossData.bonusXp; data.user.xpToNext = getXpForLevel(data.user.level); let currentLevel = data.user.level; let leveledUp = false; while (data.user.xp >= getXpForLevel(currentLevel)) { currentLevel++; leveledUp = true; } if (leveledUp) { data.user.level = currentLevel; } storage.saveData(data); } return { success: true, triggered: true, bossData, message: bossData.story }; } return { success: true, triggered: false, message: 'Today’s tasks are incomplete. Keep going!' }; } ``` ### Technical Analysis The function checks whether all habits were completed on the current date, but it does not record or verify whether the Boss reward for that date has already been claimed. Once `allCompleted` becomes true, every subsequent invocation generates a victory and adds `bossData.bonusXp` again. Saving the updated XP does not change the completion condition, so the operation remains replayable for the rest of the day. There is also no idempotency key, claim ledger, or atomic check-and-set operation. ### Attack Path 1. A user creates one ...[truncated 997 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (35)

Lp1

High
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The module derives the storage path from environment variables and command-line input, including user-controlled identifiers, but the skill only declares exec permission and not environment access. Because getUserId() feeds directly into DATA_PATH without sanitization, a crafted user ID or manipulated HOME/account environment can cause path traversal, cross-user data access, or writes outside the intended data directory.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The PRD specifies persistent local storage of personal habit data, activity history, and derived attributes, but the user-facing description does not warn that this sensitive behavioral data is retained on disk. Users may disclose health, routine, reading, finance, or wellness information without informed consent about storage location and retention, increasing privacy risk on shared or compromised machines.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The invocation scope is loosely defined as broad natural-language phrases without guardrails on when the skill should activate or how intent is disambiguated. Because the skill has exec permission and supports mutating actions, insufficient scoping can lead to accidental command execution from incidental text, quoted speech, or unrelated conversation context.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrase set includes very generic natural-language activators such as '打卡', '创建习惯', and similar short phrases that can easily appear in ordinary conversation. In a skill with exec permission, overly broad activation increases the chance of unintended invocation and accidental state-changing actions such as creating habits or recording check-ins without clear user intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

All user-facing descriptions, trigger phrases, commands explanations, and example dialogues are presented only in Chinese. Because the file does not offer user opt-in for language or explain that the skill is intentionally region-specific, it appears to impose a single language by default.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language triggers include very common everyday phrases like '我起床了'、'我跑步了'、'我读书了'. These can plausibly appear in ordinary conversation and cause unintended skill activation, which is more concerning because the skill has exec permission and can modify persisted user data.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
72% confidence
Finding

This duplicate finding points to the same persistence behavior: habit metadata and check-in history are retained across sessions in local storage. While expected for the feature, persisted behavioral data can expose sensitive routines if the host is shared or files are insufficiently protected.

Content

Scanner excerpt · SKILL.md (reported line 56)May include surrounding context.

命令行方式

bash
habits create <名称> [类型] [--desc "描述"] [--success "成功描述"]
                        # 创建习惯(支持自定义描述)
habits delete <名称>           # 删除习惯
habits list                    # 查看列表

Session Persistence

Medium
Category
Rogue Agent
Confidence
72% confidence
Finding

This duplicate finding points to the same persistence behavior: habit metadata and check-in history are retained across sessions in local storage. While expected for the feature, persisted behavioral data can expose sensitive routines if the host is shared or files are insufficiently protected.

Content

Scanner excerpt · SKILL.md (reported line 56)May include surrounding context.

命令行方式

bash
habits create <名称> [类型] [--desc "描述"] [--success "成功描述"]
                        # 创建习惯(支持自定义描述)
habits delete <名称>           # 删除习惯
habits list                    # 查看列表

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This JavaScript file contains all primary user-facing strings, commands explanations, and natural-language handling exclusively in Chinese. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified or optionality is provided.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

A habit assistant is expected to manage habits and progress, but exposing a 'test' command is a developer-oriented capability rather than an end-user habit feature. It can modify stored habit data by creating habits and check-ins during test execution, which is not justified by the manifest’s stated purpose.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The README explicitly documents automatic user identification using channel and account IDs and shows those identifiers embedded directly in per-user filenames. Even though this is documentation rather than executable code, it normalizes collection and storage of identifiable user data without any privacy notice, minimization guidance, retention policy, or warning about exposure risks, which can lead to unnecessary disclosure of cross-platform account identifiers.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This JavaScript file is entirely oriented around Chinese-language output, including the module description and all generated diary text, with no indication that the skill is China-specific or that users can opt into another language. The policy explicitly flags language or locale constraints when a skill forces a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code’s user-facing comments and returned messages are entirely in Chinese, and there is no indication that the skill is region-specific or that users can opt into another language. That creates a natural-language policy issue under the locale-choice rule because the skill effectively forces a specific language without user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This file's natural-language documentation and console output are written in Chinese, including operational descriptions and status/error messages. Under the policy, forcing a specific language without user opt-in or justification is a locale/language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code generates all user-facing story text, labels, and titles exclusively in Chinese, including the returned story strings and reward text. That creates a language-policy issue because the skill forces a specific language without offering user opt-in, fallback, or documenting that it is intended only for a Chinese-speaking context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The user-facing guidance and quick-start strings are written in Chinese throughout the file, which can amount to forcing a specific language without user opt-in. The file does not indicate that the skill is intended only for Chinese-speaking users or provide any alternative language selection.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code explicitly formats achievement dates with the 'zh-CN' locale, and the surrounding user-facing strings throughout the module are hard-coded in Chinese. That imposes a specific language/locale on users without any opt-in, language selection mechanism, or documented region-specific justification, which matches the policy-violation category.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The XP system module exposes diary generation and persistence functions that extend beyond the core habit/XP behavior described in the skill metadata. In a skill with exec permission, adding file-writing and file-reading capabilities increases the data-handling surface and creates scope creep that can surprise users, weaken least-privilege boundaries, and enable unintended storage of personal behavioral data.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

Creating and reading diary files is not obviously necessary for basic habit tracking, yet this module directly provides those operations. If exposed through agent actions, the feature may store or retrieve sensitive personal reflections without clear justification, increasing privacy risk and making the skill more dangerous than its stated purpose suggests.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The changelog is entirely written in Chinese, beginning with the title '更新日志', and nowhere indicates that other languages are supported or that Chinese is a deliberate region-specific requirement. Under the policy for natural-language violations, forcing a specific language without opt-in can be a locale-policy issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The entire skill specification, including commands, examples, and user-facing outputs, is presented only in Chinese, with no indication that users may choose another language. This can violate a language/locale policy where skills should not force a specific language unless the constraint is explicitly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The entire skill description is written in Chinese and does not indicate that other languages are supported or that Chinese is a required locale for a specific regional use case. Under the policy rule, forcing a specific language without user opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Overly Broad Trigger

Low
Category
Trigger Abuse
Confidence
92% confidence
Finding

The trigger '打卡' is extremely short and generic, so it may match benign user utterances that are not intended to invoke the skill. In this skill, accidental activation can lead to unintended check-ins or other state changes in persisted habit data.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The manifest enumerates some trigger phrases, but the README later shows additional natural-language invocations such as "创建一个早起习惯" and "有什么成就" that are not included in the trigger list. This creates ambiguity about which phrases actually activate the skill and whether matching is exact or broad.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The manifest presents the skill as a habit-building assistant, but the CLI includes a 'whoami' command that reveals channel, account ID, user ID, and backing data file path. Multi-user support may be an implementation detail, but exposing identity and storage-path details as a user-facing feature is broader than the manifest’s stated behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.