Back to skill

Security audit

DiskMan

Security checks for vulnerabilities and agentic risk

Overview

This disk-management skill matches its stated purpose, but its agent tools can delete or migrate broad local directories and send directory details to an AI provider without strong built-in safeguards.

Review this skill carefully before installing. Use it only with explicit human approval for clean and migrate actions, run dry-runs first, keep backups of important directories, avoid running the MCP server with elevated privileges, and do not configure AI provider credentials unless you are comfortable sending directory metadata and context to that provider.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
operations/cleaner.py:66
Finding
Arbitrary Recursive Directory Deletion Without Enforced Authorization or Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `mcp/server.py:122-145`; `operations/cleaner.py:25-33`; `operations/cleaner.py:66-120` **Vulnerability Type**: Unrestricted destructive filesystem operation **Risk Level**: High ### Vulnerable Code ```python # mcp/server.py:122-145 @mcp.tool() def clean_directory( path: str, dry_run: bool = True, ) -> dict[str, Any]: """ Clean a directory. Args: path: Directory path to clean dry_run: If True, only preview what would be deleted Returns: Clean result with space that would be freed """ result = cleaner.clean(path, dry_run=dry_run) return result.to_dict() ``` ```python # operations/cleaner.py:25-33 self.protected_paths = set() if protected_paths: for p in protected_paths: self.protected_paths.add(str(Path(p).expanduser().resolve())) # Add default protected paths home = str(Path.home().resolve()) self.protected_paths.add(home) self.protected_paths.add(os.path.join(home, "Documents")) self.protected_paths.add(os.path.join(home, "Desktop")) ``` ```python # operations/cleaner.py:66-120 path_obj = Path(path).expanduser().resolve() path_str = str(path_obj) # Check if path exists if not path_obj.exists(): return CleanResult( success=False, path=path_str, error="Path does not exist", dry_run=dry_run, ) # Check if protected if self.is_protected(path_str): return CleanResult( success=False, path=path_str, error="Path is protected and cannot be deleted", dry_run=dry_run, ) # Get size before deletion size = self.get_size(path_str) if dry_run: return CleanResult( success=True, path=path_str, freed_bytes=size, dry_run=True, ) # Perform deletion try: if keep_root: # Delete contents but keep directory for item in path_obj.iterdir(): if item.is_dir(): shutil.rmtree(item) ...[truncated 2981 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require a server-generated confirmation token before destructive execution: - A dry run should return a short-lived token bound to the canonical path, operation, caller, and previewed contents. - Reject `dry_run=False` unless the caller supplies the valid token. - Invalidate the token if the path or directory state changes. 2. Apply a deny-by-default path policy: - Deny filesystem roots, drive roots, home-directory ancestors, operating-system directories, program directories, and sensitive credential/configuration trees. - Protect both exact paths and all descendants or ancestors where appropriate. - Maintain platform-specific protected-path sets. 3. Restrict cleanup to explicit allowlisted cache or temporary directories. Unknown paths should require an out-of-band human approval mechanism. 4. Validate the path without following links: - Inspect the original path with `lstat()` or `Path.is_symlink()`. - Reject symlinks, junctions, mount points, and reparse points for recursive cleanup. - Revalidate immediately before deletion to reduce time-of-check/time-of-use races. 5. Run the MCP server under a dedicated, least-privileged account with access only to approved cleanup locations. 6. Record auditable details for destructive operations, including caller identity, canonical path, preview result, confirmation identifier, and outcome. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
operations/migrator.py:171
Finding
Windows Shell Command Injection and Non-Transactional Destructive Migration<![CDATA[ ## Vulnerability Details **File Location**: `operations/migrator.py:171-191`; `operations/migrator.py:204-230`; `operations/migrator.py:308-337` **Vulnerability Type**: OS command injection and unsafe filesystem migration **Risk Level**: High ### Vulnerable Code ```python # operations/migrator.py:171-191 if os.name == "nt": subprocess.run( ["rmdir", "/s", "/q", str(source_path)], shell=True, capture_output=True, timeout=300, ) else: shutil.rmtree(source_path) except Exception as e: # Rollback: remove target if we can't delete source try: if os.name == "nt": subprocess.run( ["rmdir", "/s", "/q", str(target_path)], shell=True, capture_output=True, timeout=60, ) else: shutil.rmtree(target_path) ``` ```python # operations/migrator.py:204-230 try: if os.name == "nt": if use_junction: # Junction: no admin required cmd = ["mklink", "/J", str(source_path), str(target_path)] else: # Symbolic link: requires admin cmd = ["mklink", "/D", str(source_path), str(target_path)] result = subprocess.run( cmd, shell=True, capture_output=True, timeout=30, ) if result.returncode != 0: return MigrationResult( success=False, source=str(source_path), target=str(target_path), error=( f"Create link failed: " f"{result.stderr.decode() if result.stderr else 'unknown error'}" ), ) else: os.symlink(target_path, source_path) except Exception as e: return MigrationResult( success=False, source=str(source_path), target=str(target_path), error=f"Create link failed: {str(e)}", ) ``` ``` ...[truncated 3597 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Eliminate `shell=True` for all operations involving untrusted paths. - Prefer native Windows APIs for directory removal, symbolic links, and junction creation. - Use `os.symlink()` where supported. - If a shell built-in is unavoidable, construct one rigorously quoted command using a proven Windows quoting routine and reject shell metacharacters and control characters. 2. Validate source and target paths: - Reject control characters and Windows shell metacharacters. - Reject device paths, alternate data streams, unexpected UNC paths, and reparse-point traversal. - Enforce approved source and target roots. - Ensure source and target do not overlap and that neither is an ancestor of the other. 3. Make migration transactional: - Copy into a temporary staging directory on the target filesystem. - Verify all files, directory structure, sizes, and critical metadata; use hashes where integrity is important. - Atomically rename the verified staging directory into place. - Rename the source to a recoverable backup before creating the link. - Create and verify the link. - Delete the backup only after complete success. - Restore the source automatically if link creation or verification fails. 4. Check every subprocess return code and treat nonzero status as failure before continuing. 5. Clean up partial targets after copy failures and retain recoverable backups when rollback cannot be completed safely. 6. Add Windows-specific tests using paths with spaces, Unicode, shell metacharacters, long-path prefixes, junctions, and permission failures. ]]>
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
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (30)

Credential Access

High
Category
Privilege Escalation
Content
),
    Rule(
        name="venv",
        patterns=["venv", ".venv", "env", ".env"],
        directory_type=DirectoryType.DEPENDENCY,
        risk_level=RiskLevel.MEDIUM,
        action=RecommendedAction.CAN_MOVE,
Confidence
95% confidence
Finding
Including ".env" in a rule that classifies directories as a movable Python virtual environment is dangerous because .env commonly stores secrets such as API keys, tokens, and database credentials as a file, not an environment directory. In a cleanup or relocation workflow, this misclassification could cause sensitive credential files to be moved, exposed, or deleted under the assumption that they are disposable dependency artifacts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Symbolic link: requires admin
                    cmd = ["mklink", "/D", str(source_path), str(target_path)]

                result = subprocess.run(
                    cmd,
                    shell=True,
                    capture_output=True,
Confidence
99% confidence
Finding
This is a true tool-parameter-abuse issue: shell=True is used with a command assembled from input-derived filesystem paths. In an agent or automation context, untrusted path values can be weaponized to execute arbitrary shell commands or create links at attacker-chosen locations, making the skill context more dangerous because the component is designed to act on external parameters.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Remove junction (doesn't delete target)
        try:
            if os.name == "nt":
                subprocess.run(
                    ["rmdir", path],
                    shell=True,
                    capture_output=True,
Confidence
99% confidence
Finding
This is a true parameter-abuse vulnerability because the tool invocation passes an untrusted path into a shell-mediated deletion command. In an agent setting, a caller could supply crafted values that transform a routine junction-removal action into arbitrary command execution or broader destructive deletion.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# Create symbolic link
        try:
            if os.name == "nt":
                result = subprocess.run(
                    ["mklink", "/D", path, actual_target],
                    shell=True,
                    capture_output=True,
Confidence
99% confidence
Finding
This shell-mediated mklink call accepts path and target values that may be externally influenced, creating a direct tool-parameter-abuse vector. In the context of an agent skill that may process user-supplied paths, this materially increases risk because the component can be induced to execute unintended commands or establish attacker-controlled redirections.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README presents cleanup and migration commands as normal quick-start operations without a prominent warning that they can delete contents, move directories, or alter filesystem links in ways that can break applications or cause data loss. This is more dangerous in an agent-integrated tool because an MCP-connected AI may invoke these operations semi-autonomously, increasing the chance of unsafe actions on important user directories.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README encourages AI-powered analysis that sends directory metadata, paths, and user-supplied context to third-party AI providers, but it does not prominently warn users that local system information may leave the machine. In an AI-agent/MCP context, this is more dangerous because scans of user profiles can expose sensitive path names, project names, usernames, and environment details to external services without clear user awareness.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
### Never

- Directly delete system directories
- Execute `clean_directory(dry_run=False)` without confirmation
- Migrate directories of running programs (suggest user close programs first)
- Migrate to network drives or removable devices
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.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The provider sends the constructed prompt, which includes directories and optional user context, to a remote API via HTTP. In this file there is no confirmation prompt, user-visible logging, or explanatory comment/docstring warning that local analysis data will be transmitted to an external service.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
This code sends directory metadata and optional user-provided context to an external AI provider, but there is no notice, consent flow, redaction step, or policy enforcement in this component before transmission. Even if only metadata is sent, paths, file types, drive targets, and free-form user context can contain sensitive information, creating a privacy and data-governance risk when routed to third-party or self-configured endpoints.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
This code embeds Chinese identifiers such as "微信", "腾讯", "飞书", and "钉钉" alongside English names when matching directories. Because the file does not document that the skill is specifically intended for a Chinese-language or China-market environment, this can be interpreted as an implicit locale policy choice without user opt-in or justification.

Indirect Prompt Extraction

Medium
Category
System Prompt Leakage
Content
return False

    def to_result(self, directory: DirectoryInfo) -> AnalysisResult:
        """Convert rule to analysis result."""
        return AnalysisResult(
            path=directory.path,
            directory_type=self.directory_type,
Confidence
75% confidence
Finding
Skill contains patterns that could indirectly extract system prompts through rephrasing, translation, summarization, or side-channel techniques.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
@main.command()
@click.argument("source")
@click.argument("target")
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
def migrate(source: str, target: str, yes: bool):
    """Migrate directory to new location with symbolic link."""
    migrator = DirectoryMigrator()
Confidence
85% 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.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The code configures an external AI provider from environment variables and exposes AI-backed analysis without any manifest-purpose context or visible restriction on what local metadata may be transmitted. In a filesystem-management skill, directory names, sizes, and user context can reveal sensitive personal or enterprise information when sent to a third party.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The server advertises a section as 'Local Tools (No external dependencies)' while the same module initializes an AI service that can use API credentials and later perform remote analysis. This mismatch can mislead users or reviewers about data-flow and trust boundaries, increasing the chance that local directory metadata is shared externally without informed consent.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The analyze_directories tool scans local user-profile directories and, when AI is available, sends directory data and optional user context to an external AI service with no explicit warning at the tool entrypoint. Because directory names and context often contain sensitive identifiers, this creates a confidentiality risk through silent remote transmission.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code performs irreversible deletion via shutil.rmtree() and unlink() when dry_run is false. Although docstrings describe dry-run behavior, there is no user-facing confirmation prompt, logging, or explicit warning at the execution path itself before destructive actions occur.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
The pattern-based deletion path in clean_contents() does not apply the protected-path safeguards that exist in clean(), despite the module presenting itself as a safe directory cleaner. An attacker or unsafe caller can supply broad glob patterns that match sensitive files or directories under an allowed root, causing destructive deletion without equivalent safety validation.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The clean_contents method permanently deletes matched files with unlink() and may remove directories with rmdir() when dry_run is false. The function documents its behavior for developers, but there is no user-facing disclosure, confirmation, or runtime notice for this destructive operation.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return LinkType.SYMBOLIC_LINK, target

            if os.name == "nt":
                result = subprocess.run(
                    ["fsutil", "reparsepoint", "query", path],
                    capture_output=True,
                    text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
            if os.name == "nt":
                # Use robocopy on Windows for better performance
                result = subprocess.run(
                    ["robocopy", str(source_path), str(target_path), "/E", "/R:1", "/W:1"],
                    capture_output=True,
                    timeout=3600,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The migrate operation deletes the original source directory after copying it, which is a safety-critical and potentially irreversible action. Although the docstrings describe the migration steps, there is no visible confirmation prompt, print/log disclosure, or explicit warning to the user at the point this destructive action occurs.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Delete source
        try:
            if os.name == "nt":
                subprocess.run(
                    ["rmdir", "/s", "/q", str(source_path)],
                    shell=True,
                    capture_output=True,
Confidence
98% confidence
Finding
This call uses shell=True while passing a path-derived argument into a command invocation intended to delete directories. On Windows, combining shell=True with attacker-influenced path content can enable command/argument injection or misparsing, and because the operation is recursive deletion, successful abuse can lead to destructive filesystem compromise.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
During rollback on Windows, the code removes the copied target directory using a shell command, which is another destructive filesystem operation. The code does not present any visible user disclosure, confirmation, or warning around this cleanup behavior.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Rollback: remove target if we can't delete source
            try:
                if os.name == "nt":
                    subprocess.run(
                        ["rmdir", "/s", "/q", str(target_path)],
                        shell=True,
                        capture_output=True,
Confidence
98% confidence
Finding
The rollback deletion path repeats the same unsafe pattern: shell=True combined with a path-derived argument for recursive removal. If an attacker can influence target_path, the rollback logic becomes an additional command-injection and arbitrary-deletion primitive.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Symbolic link: requires admin
                    cmd = ["mklink", "/D", str(source_path), str(target_path)]

                result = subprocess.run(
                    cmd,
                    shell=True,
                    capture_output=True,
Confidence
98% confidence
Finding
The mklink command is run with shell=True and includes source/target paths derived from input. An attacker who can influence those paths may inject additional shell syntax or alter command parsing, potentially executing arbitrary commands or creating links in unintended locations.

Static analysis

No suspicious patterns detected.