Back to skill

Security audit

Perstate

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed persistent memory skill, but it gives agents broad authority to save conversation-derived knowledge to local and remote Git state with weak scoping and unsafe graph viewing.

Install only if you are comfortable with an agent keeping long-lived memory in ~/.perstate and potentially pushing it to a Git remote. Use a private repo or local path, avoid saving secrets or sensitive personal data, review what will be committed, and avoid /perstate view on shared or untrusted graph content until the viewer sanitizes HTML and pins or vendors its JavaScript dependencies.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/perstate-view.sh:318
Finding

Stored Cross-Site Scripting in Knowledge Graph Viewer

Content
View full analysis
0) { metaRows.push('' + listKey + '' + listItems.join(', ') + ''); inList = false; listItems = []; } var m = line.match(/^(\w+):\s*(.*)$/); if (m) { var key = m[1], val = m[2].trim(); if (val === '' || val === 'null' || val === '[]') continue; if (val === '') { inList = true; listKey = key; listItems = []; } else { metaRows.push('' + key + '' + val + ''); } } var bodyMarkdown = bodyHtml ? marked.parse(bodyHtml) : ''; return metaHtml + (bodyMarkdown ? '
' + bodyMarkdown + '
' : ''); ``` The resulting HTML is inserted into the document at lines 348–349 and 359–360: ```javascript var content = contentMap[nodeId] || ''; previewContent.innerHTML = renderPreview(content); ``` ### Technical Analysis Entity and relation content is loaded from the synchronized knowledge repository. The viewer passes Markdown through `marked.parse()` and assigns the result to `innerHTML` without HTML sanitization. Markdown parsers generally preserve embedded HTML unless explicitly configured or followed by a sanitizer. Frontmatter keys, values, and list elements are also concatenated directly into HTML table markup. JSON escaping performed earlier protects JavaScript string syntax but does not provide HTML-context sanitization after those strings are parsed and rendered. Consequently, knowledge content containing HTML elements with event handlers, dangerous links, or similar active content can execute script in the generated graph page. This is especially relevant when the configured Git repository or branch is shared with other contributors. ...[truncated 1373 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/perstate-view.sh:233
Finding

Unpinned Remote JavaScript Executes in a Page Containing Private Knowledge

Content
View full analysis
``` ### Technical Analysis The generated graph page downloads and executes two third-party JavaScript packages at viewing time. Neither URL pins an exact package version, and neither script has a Subresource Integrity hash. Because the URLs resolve mutable package aliases, the code executed by a previously reviewed Skill can change independently of the installed project. The scripts execute in the same document that contains the complete graph data in JavaScript variables, including the `contentMap` object. This creates a supply-chain trust boundary inconsistent with the sensitive nature of the stored information. Compromise of the CDN, package publication channel, or upstream package could turn the graph viewer into a data-exfiltration channel. ### Attack Path 1. An upstream package release, package account, or CDN delivery path is compromised. 2. Malicious JavaScript is served from one of the unversioned dependency URLs. 3. The victim invokes `/perstate view`. 4. The generated page automatically downloads and executes the altered dependency. 5. The dependency reads graph variables or rendered content from the page. 6. The dependency transmits private entity and relation data to an external server or alters the displayed graph. ### Impact Assessment Exploitation can compromise all knowledge embedded in a generated graph page, potentially including private conversation-derived insights, people, relationships, and source references. The code executes in the browser rather than with shell privileges. However, it can access the page's graph data, perf ...[truncated 129 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/perstate-prepare.sh:198
Finding

Session Identifier Injection into Persistent YAML Configuration

Content
View full analysis
> "$CONFIG" << EOF ${SESSION_ID}: branch: $BRANCH EOF ``` Equivalent direct interpolation occurs in `scripts/perstate-fork.sh`: ```bash cat >> "$CONFIG" << EOF ${SESSION_ID}: branch: $NEW_BRANCH EOF ``` And in `scripts/perstate-switch.sh`: ```bash cat >> "$CONFIG" << EOF ${SESSION_ID}: branch: ${NAME} EOF ``` ### Technical Analysis The scripts escape some regular-expression characters when looking up a session identifier, but they do not validate or encode the identifier before writing it into YAML. Regular-expression escaping does not make a value safe for a YAML key. A session identifier containing newline characters, colons, YAML indicators, or indentation can inject additional configuration entries or corrupt the `sessions` mapping. The Skill documentation permits session identifiers to originate from external platform identifiers. If that source is not guaranteed to match a strict safe character set, it crosses a trust boundary before being persisted in `~/.perstate/config.yml`. Branch values are also written as unquoted YAML scalars. Git rejects many invalid branch characters, but explicit validation and safe serialization should still be applied before configuration persistence. ### Attack Path 1. An attacker influences or supplies a platform/session identifier containing a newline and YAML content. 2. The identifier is passed to one of the affected scripts through `--session-id`. 3. The heredoc expands the identifier verbatim into `~/.perstate/config.yml`. 4. The injected lines create additional session mappings, branch fields, or malformed configuration. 5 ...[truncated 853 chars]
Remediation
View remediation
&2 exit 1 fi ``` 2. Explicitly reject newline, carriage-return, control, colon, and YAML-indicator characters. 3. Validate branch names with `git check-ref-format --branch "$BRANCH"` before using or persisting them. 4. Replace ad hoc heredoc and `grep`-based YAML editing with a parser-based or serialization-safe configuration mechanism. 5. If YAML must remain shell-generated, quote and correctly escape every scalar according to YAML rules. 6. Set restrictive permissions on the configuration directory and file, such as directory mode `0700` and file mode `0600`. 7. Add tests covering multiline identifiers, colons, quotes, Unicode control characters, duplicate mappings, and malformed indentation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (39)

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 248)May include surrounding context.

md
| Valid relations only | `grep -rl "valid_until: null" entities/<id>/` |
| Create entity | `mkdir -p entities/<id>` + write entity.md |
| Create relation | `mkdir -p entities/<from>/<type>` + write `<to>.md` |
| Delete relation (hard) | `rm entities/<from>/<type>/<to>.md` |
| Delete entity | `rm -rf entities/<id>/` + `find entities/ -name "<id>.md" -delete` |
| Create worktree | `git worktree add <repo-dir>/<branch> <branch>` |
| Remove worktree | `git worktree remove <repo-dir>/<branch>` |

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · README.md (reported line 249)May include surrounding context.

md
| Create entity | `mkdir -p entities/<id>` + write entity.md |
| Create relation | `mkdir -p entities/<from>/<type>` + write `<to>.md` |
| Delete relation (hard) | `rm entities/<from>/<type>/<to>.md` |
| Delete entity | `rm -rf entities/<id>/` + `find entities/ -name "<id>.md" -delete` |
| Create worktree | `git worktree add <repo-dir>/<branch> <branch>` |
| Remove worktree | `git worktree remove <repo-dir>/<branch>` |
| Prune stale worktrees | `git worktree prune` |

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The trigger phrases include very common words like "remember," "recall," and "memory," which can cause the skill to activate in ordinary conversation without the user intending to invoke persistent storage or retrieval. In this skill, unintended activation is especially risky because it can lead to automatic search or persistence of conversation-derived information, creating privacy and side-effect risks.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
red session bindings, worktrees, and invalid branches (preview then confirm) | `scripts/perstate-prune.sh <days>d [--session-id <id>] [--execute]` |

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/perstate-init.sh (reported line 64)May include surrounding context.

sh
case "$REPO" in
  git@*|ssh://*)
    # SSH 协议,安全
    if [ ! -f ~/.ssh/id_ed25519 ] && [ ! -f ~/.ssh/id_rsa ]; then
      echo "警告: 使用 SSH 协议但未检测到 SSH key。"
      echo "  生成: ssh-keygen -t ed25519 -C \"your_email@example.com\""
      echo "  添加: cat ~/.ssh/id_ed25519.pub → GitHub Settings → SSH Keys"

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/perstate-init.sh (reported line 67)May include surrounding context.

sh
case "$REPO" in
  git@*|ssh://*)
    # SSH 协议,安全
    if [ ! -f ~/.ssh/id_ed25519 ] && [ ! -f ~/.ssh/id_rsa ]; then
      echo "警告: 使用 SSH 协议但未检测到 SSH key。"
      echo "  生成: ssh-keygen -t ed25519 -C \"your_email@example.com\""
      echo "  添加: cat ~/.ssh/id_ed25519.pub → GitHub Settings → SSH Keys"

Credential Access

High
Category
Privilege Escalation
Confidence
90% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/perstate-init.sh (reported line 64)May include surrounding context.

sh
case "$REPO" in
  git@*|ssh://*)
    # SSH 协议,安全
    if [ ! -f ~/.ssh/id_ed25519 ] && [ ! -f ~/.ssh/id_rsa ]; then
      echo "警告: 使用 SSH 协议但未检测到 SSH key。"
      echo "  生成: ssh-keygen -t ed25519 -C \"your_email@example.com\""
      echo "  添加: cat ~/.ssh/id_ed25519.pub → GitHub Settings → SSH Keys"

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding

npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README instructs users to use /perstate save to scan conversation context and auto-extract insights, but it does not clearly warn that those extracted insights may be written to a Git-backed persistent store and later pushed to a remote repository. In an agent context, this can lead to unintentional persistence and disclosure of sensitive user data, secrets, or confidential conversation content.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation explicitly directs agents to scan conversation context and persist extracted user-provided information into long-term state. In the context of an agent skill, that materially increases the risk of collecting and retaining sensitive data without adequate minimization, consent, or review.

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 · README.md (reported line 74)May include surrounding context.

md
1. **State identity = repo + branch** — one repo isolates multiple agents' states via branches. `main` is the personal default. No need for separate repos per agent — branch IS identity, naturally isolated, mergeable for knowledge exchange.
2. **File system IS the graph** — entities are directories, relations are files. `ls` is graph query, `grep` is full-text search, `find` is reverse traversal. No graph database, no index file, no JSON intermediate. One fact, one file — single source of truth.
3. **Knowledge processing, not simple storage** — writes are dedup, disambiguate, merge. Each write queries the existing graph first, decides create vs. merge, preventing knowledge fragmentation. This is the fundamental difference between perstate and diary-style memory.
4. **Git as temporal engine** — `valid_until` for soft delete, `git log --follow` for provenance, `git diff` for change review. Time dimension delegated to git, no separate version management.
5. **Zero-dependency, plug-and-play** — pure shell + git, no runtime, no daemon, no external tools. Any agent gets remote memory, knowledge backup, and recall on接入. All markdown + YAML frontmatter, human-readable, git-friendly.
6. **Open schema** — YAML frontmatter evolves additively, new fields never break old data. Ontology is documentation, not enforcement — unknown fields preserved, not rejected.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The README documents automatic git push after writes and automatic git pull before reads while stating that the state contains private knowledge, yet it does not present a prominent user warning about network transmission and privacy exposure. This is dangerous because users or agents may assume memory is local when in fact it may be continuously synchronized to a remote repository.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
90% confidence
Finding

The README tells operators to configure agents via system prompt to 'skip confirmation' for repo initialization and branch binding. In an autonomous agent setting, suppressing confirmation for stateful and networked actions can cause silent persistence, branch switching, or remote writes without informed user approval.

Content

Scanner excerpt · README.md (reported line 143)May include surrounding context.

Agent State Binding

Configure agents to skip confirmation via system prompt:

text
perstate repo: <repo-url>       # Only when config doesn't exist, skip init confirmation

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The skill supports write, update, delete, fork, switch, and prune behaviors, but the command summary does not prominently warn that these actions can modify local state, repository content, or remove data. Users may reasonably interpret a memory skill as informational, not as one capable of destructive repository operations.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

The skill is intentionally built around persistent session binding, local config storage, worktrees, and repository commits, so session persistence is core functionality rather than accidental behavior. However, it still creates a real security and privacy risk because conversational data and session associations are retained across turns and potentially synchronized to a remote repository.

Content

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

bash
   scripts/perstate-init.sh --repo <url> --branch <branch> --yes
  1. Script auto-completes: check git → check repo protocol (non-SSH gives security warning) → write config → clone repo → create worktree → create initial structure → commit & push

Remote vs Local (unified architecture):

  • bare clone to ~/.perstate/<repo-name>.git/, worktree at ~/.perstate/worktrees/<repo-name>/<branch>/

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The initialization flow performs cloning, local config writes, worktree creation, commits, and pushes, yet the skill does not require an upfront warning summarizing these network and repository side effects before execution. This can surprise users and result in unintentional outbound connections or persistent data creation in a remote repository.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
86% confidence
Finding

The skill allows system-prompt-specified repo or branch selection and explicitly states that no confirmation is needed in that scenario. That gives the agent authority to make persistence-targeting decisions autonomously, which is risky because it can silently direct storage or retrieval into a branch the user did not knowingly select.

Content

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

EOF

text

### Scenario 3: System prompt specified (no confirmation needed)

System prompt can contain:

Ssd 3

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The write flow directs the agent to extract entities and relations from conversation insights broadly, which creates a significant risk of persisting sensitive, proprietary, or personal information without sufficiently granular consent. Because the repository is designed for durable recall and may be remote-backed, over-collection can become long-lived disclosure and retention exposure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The guidance says to search "before answering" and save after producing "high-dimensional insights," but these conditions are subjective and leave substantial discretion to the agent. That ambiguity can cause background recall or persistence without clear user intent, increasing the chance of exposing or retaining sensitive conversational content.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill encourages automatic recall of previously accumulated knowledge before answering related questions, which can surface information from prior interactions into a new response context without the user explicitly asking for retrieval. This increases the chance of cross-context disclosure, especially when prior stored content includes sensitive or identifying details.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

These lines replace and append content in ~/.perstate/config.yml, which is a user data file. Although the script prints a message after the update, there is no prior confirmation prompt or clear pre-action disclosure in comments/usage text that the config will be modified.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The script automatically pushes the newly created branch to the remote repository without an explicit opt-in, which can unintentionally publish branch names and any later associated work to a networked service. In an agent skill context, this is more dangerous because users may invoke memory/state operations expecting local persistence, not remote publication side effects, increasing the risk of accidental data exposure or repository changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script derives an index path under $HOME and stores a persistent cache of repository-derived content there without any user-facing disclosure or consent. In an agent memory skill, silently writing data outside the repository boundary increases the chance that sensitive notes, secrets, or private knowledge are retained longer than the user expects and may be exposed to other local processes, users, backups, or future sessions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The indexing step copies the full contents of entities/*.md into a single searchable file, which can aggregate sensitive repository data into one high-value target. Even though the comments describe this as a transient cache, the file is persisted on disk under $HOME and may outlive the user's expectations, making accidental disclosure or local compromise more damaging.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script's comments, help text, status messages, and errors are written in Chinese, which effectively fixes the interaction language without any visible opt-in or locale selection. Under the stated policy, forcing a specific language is a natural-language policy violation unless the locale constraint is explicitly documented and justified.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.prompt_injection_instructions

Prompt-injection style instruction pattern detected.

Warn
Code
suspicious.prompt_injection_instructions
Location
README.md:143