Back to skill

Security audit

Agentic MCP Server Builder

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent scaffolding purpose, but its bundled script can overwrite user-chosen files outside the workspace without enough safeguards.

Review this before installing if you expect strict workspace-only writes. Use only trusted input paths, place output inside the project, avoid running it in directories that may contain attacker-controlled symlinks, and treat dry-run as skipping scaffold files rather than guaranteeing no filesystem changes.

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

Warning
Location
scripts/scaffold_mcp_server.py:61
Finding

Unrestricted File Creation and Overwrite Through the Output Path

Content
View full analysis

Vulnerability Details

File Location: scripts/scaffold_mcp_server.py, lines 15-17, 61-91, and 156
Vulnerability Type: Unrestricted filesystem write
Risk Level: Medium

Vulnerable Code

python
parser.add_argument("--output", required=True, help="Path to output artifact.")
python
def render(result: dict, output_path: Path, fmt: str) -> None:
    output_path.parent.mkdir(parents=True, exist_ok=True)

    if fmt == "json":
        output_path.write_text(json.dumps(result, indent=2), encoding="utf-8")
        return

    if fmt == "md":
        lines = [
            f"# {result['summary']}",
            "",
            f"- status: {result['status']}",
            "",
            "## Planned Files",
        ]
        for item in result["details"]["file_map"]:
            lines.append(f"- {item}")
        lines.extend(["", "## Tools"])
        for tool in result["details"]["tools"]:
            lines.append(f"- {tool['name']}: {tool['description']}")
        output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
        return

    with output_path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.writer(handle)
        writer.writerow(["name", "description"])
        for tool in result["details"]["tools"]:
            writer.writerow([tool["name"], tool["description"]])
python
render(result, Path(args.output), args.format)

Technical Analysis

The user-controlled --output value is converted directly into a Path and passed to render(). Unlike scaffold_root, this path is not resolved and checked against the current workspace. The function creates missing parent directories and then uses write_text() or open("w"), both of which create a new file or truncate an existing one.

Consequently, an invocation can specify an absolute path or a relative path containing traversal components. The operation is lim ...[truncated 945 chars]

Remediation
View remediation

Remediation Suggestions

  • Resolve the output path before writing and verify that it remains beneath the approved workspace root.
  • Reject absolute paths and traversal outside the workspace unless a separate, explicit external-write option is enabled.
  • Refuse to overwrite existing output files by default.
  • Add a clearly named --force option if replacement is required.
  • Reject symbolic-link destinations and validate every path component before opening the file.
  • Where practical, open new files using exclusive creation semantics to prevent accidental truncation.
  • Add tests covering absolute paths, .. traversal, existing files, and symbolic links.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/scaffold_mcp_server.py:94
Finding

Workspace Boundary Bypass Through Symlinked Scaffold Destinations

Content
View full analysis

Vulnerability Details

File Location: scripts/scaffold_mcp_server.py, lines 43-59 and 94-105
Vulnerability Type: Symlink-following filesystem boundary bypass
Risk Level: Medium

Vulnerable Code

python
def resolve_path_in_workspace(
    raw_path: Path,
    workspace_root: Path,
    label: str,
    allow_outside_workspace: bool,
) -> Path:
    resolved = raw_path.resolve()
    if allow_outside_workspace:
        return resolved
    try:
        resolved.relative_to(workspace_root)
    except ValueError as exc:
        raise ValueError(
            f"{label} must be inside workspace root: {workspace_root}"
        ) from exc
    return resolved
python
def maybe_write_scaffold(root: Path, file_map: list[str], dry_run: bool) -> None:
    if dry_run:
        return
    for relative_path in file_map:
        path = root / relative_path
        path.parent.mkdir(parents=True, exist_ok=True)
        if path.suffix == ".py":
            path.write_text("# Starter file\n", encoding="utf-8")
        elif path.suffix == ".json":
            path.write_text("{}\n", encoding="utf-8")
        else:
            path.write_text("# Starter document\n", encoding="utf-8")

