Back to skill

Security audit

Wip File Guard

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed file-protection hook, but its broad path exceptions and fail-open behavior mean it needs review before installing.

Before installing, treat this as a useful but imperfect guardrail. It will persist as a file-edit hook and can block agent writes, but it should be fixed to canonicalize paths, fail closed for malformed protected-file events, and align tests and documentation with the intended policy.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
guard.mjs:45
Finding
Protected-file overwrite through non-canonical shared-state paths<![CDATA[ ## Vulnerability Details **File Location**: `guard.mjs:45-60, 119-123` **Vulnerability Type**: Path traversal and unsafe path allowlisting **Risk Level**: Medium ### Vulnerable Code ```js const SHARED_STATE_PATHS = [ /\.openclaw\/workspace\//, // OpenClaw agent workspace (live shared state, not code) /workspace\/memory\/\d{4}-\d{2}-\d{2}\.md$/, /\.ldm\/agents\/.*\/memory\/daily\/.*\.md$/, /\.ldm\/memory\/daily\/.*\.md$/, /\.ldm\/memory\/shared-log\.jsonl$/, /\.claude\/projects\/.*\/memory\/.*\.md$/, // harness auto-memory files /\.claude\/memory\/.*\.md$/, // harness global memory files ]; function isSharedState(filePath) { const name = basename(filePath); if (SHARED_STATE_FILES.has(name)) return true; return SHARED_STATE_PATHS.some(p => p.test(filePath)); } ``` ```js if (toolName === 'Write') { // Path-based shared state gets Write access (workspace files, harness memory). // Checked before exact-match so workspace TOOLS.md/MEMORY.md are writable. // Name-based shared state (SHARED_STATE_FILES) still goes through exact-match // to prevent accidental overwrites of SHARED-CONTEXT.md outside known paths. if (SHARED_STATE_PATHS.some(p => p.test(filePath))) { process.exit(0); } ``` ### Technical Analysis The hook applies regular-expression allowlists directly to the untrusted path string supplied in `tool_input.file_path`. It does not first normalize the path with `path.resolve()`, canonicalize existing paths with `realpathSync()`, or verify that the resulting target remains inside an authorized shared-state directory. The shared-state check occurs before exact protected-filename enforcement. Consequently, any raw path containing a permitted substring such as `.openclaw/workspace/` is allowed, even when `..` components cause the filesystem operation to resolve outside that directory. Similar discrepancies can arise from symbolic links placed inside an allowed directory. A path ...[truncated 1395 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Normalize every supplied path with `path.resolve()` before applying protection or allowlist rules. 2. For existing files, use `realpathSync()` to resolve symbolic links and compare the canonical target. 3. Define shared-state locations as canonical directory roots rather than substring regular expressions. 4. Verify containment with a directory-boundary-safe check, for example by using `path.relative()` and rejecting results that equal `..`, begin with `../`, or are absolute. 5. Perform exact protected-filename checks against the canonical target before granting any shared-state exception. 6. For new files, canonicalize the nearest existing parent directory and ensure it remains beneath an authorized root. 7. Add tests covering: - `..` traversal out of every shared-state directory; - repeated and mixed path separators; - symbolic links pointing outside shared-state roots; - protected filenames reached through allowed-path substrings; - platform-specific Windows paths where supported. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
guard.mjs:99
Finding
File-protection hook fails open on malformed input and runtime errors<![CDATA[ ## Vulnerability Details **File Location**: `guard.mjs:99-105, 181-183` **Vulnerability Type**: Fail-open authorization behavior **Risk Level**: Low ### Vulnerable Code ```js let input; try { input = JSON.parse(raw); } catch { // Can't parse input, allow by default process.exit(0); } ``` ```js // Allow process.exit(0); } main().catch(() => process.exit(0)); ``` ### Technical Analysis The hook treats malformed JSON and every unhandled exception as authorization to continue. It exits successfully without returning a structured denial response or diagnostic information. For a security enforcement hook, an inability to parse or evaluate a file-modification event should not silently produce the same result as an approved operation. The broad terminal `catch` is especially problematic because programming errors, unexpected data types, resource failures, and integration defects all disable enforcement. The code also lacks explicit schema validation. Parsed values such as `tool_input`, `file_path`, and operation-specific fields are assumed to have suitable types, so unexpected input can potentially cause an exception that reaches the fail-open handler. ### Attack Path 1. The hook receives malformed or truncated JSON due to an integration error, input corruption, or a caller capable of controlling the hook payload. 2. Alternatively, a valid JSON payload supplies unexpected value types that trigger an unhandled runtime error. 3. `JSON.parse()` failure or the terminal `main().catch()` handler calls `process.exit(0)`. 4. No deny decision is emitted. 5. If the host interprets this successful, empty response as permission to continue, the requested `Write` or `Edit` operation proceeds without protection. ### Impact Assessment During a parsing or runtime failure, destructive writes or edits to protected files may proceed unchecked. This can compromise the integrity of identity files, behavioral instructions, context, and persistent memory. The is ...[truncated 214 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed when an event that may represent `Write` or `Edit` cannot be parsed or validated. 2. Return a structured `permissionDecision: "deny"` response explaining that the hook could not safely evaluate the request. 3. Replace the broad silent terminal catch with explicit error handling and sanitized diagnostics written to stderr. 4. Validate the complete input schema before use, including: - `tool_name` must be a string; - `tool_input` must be a non-null object; - `file_path` or `filePath` must be a non-empty string; - `old_string` and `new_string` must be strings for `Edit`. 5. Place a reasonable upper bound on stdin size to avoid resource exhaustion. 6. If host compatibility requires allowing unknown non-file events, distinguish those events from malformed `Write` and `Edit` payloads rather than allowing every error. 7. Add regression tests for malformed JSON, truncated input, null values, arrays, incorrect field types, oversized input, and deliberately triggered exceptions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Agent Config Directory Access

High
Category
Agent Snooping
Content
## Claude Code

Add to `~/.claude/settings.json`:

```json
{
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Agent Config Directory Access

High
Category
Agent Snooping
Content
## Claude Code

Add to `~/.claude/settings.json`:

```json
{
Confidence
90% confidence
Finding
Skill reads from agent configuration directories (.claude/, .codex/, .gemini/). These directories may contain API keys, personal settings, and other credentials that the skill has no legitimate need to access.

Session Persistence

Medium
Category
Rogue Agent
Content
# File Guard

PreToolUse hook that blocks destructive edits to protected files. When an AI agent tries to overwrite or strip content from files like CLAUDE.md, SHARED-CONTEXT.md, or SOUL.md... it gets blocked with a clear explanation of what went wrong.

## The Problem
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
wip-file-guard tests
===================

PASS: Block Write to CLAUDE.md
PASS: Block Write to SHARED-CONTEXT.md
PASS: Allow Write to random file
PASS: Block Edit removing 5 lines from CLAUDE.md
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
Install to your LDM OS home:

```bash
mkdir -p ~/.ldm/extensions/wip-file-guard
cp guard.mjs openclaw.plugin.json package.json ~/.ldm/extensions/wip-file-guard/
```
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
Two rules:

1. **Write is blocked** on protected files. Always. Use Edit instead.
2. **Edit is blocked** when it removes more than 2 net lines from a protected file.

### Protected Files
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Intent-Code Divergence

Low
Confidence
96% confidence
Finding
The test case documentation says an existing pattern-matched file write should be blocked, but the assertion expects allow instead. This can mask regressions in the guard logic by causing CI or manual testing to approve behavior that contradicts the stated security policy, weakening protection for pattern-matched files.

Static analysis

No suspicious patterns detected.