Back to skill

Security audit

Agent Memory System Guide

Security checks for vulnerabilities and agentic risk

Overview

This memory-workflow skill is mostly coherent, but its backup tool can restore or archive more files than users would reasonably expect.

Install only if you trust the source and will use backup import/export carefully. Do not import archives from untrusted sources, inspect backup contents before restoring, avoid symlinks inside memory and attachments, and keep separate backups before using --clean.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/memory_capture.py:345
Finding

Crafted Backup Archives Can Overwrite Arbitrary Files Within the Target Workspace

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_capture.py:345-361 and scripts/memory_capture.py:479-483
Vulnerability Type: Insufficient archive-member allowlisting
Risk Level: Medium

Vulnerable Code

python
def safe_members_from_manifest(archive: zipfile.ZipFile) -> list[str]:
    try:
        manifest = json.loads(archive.read("manifest.json").decode("utf-8"))
    except KeyError as error:
        raise ValueError("Archive is missing manifest.json.") from error

    included_paths = manifest.get("included_paths")
    if not isinstance(included_paths, list) or not all(isinstance(item, str) for item in included_paths):
        raise ValueError("Archive manifest has invalid included_paths.")

    archive_names = set(archive.namelist())
    safe_paths: list[str] = []
    for item in included_paths:
        pure_path = PurePosixPath(item)
        if pure_path.is_absolute() or ".." in pure_path.parts:
            raise ValueError(f"Archive contains unsafe path: {item}")
        if item not in archive_names:
            raise ValueError(f"Archive is missing expected file: {item}")
        safe_paths.append(item)
    return safe_paths
python
def restore_archive(archive: zipfile.ZipFile, workspace: Path, members: list[str]) -> None:
    for member in members:
        target_path = workspace / PurePosixPath(member)
        target_path.parent.mkdir(parents=True, exist_ok=True)
        target_path.write_bytes(archive.read(member))

Technical Analysis

The archive validation rejects absolute paths and conventional .. traversal, but it does not enforce the documented import boundary. Imported files are expected to be limited to the supported root files—MEMORY.md, SESSION-STATE.md, working-buffer.md, and memory-capture.md—or descendants of memory/ and attachments/.

Instead, every relative path listed in attacker-controlled manifest.json is acce ...[truncated 1974 chars]

Remediation
View remediation

Remediation Suggestions

Enforce an explicit allowlist before performing any backup, deletion, or restoration:

  1. Accept only the exact supported root files:
    • MEMORY.md
    • SESSION-STATE.md
    • working-buffer.md
    • memory-capture.md
  2. Accept other entries only when their normalized paths are descendants of memory/ or attachments/.
  3. Reject empty names, directory entries, duplicate entries, NUL characters, absolute paths, .. components, and platform-specific path ambiguities.
  4. Validate archive_version and archive_kind before restoration.
  5. Verify that every archive member intended for restoration is a regular file.
  6. Complete validation of the entire archive before creating the pre-import backup or invoking --clean.
  7. Extract into a temporary staging directory first and commit the validated result only after all reads and writes succeed.
  8. Add regression tests using manifests that reference AGENTS.md, .env, nested script paths, duplicate entries, and unsupported archive members.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/memory_capture.py:145
Finding

Backup Export Follows Symbolic Links Outside the Workspace

Content
View full analysis

Vulnerability Details

File Location: scripts/memory_capture.py:145-158 and scripts/memory_capture.py:293-313
Vulnerability Type: Symbolic-link traversal during archive creation
Risk Level: Medium

Vulnerable Code

python
def collect_workspace_paths(workspace: Path) -> list[Path]:
    paths: list[Path] = []
    for name in SUPPORTED_FILES:
        candidate = workspace / name
        if candidate.is_file():
            paths.append(candidate)
    for directory_name in SUPPORTED_DIRECTORIES:
        directory = workspace / directory_name
        if directory.is_dir():
            for path in sorted(item for item in directory.rglob("*") if item.is_file()):
                paths.append(path)
    return paths
python
def create_archive(
    *,
    workspace: Path,
    archive_path: Path,
    entries: list[Path],
    generated_at: str,
    archive_kind: str,
) -> Path:
    archive_path.parent.mkdir(parents=True, exist_ok=True)
    included_paths = [relative_archive_name(path, workspace) for path in entries]
    payload = manifest_payload(
        generated_at=generated_at,
        workspace=workspace,
        included_paths=included_paths,
        archive_kind=archive_kind,
    )
    with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive:
        archive.writestr("manifest.json", json.dumps(payload, ensure_ascii=False, indent=2))
        for entry, archive_name in zip(entries, included_paths):
            archive.write(entry, archive_name)
    return archive_path

