Back to skill

Security audit

Hsk Learning

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its HSK learning purpose, but one exposed tool can read arbitrary local files instead of being limited to quiz logs.

Review before installing. This skill is not obviously malicious and does not show remote code execution or exfiltration, but it should be fixed so hsk_parse_quiz_log only reads allowed quiz-log files under a dedicated directory. Users should also understand that the vocabulary tracker scans memory Markdown files and writes derived learning reports and mastery data locally.

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
index.js:429
Finding
Unrestricted Local File Read Through User-Controlled Quiz Log Path## Vulnerability Details **File Location**: `index.js:429-445`; file content is read at `lib/parser.js:9-10` **Vulnerability Type**: Path traversal and unrestricted local file access **Risk Level**: Medium ### Vulnerable Code ```javascript // index.js:429-445 async hsk_parse_quiz_log({ filePath }) { try { const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(__dirname, '..', '..', filePath); if (!fs.existsSync(absolutePath)) { return { success: false, error: `File not found: ${absolutePath}` }; } const items = parseQuizLog(absolutePath); ``` ```javascript // lib/parser.js:9-10 function parseQuizLog(filePath) { const content = fs.readFileSync(filePath, 'utf8'); ``` ### Technical Analysis The `filePath` argument is externally controlled. If it is absolute, the implementation accepts it without restriction. If it is relative, it is joined to a base directory without subsequently canonicalizing the result and confirming that it remains inside an approved quiz-log directory. Consequently, absolute paths such as `/etc/hosts` and relative traversal paths containing `../` can reach files outside the directory required by the skill's declared quiz-log parsing functionality. `fs.existsSync()` only checks existence and does not establish authorization. The resolved path is passed directly to `fs.readFileSync()`. The parser does not return arbitrary raw file content verbatim. Disclosure through a successful response is constrained to file lines matching its quiz-log patterns, including bold vocabulary entries and recognized vocabulary-list formats. Nevertheless, any process-readable file is opened and parsed, matching content may be returned in structured results, and error responses can expose path or parser details. This violates least-privilege filesystem access and creates a local file disclosure primitive. ### Att ...[truncated 1701 chars]
Remediation
## Remediation Suggestions 1. Define a single approved directory for quiz-performance logs, such as the OpenClaw `memory` directory or a dedicated `memory/quiz-logs` subdirectory. 2. Reject absolute paths supplied by callers. 3. Resolve both the approved directory and requested target with `path.resolve()`, then verify that the target remains inside the approved directory using `path.relative()` or a separator-safe prefix check. 4. Restrict accepted files to the expected `.md` extension and, where practical, the documented quiz-performance filename pattern. 5. Use `fs.statSync()` or its asynchronous equivalent to require a regular file. Consider rejecting symbolic links or validating the real path with `fs.realpath()` to prevent symlink escapes. 6. Do not return absolute paths, raw stack traces, or internal parser errors to callers. Log detailed diagnostics only to a protected server-side log. 7. Prefer asynchronous, size-limited reads to reduce denial-of-service risk from very large files. Example hardening pattern: ```javascript const allowedDir = path.resolve(__dirname, '..', '..', 'memory'); if (path.isAbsolute(filePath)) { return { success: false, error: 'Absolute paths are not allowed' }; } if (!filePath.endsWith('.md') || !path.basename(filePath).includes('quiz-performance')) { return { success: false, error: 'Invalid quiz log filename' }; } const candidate = path.resolve(allowedDir, filePath); const relative = path.relative(allowedDir, candidate); if (relative.startsWith('..') || path.isAbsolute(relative)) { return { success: false, error: 'Path is outside the allowed directory' }; } const realAllowedDir = fs.realpathSync(allowedDir); const realCandidate = fs.realpathSync(candidate); const realRelative = path.relative(realAllowedDir, realCandidate); if (realRelative.startsWith('..') || path.isAbsolute(realRelative)) { return { success: false, error: 'Path is outside the allowed directory' ...[truncated 352 chars]
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Most of the implementation aligns with the declared HSK-focused learning purpose: mastery tracking, spaced repetition, vocabulary analysis, and adaptive quiz generation are all present. However, the code also performs undeclared file-system based scanning of a memory directory and report generation, which is a broader resource-access/reporting capability not explicitly described. More importantly, the description explicitly says the skill is NOT for pronunciation practice or handwriting practice, yet the code generates listening practice quizzes and writing practice exercises. Even though the listening quiz uses imagined audio rather than actual speech processing, it is still a listening/pronunciation-adjacent learning mode outside the stated constraints. The writing quiz is directly inconsistent with the 'NOT for ... handwriting practice' limitation. Therefore the description does not fully and accurately represent the behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code does support part of the declared theme: it analyzes Chinese/CJK token exposure and categorizes tokens by HSK level. However, the declared purpose presents a broader HSK learning system centered on spaced repetition mastery tracking, adaptive quiz generation, and review management. None of those core capabilities appear in this code chunk. Instead, the actual behavior is narrower and different: it reads .md files from a memory directory, counts CJK tokens, labels them as HSK1/2/3/beyond/unknown, generates a text report, and saves that report. This filesystem-based vocabulary report generation is only partially aligned with 'analyzing Chinese language exposure,' and even there it targets Markdown memory files rather than conversations specifically. Because the primary described capabilities are largely absent and the implemented behavior is materially narrower and somewhat different, this is a mismatch.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README advertises automatic scanning of conversation logs and `memory/*.md` files for CJK tokens, but it does not clearly warn users that potentially sensitive personal content will be parsed as part of normal operation. In a learning/progress-tracking skill, this creates a real privacy risk because users may not expect private notes, chat history, or memory files to be inspected and transformed into derived vocabulary data.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill processes `memory/*.md` files and conversation logs to extract CJK tokens and generate progress reports, but the user-facing description does not prominently warn that personal conversation content may be scanned. This creates a privacy and transparency issue: users may invoke the skill for learning support without realizing it inspects local memory files containing potentially sensitive text.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The manifest says the skill is NOT for pronunciation practice, but the code exposes a `listening` quiz format and presents it as audio-based practice. Even though it uses text placeholders rather than real audio, this still extends the skill into listening/pronunciation-oriented training beyond the declared scope.

