Back to skill

Security audit

Control4 Home

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Control4 smart-home controller, but it exposes a broad generic command path that can affect sensitive home and security devices.

Install only if you understand that this gives an agent authenticated control over your Control4 system, including potential security-panel and relay actions. Prefer removing or disabling the generic call command, pinning the manual dependency install, protecting scripts/.env with restrictive permissions or using environment-injected secrets, and requiring explicit confirmation for alarms, doors, gates, garages, locks, blinds, and sensitive relays.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/control4_cli.py:248
Finding
Overbroad Generic Method Invocation with Incomplete Safety Enforcement## Vulnerability Details **File Location**: `scripts/control4_cli.py:248-270`; related command-line configuration at `scripts/control4_cli.py:360-366` **Vulnerability Type**: Arbitrary public API method invocation through reflection **Risk Level**: High **Affected code:** ```python async def cmd_call(args: argparse.Namespace) -> int: director = await _login_and_director() obj = _entity_factory(args.entity, director, args.id) fn = getattr(obj, args.method, None) is_sensitive = any(k in args.method.lower() for k in SENSITIVE_METHOD_KEYWORDS) if is_sensitive and not args.allow_sensitive: raise RuntimeError( "Sensitive method blocked. Re-run with --allow-sensitive if intentional." ) if not callable(fn): raise RuntimeError(f"Method not found: {args.method}") call_args = json.loads(args.args_json) if args.args_json else [] call_kwargs = json.loads(args.kwargs_json) if args.kwargs_json else {} if not isinstance(call_args, list): raise RuntimeError("--args-json must decode to a JSON array") if not isinstance(call_kwargs, dict): raise RuntimeError("--kwargs-json must decode to a JSON object") result = fn(*call_args, **call_kwargs) if inspect.isawaitable(result): result = await result ``` Related command-line exposure: ```python sp = sub.add_parser("call", help="Call any exposed method on an entity") sp.add_argument("--entity", required=True, choices=["director", "light", "relay", "climate", "blind", "room", "fan", "security-panel", "contact-sensor"]) sp.add_argument("--id", type=int) sp.add_argument("--method", required=True) sp.add_argument("--args-json", help='JSON array, e.g. "[10,1000]"') sp.add_argument("--kwargs-json", help='JSON object, e.g. "{\"LEVEL\":20}"') sp.add_argument("--allow-sensitive", action="store_true", help="Allow sensitive methods (arm/disarm/open/close/etc)") ``` ...[truncated 2596 chars]
Remediation
## Remediation Suggestions 1. Remove unrestricted reflective invocation from normal Skill operation. 2. Define explicit per-entity allowlists containing only the methods required for supported use cases. 3. Separate read-only methods from mutating methods and deny all unrecognized methods by default. 4. Disable director and security-panel mutations unless an administrator explicitly enables them in local configuration. 5. Require interactive confirmation for physical-security operations, including alarm changes, locks, gates, garages, doors, and sensitive relays. 6. Do not treat a command-line flag alone as sufficient authorization. Consider a separate privileged configuration file or execution mode with restrictive file permissions. 7. Validate argument count, types, ranges, and accepted values independently for every permitted method. 8. Record security-sensitive operations in an audit log without recording passwords or bearer tokens. 9. If generic method inspection is required, retain `methods` as a read-only diagnostic command while replacing `call` with narrowly scoped commands.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:29
Finding
Manual Setup Installs an Unpinned Third-Party Dependency## Vulnerability Details **File Location**: `SKILL.md:29-32` **Vulnerability Type**: Unpinned dependency installation **Risk Level**: Medium **Affected code:** ```markdown 1. Create a Python venv (example): - `python3 -m venv .venv-control4` 2. Install dependency: - `.venv-control4/bin/pip install pyControl4` ``` ### Technical Analysis The manual setup instructions install `pyControl4` without a version constraint or integrity hash. This causes pip to resolve whichever release is current at installation time. The behavior is inconsistent with the Skill metadata, which specifies `pyControl4==1.6.0`. As a result, users following the documented manual setup process do not receive the reviewed version deterministically. A future compromised, malicious, or incompatible package release could execute installation-time or import-time code and would subsequently run in the context of the user operating the Skill. No evidence was found that the currently referenced package is malicious. The vulnerability is the mutable and unverifiable dependency resolution process. ### Attack Path 1. A user follows the manual installation instructions in `SKILL.md`. 2. Pip queries its configured package index and resolves the latest available `pyControl4` release rather than the metadata-declared version. 3. A compromised or malicious future release is downloaded and installed. 4. Package-controlled code executes during installation, import, or normal Skill use. 5. During normal operation, the dependency runs in a process that handles Control4 credentials, bearer tokens, controller addresses, and authenticated home-automation operations. ### Impact Assessment A malicious dependency would execute with the operating-system permissions of the user running pip or the Skill. It could potentially read files accessible to that user, access Control4 credentials from environment variables or `scripts/.env`, capture authentication tokens, ...[truncated 301 chars]
Remediation
## Remediation Suggestions 1. Make the manual installation command consistent with the metadata: ```bash .venv-control4/bin/pip install pyControl4==1.6.0 ``` 2. Prefer a locked requirements file that pins all direct and transitive dependencies. 3. Add cryptographic hashes and install with `pip install --require-hashes -r requirements.txt`. 4. Review and deliberately update dependency versions rather than resolving the latest release at installation time. 5. Use only trusted package indexes and explicitly configure the expected index where practical. 6. Run dependency vulnerability and provenance checks as part of release maintenance. 7. Keep the virtual environment unprivileged and never install the package as root.
Vulnerability Patterns
  • 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
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (9)

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The file advertises generic exposure of most pyControl4 entity methods, which exceeds the declared smart-home use case of lights, relays, room media, and device mapping. In a home automation context, broad capability exposure is dangerous because pyControl4 may include functions affecting alarms, locks, blinds, garage-like relays, or other safety/security functions that users of this skill did not consent to expose.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
Including `C4SecurityPanel` and `C4ContactSensor` support introduces alarm and occupancy/security-related capabilities not justified by the skill description. Even if some methods are read-only, their presence enables sensitive state disclosure and, when combined with the generic method caller, potentially alarm control beyond the user's expected device-control scope.