Technical Analysis

Path.is_file() follows symbolic links. Therefore, a supported root file or a file discovered beneath memory/ or attachments/ may be a symbolic link whose target resides outside the workspace.

relative_archive_name() verifies only that the symbolic-link path is lexically beneath the workspace. It does not resolve the link ta ...[truncated 1912 chars]

Remediation
View remediation

Remediation Suggestions

Harden collection and archive creation against symbolic-link traversal:

  1. Reject every candidate for which path.is_symlink() is true.
  2. Resolve the workspace and each candidate, then require the resolved candidate to remain beneath the resolved workspace:
    python
    workspace_root = workspace.resolve()
    candidate_resolved = candidate.resolve(strict=True)
    if not candidate_resolved.is_relative_to(workspace_root):
        raise ValueError(f"Path escapes workspace: {candidate}")
    
  3. Apply containment checks to supported root files and all recursively discovered entries.
  4. Avoid following symbolic directory entries during recursive traversal.
  5. Where supported, open files using no-follow semantics to reduce time-of-check/time-of-use races.
  6. Revalidate immediately before reading each file into the archive.
  7. Clearly document whether symlinks are rejected or archived as links without dereferencing.
  8. Add tests for:
    • A supported root file linked outside the workspace.
    • A file under attachments/ linked outside the workspace.
    • A symlink whose target is replaced between validation and archive creation.
    • Internal symlinks, according to the chosen policy.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description says this skill is for configuring or improving an agent memory workflow, but the supplied code does not implement memory workflows, daily notes, session recovery, or OpenViking integration. Instead, it performs repository maintenance and release QA tasks: reading manifest.toml and SKILL.md, validating version mentions in documentation files, enforcing specific frontmatter metadata, and executing the test suite. This is a materially different primary purpose from the declared description, so it is a clear mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description is about agent memory workflow functionality, including MEMORY.md, daily notes, and session recovery integrations. The supplied code does not implement or support memory workflows; it is a unit test that checks whether certain artifact filenames are present in .gitignore. This is a materially different primary purpose and concerns repository hygiene rather than memory management behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared purpose is about agent memory workflow setup and improvement, but the supplied code chunk is unrelated to memory workflows, notes, session recovery, or OpenViking integration. Its primary function is automated testing of a repository release-check script. This is a materially different purpose and introduces capabilities—running subprocesses for release validation and checking release output—not reflected in the description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents the skill as a memory workflow aid for agent systems and note-taking/session recovery setups. The supplied code does not implement, configure, or assist with memory workflows. Instead, it performs repository contract tests for naming, manifest structure, release metadata, registry publishing flags, and documentation version references. That is a materially different primary purpose from the declared description, so this is a clear mismatch.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/check_release.py (reported line 63)May include surrounding context.

