T09 · Insecure Skill Coding Practices
- Location
index.js:5- Finding
State File Path Escapes the Skill Directory
- Content
View full analysis
Vulnerability Details
File Location:
index.js, lines 5-8 and 31-33
Vulnerability Type: Filesystem boundary violation caused by an incorrect state directory
Risk Level: MediumVulnerable Code
js const SKILL_DIR = path.join(__dirname, '..', 'claw-todolist'); const STATE_FILE = path.join(SKILL_DIR, 'task_state.json'); const RULES_FILE = path.join(SKILL_DIR, 'todo-rules-v3.2.md'); const DISPLAY_CONFIG_FILE = path.join(SKILL_DIR, 'display_config.json');js function saveState(state) { fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2)); }Technical Analysis
__dirnamealready identifies the directory containingindex.js. The expressionpath.join(__dirname, '..', 'claw-todolist')first moves to the parent directory and then selects a sibling directory namedclaw-todolist.As a result, the application reads and writes state using:
text <parent-of-current-skill>/claw-todolist/task_state.jsonrather than:
text <current-skill>/task_state.jsonThis behavior contradicts the persistence boundary declared in
SKILL.md, which states that persistence is confined to the Skill directory. The path is fixed rather than directly user-controlled, so the evidence does not establish an arbitrary-file-write vulnerability. Nevertheless, it permits the Skill to modify a file outside its own installation directory when a matching sibling directory exists.The same incorrect base directory is used for the rules and display configuration files. This can cause the application to consume configuration from a different installation or to silently use fallback values when the sibling files do not exist.
Attack Path
- The Skill is installed in a directory whose name or location differs from the hardcoded sibling path.
- A directory named
claw-todolistexists under the current Skill directory's parent, potentially belonging to another installatio ...[truncated 1422 chars]
- Remediation
View remediation
Remediation Suggestions
-
Use the actual module directory as the Skill directory:
js const SKILL_DIR = __dirname; const STATE_FILE = path.join(SKILL_DIR, 'task_state.json'); const RULES_FILE = path.join(SKILL_DIR, 'todo-rules-v3.2.md'); const DISPLAY_CONFIG_FILE = path.join(SKILL_DIR, 'display_config.json'); -
Before filesystem access, resolve and verify that every path remains beneath the approved base directory:
js const SKILL_DIR = path.resolve(__dirname); function resolveWithinSkill(fileName) { const resolved = path.resolve(SKILL_DIR, fileName); if ( resolved !== SKILL_DIR && !resolved.startsWith(SKILL_DIR + path.sep) ) { throw new Error('Resolved path escapes the Skill directory'); } return resolved; } const STATE_FILE = resolveWithinSkill('task_state.json'); -
If installed package directories are intended to be read-only, store mutable state in an explicitly approved per-user application data directory rather than beside the source code. Keep rules and display configuration read-only inside the package.
-
Use atomic state updates by writing to a securely created temporary file in the same approved directory and then renaming it over the state file. This reduces corruption from interrupted writes.
-
Add tests that install or execute the Skill under arbitrary directory names and assert that all read and write targets remain within the configured storage boundary.
-
Fail closed when path validation fails, and log the resolved destination without exposing task contents.
-
