Back to skill

Security audit

Xiaomi Air Purifier

Security checks for vulnerabilities and agentic risk

Overview

This purifier-control skill is mostly aligned with its purpose, but it ships unsafe token-handling utilities and stores device secrets too openly.

Install only if you are comfortable with this skill reading Mi Home credentials and controlling purifier settings. Before use, remove the shipped test-local.js credential values, avoid running extract-token.js in logged or shared sessions, keep generated config.json out of source control and backups, and prefer a private secret store or restrictive file permissions for device tokens.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/test-local.js:3
Finding
Hard-Coded Xiaomi Device Authentication Token## Vulnerability Details **File Location**: `scripts/test-local.js:3-5` **Vulnerability Type**: Hard-coded credential **Risk Level**: High ```javascript const DEVICE_IP = '192.168.1.4'; const DEVICE_TOKEN = '3a8e11ebfb306f9d87e941c6b0f0e910'; const DEVICE_DID = '875218925'; ``` ### Technical Analysis The test script embeds a complete Xiaomi device token together with the corresponding device identifier and local IP address. The token is subsequently passed to `miHome.getDevice()` as an authentication credential. A real credential is not required for a reusable local connection test and should never be distributed in source code. Because the token is stored directly in the project, anyone who can read a copy of the repository, a source archive, a backup, or an Agent workspace can recover it without needing access to the Xiaomi account. Although exploitation generally requires network reachability to the purifier, the token eliminates the device authentication barrier once such reachability exists. ### Attack Path 1. An attacker obtains a copy of the project or reads `scripts/test-local.js`. 2. The attacker extracts the device IP, DID, and full authentication token. 3. The attacker gains access to the same LAN or otherwise obtains network reachability to the device. 4. The attacker creates a compatible MIIO/MIOT client using the disclosed values. 5. The attacker authenticates to the purifier and issues supported property-read or property-write operations. ### Impact Assessment An attacker with network reachability may authenticate as an authorized local client and monitor or control the identified purifier. Potential actions include reading environmental data and changing power, operating mode, fan level, brightness, buzzer, or child-lock settings. The direct scope is the device associated with the exposed token; the finding does not by itself establish Xiaomi account compromise or host-level code execution.
Remediation
## Remediation Suggestions - Immediately revoke or rotate the exposed device token. - Remove the token, IP address, and DID from the source and repository history. - Replace them with clearly nonfunctional example values or accept them through protected runtime configuration. - Store runtime credentials in an operating-system credential manager or a user-private file with mode `0600`. - Ensure credential-bearing files are excluded from version control and build artifacts. - Add automated secret scanning to development and release workflows. - Avoid printing even partial tokens unless diagnostic output is explicitly enabled.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/purifier.js:125
Finding
Plaintext Persistence of Device Tokens and Household Metadata## Vulnerability Details **File Location**: `scripts/purifier.js:10-11, 35-37, 125-127, 142` **Vulnerability Type**: Insecure local storage of authentication material **Risk Level**: High ```javascript const CREDS_FILE = path.join(process.env.HOME, '.config/xmihome/credentials.json'); const CONFIG_FILE = path.join(__dirname, '..', 'config.json'); ``` ```javascript function saveConfig(config) { fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); } ``` ```javascript const entry = { did: d.did, name: d.name, room: room, token: d.token, model: d.model, address: d.localip }; ``` ```javascript if (updated) saveConfig(config); ``` ### Technical Analysis During cloud discovery, the script obtains each purifier's local authentication token and persists it in `config.json` inside the project directory. The same record includes the device DID, friendly name, room, model, and local IP address. `fs.writeFileSync()` is called without an explicit restrictive file mode, encryption, or use of a protected credential store. Caching a token supports the declared local-first behavior, but storing it as unrestricted plaintext in the project directory exceeds the minimum safe storage requirements. Project directories are commonly copied into backups, included in archives, shared with collaborators, or accidentally committed. The stored room names and network addresses additionally expose household and network topology information. ### Attack Path 1. The legitimate user runs a purifier command when cloud discovery is required. 2. The Xiaomi API returns purifier metadata, including the device token. 3. The script writes the token and associated household metadata to the project-level `config.json`. 4. Another local user, backup operator, malicious process, repository recipient, or accidental source-control recipient obtains the file. 5. The recipient extracts the address, DID, and token. 6. If the recipient ...[truncated 582 chars]
Remediation
## Remediation Suggestions - Do not place credential-bearing configuration in the project directory. - Store device tokens in an operating-system credential manager or a dedicated user-private state directory. - Create secret files with an explicit mode of `0600` and verify ownership before reading them. - Separate non-sensitive metadata from authentication tokens. - Make local token caching opt-in and clearly disclose what information is stored. - Add `config.json` and any secret-state files to version-control ignore rules. - Use atomic, restrictive file creation to avoid temporary exposure or permission races. - Provide a command to delete cached credentials and rotate tokens after suspected disclosure.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract-token.js:5
Finding
Full Device Tokens Exposed Through Standard Output## Vulnerability Details **File Location**: `scripts/extract-token.js:5-29` **Vulnerability Type**: Sensitive credential disclosure **Risk Level**: High ```javascript const CREDS_FILE = path.join(process.env.HOME, '.config/xmihome/credentials.json'); async function extract() { if (!fs.existsSync(CREDS_FILE)) { console.error('❌ Credentials not found.'); return; } const creds = JSON.parse(fs.readFileSync(CREDS_FILE, 'utf8')); const client = new XiaomiMiHome({ credentials: creds }); // Request device list with tokens const response = await client.miot.request('/v2/home/device_list', { getVirtualModel: false, getHuamiDevices: 0 }); const devices = response.result.list.filter(d => d.model && (d.model.includes('airp') || d.model.includes('airpurifier'))); console.log('--- DEVICE TOKENS ---'); devices.forEach(d => { console.log(`Name: ${d.name}`); console.log(`DID: ${d.did}`); console.log(`IP: ${d.localip || 'Unknown'}`); console.log(`Token: ${d.token}`); console.log('---------------------'); }); } ``` ### Technical Analysis This utility reads the user's Mi Cloud credentials, requests device records from the Xiaomi API, and prints every matching purifier's complete authentication token to standard output. Credential access and Xiaomi API communication are expected for cloud-backed device control, but emitting full tokens is not required for the documented status and control interface. Standard output may be retained by terminal capture, CI/CD logs, process supervisors, Agent transcripts, shell redirection, observability systems, or support bundles. Once captured, the token remains reusable until it is rotated or invalidated. The script does not request confirmation, redact output, restrict its destination, or warn the user that it is disclosing authenticatio ...[truncated 1029 chars]
Remediation
## Remediation Suggestions - Remove the token-extraction utility if it is not necessary for declared Skill operation. - Never print full tokens by default; display only a short fingerprint or redacted form. - Require an explicit confirmation flag before any sensitive export. - If export is essential, write directly to a user-selected file created with mode `0600`, rather than standard output. - Avoid running the utility in CI, shared terminals, or Agent sessions whose output is retained. - Ensure the Xiaomi client is destroyed in a `finally` block after use. - Document token sensitivity and provide rotation procedures.

