Back to skill

Security audit

Neckr0ik Session Healer

Security checks for vulnerabilities and agentic risk

Overview

This skill is a plausible OpenClaw session repair tool, but it can remove active session locks or rewrite session files with weak safeguards.

Install only if you are comfortable using a local repair tool that can delete OpenClaw lock files and modify session JSONL files. Prefer dry-run first, stop active OpenClaw processes before unlocking or healing, use exact session IDs, and keep backups before running recovery.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/healer.py:105
Finding

Malformed or Empty Lock Files Are Incorrectly Classified as Stale

Content
View full analysis

Vulnerability Details

File Location: scripts/healer.py, lines 105-119
Vulnerability Type: Fail-open lock ownership validation
Risk Level: Medium

Vulnerable Code

python
# If no PID in file, try to get from filename pattern or stat
if pid is None:
    # Use stat to get creation time
    stat = lock_path.stat()
    mtime = stat.st_mtime
    age_seconds = time.time() - mtime

    # Create lock info without PID
    return LockInfo(
        lock_path=lock_path,
        session_path=session_path,
        session_id=session_id,
        pid=0,
        is_alive=False,  # Assume stale if we can't find PID
        age_seconds=age_seconds,
        agent_name=agent_name
    )

Technical Analysis

When a lock file is empty, malformed, partially written, or does not contain a parseable PID, the implementation assigns pid=0 and unconditionally sets is_alive=False. The heal operation treats this value as proof that the lock is stale and can delete it.

Failure to obtain ownership information is not equivalent to proving that the owner is dead. Lock metadata may be temporarily incomplete while an active process creates or updates the file. Consequently, the implementation fails open during an uncertain state.

This behavior also conflicts with the documented safety guarantee that locks belonging to active processes are not removed unless the user supplies --force.

Attack Path

  1. An active OpenClaw process creates a lock file that is empty, partially written, or contains metadata without a parseable PID.
  2. A user or automated task invokes healer.py heal.
  3. _parse_lock cannot extract the PID and sets is_alive=False.
  4. heal classifies the lock as stale and deletes it without requiring --force.
  5. Another process may enter the session while the original process still considers itself the owner.
  6. Concurrent access can corrupt session data or cause inconsisten ...[truncated 392 chars]
Remediation
View remediation

Remediation Suggestions

  • Represent missing or invalid ownership information as an explicit UNKNOWN state rather than is_alive=False.
  • Never remove an unknown-owner lock during normal heal execution.
  • Require an explicit --force option to remove locks whose ownership cannot be verified.
  • Consider requiring both an age threshold and unavailable ownership before presenting such a lock as potentially stale.
  • Re-read and validate the lock immediately before deletion to reduce time-of-check/time-of-use exposure.
  • Use a lock protocol that writes ownership metadata atomically, such as writing to a temporary file and then using an atomic rename.
  • Clearly report unknown ownership to the user instead of labeling the process as dead.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/healer.py:224
Finding

The Unlock Command Unconditionally Deletes Locks Owned by Active Processes

Content
View full analysis

Vulnerability Details

File Location: scripts/healer.py, lines 224-240
Vulnerability Type: Missing active-owner safety check
Risk Level: Medium

Vulnerable Code

python
def unlock(self, session_id: str, dry_run: bool = False) -> bool:
    """Unlock a specific session by ID."""

    locks = self.find_locks()

    for lock in locks:
        if lock.session_id == session_id or lock.session_id.startswith(session_id):
            if dry_run:
                print(f"[DRY-RUN] Would clear: {lock.lock_path}")
                return True

            try:
                lock.lock_path.unlink()
                print(f"[CLEARED] {lock.session_id}")
                self._log_heal(lock)
                return True
            except Exception as e:
                print(f"[ERROR] Failed to clear: {e}")
                return False

Technical Analysis

The general heal command checks lock.is_alive and skips locks owned by active processes unless force mode is enabled. The unlock command does not perform the equivalent check. Once a matching session is found, it directly calls unlink() regardless of whether the recorded owner is still running.

The unlock CLI also does not expose a --force option, so there is no distinction between safe stale-lock removal and intentional removal of a live lock. This contradicts the documented statement that live-process locks are never cleared unless force is requested.

Attack Path

  1. An OpenClaw process holds an active session lock.
  2. A local user or automation invokes healer.py unlock with that session ID or a matching prefix.
  3. find_locks identifies that the owning process is alive.
  4. unlock ignores the is_alive result.
  5. The command deletes the active lock file.
  6. Another process can subsequently open the same session for writing while the original owner remains active.

Impact Assessment

The command opera ...[truncated 342 chars]

Remediation
View remediation

