Back to skill

Security audit

EZ Unifi

Security checks for vulnerabilities and agentic risk

Overview

This is a real UniFi administration skill, but it grants broad network-control authority with weak guardrails around credentials, TLS, raw API access, and disruptive actions.

Install only if you are comfortable giving the agent administrative control of your UniFi controller. Use the narrowest dedicated local account possible, avoid Super Admin unless truly required, protect or avoid the .env file, rotate any exposed credentials, avoid passing WiFi passwords on the command line, and treat raw API or disruptive commands as operations that should require explicit human approval.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/unifi.py:233
Finding

TLS Certificate Verification Is Disabled for Controller Connections

Content
View full analysis
Controller: """Create and login to controller.""" host, username, password, port, site, _ = get_config() controller = Controller( Configuration( session, host, username=username, password=password, port=port, site=site, ssl_context=False, # Disable SSL verification for self-signed certs ) ) ``` ### Technical Analysis The controller is always initialized with `ssl_context=False`, disabling TLS certificate verification for every connection. Although this accommodates self-signed certificates, it prevents the client from authenticating the identity of the UniFi controller. An attacker with a suitable network position can present an arbitrary certificate without causing validation to fail. This makes HTTPS connections vulnerable to man-in-the-middle interception. Because the connection carries administrator credentials, authenticated session data, controller responses, and privileged management requests, disabling verification undermines both confidentiality and integrity. ### Attack Path 1. The user configures the Skill to connect to a UniFi controller over HTTPS. 2. An attacker obtains a network interception position, such as through a compromised gateway, malicious access point, ARP spoofing, or DNS manipulation. 3. The attacker redirects or intercepts traffic intended for the controller. 4. The attacker presents an untrusted certificate for an impersonated controller. 5. Because certificate verification is disabled, the client accepts the connection. 6. The attacker captures authentication data or session cookies, observes sensitive controller information, and may modify privileged API ...[truncated 604 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
SKILL.md:17
Finding

Setup Instructions Permit Excessive Controller Privileges

Content
View full analysis
To manage your UniFi network, I need API access. Please create a dedicated local admin account: > > 1. Open your UniFi controller (e.g., https://192.168.1.1) > 2. Go to **Settings → System → Admins & Users** > 3. Click **Add Admin** > 4. Enter a username (e.g., `agent-api`) > 5. Enter an email and password > 6. **Important: Disable "Remote Access"** - local-only avoids MFA issues > 7. Set Role to **Super Admin** or **Site Admin** > 8. Click **Add** ``` ### Technical Analysis The setup instructions explicitly allow the user to assign the dedicated automation account the `Super Admin` role. This grants broader authority than is necessary for many of the documented, site-scoped operations. The danger is amplified by the Skill's raw API feature, which accepts an arbitrary controller path, HTTP method, and JSON request body. Although the raw API feature does not independently escalate privileges, it can exercise any controller operation authorized for the configured account. A Super Admin account therefore expands the consequences of credential theft, operator error, or misuse beyond a single site and beyond the ordinary command set. ### Attack Path 1. The user follows the documented setup procedure and creates an account with the `Super Admin` role. 2. The account credentials or authenticated session are exposed through local credential theft, network interception, a compromised dependency, or misuse by a process capable of invoking the Skill. 3. The attacker invokes ordinary management commands or the documented raw API command. 4. Requests execute with the full permissions of the Super Admin account. 5. The attacker modifies or disrupts controller-wide resources, including ...[truncated 502 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/unifi.py:147
Finding

Controller and WLAN Credentials Are Exposed Through Plaintext Files and Process Arguments

Content
View full analysis
tuple[str, str, str, int, str, bool]: """Get configuration from environment variables.""" host = os.environ.get("UNIFI_HOST", "").rstrip("/") username = os.environ.get("UNIFI_USERNAME", "") password = os.environ.get("UNIFI_PASSWORD", "") site = os.environ.get("UNIFI_SITE", "default") is_udm = os.environ.get("UNIFI_IS_UDM", "true").lower() == "true" if not all([host, username, password]): print("ERROR: Missing config in .env", file=sys.stderr) print("Required: UNIFI_HOST, UNIFI_USERNAME, UNIFI_PASSWORD", file=sys.stderr) sys.exit(1) # Extract host and port if "://" in host: host = host.split("://")[1] port = 443 if is_udm else 8443 if ":" in host: host, port_str = host.rsplit(":", 1) port = int(port_str) return host, username, password, port, site, is_udm ``` The WLAN password command accepts the new password as a positional command-line argument: ```python p = subparsers.add_parser("wlan-password", help="Change WLAN password") p.add_argument("id", help="WLAN ID") p.add_argument("password", help="New password") ``` The documented invocation is: ```bash unifi.py wlan-password ID NEWPASS # Change WLAN password ``` ### Technical Analysis The documentation does not instruct users to protect the `.env` file with restrictive permissi ...[truncated 2034 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/unifi.py:2
Finding

Runtime Dependencies Are Not Reproducibly or Integrity Pinned

Content
View full analysis
=3.13" # dependencies = [ # "aiounifi>=88", # "aiohttp>=3.9.0", # "python-dotenv>=1.0.0", # ] # /// ``` ### Technical Analysis All third-party dependencies use open-ended lower-bound constraints. No exact versions, lockfile, cryptographic hashes, or other integrity constraints are present in the audited project. Running the script through `uv run` may therefore resolve newer versions than those originally reviewed. The effective code executed by the Skill can change over time without changes to the repository itself. This creates a supply-chain exposure if a future compatible package release is compromised, malicious, or unexpectedly incompatible. This finding does not establish that any currently named package is malicious. The risk arises from mutable resolution and the privileged context in which dependency code executes. ### Attack Path 1. A future release satisfying one of the declared lower bounds is compromised or contains malicious code. 2. A user runs the Skill in an environment that resolves dependencies without a reviewed lockfile. 3. The package manager selects and installs the affected release. 4. Package code executes during import or runtime in the same process as the Skill. 5. The dependency can access process memory and environment variables containing UniFi credentials and can perform network or filesystem operations with the user's privileges. 6. The compromised code steals credentials, alters controller requests, or performs other unauthorized actions. ### Impact Assessment A compromised dependency would execute with the local privileges of the user running the Skill and inside a process handling privileged UniFi credentials. It could potentially read environment secrets, modify controller ...[truncated 200 chars]
Remediation
View remediation
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (7)

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill advertises bounded UniFi administration tasks, but the code also exposes a generic raw API command that can send arbitrary GET/POST/PUT/DELETE requests to the controller. That hidden capability bypasses the intended command-level safety boundaries and enables actions far beyond the declared skill surface, including potentially dangerous configuration changes or data access.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/unifi.py (reported line 158)May include surrounding context.

python
is_udm = os.environ.get("UNIFI_IS_UDM", "true").lower() == "true"

    if not all([host, username, password]):
        print("ERROR: Missing config in .env", file=sys.stderr)
        print("Required: UNIFI_HOST, UNIFI_USERNAME, UNIFI_PASSWORD", file=sys.stderr)
        sys.exit(1)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The raw command constructs ApiRequest objects directly from user-supplied method, path, and JSON body, allowing arbitrary controller API mutations. In an agent skill, this creates a broad capability escalation channel that defeats the safer abstractions of the named commands and could be abused to alter network, security, or account settings.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill documents capabilities that require environment access and outbound network communication, but it does not declare any explicit tool scope or permissions boundary. In an agent setting, missing scope metadata increases the chance that the skill is invoked with broader-than-expected privileges, making credential handling and controller access less constrained and less auditable.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill exposes many disruptive and irreversible administrative actions—such as restarting devices, disabling ports/WLANs, blocking clients, changing passwords, upgrading firmware, and raw API calls—without requiring explicit user confirmation or prominently warning about service impact. In an agent workflow, that creates a real risk of accidental outages, lockouts, or policy changes from ambiguous or misinterpreted prompts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Many commands perform disruptive or destructive actions immediately—such as restarts, firmware upgrades, port disablement, PoE power cycling, client blocking/forgetting, WLAN disablement, and voucher deletion—without any confirmation, dry-run mode, or policy guardrail. In an agent-mediated environment, a mistaken prompt interpretation or malicious instruction could directly cause outages or lock out users.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/unifi.py (reported line 209)May include surrounding context.

python
for key in keys:
            val: Any = ""
            if hasattr(item, key):  # type: ignore[arg-type]
                val = getattr(item, key)  # type: ignore[arg-type]
            elif hasattr(item, "raw") and key in item.raw:  # type: ignore[union-attr]
                val = item.raw[key]  # type: ignore[union-attr]
            elif isinstance(item, dict) and key in item:

Static analysis

No suspicious patterns detected.