Description-Behavior Mismatch

Medium
Confidence
78% confidence
Finding
The manifest excludes handwriting practice, but the skill generates a dedicated `Writing Practice` quiz mode that asks the learner to compose sentences and paragraphs. While typing is not identical to handwriting, this is a writing-production capability outside the narrow HSK vocabulary tracking and adaptive quiz scope described in the manifest's exclusions.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The generated quiz always includes the instruction string in Chinese: "请完成所有题目,完成后发送'答案'查看正确答案。" This imposes a specific language on all users, and the file does not provide any alternative locale or opt-in mechanism.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The quiz-log parser accepts an arbitrary filePath and permits absolute paths or relative traversal outside the intended memory area, then reads and parses the file if it exists. This broad file-read capability exceeds the skill's stated purpose and can expose sensitive local files if an attacker can influence the argument.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest describes the skill as 'user-agnostic' yet exposes a tool that updates vocabulary tracking by scanning unspecified 'memory files'. That creates an unsafe ambiguity about data scope and consent, and could lead an agent to ingest unrelated or sensitive user data while performing learning-state updates.

Vague Triggers

Medium
Confidence
87% confidence
Finding
Broad update-tool descriptions such as scanning memory files or processing quiz logs without clear trigger conditions can cause an agent to invoke state-changing actions too eagerly. In a skill that maintains learning records, that increases the risk of unauthorized reads, unintended data modification, and silent corruption of progress tracking.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The broader skill context says the skill is not for pronunciation practice or handwriting practice, yet the quiz tool advertises 'listening' and 'writing' formats and 'actual questions' in those categories. Those capabilities suggest practice modalities beyond the stated limits, creating a semantic mismatch between declared scope and exposed functionality.

Natural-Language Policy Violations

Low
Confidence
72% confidence
Finding
The description defines the skill strictly around HSK Chinese learning, which implies a specific language context. While this may be intentional, the file does not explicitly present this as a user opt-in language constraint or justify it as a region- or language-specific tool.

Natural-Language Policy Violations

Low
Confidence
83% confidence
Finding
This JSON file is entirely a Mandarin vocabulary dataset sourced from a Chinese-language learning site, but it does not document that the content is intentionally Chinese-specific or scoped to a Mandarin-learning use case. Under the policy rule for language or locale constraints, hard-coded language-specific content without opt-in or justification can be a policy concern.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The function updates metadata and writes to `hsk-mastery-db.json` using `fs.writeFileSync`, which modifies persistent user study data. In this file there is no confirmation prompt, logging, or user-facing comment/docstring disclosing that the skill will overwrite the local mastery database.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The code generates and saves `hsk-word-report.md` into the configured memory directory using `fs.writeFileSync`, which modifies user or system data. While there is an internal comment about saving the file, there is no confirmation prompt, user-facing log/print, or other visible disclosure in this file that the write will occur.

Natural-Language Policy Violations

Low
Confidence
75% confidence
Finding
The manifest description presents the skill as a Chinese learning system, which imposes a specific language context in natural-language metadata without indicating any user choice or opt-in. Under the policy, language or locale constraints should either be optional, explicitly user-selected, or clearly justified as region- or domain-specific.

Natural-Language Policy Violations

Low
Confidence
79% confidence
Finding
The package description states the skill is an "HSK Chinese learning system," which implies a fixed language/locale context. Under the policy rule for natural-language violations, forcing a specific language can be problematic when no opt-in or explicit justification is present in the file.