Back to skill

Security audit

Codebase Radar

Security checks for vulnerabilities and agentic risk

Overview

This is a read-only codebase scanner, but its path and limit controls are weak enough that it may read or report files outside the intended project.

Install only if you are comfortable running it on trusted repositories or after checking for symlinks and very large trees. Do not rely on --exclude or --max-files to fully limit what it reads, and treat generated Mermaid output from untrusted code as untrusted text.

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

Warning
Location
scripts/radar.py:44
Finding

Scan Limits and Exclusion Controls Do Not Restrict File Content Access

Content
View full analysis

Vulnerability Details

File Location: scripts/radar.py:44-60, scripts/radar.py:71-75, scripts/radar.py:111-116, scripts/radar.py:166, scripts/radar.py:177-179
Vulnerability Type: Inadequate path-boundary enforcement and resource limiting
Risk Level: Medium

Vulnerable Code

python
def scan_project(root: Path, max_files: int = 5000) -> dict:
    """Scan project structure."""
    info = {"files": 0, "dirs": 0, "loc": 0, "languages": Counter(), "extensions": Counter()}
    if not root.exists():
        return info

    for f in root.rglob("*"):
        if f.is_dir():
            # Skip hidden dirs and common dependency dirs
            if f.name.startswith(".") or f.name in ("node_modules", "__pycache__", "target", "build", "dist", ".git"):
                continue
            info["dirs"] += 1
        elif f.is_file():
            info["files"] += 1
            ext = f.suffix.lstrip(".") or "noext"
            info["extensions"][ext] += 1
            if info["files"] > max_files:
                break
    return info

The dependency scan independently traverses and reads files without applying the configured file limit:

python
for f in sorted(root.rglob("*")):
    if not f.is_file() or f.name.startswith("."):
        continue
    ext = f.suffix.lower()
    lang = None

The technical-debt scan similarly reads every matching entry without a shared limit or path-boundary check:

python
for f in sorted(root.rglob("*")):
    if not f.is_file() or f.name.startswith("."):
        continue
    try:
        text = f.read_text(encoding="utf-8", errors="replace")
    except Exception:
        continue

The exclusion option is accepted but never used:

python
parser.add_argument("--exclude", nargs="+", default=[])

All scan phases run regardless of the project scan's max_files limit:

python
project = scan_projec
...[truncated 2966 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace independent rglob() traversals with one centralized traversal using os.walk() so excluded directories can be removed from dirnames before descent.
  2. Normalize the root once with root = root.resolve(strict=True).
  3. Resolve each file candidate and verify that it remains beneath the normalized root before opening it:
    python
    resolved = candidate.resolve(strict=True)
    try:
        resolved.relative_to(root)
    except ValueError:
        continue
    
  4. Reject symbolic links by default with candidate.is_symlink(), or expose an explicit opt-in option for following them.
  5. Compile --exclude values into normalized path or glob rules and apply them consistently to project counting, dependency scanning, and technical-debt scanning.
  6. Enforce one shared file budget across every scan phase rather than limiting only metadata collection.
  7. Add a maximum cumulative byte budget and a per-file size limit before calling read_text().
  8. Avoid materializing complete traversals with sorted(root.rglob("*")) for untrusted or large repositories.
  9. Clearly report skipped files, exhausted limits, symlink rejections, and exclusions so users can assess scan completeness.
  10. Add tests covering hidden directories, nested dependency directories, exclusions, oversized trees, and symlinks resolving outside the root.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/radar.py:145
Finding

Repository-Controlled Import Names Are Embedded into Mermaid Without Safe Encoding

Content
View full analysis

Vulnerability Details

File Location: scripts/radar.py:145-156
Vulnerability Type: Mermaid syntax injection
Risk Level: Low

Vulnerable Code

python
def generate_mermaid(deps: dict) -> str:
    """Generate Mermaid graph."""
    lines = ["```mermaid", "graph TD"]
    for n in deps.get("nodes", [])[:30]:
        safe = n.replace(".", "_").replace("/", "_").replace("-", "_")
        lines.append(f"  {safe}[\"{n[:30]}\"]")
    for a, b in deps.get("edges", [])[:50]:
        safe_a = a.replace(".", "_").replace("/", "_").replace("-", "_")
        safe_b = b.replace(".", "_").replace("/", "_").replace("-", "_")
        lines.append(f"  {safe_a} --> {safe_b}")
    lines.append("```")
    return "\n".join(lines)

Technical Analysis

Dependency names are extracted from source files in the scanned repository and must therefore be treated as attacker-controlled input. The sanitization replaces only periods, forward slashes, and hyphens. It does not safely encode quotation marks, brackets, semicolons, backticks, control characters, or other tokens with meaning in Mermaid syntax.

The same partially transformed value is used as a Mermaid node identifier, while the original value is directly inserted into a quoted label. A crafted import token can terminate or alter the expected node declaration and introduce additional Mermaid syntax. This is an output-injection issue rather than local command execution by the Python scanner itself.

Exploitability and final consequences depend on the downstream application that renders the generated Mermaid document. A strict and current renderer may limit the result to diagram corruption, while a permissive, outdated, or insecurely configured renderer could expose additional rendering-layer risks.

Attack Path

  1. An attacker places a source file containing a specially crafted import token in a repository.
  2. A victim runs the scanner with `--visual ...[truncated 1285 chars]
Remediation
View remediation

Remediation Suggestions

  1. Never derive Mermaid identifiers directly from dependency names. Assign opaque identifiers such as sequential values (node_1, node_2) or fixed-length hashes.
  2. Maintain a mapping between each dependency name and its opaque identifier so edges reference only trusted identifiers.
  3. Escape labels according to Mermaid's quoted-string rules rather than relying on a small replacement list.
  4. Reject or encode quotation marks, brackets, braces, semicolons, backticks, line breaks, carriage returns, and other control or syntax characters.
  5. Consider serializing labels through a well-tested Mermaid generation library instead of constructing statements with string interpolation.
  6. Limit label length after encoding and normalize unusual Unicode control characters.
  7. Recommend strict Mermaid security mode and a current renderer version wherever generated output is displayed.
  8. Add adversarial tests using dependency names containing quotes, brackets, semicolons, backticks, Unicode controls, and other Mermaid metacharacters.
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (2)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill describes repository scanning behavior that necessarily reads project files, but it does not declare any explicit tool scope such as permissions or allowed-tools. Even if the intended behavior is read-only, the absence of a constrained scope can cause an agent runtime to grant broader-than-necessary file access or make the skill's file-reading capability insufficiently auditable.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The inline comment on L144 says coverage is estimated from comment ratio, which describes a real analysis step. However, L145 hard-codes coverage_estimate to 5, so the documented intent contradicts the implemented behavior rather than merely omitting detail.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.