Back to skill

Security audit

Self Improvement (done properly)

Security checks for vulnerabilities and agentic risk

Overview

This skill is mostly coherent, but it persistently changes agent memory and includes an overwrite path that can delete or write files outside the intended workspace in symlinked directories.

Install only if you want this skill to maintain persistent project memory. Review .learnings entries before promotion, avoid enabling broad automatic hooks, do not use extract_skill.py --force unless the target path and symlinks are verified, and avoid logging secrets or raw untrusted output into memory files.

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

Error
Location
scripts/extract_skill.py:50
Finding
Workspace Boundary Bypass Through Symlinked Output Directories## Vulnerability Details **File Location**: `scripts/extract_skill.py:50-58` and `scripts/extract_skill.py:180-205` **Vulnerability Type**: Insufficient path-containment validation and symlink traversal **Risk Level**: High ### Vulnerable Code ```python def validate_output_dir(output_dir: str) -> str: if output_dir.startswith("/"): raise CliError("--output-dir must be relative to --root") if ".." in Path(output_dir).parts: raise CliError("--output-dir must not contain '..' path segments") cleaned = output_dir.strip() or "skills" return cleaned ``` ```python root = resolve_root(args.root) output_dir = validate_output_dir(args.output_dir) skill_path = root / output_dir / args.name if skill_path.exists() and not args.force: raise CliError(f"Skill path already exists: {skill_path}. Use --force to overwrite.") preview = { "ok": True, "workspace_root": str(root), "skill_path": str(skill_path), "scaffold_evals": args.scaffold_evals, } if args.dry_run: preview["sketch"] = skill_template(args) print_output(preview, args.format) return 0 if skill_path.exists() and args.force: for child in sorted(skill_path.rglob("*"), reverse=True): if child.is_file(): child.unlink() elif child.is_dir(): child.rmdir() created = create_files(skill_path, args) ``` ### Technical Analysis The output-directory validation only rejects absolute path strings and explicit `..` components. It does not resolve the final destination and verify that it remains beneath the resolved workspace root. Consequently, a directory component beneath the workspace can be a symbolic link to a location outside the workspace. For example, if `workspace/skills` is a symlink to an external directory, the computed `root / "skills" / args.name` path is textually beneath the workspace but resolves outside it. This is espe ...[truncated 1887 chars]
Remediation
## Remediation Suggestions 1. Resolve the workspace root and final destination before performing any operation: ```python root = resolve_root(args.root) skill_path = (root / output_dir / args.name).resolve() try: skill_path.relative_to(root) except ValueError: raise CliError("Resolved skill path escapes the workspace root") ``` 2. Inspect every path component with `lstat()` and reject symbolic links in the output path. 3. Repeat the containment check immediately before deletion and immediately before file creation to reduce time-of-check/time-of-use exposure. 4. Refuse destructive overwrite when the destination is a symlink or contains symlinked ancestors. 5. Replace the custom recursive deletion loop with a hardened deletion routine that explicitly enforces the approved root. 6. Require explicit confirmation or a narrowly scoped overwrite option before deleting an existing directory. 7. Add tests covering: - A symlinked `--output-dir`. - A symlinked Skill directory. - Nested symlink components. - `--force` against destinations outside the root. - Symlink replacement between validation and write operations.

T02 · Agent Memory Poisoning