Technical Analysis

The script resolves and validates only the scaffold root. It subsequently appends fixed child paths and writes to them without resolving each final destination or checking for symbolic links.

If an existing child such as server.py, tool_registry.py, schemas/tools.json, or README.md is a symbolic link to a file outside the workspace, Python's normal file-writing operations follow that link. The root directory can therefore pass the workspace check while the effective write target lies outside the permitted boundary. Existing ordinary files are also overwritten without confirmation.

Attack Path

  1. An attacker prepares or modifies a scaffold directory located ins ...[truncated 937 chars]
Remediation
View remediation

Remediation Suggestions

  • Resolve and validate every final destination rather than validating only the scaffold root.
  • Confirm that each resolved destination remains beneath the resolved workspace root.
  • Reject symbolic links in the destination and in all relevant parent path components.
  • Use no-follow operating-system semantics where available.
  • Refuse to overwrite any existing scaffold file unless an explicit --force option is provided.
  • Consider creating a new scaffold directory atomically and failing if it already exists.
  • Add regression tests using symlinked files and symlinked intermediate directories that point outside the workspace.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/scaffold_mcp_server.py:145
Finding

Dry-Run Mode Violates Its No-Write Contract

Content
View full analysis

Vulnerability Details

File Location: scripts/scaffold_mcp_server.py, lines 94-96 and 145-157; references/mcp-scaffold-guide.md, line 21
Vulnerability Type: Unexpected destructive filesystem operation
Risk Level: Low

Vulnerable Code

The documentation promises that dry-run mode performs no file writes:

markdown
- Dry-run mode performs no file writes.

Scaffold writes are conditionally disabled:

python
def maybe_write_scaffold(root: Path, file_map: list[str], dry_run: bool) -> None:
    if dry_run:
        return

However, the output artifact is written unconditionally:

python
result = {
    "status": "ok",
    "summary": f"Prepared MCP scaffold for '{server_name}'",
    "artifacts": [str(Path(args.output))],
    "details": {
        "server_name": server_name,
        "scaffold_root": str(scaffold_root),
        "file_map": file_map,
        "tools": normalized_tools,
        "dry_run": args.dry_run,
    },
}

render(result, Path(args.output), args.format)
return 0

Technical Analysis

The dry_run condition prevents only the creation of scaffold files. It does not prevent render() from creating parent directories or creating and truncating the output artifact. This contradicts the documented guarantee that dry-run mode performs no file writes.

Because the output path is separately unrestricted, users who rely on dry-run as a non-mutating validation operation may still overwrite a file outside the workspace.

Attack Path

  1. A user or automated Agent invokes the generator with --dry-run, expecting no filesystem changes.
  2. A writable path is supplied through --output.
  3. Scaffold creation is skipped by maybe_write_scaffold().
  4. The script nevertheless invokes render().
  5. The output path is created or an existing file is truncated and replaced.

Impact Assessment

The primary impact is violation of a document ...[truncated 272 chars]

Remediation
View remediation

Remediation Suggestions

  • In dry-run mode, avoid all filesystem writes, including output artifact creation.
  • Emit the generated report to standard output when --dry-run is active.
  • Alternatively, introduce separate options for scaffold dry-run and report destination, and document their behavior precisely.
  • Ensure directory creation is also disabled during dry-run.
  • Add tests that snapshot the filesystem before and after a dry-run and verify that no files or directories changed.
  • Apply the output-path containment and overwrite protections described for the unrestricted output-path finding.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (1)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill instructs use of a bundled scaffold script and references generation of starter artifacts, which implies file read/write behavior, but it does not declare any explicit tool scope such as permissions or allowed-tools. That omission weakens least-privilege controls and can let an agent invoke broader filesystem capabilities than the skill contract makes clear, increasing the chance of unintended file access or modification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.