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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
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.

Content

Scanner excerpt · scripts/control4_cli.py (reported line 29)May include surrounding context.

python
from pyControl4.relay import C4Relay
from pyControl4.room import C4Room

ENV_FILE = Path(__file__).resolve().parent / ".env"
SENSITIVE_METHOD_KEYWORDS = (
    "arm",
    "disarm",

Description-Behavior Mismatch

High
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/nl_control4.py (reported line 42)May include surrounding context.

python
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]:

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/control4_cli.py (reported line 118)May include surrounding context.

python
return sorted(
        m
        for m in dir(obj)
        if not m.startswith("_") and callable(getattr(obj, m, None))
    )

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/control4_cli.py (reported line 261)May include surrounding context.

python
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:

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
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.

Content

Scanner excerpt · scripts/control4_cli.py (reported line 274)May include surrounding context.

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(