Remediation Suggestions

  • Check lock.is_alive before deleting the selected lock.
  • Refuse to remove a lock owned by a live process under normal operation.
  • Add an explicit --force option to unlock if live-lock removal is required for exceptional recovery.
  • Display a prominent warning and require deliberate confirmation before force-removing an active lock.
  • Revalidate the lock owner immediately before unlink() to reduce race conditions.
  • Return a nonzero process exit status when deletion is refused or fails so automation can detect the unsafe condition.
  • Add tests confirming that unlock cannot remove active locks without an explicit force request.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/healer.py:227
Finding

Ambiguous Session Identifier Matching Can Modify or Unlock the Wrong Session

Content
View full analysis

Vulnerability Details

File Location: scripts/healer.py, lines 227-269
Vulnerability Type: Improper input validation and ambiguous resource selection
Risk Level: Medium

Vulnerable Code

python
locks = self.find_locks()

for lock in locks:
    if lock.session_id == session_id or lock.session_id.startswith(session_id):
        if dry_run:
            print(f"[DRY-RUN] Would clear: {lock.lock_path}")
            return True

        try:
            lock.lock_path.unlink()
            print(f"[CLEARED] {lock.session_id}")
            self._log_heal(lock)
            return True
        except Exception as e:
            print(f"[ERROR] Failed to clear: {e}")
            return False

The recovery path uses similarly ambiguous prefix and substring matching:

python
locks = self.find_locks()
session_path = None

for lock in locks:
    if lock.session_id == session_id or lock.session_id.startswith(session_id):
        session_path = lock.session_path
        break

if not session_path:
    # Try to find session without lock
    for agent_dir in self.agents_dir.iterdir():
        if not agent_dir.is_dir():
            continue
        sessions_dir = agent_dir / "sessions"
        if not sessions_dir.exists():
            continue

        for session_file in sessions_dir.glob("*.jsonl"):
            if session_id in session_file.name:
                session_path = session_file
                break

Technical Analysis

Both unlock and recover accept non-exact identifiers. The unlock path accepts any prefix through startswith, while the unlocked-session recovery path accepts any substring appearing in a filename. Neither path verifies that the supplied identifier is non-empty, canonical, or uniquely identifies one session.

The code acts on the first match returned by filesystem traversal. Filesystem iteration order is not a security boun ...[truncated 1513 chars]

Remediation
View remediation

Remediation Suggestions

  • Require exact equality with a canonical session identifier for destructive operations.
  • Validate the identifier format, reject empty values, and enforce the expected UUID or session-ID syntax.
  • If abbreviated identifiers are intentionally supported, enforce a safe minimum length and collect every match before taking action.
  • Proceed only when exactly one session matches; otherwise list the candidates and require an exact identifier.
  • Do not use unrestricted substring matching against filenames.
  • Resolve and verify that the selected path remains inside an expected agent sessions directory before modification.
  • Show the full selected session path and require confirmation before recovery.
  • Add tests for empty identifiers, duplicate prefixes, substring collisions, and sessions located under different agents.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (8)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill documents capabilities that read and modify session-related files but does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, missing scope declarations can cause the skill to run with broader file access than users expect, increasing the chance of unintended file reads/writes or unsafe automation.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 29)May include surrounding context.

md
The "session file locked" error occurs when:
- OpenClaw crashes while writing to a session
- Multiple processes try to access the same session
- Network timeout during session write
- System crash leaves stale lock files

**Symptoms:**

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The heal and unlock commands perform destructive lock removal, and --force can clear locks even for live processes, but the documentation does not present a prominent warning about session corruption, concurrent access hazards, or service disruption. Users may run these commands during active sessions and cause data loss or break in-progress writes.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The heal --force path intentionally deletes lock files even when the recorded owning process is still alive. This defeats concurrency protections and can enable simultaneous writes to the same session file, causing corruption, state desynchronization, or interference with a running agent.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The unlock command deletes a matching lock file solely by session ID match and does not verify whether the owning process is still alive. In context, the tool is presented as a stale-lock healer, so this behavior is riskier because operators may assume it is safe and use it against active sessions, bypassing the application's locking model.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The unlock operation removes the lock file immediately and gives no explicit warning that it may be overriding a live lock. This increases the chance of dangerous operator error, especially because the skill branding suggests safe healing of stale locks rather than destructive lock bypass.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill goes beyond stale-lock cleanup and performs in-place session content recovery by filtering and rewriting JSONL files. Even with a backup, this can silently remove data, alter forensic history, or corrupt active application state if used on the wrong file or while another process is interacting with it.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The recover operation rewrites the original session file in place after dropping lines that fail JSON parsing, without an explicit warning that user data will be modified. Although a backup is created, in-place rewriting can still surprise users, discard partially recoverable content, and damage evidence or session continuity if run casually.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.