T08 · Insecure Dependencies

Warning
Location
package.json:11
Finding
Third-Party Dependency Chain Includes GitHub Tarballs Without Lockfile Integrity Values## Vulnerability Details **File Location**: `package.json:11-13`; `pnpm-lock.yaml:239-240, 428-429, 574-577, 1191-1198` **Vulnerability Type**: Insecure dependency provenance and credential-handling supply chain **Risk Level**: Medium ```json "dependencies": { "xmihome": "^1.4.0" } ``` ```yaml dbus-next@https://codeload.github.com/dcodeIO/node-dbus-next/tar.gz/0d0cea2ebb0487a051e735f1f488db2115fc16e1: resolution: {tarball: https://codeload.github.com/dcodeIO/node-dbus-next/tar.gz/0d0cea2ebb0487a051e735f1f488db2115fc16e1} ``` ```yaml mijia-io@https://codeload.github.com/salamwaddah/mijia-io/tar.gz/21b46c3fc4cdb6a4b520f0867d63715de81c5acc: resolution: {tarball: https://codeload.github.com/salamwaddah/mijia-io/tar.gz/21b46c3fc4cdb6a4b520f0867d63715de81c5acc} ``` ```yaml xmihome@1.4.0: resolution: {integrity: sha512-+yLV2puETuz224binzEsR4mAk6a2fWQDaYa655uaBYbv6xEA/opp2H6USHDNCTv/L06cuHQpF2t8MAXycpDF9A==} engines: {node: '>=18.0.0'} hasBin: true ``` ### Technical Analysis The direct `xmihome` dependency is resolved to version 1.4.0 with a registry integrity hash, but its resolved dependency graph includes tarballs downloaded directly from GitHub. The tarball URLs are pinned to commit identifiers, which reduces version mutability, but the displayed lockfile entries do not include independent integrity hashes for those tarballs. The documented setup also invokes the `xmihome` executable for Xiaomi login. Consequently, the dependency executes in a context where Xiaomi account credentials are entered and credential files are accessible. A compromise of the package release, a transitive source, or the downloaded artifact could therefore affect both installation-time and login-time security. The audit evidence does not establish that the current dependencies are malicious; the risk is weakened provenance and elevated consequence if the supply chain is compromised. ### Attack Path 1. A user follows t ...[truncated 996 chars]
Remediation
## Remediation Suggestions - Pin the direct dependency to an exact reviewed version instead of a semver range. - Prefer registry-published dependencies with cryptographic integrity hashes over direct source tarballs. - Independently hash and verify any unavoidable GitHub tarball artifacts. - Review the source and release provenance of `xmihome`, `mijia-io`, and `dbus-next`. - Disable package lifecycle scripts during installation where compatible with required functionality. - Run installation and credential-handling commands in a constrained environment with minimal filesystem access. - Use dependency vulnerability, provenance, and license scanning in the release workflow. - Re-review the lockfile whenever dependency resolutions change.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
If the implementation uses hardcoded IPs/credentials and only limited status reads while claiming multi-room Mi Cloud control, the skill is materially misrepresented. Hardcoded device credentials are especially risky because they can expose direct device access and undermine any expectation of per-user, cloud-mediated authorization.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
If the implementation uses hardcoded IPs/credentials and only limited status reads while claiming multi-room Mi Cloud control, the skill is materially misrepresented. Hardcoded device credentials are especially risky because they can expose direct device access and undermine any expectation of per-user, cloud-mediated authorization.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
If the implementation uses hardcoded IPs/credentials and only limited status reads while claiming multi-room Mi Cloud control, the skill is materially misrepresented. Hardcoded device credentials are especially risky because they can expose direct device access and undermine any expectation of per-user, cloud-mediated authorization.