Warning
Location
scripts/learnings.py:111
Finding
Persistent Agent Memory Poisoning Through Unsanitized Learning Content## Vulnerability Details **File Location**: `scripts/learnings.py:111-119`, `scripts/learnings.py:163-169`, `scripts/learnings.py:341-379`, and `scripts/learnings.py:397-441`; promotion workflow in `SKILL.md:165-182` **Vulnerability Type**: Persistent indirect prompt injection through untrusted stored content **Risk Level**: Medium ### Vulnerable Code ```python def read_text_or_file(text: Optional[str], file_path: Optional[str], field_name: str) -> str: if text and file_path: raise CliError(f"Provide either --{field_name} or --{field_name}-file, not both.") if file_path: path = Path(file_path).expanduser().resolve() if not path.exists(): raise CliError(f"File not found for --{field_name}-file: {path}", 2) return path.read_text(encoding="utf-8").rstrip() return (text or "").rstrip() ``` ```python def append_entry(target_file: Path, entry: str) -> None: target_file.parent.mkdir(parents=True, exist_ok=True) prefix = "\n" if target_file.exists() and target_file.read_text(encoding="utf-8").rstrip() else "" with target_file.open("a", encoding="utf-8") as f: f.write(prefix) f.write(entry.rstrip()) f.write("\n") ``` The error logger places the supplied content directly into persistent Markdown: ~~~~python entry = f"""## [{entry_id}] {args.name} **Logged**: {now_iso()} **Priority**: {args.priority} **Status**: {args.status} **Area**: {args.area} ### Summary {args.summary} ### Error ``` {error_text or '[TODO: add representative error output]'} ``` ### Context {context or '[TODO: add context]'} ### Suggested Fix {suggested_fix or '[TODO: add suggested fix]'} ### Metadata {render_metadata(metadata_lines)} --- """ ~~~~ The Skill then instructs Agents to review these records and promote selected information into persistent instruction files: ```markdown ### 6) Promote proven ...[truncated 2986 chars]
Remediation
## Remediation Suggestions 1. Explicitly classify all captured command output, repository text, and imported files as untrusted data. 2. Store raw evidence separately from Agent-authored conclusions. Prefer a structured format with fields such as `source`, `trust_level`, and `review_status`. 3. Add prominent boundaries around raw content stating that it must never be followed as an instruction. 4. Detect and flag instruction-like phrases, role directives, requests to ignore prior instructions, tool-execution requests, and promotion requests. 5. Prefer Agent-generated summaries over complete raw output. Retain only the minimal diagnostic excerpt required. 6. Require explicit human approval before copying any lesson into persistent Agent instruction or memory files. 7. Prohibit direct promotion of raw `Details`, `Error`, `Context`, or `User Context` fields. 8. Validate Markdown structure and escape content that can terminate fences or create deceptive headings. 9. Add promotion provenance, including the original entry ID, reviewer, review timestamp, and evidence supporting the rule. 10. Update the Skill instructions to state that `.learnings/` entries may contain hostile indirect prompt injections and must be treated as evidence, not executable instructions.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (12)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill's stated purpose sounds like lightweight lesson capture, but the instructions authorize broader filesystem modification, promotion into shared memory files, and generation of new skill scaffolds. That mismatch can mislead operators about the real write scope, causing them to approve or invoke the skill without understanding it can create or overwrite persistent files in the workspace.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill's stated purpose sounds like lightweight lesson capture, but the instructions authorize broader filesystem modification, promotion into shared memory files, and generation of new skill scaffolds. That mismatch can mislead operators about the real write scope, causing them to approve or invoke the skill without understanding it can create or overwrite persistent files in the workspace.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
When --force is used, the script recursively removes all files and directories under an existing skill path before recreating content. That creates a destructive overwrite primitive that can erase prior lessons or other contents in the target tree, which is especially risky for a skill whose purpose is to preserve and promote learnings.

Lp3

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding
The skill clearly directs the agent to read and write workspace files such as .learnings/*.md and project memory files, but it does not declare any explicit tool scope or permissions boundary. In platforms that support permission scoping, this can cause the skill to receive broader filesystem access than users expect, increasing the risk of unintended or unauthorized persistence of project data.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The instruction to 'Log silently' encourages persistent storage of user corrections, project conventions, failures, and feature requests without clear user notice or consent at the time of writing. This creates a meaningful privacy and governance risk because sensitive project details, user preferences, internal errors, or operational context may be retained in durable files and later propagated into broader memory files.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The trigger examples use natural, loosely bounded phrases such as 'remember that for future tasks,' 'capture the lesson,' and 'check whether we already logged any learnings,' which can match ordinary conversation outside the intended self-improvement workflow. In a skill that writes or consults durable memory, overbroad triggering increases the chance of unintended invocation, causing inappropriate memory creation, retrieval, or workflow diversion from the user's actual task.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The example configuration uses an empty matcher, which will cause the hook to run on every user prompt submission rather than only when self-improvement behavior is relevant. In this skill context, that broad activation increases the chance of unnecessary or privacy-sensitive logging prompts, workflow interference, and accidental persistence of information across unrelated tasks.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
This script goes beyond merely capturing lessons and can create directories and write multiple files under a caller-supplied workspace. In the context of a self-improvement skill, that expands the capability surface from memory/knowledge capture into repository mutation, which can be abused to plant scaffolds or modify project state in places the user may not expect.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The destructive --force path performs deletion immediately with no confirmation prompt or last-chance warning at the point of action. This makes accidental data loss much more likely, particularly in automation or agent-driven contexts where flags may be passed programmatically and the user may not directly observe the impending deletion.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This code reads arbitrary local files supplied via `--details-file`, `--error-text-file`, `--context-file`, and similar options, then incorporates their contents into skill-managed output files. Although the module docstring describes non-interactive behavior, there is no user-facing prompt, warning, or explicit disclosure near the file-read path that local file contents will be ingested and persisted.

Missing User Warnings

Low
Confidence
76% confidence
Finding
The script appends generated content into `.learnings` markdown files for multiple commands, creating or modifying files under the provided workspace root. While this is part of the tool's purpose, the code path itself has no user-facing log or warning indicating that provided text and file-derived content will be permanently written unless `--dry-run` is used.

Missing User Warnings

Low
Confidence
80% confidence
Finding
When `init --force` is used, existing `.learnings` files are replaced via `shutil.copyfile`, which is a potentially destructive file write. The parser exposes the `--force` option, but the code provides no additional warning, confirmation, or user-facing disclosure at the overwrite point about data loss risk.

Static analysis

No suspicious patterns detected.