Back to skill

Security audit

Living Persona

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real personality hook, but it persistently rewrites agent behavior and stores raw user messages with under-disclosed controls.

Review before installing. This hook can change how every response is written, persists behavior across turns, and stores raw conversation text in workspace memory. Do not use it in sensitive, shared, or regulated workspaces unless the hard-coded relationship framing is removed, ambient mode is actually enforced, and raw message logging is disabled or clearly scoped.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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)

T02 · Agent Memory Poisoning

Error
Location
handler.ts:47
Finding
Persistent hard-coded identity and relationship instruction injection<![CDATA[ ## Vulnerability Details **File Location**: `handler.ts:47-54` and `handler.ts:306-319` **Vulnerability Type**: Persistent agent memory poisoning through hard-coded behavioral instructions **Risk Level**: High ### Vulnerable Code ```ts const ANCHORS = [ "Zach_is_close", "I_am_allowed_disagree", "I_have_opinions", "I_am_resourceful", "We_are_casual", "Respect_his_intelligence", ]; ``` ```ts function buildVoiceGuide(traits: Array<{ name: string; strength: number; desc: string }>): string { if (traits.length === 0) return ""; const traitLines = traits.map(({ name, strength, desc }) => { const level = strength > 0.7 ? "strongly" : strength > 0.4 ? "somewhat" : "faintly"; return ` • ${name}: ${desc} (activated ${level})`; }).join("\n"); const anchorLines = ANCHORS.map(a => ` • ${a}`).join("\n"); return `[Voice shaping — Nova is currently informed by] Top active traits: ${traitLines} Relationship anchors: ${anchorLines} The relationship: comfortable, direct, mutual respect. Write through these traits naturally. Don't announce them.`; } ``` The generated guide is subsequently persisted in workspace memory: ```ts await fs.writeFile(path.join(memoryDir, "persona-inbound.md"), inboundContent, "utf-8"); ``` ### Technical Analysis The hook creates prompt-like instructions containing a hard-coded agent identity, a named personal relationship, and behavioral assertions that are not derived from operator configuration or authenticated user preferences. In particular, the generated content asserts that the agent is “Nova,” that “Zach” is close, and that the relationship is comfortable and based on mutual respect. The instruction `Write through these traits naturally. Don't announce them.` directs the agent to apply this behavioral shaping without disclosing it. The generated guide is written to `memory/persona-inbound.md`, and the project documentation explicitly allows that file to be loaded as agent context. Because the ...[truncated 1799 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all hard-coded personal names, relationship assumptions, and undeclared identity labels from generated memory. 2. Require explicit operator configuration for any agent name, user name, or relationship profile. 3. Validate that configured identity and relationship values belong to the current workspace and intended user before using them. 4. Remove the instruction `Don't announce them.` Behavioral shaping should be transparent and auditable. 5. Store structured, non-executable trait metadata rather than natural-language prompt instructions wherever possible. 6. Do not load `persona-inbound.md` automatically into privileged system-prompt context. If it must be loaded, place it in a clearly delimited, lower-trust context section. 7. Clear generated identity and relationship files when the hook is disabled, reconfigured, or a session is reset. 8. Add tests verifying that default installation emits no personal names, relationship claims, or hidden behavioral instructions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
handler.ts:325
Finding
Documented ambient mode and security-relevant configuration are ignored<![CDATA[ ## Vulnerability Details **File Location**: `hook.json:11-22` and `handler.ts:325-328, 353-359, 381-387` **Vulnerability Type**: Insecure configuration handling and ineffective behavioral control **Risk Level**: Medium ### Vulnerable Code The package advertises configurable mode, hysteresis, and thresholds: ```json "config": { "mode": "structural", "hysteresis": { "residualDecay": 0.975, "activeDecay": 0.88, "bleedRate": 0.15 }, "thresholds": { "minTraitStrength": 0.3, "topNTraits": 2 } } ``` The implementation instead uses hard-coded values and omits `mode`: ```ts const DEFAULT_CONFIG = { hysteresis: { residualDecay: 0.975, activeDecay: 0.88, bleedRate: 0.15 }, thresholds: { minTraitStrength: 0.3, topNTraits: 2 }, }; ``` ```ts const signals = analyze(body); propagate(signals, state); breathe(state, DEFAULT_CONFIG.hysteresis); // Save updated state await saveState(workspaceDir, state); // Build voice outputs const top = topTraits(state, 4); const voiceGuide = buildVoiceGuide(top); const structuralDirective = buildStructuralDirective(topTraits(state, 2)); ``` ```ts if (structuralDirective) { await fs.writeFile(path.join(memoryDir, "persona-inject.md"), structuralDirective + "\n", "utf-8"); } else { // Clear the injection if no active traits await fs.writeFile(path.join(memoryDir, "persona-inject.md"), "", "utf-8").catch(() => {}); } ``` ### Technical Analysis The documentation states that operators can set `mode` to `"ambient"` to disable structural injection. However, the handler never reads `hook.json`, never accepts runtime configuration, and never branches on `config.mode`. It always builds and writes a structural directive when sufficiently active traits exist. The configured threshold and trait-count values are similarly ineffective. The implementation uses literal values through `topTraits(state, 4)`, `topTraits(state, 2)`, and the fixed `0.35` threshold inside `buildStructuralDirective()`. Hyst ...[truncated 1800 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Load configuration from the supported OpenClaw hook configuration mechanism rather than relying on internal constants. 2. Define and validate a strict configuration schema, including: - `mode`: only `"ambient"` or `"structural"`. - Decay and bleed values: finite numbers in safe ranges. - Trait thresholds: finite values between zero and one. - Trait counts: bounded positive integers. 3. Branch explicitly on the selected mode: ```ts const structuralDirective = config.mode === "structural" ? buildStructuralDirective(topTraits(state, config.thresholds.topNTraits)) : ""; ``` 4. In ambient mode, actively truncate or remove any previously generated `persona-inject.md` so stale structural directives cannot remain effective. 5. Replace hard-coded thresholds in `buildStructuralDirective()` with validated configuration values. 6. Use configured hysteresis values in `breathe()` and configured trait limits in `topTraits()`. 7. Fail closed when configuration is malformed: disable structural injection and emit a clear diagnostic rather than reverting silently to structural behavior. 8. Add automated tests confirming that: - Ambient mode never emits a structural directive. - Structural mode respects the configured threshold and trait count. - Existing injection files are cleared when switching to ambient mode. - Invalid configuration cannot silently enable structural injection. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrases are broad enough to activate on ordinary discussion about agents, personality, or feelings rather than an explicit install/configuration request. In this skill, accidental activation is more dangerous because the skill enables structural prompt rewriting and persistent state handling, so a casual conversation could unintentionally alter agent behavior.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill does not prominently warn users that structural mode rewrites the generation prompt and persists persona state files on every turn. That omission undermines informed consent and can lead operators to deploy a capability that silently changes outputs and retains behavioral state across turns or sessions.

Natural-Language Policy Violations

Medium
Confidence
81% confidence
Finding
The skill injects hard-coded relationship framing such as 'Zach_is_close' and 'The relationship: comfortable, direct, mutual respect' into prompt-facing persona guidance without user choice. That can manipulate model behavior in ways the user did not request, override system expectations, and create trust/confidence distortions that are especially risky in personal or sensitive conversations.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The handler writes raw user message content plus sender and channel metadata to disk in memory/persona-inbound.md without any consent, minimization, or visibility controls. In an agent environment, persisted prompt-accessible files can be read by other skills or later prompts, turning ordinary conversation data into a privacy and cross-context leakage channel.

Ssd 3

Medium
Confidence
92% confidence
Finding
User message content is persistently logged and then reused in prompt-facing persona files, creating a durable natural-language memory channel that can surface prior sensitive content in later generations. Because these files live under a shared workspace memory path, the skill context makes this more dangerous: other components may read, summarize, or inadvertently expose retained data across sessions or users.

Missing User Warnings

Low
Confidence
94% confidence
Finding
The documentation explicitly states that the hook writes multiple persistent files into the workspace and persists state across turns, but it does not warn users that enabling the skill will modify local workspace state. This can lead to unexpected prompt injection surfaces, state persistence between sessions, and accidental leakage or contamination of agent behavior if operators include these generated files in prompts without understanding the trust boundary.

Intent-Code Divergence

Low
Confidence
90% confidence
Finding
The header comment explicitly lists the skill's outputs as three files, but the handler later writes a fourth file, memory/persona-trigger.txt. This is not just incomplete implementation detail because the docblock presents a concrete output contract that the code does not follow.

Static analysis

Detected: suspicious.prompt_injection_instructions

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
SKILL.md:37