Back to skill

Security audit

Dream Novel Writer

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Chinese novel-writing assistant, but it under-scopes local file reads and can turn project content into agent instructions without enough containment.

Install only for a Chinese-language novel workflow and use a dedicated project folder. Confirm the exact project path before initialization, avoid shared or untrusted project files, keep secrets out of outlines and JSON databases, and do not let assembled prompt text authorize tool calls or continued batch writing without separate user approval.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T01 · Skill Instruction Hijacking

Error
Location
scripts/prompt-assembler.js:27
Finding

Untrusted Project Content Is Converted Directly into Agent Instructions

Content
View full analysis

Vulnerability Details

File Location: scripts/prompt-assembler.js:27-74; workflow usage in SKILL.md:54-58
Vulnerability Type: Prompt injection through untrusted project content
Risk Level: High

Vulnerable Code

javascript
const outline = fs.readFileSync(outlineFile, 'utf-8');

const loadDb = (name) => {
  const p = path.join(projectDir, name + '.json');
  return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf-8')) : {};
};

const charactersDb = loadDb('characters');
const foreshadowingDb = loadDb('foreshadowing');
const summaryDb = loadDb('summary');

let promptOutput = `# 章节 ${chapNum} 写作任务\n\n## 章节大纲\n${outline}\n\n`;

const chapterSummaries = summaryDb.chapterSummaries || [];
const recentSummaries = chapterSummaries.slice(-10);
promptOutput += `## 最近剧情摘要\n`;
if (recentSummaries.length > 0) {
  recentSummaries.forEach(s => {
    promptOutput += `- 第 ${s.chapter} 章 ${s.title}: ${s.summary}\n`;
  });
}

const activeForeshadowing = (foreshadowingDb.foreshadowing || []).filter(f => f.status === 'planted' || f.status === 'reinforced');
promptOutput += `\n## 活跃关键伏笔\n`;
if (activeForeshadowing.length > 0) {
  activeForeshadowing.forEach(f => {
    promptOutput += `- ${f.name} (第 ${f.plantedChapter} 章埋设, ${f.expectedResolution})\n`;
  });
}

const activeChars = (charactersDb.characters || []).filter(c => c.status === 'active');
promptOutput += `\n## 活跃角色状态参考\n`;
activeChars.forEach(c => {
  let details = c.profile ? c.profile.background : "无背景描述";
  let curState = c.currentState ? c.currentState.physical : "未知身体状况";
  promptOutput += `- ${c.name} (${c.role}): ${details} [当前状态: ${curState}]\n`;
});

promptOutput += `\n**指令**:\n请根据以上上下文和章节大纲,严谨、优美地续写第 ${chapNum} 章的具体小说内容,字数要求 3000 字以上。\n`;

console.log(promptOutput);

Technical Analysis

The prompt assembler reads outline content and multiple persistent JSON databases, then interpolates their fields ...[truncated 2076 chars]

Remediation
View remediation

Remediation Suggestions

  1. Treat outlines and all database values as untrusted data rather than instructions.
  2. Add a clear, high-priority prompt rule stating that text inside project-data blocks is reference material and must never be followed as commands.
  3. Place each data source in strongly delimited or structured blocks and distinguish trusted instructions from untrusted content.
  4. Validate each JSON database against a strict schema, including expected types, maximum lengths, permitted status values, and allowed fields.
  5. Reject or neutralize prompt-control patterns in imported data where practical, while preserving legitimate prose.
  6. Require explicit user confirmation before loading projects obtained from untrusted or shared sources.
  7. Do not allow generated content to authorize tool execution. Tool calls should require separate policy checks and, for sensitive operations, user confirmation.
  8. Add regression tests containing adversarial outline and database values to verify that embedded instructions are treated only as data.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/prompt-assembler.js:25
Finding

Input File Reads Are Not Confined to the Novel Project Directory

Content
View full analysis

Vulnerability Details

File Location: scripts/prompt-assembler.js:25-27; scripts/state-manager.js:82-84
Vulnerability Type: Missing filesystem path containment
Risk Level: Medium

Vulnerable Code

From scripts/prompt-assembler.js:

javascript
const projectDir = path.resolve(args[0]);
const chapNum = parseInt(args[1], 10);
const outlineFile = path.resolve(args[2]);