python
def run_pytest(root: Path) -> None:
    command = [sys.executable, "-m", "pytest", "-q"]
    print(f"$ {' '.join(command[2:])}")
    env = dict(os.environ)
    env["AGENT_MEMORY_RELEASE_CHECK"] = "1"
    result = subprocess.run(
        command,

Memory Manipulation

High
Category
Memory Poisoning
Confidence
85% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/memory_capture.py (reported line 91)May include surrounding context.

python
bootstrap.add_argument(
        "--refresh-capture",
        action="store_true",
        help="Overwrite memory-capture.md with a regenerated capture sheet.",
    )

    export_parser = subparsers.add_parser(

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The README documents a --clean import mode as a way to perform a clean restore, but it does not clearly and explicitly warn that this mode can replace existing memory data and may remove or overwrite supported files in the destination workspace. In a memory-management skill, users are especially likely to run copy-pasted commands on important personal data, so insufficient warning increases the chance of accidental destructive use even if the feature itself is legitimate.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The README documents a --clean import mode that performs an overwrite-style restore to workspace memory files, which can replace user data if invoked incorrectly. Although the text says this is for making the workspace match the backup, it does not present a strong, explicit warning about destructive consequences, confirmation requirements, or recovery expectations at the point of use, so users or downstream agents may trigger it unsafely.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README documents a --clean import mode described as a deterministic clean restore, which implies potentially destructive behavior, but it does not clearly and explicitly warn that existing workspace data may be overwritten or removed. In a memory-management skill, users may run these commands against valuable notes and attachments, so insufficient warning can lead to accidental data loss even without malicious intent.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
81% confidence
Finding

The skill instructs use of shell commands and write-capable operations (python3 scripts/memory_capture.py ..., creating/updating memory files) but does not declare any explicit tool scope such as permissions or allowed-tools. That omission weakens least-privilege controls and makes it easier for an invoking agent to run broader file or shell actions than the user would infer from the manifest.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The document title, instructions, and trigger section are presented in Chinese, which effectively forces a specific language for users invoking or reading the skill. The policy allows locale constraints only when justified or when users are given a choice, neither of which is stated here.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The plan adds a destructive import --clean mode that deletes supported files and directories before restore, but the documentation requirements shown do not explicitly require a strong deletion warning, confirmation language, or examples of data-loss risk. In a memory-management skill, users may reasonably run import operations against valuable notes and attachments, so under-warning a destructive mode increases the chance of accidental data loss even if a backup is created.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This markdown file specifies that import will overwrite destination files after creating a backup, which is a user-data-affecting operation. While the design includes a safety backup, it does not explicitly warn users in the documentation about overwrite behavior, possible conflicts, or the need to verify the destination workspace before running import.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/check_release.py (reported line 65)May include surrounding context.

python
print(f"$ {' '.join(command[2:])}")
    env = dict(os.environ)
    env["AGENT_MEMORY_RELEASE_CHECK"] = "1"
    result = subprocess.run(
        command,
        cwd=root,
        text=True,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

These assertions require specific Chinese phrases to be present in the skill documentation, which semantically enforces a particular language/locale in the skill's natural-language content. The file does not show any accompanying opt-in, language selection, or documented justification for making Chinese mandatory.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/test_memory_capture_script.py (reported line 14)May include surrounding context.

python
def run_script(tmp_path: Path, *args: str) -> subprocess.CompletedProcess[str]:
    repo_root = Path(__file__).resolve().parents[1]
    return subprocess.run(
        [sys.executable, str(script_path()), '--workspace', str(tmp_path), *args],
        cwd=repo_root,
        text=True,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/test_memory_capture_script.py (reported line 25)May include surrounding context.

python
def run_command(*args: str, cwd: Path | None = None) -> subprocess.CompletedProcess[str]:
    repo_root = Path(__file__).resolve().parents[1]
    return subprocess.run(
        [sys.executable, str(script_path()), *args],
        cwd=cwd or repo_root,
        text=True,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The test requires SKILL.md to contain specific Chinese terms such as '启动时', '结束时', and '恢复', which semantically enforces a particular language in the skill contract. There is no indication in this file that the language requirement is optional, user-selected, or justified as region-specific, so it may violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The test asserts that the session-state template must use Chinese section headings like '当前任务', '已完成', '卡点', '下一步', and '恢复信息'. This is a natural-language policy concern because it hard-codes a locale-specific interface without showing any user opt-in or documented justification in the file.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

These assertions require exact Chinese-language contract text in SKILL.md, including instructions about how SESSION-STATE.md must be written. Enforcing one language in user-facing or maintainer-facing documentation without opt-in or justification can conflict with organizational language/locale policy.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This test requires SKILL.md to contain specific Chinese phrases, which semantically enforces a particular language/locale in the skill documentation. The file provides no indication of user choice, opt-in, or a documented region-specific justification, so it appears to violate the language/locale policy criterion.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · tests/test_release_check_script.py (reported line 13)May include surrounding context.

python
pytest.skip('Avoid recursive invocation while check_release.py is already running pytest.')

    repo_root = Path(__file__).resolve().parents[1]
    result = subprocess.run(
        [sys.executable, str(repo_root / 'scripts' / 'check_release.py')],
        cwd=repo_root,
        text=True,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This test requires multiple exact Chinese phrases to be present in the skill documentation, which semantically enforces a specific language/locale. The file provides no indication that this language requirement is optional, user-selected, or justified by a documented region-specific constraint.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
74% confidence
Finding

The file specifies that SESSION-STATE.md uses section headings in Chinese (当前任务, 已完成, 卡点, 下一步, 恢复信息) and instructs users not to expand into an alternative schema. This can be read as enforcing a specific language/locale convention rather than offering a language choice or explaining that the locale is optional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The test instruction explicitly requires the generated capture text to contain the Chinese phrase 候选决策. This imposes a specific language output requirement in natural-language content without indicating user choice, opt-in, or a documented locale-specific justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.