Back to skill

Security audit

LG ThinQ

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its LG appliance-control purpose, but it includes an under-documented raw command that can send arbitrary authenticated control payloads to connected appliances.

Review this before installing if you want only bounded, predictable appliance control. Use a dedicated or least-privileged ThinQ token if available, store it with restrictive file permissions, and avoid using the raw command unless you understand the exact payload and device impact. Prefer a version that removes or gates raw control and validates all state-changing commands.

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)

T08 · Insecure Dependencies

Warning
Location
scripts/thinq.py:15
Finding

Unpinned Third-Party Dependency Installation

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/thinq.py:24
Finding

Sensitive Token and Device Metadata Stored Without Enforced Private Permissions

Content
View full analysis
~/.config/lg-thinq/token` 3. Store country code: `echo "MX" > ~/.config/lg-thinq/country` ``` Configuration and cache handling: ```python CONFIG_DIR = Path.home() / ".config" / "lg-thinq" TOKEN_FILE = CONFIG_DIR / "token" COUNTRY_FILE = CONFIG_DIR / "country" DEVICES_CACHE = CONFIG_DIR / "devices.json" def get_config(): """Load token and country from config files.""" if not TOKEN_FILE.exists(): print(f"❌ Token not found. Save it to: {TOKEN_FILE}") sys.exit(1) token = TOKEN_FILE.read_text().strip() country = COUNTRY_FILE.read_text().strip() if COUNTRY_FILE.exists() else "US" return token, country def get_cached_devices(): """Get cached device list.""" if DEVICES_CACHE.exists(): return json.loads(DEVICES_CACHE.read_text()) return None def cache_devices(devices): """Cache device list.""" CONFIG_DIR.mkdir(parents=True, exist_ok=True) DEVICES_CACHE.write_text(json.dumps(devices, indent=2)) ``` ### Technical Analysis The access token is stored as plaintext in the user's home directory, and neither the setup command nor the Python implementation verifies or enforces restrictive permissions. The actual permissions therefore depend on the existing directory permissions and the user's `umask`. The device cache is also written without an explicit file mode. It can contain appliance identifiers, aliases, models, and device types. These details disclose information about the user's household and can facilitate targeted use of a stolen API credential. The issue is primarily relevant on multi-user systems, systems with an unsafe `u ...[truncated 1176 chars]
Remediation
View remediation
~/.config/lg-thinq/token ``` 6. Store only the minimum device metadata needed for alias resolution and document the cache's sensitivity. 7. Provide token revocation and rotation guidance in case local disclosure is suspected. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/thinq.py:288
Finding

Undocumented Arbitrary Appliance-Control Interface

Content
View full analysis
") sys.exit(1) device_id = find_device_id(sys.argv[2], devices) payload = json.loads(sys.argv[3]) result = asyncio.run(control_device(token, country, device_id, payload)) print(f"✅ Done: {result}") return ``` The control function submits the supplied payload with the configured account token: ```python async def control_device(token, country, device_id, payload): """Send control command to device.""" client_id = str(uuid.uuid4()) async with ClientSession() as session: api = ThinQApi(session=session, access_token=token, country_code=country, client_id=client_id) return await api.async_post_device_control(device_id, payload) ``` ### Technical Analysis The Skill documentation presents bounded operations such as setting refrigerator temperatures and toggling specific refrigerator modes. The implementation additionally accepts arbitrary JSON and submits it to any device found through an alias, partial identifier, or direct identifier. The raw operation has no per-device command allowlist, JSON schema validation, device-type restriction, user confirmation, or separation from the same credential used by ordinary status and control commands. Consequently, callers are not limited to the documented control surface. The effective capabilities depend on which control payloads the ThinQ API and associated appliances accept. This does not bypass ThinQ's server-side authorization: the request remains limited to devices and operations permitted by the configured token. Nevertheless, it removes the local least-privilege boundary implied by the Skill's documente ...[truncated 1161 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/thinq.py:228
Finding

Insufficient Validation of Safety-Relevant Appliance Controls

Content
View full analysis
") sys.exit(1) temp = int(sys.argv[2]) payload = {"temperature": {"targetTemperature": temp, "locationName": "FREEZER", "unit": "C"}} asyncio.run(control_device(token, country, fridge_id, payload)) print(f"✅ Freezer set to {temp}°C") return ``` Toggle commands interpret every value other than `on` as `off`: ```python # Express fridge if cmd == "express-fridge": if len(sys.argv) < 3: print("Usage: thinq.py express-fridge on|off") sys.exit(1) value = sys.argv[2].lower() == "on" payload = {"refrigeration": {"expressFridge": value}} asyncio.run(control_device(token, country, fridge_id, payload)) print(f"✅ Express Fridge: {'ON' if value else 'OFF'}") return # Express freeze if cmd == "express-freeze": if len(sys.argv) < 3: print("Usage: thinq.py express-freeze on|off") sys.exit(1) value = sys.argv[2].lower() == "on" payload = {"refrigeration": {"expressMode": value}} asyncio.run(control_device(token, country, fridge_id, payload)) print(f"✅ Express Freeze: {'ON' if value else 'OFF'}") return # Eco mode if cmd == "eco": if len(sys.argv) < 3: print("Usage: thinq.py eco on|off") sys.exit(1) value = sys.argv[2].lower() == "on" payload = {"ecoFriendly": {"ecoFriendlyMode": value}} asyncio.run(control_device(token, country, fridge_id, payload)) print(f"✅ Eco Mode: {'ON' if value else 'OFF ...[truncated 1848 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (7)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
## Setup

1. Get a Personal Access Token from https://connect-pat.lgthinq.com
2. Store token: `echo "YOUR_TOKEN" > ~/.config/lg-thinq/token`
3. Store country code: `echo "MX" > ~/.config/lg-thinq/country`

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill’s stated purpose is targeted control of specific LG appliances and limited operations, but the implementation also exposes a generic raw path that forwards arbitrary JSON payloads to any resolved ThinQ device. This bypasses the narrow command model, enabling unreviewed capabilities against devices outside the declared scope and increasing the chance of unsafe or unintended physical device operations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The raw command accepts attacker- or user-supplied JSON and sends it directly to async_post_device_control for any matched device, with no schema validation, capability restriction, or safety checks. In an agent setting, this creates arbitrary device-control capability that can be abused to trigger undocumented or higher-risk operations on smart appliances.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill exposes capabilities that imply file read/write and network access but does not declare an explicit tool scope or permissions boundary. That makes the skill harder to review and can allow broader-than-expected access when handling tokens and sending control commands to appliances.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This skill can issue real-world control actions such as changing temperatures and toggling modes on appliances, but the description does not warn that actions affect physical devices. In an agent setting, that increases the risk of unintended or unsafe changes if a user request is ambiguous or spoofed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The setup instructions tell users to place a Personal Access Token in a local file without any warning about credential sensitivity, permissions, rotation, or exposure risks. If the token file is readable by other users, backed up insecurely, or accidentally disclosed, an attacker could control connected appliances through the API.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The raw control path executes immediately once arguments are supplied, without any warning, dry-run output, or confirmation step for actions that may affect physical appliances. Because this skill controls real-world devices, the lack of a confirmation barrier materially increases the risk of accidental or socially engineered unsafe actions.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.