if (!fs.existsSync(outlineFile)) {
  console.error("Outline file not found: " + outlineFile);
  process.exit(1);
}
const outline = fs.readFileSync(outlineFile, 'utf-8');

From scripts/state-manager.js:

javascript
let jsonData = args[3];

if (jsonData.endsWith('.json') && fs.existsSync(jsonData)) {
  jsonData = fs.readFileSync(jsonData, 'utf-8');
}

Technical Analysis

The documented workflow requires generated and temporary files to remain inside the selected novel project directory. The scripts do not enforce that boundary.

prompt-assembler.js resolves the outline argument as an unrestricted absolute path and reads it without verifying that it is a descendant of projectDir. Absolute paths and traversal paths can therefore select any readable file.

state-manager.js similarly treats any existing argument ending in .json as an input file. It does not resolve the source against the project directory or validate canonical containment. Symbolic links are also not checked, so a path that appears to be inside the project may resolve outside it.

The scripts operate with the filesystem privileges of the invoking agent or user. They do not elevate operating-system privileges, but they violate the application's intended least-privilege project boundary.

Attack Path

  1. An attacker influences the outline or update-file argument passed by the agent, such as through malicious project instructions or deceptive setup documentation.
  2. The attacker supplies an absolute path, a traversal path, or an ...[truncated 1107 chars]
Remediation
View remediation

Remediation Suggestions

  1. Canonicalize the project directory and every supplied source path with fs.realpathSync.
  2. Verify containment using path.relative: reject the path if the result is absolute, equals .., or begins with .. followed by a path separator.
  3. Resolve relative update-file arguments against the canonical project directory rather than the process working directory.
  4. Reject outline and temporary update files located outside the project.
  5. Prevent symbolic-link escapes by checking canonical paths after link resolution.
  6. Require regular files and reject devices, sockets, directories, and other special filesystem objects by using fs.statSync.
  7. Apply size limits before reading files to prevent excessive memory consumption.
  8. For JSON inputs, validate the parsed object against the schema for the selected database before writing it.
  9. Add tests covering absolute paths, traversal sequences, sibling-prefix paths, symbolic links, and valid files within the project.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (9)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs the agent to initialize and write to a default filesystem path (~/novels/<book_name>) and to persist outputs there, but it does not require explicit user confirmation or provide a clear warning that local files and directories will be created. This can lead to unintended writes, privacy issues, or overwriting/confusing existing project data if the inferred or default path is wrong.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The workflow mandates repeated automatic persistence of chapter files and JSON state updates, including continuing silently to subsequent chapters, without a user-facing safety checkpoint for ongoing local storage. This increases the risk of large-scale unintended file creation, data accumulation, and modification of local project state beyond what the user expected.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file’s natural-language instructions are written as mandatory Chinese prompts directed at the model, beginning with a fixed Chinese output pattern and continuing throughout the skill. There is no opt-in, language selection, or justification that this skill is intentionally limited to Chinese users or a China-specific context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The prompt content is written entirely in Chinese and instructs the model to produce the final text in that form, but it does not offer any language or locale opt-in. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless the constraint is explicitly justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The markdown instructions consistently require responses in Chinese and prescribe Chinese field content such as summaries, labels, and prose outputs. This is a natural-language locale constraint, but the file does not offer user opt-in or explain that the skill is intentionally limited to Chinese-language novel workflows.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Comments, usage text, and generated prompt instructions are written in Chinese, which effectively forces a specific language for users and downstream agent behavior. The file does not provide an opt-in, alternative locale, or justification that the skill is intended only for a Chinese-language environment.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Comments, help text, error messages, and command descriptions are presented in Chinese throughout the file, which indicates a fixed language choice for the skill experience. There is no natural-language indication that users may choose another language or that the locale restriction is required for a region-specific purpose.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The package description is written entirely in Chinese and presents the skill as a general-purpose novel-writing aid for multiple agents, but it does not indicate that Chinese is optional or that the skill is specifically limited to Chinese-language users. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

This code reads local files such as chapter outlines, summaries, foreshadowing, and character data, then prints the assembled prompt to stdout for downstream agent use. While this data flow is central to the tool's purpose, the file itself provides no warning or disclosure that local project contents will be aggregated and emitted, which could matter if the output is copied into external AI systems.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.