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<![CDATA[ ## Vulnerability Details **File Location**: `scripts/compress_novel_memory.sh`, lines 19–46; additional arithmetic use at lines 75, 109, 113, 130, and 134 **Vulnerability Type**: Bash arithmetic-expression command injection **Risk Level**: High ### Vulnerable Code ```bash # Parse arguments MEMORY_FILE="" while [[ $# -gt 0 ]]; do case $1 in --keep-chapters) KEEP_CHAPTERS="$2" shift 2 ;; *) MEMORY_FILE="$1" shift ;; esac done # Check file if [[ -z "$MEMORY_FILE" ]] || [[ ! -f "$MEMORY_FILE" ]]; then echo "Usage: $0 <memory_file.md> [--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
<![CDATA[ ## Remediation Suggestions Validate the option before it reaches any numeric comparison or arithmetic expansion: ```bash while [[ $# -gt 0 ]]; do case "$1" in --keep-chapters) if [[ $# -lt 2 ]] || [[ ! "$2" =~ ^[1-9][0-9]*$ ]]; then printf '%s\n' "Error: --keep-chapters requires a positive decimal integer." >&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)

Lp3

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.

Vague Triggers

Medium
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.

Missing User Warnings

Medium
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.

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

Medium
Category
Data Flow
Content
print(f"  原文件备份:{backup_path}")
    
    # 写入新文件
    path.write_text(new_content, encoding='utf-8')
    print(f"  新文件已保存:{path}")
    
    return True
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.

Natural-Language Policy Violations

Low
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.

Static analysis

No suspicious patterns detected.