Credential Access

High
Category
Privilege Escalation
Content
from pyControl4.relay import C4Relay
from pyControl4.room import C4Room

ENV_FILE = Path(__file__).resolve().parent / ".env"
SENSITIVE_METHOD_KEYWORDS = (
    "arm",
    "disarm",
Confidence
83% confidence
Finding
The script loads Control4 credentials from a local `.env` file in the skill directory, creating a credential-at-rest risk if the file is committed, copied, or readable by unintended users/processes. In this context those credentials grant access to the home automation account and controller, so compromise can lead to broad device control and sensitive home-state exposure.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The `call` command accepts an arbitrary public method name plus attacker-controlled arguments and executes it on privileged Control4 objects. The only safeguard is a substring blacklist for a few keywords, which is not a sound authorization control and can miss impactful methods such as configuration changes, source routing, sensor queries, or vendor-specific actions with non-obvious names; in a smart-home environment this can directly affect physical security and privacy.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes local Python scripts, reads environment-based credentials, and relies on shell execution, but it does not declare any explicit tool scope or permissions boundaries. This creates an authorization gap where an agent runtime may grant broader-than-intended access to shell, files, and secrets, increasing the chance of unintended command execution or data exposure.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run_cli(args: list[str]) -> int:
    cmd = [sys.executable, str(CLI), *args]
    return subprocess.run(cmd).returncode


def parse_light_level(text: str) -> Optional[int]:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
return sorted(
        m
        for m in dir(obj)
        if not m.startswith("_") and callable(getattr(obj, m, None))
    )
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
obj = _entity_factory(args.entity, director, args.id)
    methods = []
    for m in _public_methods(obj):
        fn = getattr(obj, m)
        try:
            sig = str(inspect.signature(fn))
        except Exception:
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
async def cmd_call(args: argparse.Namespace) -> int:
    director = await _login_and_director()
    obj = _entity_factory(args.entity, director, args.id)
    fn = getattr(obj, args.method, None)
    is_sensitive = any(k in args.method.lower() for k in SENSITIVE_METHOD_KEYWORDS)
    if is_sensitive and not args.allow_sensitive:
        raise RuntimeError(
Confidence
96% confidence
Finding
The `call` command performs dynamic method lookup from user-controlled input and then invokes the selected method on privileged Control4 objects. In this skill, that is not just reflective programming: it exposes a broad, undocumented control surface that can reach security, access, or destructive device actions, while the keyword-based sensitive-method block is easily incomplete and bypassable for dangerous methods not matching those substrings.