Back to skill

Security audit

Phy Xss Audit

Security checks for vulnerabilities and agentic risk

Overview

This is a coherent XSS scanner, but it needs review because recursive scanning can follow symlinks and read files outside the selected project.

Install only if you are comfortable with a local scanner reading source files under the target path and printing short matching code snippets. Avoid running it on untrusted repositories or CI workspaces that may contain symlinks until symlink containment is fixed or you manually remove symlinks before scanning.

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 (1)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/xss_audit.py:381
Finding

Recursive Scanner Follows Symbolic Links Outside the Selected Scan Root

Content
View full analysis

Vulnerability Details

File Location: scripts/xss_audit.py:153-158 and scripts/xss_audit.py:381-386
Vulnerability Type: Unrestricted symbolic-link traversal / scan-boundary bypass
Risk Level: Medium

Vulnerable Code

python
def read_lines(path: Path) -> list[str]:
    for enc in ("utf-8", "latin-1"):
        try:
            return path.read_text(encoding=enc).splitlines()
        except Exception:
            pass
    return []
python
def scan_directory(root: Path) -> list[Finding]:
    findings: list[Finding] = []
    SKIP_DIRS = {".git", "node_modules", ".venv", "venv", "__pycache__",
                 "dist", "build", ".next", "vendor", "coverage"}
    for path in root.rglob("*"):
        if not path.is_file():
            continue
        if any(p in SKIP_DIRS for p in path.parts):
            continue
        findings.extend(scan_file(path))
    return findings

Technical Analysis

The recursive scanner does not reject symbolic links or verify that a discovered file resolves beneath the selected scan root. Both Path.is_file() and Path.read_text() follow symbolic links.

An attacker who controls files in a scanned repository can create a symbolic link with a supported source-code extension, such as .py, .js, or .html, that points to a file elsewhere on the host. When the repository is scanned, root.rglob("*") discovers that link, path.is_file() follows it, and read_text() reads the external target.

The extension check is performed against the link's filename rather than the resolved target. Therefore, a link named external.py can cause an arbitrary readable target to be processed even if the target itself has another extension.

Attack Path

  1. An attacker adds a symbolic link to a repository or other directory that will be scanned.
  2. The link uses a supported extension and points outside the repository, for example:
    text

...[truncated 1444 chars]

Remediation
View remediation

Remediation Suggestions

  1. Reject symbolic links before inspecting or reading candidates:

    python
    if path.is_symlink():
        continue
    
  2. Resolve the scan root and every candidate, then enforce containment:

    python
    root_resolved = root.resolve()
    
    for path in root.rglob("*"):
        if path.is_symlink():
            continue
    
        try:
            resolved = path.resolve(strict=True)
            resolved.relative_to(root_resolved)
        except (OSError, ValueError):
            continue
    
        if not resolved.is_file():
            continue
    
        findings.extend(scan_file(resolved))
    
  3. Perform containment validation immediately before opening the file to reduce time-of-check/time-of-use exposure. For stronger protection on supported platforms, open files without following symbolic links and validate the resulting file descriptor.

  4. Only process regular files and impose a reasonable maximum file-size limit before calling read_text() to reduce availability risks from oversized files.

  5. Add automated tests covering:

    • A symlink to a file outside the scan root.
    • A symlink whose name has a supported extension but whose target does not.
    • Broken and circular symbolic links.
    • Ordinary files inside the root.
    • Files changed into symbolic links during scanning.
Vulnerability Patterns
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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
Findings (19)

Unvalidated Output Injection

High
Category
Output Handling
Confidence
75% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · SKILL.md (reported line 76)May include surrounding context.

md
message="dangerouslySetInnerHTML used without DOMPurify.sanitize() guard",
            file=str(path), line=i + 1,
            snippet=line.strip()[:120],
            fix="Wrap the value: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userHtml) }}",
        ))

def check_xv005(path: Path, lines: list[str], findings: list[Finding]) -> None:

Unvalidated Output Injection

High
Category
Output Handling
Confidence
75% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · SKILL.md (reported line 194)May include surrounding context.

md
message="dangerouslySetInnerHTML used without DOMPurify.sanitize() guard",
            file=str(path), line=i + 1,
            snippet=line.strip()[:120],
            fix="Wrap the value: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userHtml) }}",
        ))

def check_xv005(path: Path, lines: list[str], findings: list[Finding]) -> None:

Unvalidated Output Injection

High
Category
Output Handling
Confidence
75% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · SKILL.md (reported line 195)May include surrounding context.

md
message="dangerouslySetInnerHTML used without DOMPurify.sanitize() guard",
            file=str(path), line=i + 1,
            snippet=line.strip()[:120],
            fix="Wrap the value: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userHtml) }}",
        ))

def check_xv005(path: Path, lines: list[str], findings: list[Finding]) -> None:

Unvalidated Output Injection

High
Category
Output Handling
Confidence
75% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 257)May include surrounding context.

python
message="dangerouslySetInnerHTML used without DOMPurify.sanitize() guard",
            file=str(path), line=i + 1,
            snippet=line.strip()[:120],
            fix="Wrap the value: dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userHtml) }}",
        ))

def check_xv005(path: Path, lines: list[str], findings: list[Finding]) -> None:

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · SKILL.md (reported line 59)May include surrounding context.

md
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · SKILL.md (reported line 99)May include surrounding context.

md
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

md
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 28)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 124)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 126)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 127)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 128)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 129)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 130)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 371)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 382)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
80% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/xss_audit.py (reported line 385)May include surrounding context.

python
"XV007": ("PHP echo/print without htmlspecialchars",   "HIGH",     "CWE-79"),
    "XV008": ("ERB raw / html_safe without sanitize",      "HIGH",     "CWE-79"),
    "XV009": ("Vue v-html with dynamic expression",        "HIGH",     "CWE-79"),
    "XV010": ("Angular bypassSecurityTrust / Go template.HTML cast", "HIGH", "CWE-79"),
}

SEVERITY_ORDER = {"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3}

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

This code walks the target directory recursively and opens every matching file it finds, which is a safety-relevant filesystem read operation. While the CLI help names a file or directory target, there is no explicit warning, confirmation, or comment disclosing that a directory target results in recursive project-wide scanning and file access.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/xss_audit.py:21