Credential Access

High
Category
Privilege Escalation
Content
const fs = require('fs');
const path = require('path');

const CREDS_FILE = path.join(process.env.HOME, '.config/xmihome/credentials.json');

async function extract() {
    if (!fs.existsSync(CREDS_FILE)) {
Confidence
92% confidence
Finding
Referencing a specific credential store path is itself not always malicious, but in this file it is part of a workflow that loads account credentials for secret extraction. In the context of a consumer air-purifier skill, this makes the behavior more dangerous because credential access is not clearly justified by the user-facing feature set and directly enables downstream token disclosure.

Missing User Warnings

High
Confidence
99% confidence
Finding
Sensitive credentials-derived data is accessed and then printed without any warning, confirmation, or protective handling. This is dangerous because terminal output is often captured in logs, shell history, CI systems, or monitoring tools, turning temporary secret access into durable secret exposure.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
This script enumerates purifier devices and prints each device's local IP and token to stdout, exposing secrets that are not required for the stated skill purpose of monitoring and controlling the purifier via Mi Cloud. Device tokens can enable unauthorized local control or further compromise of the device ecosystem, and printing them makes accidental leakage to logs, terminals, or other processes much more likely.

Credential Access

High
Category
Privilege Escalation
Content
const fs = require('fs');
const path = require('path');

const CREDS_FILE = path.join(process.env.HOME, '.config/xmihome/credentials.json');
const CONFIG_FILE = path.join(__dirname, '..', 'config.json');

const PROPS = {
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Lp3

Medium
Category
MCP Least Privilege
Confidence
77% confidence
Finding
The skill invokes commands that require environment/credential access but declares no explicit tool scope or permission boundaries. In an agent setting, this weakens least-privilege guarantees and can enable unintended access to secrets or execution contexts during setup and operation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The setup instructions tell users to log in with Xiaomi account credentials and OTP but provide no guidance on secure handling, storage, or redaction. In an agent or shared-terminal context, this can lead to accidental credential exposure in shell history, logs, screenshots, or cached files.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script reads a local credentials file and uses it to retrieve additional device secrets, which goes beyond the declared monitoring/control behavior of the skill. Accessing credential material for extraction purposes expands the attack surface and creates a clear path for secret harvesting from the host environment.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding
Suspicious Unicode normalization or mixed-script content

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The script exposes write-capable controls for brightness, child lock, and buzzer that are not disclosed in the skill description, expanding device control beyond user-expected scope. This is dangerous because a caller may gain operational control over auxiliary settings without informed consent, violating least privilege and potentially enabling nuisance or unauthorized state changes across rooms.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script caches cloud-discovered device metadata including local IP and token to disk in config.json, creating a persistent local secret store. If that file is readable by other local users, backup systems, or other skills, an attacker could reuse the token to control the purifier on the local network or infer household device topology.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
The child-lock and buzzer commands permit direct modification of device behavior unrelated to the core stated purpose of checking air quality and basic purifier operation. In a multi-room household context, unauthorized toggling can disrupt normal use, mask device feedback, or interfere with safety/parental expectations.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"off": "node scripts/purifier.js off"
  },
  "dependencies": {
    "xmihome": "^1.4.0"
  }
}
Confidence
93% confidence
Finding
The dependency is specified with a caret range (^1.4.0), which permits automatically installing newer 1.x releases. That creates a supply-chain risk: a compromised or malicious upstream release could be pulled in without review, changing the behavior of a skill that can control IoT devices and likely handles Mi Cloud credentials.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
This hard-coded fallback imposes a specific locale/region policy on the user rather than prompting for a choice or failing with guidance. Under the policy, forcing a locale without opt-in is a natural-language policy concern even though it appears in code behavior rather than prose.

Context-Inappropriate Capability

Low
Confidence
84% confidence
Finding
Brightness control is an undocumented write capability that exceeds the declared user-facing scope. While lower impact than power or lock manipulation, it still enables unauthorized environmental/device-state changes and indicates overbroad control surface.