Back to skill

Security audit

Novel Assistant

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent novel-writing helper, but its shell compression script has an artifact-backed command-execution flaw and its remote Git guidance under-discloses that private writing may leave the machine.

Review before installing. Use the Python compressor instead of the shell compressor unless the shell script is fixed to validate --keep-chapters as a plain positive integer. Keep novel memory files in a dedicated workspace, review tracked files before any Git push, and use a private remote if synchronizing unpublished writing.

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/compress_novel_memory.sh:19
Finding

Command Injection Through Unvalidated Arithmetic Argument

Content
View full analysis
[--keep-chapters N]" exit 1 fi # Get file information ORIGINAL_SIZE=$(wc -c < "$MEMORY_FILE") TITLE=$(grep -oP '(?<=# 《)[^》]+' "$MEMORY_FILE" 2>/dev/null || echo "Unknown novel") TOTAL_CHAPTERS=$(grep -c "^### 第" "$MEMORY_FILE" 2>/dev/null || echo "0") # The unvalidated value is evaluated as a Bash arithmetic expression. if [[ "$TOTAL_CHAPTERS" -le "$KEEP_CHAPTERS" ]]; then echo " Chapter count does not exceed the threshold; no compression is needed" exit 0 fi ``` The same untrusted value is subsequently used in further arithmetic contexts: ```bash CHAPTER_START=$((TOTAL_CHAPTERS - KEEP_CHAPTERS + 1)) ``` ### Technical Analysis The `--keep-chapters` argument is assigned directly to `KEEP_CHAPTERS` without validating that it is a positive decimal integer. Bash numeric comparisons and arithmetic expansions do not treat variable contents as inert strings. Instead, values can be recursively interpreted as arithmetic expressions. Arithmetic expressions can reference array subscripts, and those subscripts may contain command substitutions. Consequently, a crafted argument such as an expression structurally equivalent to: ```bash 'x[$(ATTACKER_COMMAND)0]' ``` can cause `ATTACKER_COMMAND` to run when Bash evaluates `KEEP_CHAPTERS` in the `-le` comparison or the later `$((...))` expression. Quoting the variable i ...[truncated 1766 chars]
Remediation
View remediation
&2 exit 2 fi KEEP_CHAPTERS=$2 shift 2 ;; --) shift break ;; -*) printf 'Error: unknown option: %s\n' "$1" >&2 exit 2 ;; *) if [[ -n "$MEMORY_FILE" ]]; then printf '%s\n' "Error: multiple memory files were supplied." >&2 exit 2 fi MEMORY_FILE=$1 shift ;; esac done ``` Additional hardening measures: 1. Enforce a reasonable upper bound, such as `1–10000`, to prevent pathological input. 2. Reject signed numbers, whitespace, arithmetic operators, variable names, brackets, and command-substitution syntax. 3. Validate `TOTAL_CHAPTERS` before using it in arithmetic, even though it currently originates from `grep -c`. 4. Use `printf` rather than `echo` for diagnostics containing variable data. 5. Add regression tests proving rejection of missing values, negative values, nonnumeric input, arithmetic expressions, array-subscript expressions, and command-substitution payloads. 6. Run the script with the least filesystem and network privileges required for compression. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill clearly instructs the assistant to read and write local files such as memory/novels/{小说名}.md and backup chapters under novels/{小说名}/..., but it does not declare any explicit tool scope or permissions boundaries. This creates a real security issue because an agent may perform filesystem actions without transparent least-privilege constraints, increasing the risk of unintended file access, overwrites, or abuse if the skill is triggered in the wrong context.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list includes very broad, common terms like 小说, 章节, 续写, 世界观, and 时间线, which are likely to appear in ordinary conversation. Because this skill can read and write local files, overbroad invocation increases the chance that it activates unintentionally and performs stateful filesystem actions on the wrong project or without clear user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The Git synchronization instructions include git remote add origin <repo-url>, git push origin main, and git pull origin main but do not warn that novel content, character notes, collaboration metadata, or other local files may be transmitted to an external repository. This is dangerous because users may unknowingly exfiltrate private or unpublished creative material to third-party services, especially in collaborative or remote-hosted environments.

Content

No source excerpt is available for this finding.

Tainted flow: 'new_content' from pathlib.Path.read_text (line 184, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/compress_novel_memory.py (reported line 199)May include surrounding context.

python
print(f"  原文件备份:{backup_path}")
    
    # 写入新文件
    path.write_text(new_content, encoding='utf-8')
    print(f"  新文件已保存:{path}")
    
    return True

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The module docstring, usage text, and user-facing messages are written only in Chinese, indicating the skill is intended to operate in a single language. The policy requires avoiding forced language or locale constraints unless the user is given a choice or the limitation is clearly justified as region-specific.

Content

No source excerpt is available for this finding.