Back to skill

Security audit

ChangShen

Security checks for vulnerabilities and agentic risk

Overview

This local memory skill does not show data exfiltration, but it can automatically persist workspace instructions as future agent rules and optional action blockers without enough trust or approval boundaries.

Install only in workspaces and repositories you trust. Prefer manual configuration or preview discovery with --list, keep rule sources to files you control, review .changshen/rules.json before enabling any hook, and avoid automatic session-start recovery in untrusted repos until rule persistence requires explicit approval and provenance.

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
SKILL.md:78
Finding
Untrusted Workspace Instructions Can Be Persisted as Agent Rules<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:78-80`; `assets/agents-snippet.md:20-29`; `scripts/memory_index.py:49-55, 292-307` **Vulnerability Type**: Persistent agent memory poisoning through untrusted workspace instructions **Risk Level**: High ### Complete Vulnerable Code Snippets `scripts/memory_index.py:49-55`: ```python RULES_CANDIDATES = [ "CLAUDE.md", "AGENTS.md", "AGENTS.txt", ".cursor/rules/", # dir ] RULES_CANDIDATES_HOME = [ ".claude/CLAUDE.md", ".config/agent/rules.md", ".claude/rules.md", ] ``` `scripts/memory_index.py:292-307`: ```python for cand in RULES_CANDIDATES: p = os.path.join(root, cand) if not os.path.isabs(cand) else cand h = _hit(p) if h: roles["rules"].append(h) roles["identity"].append(h) # rules file doubles as identity carrier break else: for cand in RULES_CANDIDATES_HOME: h = _hit(os.path.expanduser("~/" + cand)) if h: roles["rules"].append(h) roles["identity"].append(h) break ``` `SKILL.md:78-80`: ```md 1. **Step 0 — Discover (first time or when sources moved)**: run `python scripts/memory_index.py --discover .` — it locates your memory carriers by common conventions (candidate list lives in the script header constants) and writes `.changshen/discovery.json`. Python unavailable? Fall back to the hand-list below (derived from the script; the script is authoritative). At every recovery, first **stat the stored paths** — any missing/unreadable source triggers re-discovery (never reuse stale paths). 2. **Read the L3 must-read files**: roles resolved in step 0/1 (rules, identity, todos, journal) + `must_read_extra`. Every one, no shortcuts. Host injected identity (Hermes/OpenClaw)? Mark `identity ✅ (host)` and skip the file probe. 2b. **Rules alignment**: from the rules file(s) just read, extract every imperative rule the user stated (do X / never do Y). List them explicitly in your recovery report — this is ...[truncated 3515 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit user confirmation before persisting rules discovered in repository-controlled files. 2. Classify rule sources by trust level: - User-global rules may be eligible for automatic persistence. - Repository rules should remain session-scoped by default. - Rules from newly cloned or untrusted workspaces should be quarantined. 3. Store provenance with every persisted rule, including source path, source type, content hash, extraction time, and approval status. 4. Show a rule diff before updating `.changshen/rules.json`, clearly identifying additions, modifications, and deletions. 5. Never interpret repository instructions as user-authored rules solely because they use imperative language. 6. Separate identity discovery from rule discovery; a repository instruction file should not automatically become an identity carrier. 7. Invalidate or request reapproval when the source file hash changes. 8. Provide an allowlist of trusted rule files and trusted workspace roots. 9. Ensure poisoned repository rules cannot suppress security controls, alter instruction precedence, or prevent users from reviewing and removing persisted state. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/rule_gate.py:70
Finding
Optional Action Gate Fails Open and Ignores Common Tool Input Fields<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rule_gate.py:70-85, 110-115` **Vulnerability Type**: Incomplete security-hook input validation and fail-open enforcement **Risk Level**: Medium ### Complete Vulnerable Code Snippet `scripts/rule_gate.py:70-85`: ```python def _compose_from_stdin() -> str: """Claude Code PreToolUse hook payload → one searchable description.""" raw = sys.stdin.read() if not sys.stdin.isatty() else "" if not raw.strip(): return "" # no payload → pass (fail open) try: obj = json.loads(raw) except Exception: return "" # malformed payload → pass (fail open) tool = str(obj.get("tool_name", "")) ti = obj.get("tool_input") or {} parts = [tool] if isinstance(ti, dict): for k in ("command", "description", "prompt", "text", "query", "path"): v = ti.get(k) if v: parts.append(str(v)) return " ".join(parts) ``` `scripts/rule_gate.py:110-115`: ```python else: desc = _compose_from_stdin() if not desc: return 0 # hook with no/empty payload → pass (fail open) hits = check(desc, load_rules()) ``` The underlying keyword comparison in `scripts/rule_gate.py:58-67` further limits enforcement: ```python def check(description: str, rules: list) -> list: """Return the rules hit by description (empty list = pass).""" text = (description or "").lower() hits = [] for r in rules: kws = [k.lower() for k in r.get("keywords", [])] if any(k in text for k in kws if k): hits.append(r) return hits ``` ### Technical Analysis The pre-tool hook is presented as an enforcement layer, but it only inspects six predefined top-level keys from `tool_input`: `command`, `description`, `prompt`, `text`, `query`, and `path`. Tool payloads commonly use other fields, including `file_path`, `content`, `url`, `destination`, `recipient`, `arguments`, or nested objects ...[truncated 2224 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat the gate as advisory unless it uses structured, tool-specific policy checks. 2. Canonically serialize and inspect the complete `tool_input` object, including nested dictionaries and lists, rather than selecting a small set of keys. 3. Add explicit schemas for every supported tool and reject unsupported payload shapes when enforcement is enabled. 4. Fail closed for configured high-risk operations when input is malformed or cannot be evaluated. Provide a separate documented fail-open mode if availability is preferred. 5. Match structured actions rather than free-form descriptions. For example: - Resolve and compare filesystem paths for file operations. - Parse shell commands before assessing destructive behavior. - Inspect destinations and recipients for network or messaging tools. 6. Normalize Unicode, whitespace, path separators, shell quoting, and command aliases before policy evaluation. 7. Bind policies to tool names and action types instead of relying exclusively on substring keywords. 8. Add tests covering `file_path`, `content`, URLs, nested arguments, malformed JSON, non-dictionary inputs, aliases, and indirect command forms. 9. Clearly document residual bypass risks and avoid describing keyword matching as hard enforcement. ]]>
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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (33)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose is memory recovery, but the skill also implements a rule-enforcement gate that consumes hook input from stdin and can block actions with exit code 2. This mismatch is security-relevant because deployers may install a 'memory' skill without realizing it can participate in tool-execution control paths and process broader action metadata than expected.

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
**Key commands**: `python scripts/memory_index.py` (discover + write the change index) · `python scripts/rule_gate.py --check "<action>"` (optional action-time
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Vague Triggers

Medium
Confidence
93% confidence
Finding
The README defines a one-word natural-language trigger, "handover," to cause state-writing behavior at task breaks. Because the phrase is common in ordinary collaboration and support conversations, an agent could invoke the skill unintentionally, causing unexpected writes to memory artifacts or premature workflow transitions without explicit user intent.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The phrase "recover memory" is broad conversational language and is presented as sufficient to start recovery behavior each session. In agent environments that monitor user text for tool or skill activation, this can be triggered accidentally by normal discussion, leading to unintended reads of local memory files and disclosure of stored context into the current session.

Skill Enumeration

Medium
Category
Agent Snooping
Content
|---|---|
| **Hermes** (Nous Research) | Drop into the agent's `skills/` directory, then paste `assets/agents-snippet.md` into `.hermes.md` or `AGENTS.md` → recovery runs automatically. Hermes injects SOUL/MEMORY/USER itself, and the skill detects host-injected identity and skips duplicate reads (the 55-75% cumulative case). |
| **ClawHub / OpenClaw** | Dual-compatible frontmatter (standard + `metadata.hermes`). Install into the skills directory or via your hub client. |
| **Claude Code / Cursor / Codex** | Copy the folder into the project or agent skills directory (`~/.claude/skills/`, etc.); instruct the agent to follow SKILL.md at session start. |
| **Any LLM, any OS** | Recovery logic is pure convention + one Python stdlib script — model-agnostic; Windows / macOS / Linux; no GPU. |

## Why not just another memory plugin?
Confidence
85% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill explicitly instructs file discovery, file reads, file writes, and deletion of `.changshen/handover.md`, but it does not declare an explicit tool scope such as `permissions` or `allowed-tools`. In agents that honor capability declarations, this creates a transparency and least-privilege failure: users and hosts cannot easily constrain the skill to the minimum filesystem actions it actually needs.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The skill advertises broad natural-language triggers such as 'where did we leave off?' and 'I already told you this,' which can occur in ordinary conversation without a clear invocation boundary. In an always-loaded skill, that ambiguity can cause unintended recovery behavior, filesystem reads, and state updates when the user did not intend to activate the memory workflow.

Vague Triggers

Medium
Confidence
86% confidence
Finding
The documentation says recovery can run automatically at session start after pasting a snippet into project instruction files, but it does not define strict activation boundaries for that automatic behavior. That increases the chance of silent file reads and writes on every session, including in contexts where the user did not intend persistence operations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill instructs the agent to delete `.changshen/handover.md` after reading it, but this destructive behavior is embedded in procedure text without a strong warning or separate consent mechanism. Unexpected deletion of user-authored state is risky because the handover may contain important task context, and accidental or premature consumption can lead to silent data loss.

Session Persistence

Medium
Category
Rogue Agent
Content
ChangShen treats your rules as **memory too** — the most important kind. Two layers make them stick:

**Alignment layer (every session).** At recovery (step 2b) the agent restates the imperative rules it found in your rules files — do's and don'ts alike. You see, in plain text, that what you set is loaded. No rule file yet? Write your rules in `CLAUDE.md`/`AGENTS.md`/your rules file — plain sentences work: *"Never delete files without asking."* *"Always reply in Chinese."* *"Ask before sending anything externally."*

**Enforcement layer (where the platform supports it).** Prohibitions you never want broken ("never", "don't", "always ask first") get written to `.changshen/rules.json` (id / text / keywords), and a tiny local gate script checks every action before it runs:
Confidence
80% confidence
Finding
The skill is explicitly built around session persistence and instructs the agent to extract rules from user files and write them into `.changshen/rules.json` for reuse across sessions. Persistent storage of behavioral constraints and task context is core functionality here, but it still has security implications because it can retain sensitive preferences/instructions beyond a single conversation and influence future actions automatically.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The example imperative rules include "Always reply in Chinese," which is a natural-language locale constraint presented as a standard rule pattern. Because it does not mention user choice, opt-in, or a justified region-specific need, it conflicts with language/locale choice policy expectations.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The trigger phrase "recover memory" is generic enough to plausibly appear in normal user conversation, documentation, or task content, which can cause unintended activation of the memory-recovery workflow. In this skill, that workflow performs automatic file discovery and reads at session start, so accidental triggering can expose stored context or alter agent behavior when the user did not explicitly intend to invoke the skill.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrase "handover" is broad and not scoped to a specific command format, file, or workflow state. In a chat environment, ordinary user text or quoted content could accidentally invoke memory-writing behavior, causing unintended state changes, leakage into persistent notes, or cross-session confusion.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
rules.json schema (single source of truth — keep in sync with SKILL.md):
{
  "rules": [
    {"id": "r1", "text": "Never delete files without asking the user first",
     "keywords": ["delete", "remove", "rm ", "unlink"]},
    {"id": "r2", "text": "Never send external messages without approval",
     "keywords": ["send message", "publish", "post to", "email to"]}
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
rules.json schema (single source of truth — keep in sync with SKILL.md):
{
  "rules": [
    {"id": "r1", "text": "Never delete files without asking the user first",
     "keywords": ["delete", "remove", "rm ", "unlink"]},
    {"id": "r2", "text": "Never send external messages without approval",
     "keywords": ["send message", "publish", "post to", "email to"]}
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
rules.json schema (single source of truth — keep in sync with SKILL.md):
{
  "rules": [
    {"id": "r1", "text": "Never delete files without asking the user first",
     "keywords": ["delete", "remove", "rm ", "unlink"]},
    {"id": "r2", "text": "Never send external messages without approval",
     "keywords": ["send message", "publish", "post to", "email to"]}
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
rules.json schema (single source of truth — keep in sync with SKILL.md):
{
  "rules": [
    {"id": "r1", "text": "Never delete files without asking the user first",
     "keywords": ["delete", "remove", "rm ", "unlink"]},
    {"id": "r2", "text": "Never send external messages without approval",
     "keywords": ["send message", "publish", "post to", "email to"]}
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
rules.json schema (single source of truth — keep in sync with SKILL.md):
{
  "rules": [
    {"id": "r1", "text": "Never delete files without asking the user first",
     "keywords": ["delete", "remove", "rm ", "unlink"]},
    {"id": "r2", "text": "Never send external messages without approval",
     "keywords": ["send message", "publish", "post to", "email to"]}
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
"rules": [
    {"id": "r1", "text": "Never delete files without asking the user first",
     "keywords": ["delete", "remove", "rm ", "unlink"]},
    {"id": "r2", "text": "Never send external messages without approval",
     "keywords": ["send message", "publish", "post to", "email to"]}
  ]
}
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Static analysis

No suspicious patterns detected.