Back to skill

Security audit

MicroPython Skills

Security checks for vulnerabilities and agentic risk

Overview

This MicroPython hardware skill fits its stated purpose, but it needs review because it can persistently enable wireless device access, handle credentials unsafely, broaden serial-port access, and flash unverified firmware.

Install only if you are comfortable letting the agent control connected microcontroller hardware. Prefer USB over WebREPL, avoid the default WebREPL password, do not use chmod 666 for serial access, review any boot.py/main.py changes before running WiFi setup, and verify firmware sources before flashing because flashing can erase or permanently change the device.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wifi_setup.py:60
Finding
Persistent WebREPL Provisioning Exposes Plaintext Credentials and Bypasses Dangerous-Operation Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wifi_setup.py:60-110, 127-128` **Vulnerability Type**: Plaintext credential storage, credential disclosure, predictable default password, and unsafe persistent configuration **Risk Level**: High ### Vulnerable Code ```python # Configure WebREPL password try: with open("webrepl_cfg.py", "w") as f: f.write("PASS = " + repr(webrepl_password) + "\n") print("LOG:WebREPL password configured") except Exception as e: print("ERROR:Failed to write webrepl_cfg.py: " + str(e)) raise SystemExit # Enable WebREPL try: import webrepl webrepl.start() print("LOG:WebREPL started on port 8266") except Exception as e: print("ERROR:Failed to start WebREPL: " + str(e)) raise SystemExit # Write boot.py for auto-connect on power-up boot_code = ''' import network, time sta = network.WLAN(network.STA_IF) sta.active(True) sta.connect({ssid!r}, {password!r}) for _ in range(30): if sta.isconnected(): break time.sleep(0.5) import webrepl webrepl.start() ''' try: # Backup existing boot.py try: with open("boot.py", "r") as f: backup = f.read() with open("boot.py.bak", "w") as f: f.write(backup) print("LOG:Existing boot.py backed up to boot.py.bak") except OSError: pass with open("boot.py", "w") as f: f.write(boot_code) print("LOG:boot.py updated for auto-connect") except Exception as e: print("ERROR:Failed to write boot.py: " + str(e)) raise SystemExit print("RESULT:" + json.dumps({ "ip": ip, "webrepl_port": 8266, "webrepl_password": webrepl_password })) ``` ```python parser.add_argument( "--webrepl-password", default="micropython", help="WebREPL access password (default: micropython)" ) ``` ### Technical Analysis The provisioning script writes the WiFi password into `boot.py` and the WebREPL password into `webrepl_cfg.py` as plaintext. It then configure ...[truncated 2502 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not accept WiFi or WebREPL passwords directly through command-line arguments. Read them from an interactive no-echo prompt, a protected file descriptor, or another secret-handling mechanism. - Never include passwords in `RESULT:`, `LOG:`, exception text, or other captured output. - Remove the predictable default WebREPL password. Require a unique, sufficiently long password or generate one using a cryptographically secure random source. - Require explicit, informed confirmation immediately before modifying `boot.py`. - Clearly disclose that the native MicroPython configuration stores credentials in plaintext on the device. - Make persistent auto-start optional rather than an implicit part of WiFi setup. - Abort if the existing `boot.py` cannot be backed up, unless the user separately confirms replacement without a backup. - Preserve existing startup logic through a dedicated configuration module or carefully reviewed merge rather than replacing `boot.py` wholesale. - Apply restrictive permissions or platform-appropriate protections where the device filesystem supports them. - Provide a supported deprovisioning command that disables WebREPL and securely removes stored credentials. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/webrepl_exec.py:37
Finding
WebREPL Transmits Authentication Credentials and Executable Code Without Encryption or Endpoint Authentication<![CDATA[ ## Vulnerability Details **File Location**: `scripts/webrepl_exec.py:37-55` **Vulnerability Type**: Plaintext transmission of credentials and executable commands **Risk Level**: High ### Vulnerable Code ```python def webrepl_exec(host, password, code, port=8266, connect_timeout=5, exec_timeout=30): """Execute code on device via WebREPL, return output.""" import websocket url = f"ws://{host}:{port}" try: ws = websocket.create_connection(url, timeout=connect_timeout) except Exception as e: return None, f"Connection failed to {url}: {e}" try: # Read initial prompt (password prompt) initial = ws.recv() if isinstance(initial, bytes): initial = initial.decode("utf-8", errors="replace") # Send password ws.send(password + "\r\n") ``` The same connection subsequently carries executable code: ```python if isinstance(code, str): code = code.encode("utf-8") ws.send(code + b"\x04") ``` ### Technical Analysis The client deliberately connects using `ws://`, not a TLS-protected `wss://` channel. It sends the password as the first authentication message and then sends arbitrary executable MicroPython code over the same plaintext connection. The client performs no cryptographic verification of the device endpoint. An attacker with local-network visibility can observe the password and commands. An active attacker can impersonate the device, intercept the connection, alter commands or results, or capture credentials for later access. A password authenticates the client to WebREPL but does not provide confidentiality, integrity, or server authentication. Therefore, even a strong password does not mitigate network interception. ### Attack Path 1. The user enables WebREPL and invokes `webrepl_exec.py` on a shared or attacker-observable network. 2. The client opens a plaintext WebSocket connection to TCP port 8266. 3. A local-network attacker captures or redirects the ...[truncated 924 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer USB serial communication for sensitive operations. - If wireless access is necessary, place WebREPL behind an authenticated, encrypted tunnel such as a trusted VPN, SSH tunnel through a controlled gateway, or another mutually authenticated transport. - Restrict WebREPL to an isolated management VLAN or private access point with client isolation and strong link-layer security. - Warn users prominently that native WebREPL uses plaintext WebSocket transport and exposes both passwords and commands to network observers. - Use a unique, randomly generated password for each device and rotate it after any use on an untrusted network. - Validate the intended device through an out-of-band identity mechanism before sending credentials. - Add host validation to reject public, multicast, or otherwise unexpected destinations unless the user explicitly approves them. - Disable WebREPL when it is not actively required. ]]>

T08 · Insecure Dependencies

Error
Location
scripts/firmware_flash.py:147
Finding
Firmware Flashing Trusts Unverified Downloads and a Poisonable Shared Temporary Cache<![CDATA[ ## Vulnerability Details **File Location**: `scripts/firmware_flash.py:147-169, 187-199` **Vulnerability Type**: Missing firmware integrity verification and unsafe temporary-file caching **Risk Level**: High ### Vulnerable Code ```python def download_firmware(url, chip): """Download firmware to cache directory. Returns local path.""" os.makedirs(CACHE_DIR, exist_ok=True) filename = url.split("/")[-1] local_path = os.path.join(CACHE_DIR, filename) # Use cached version if exists if os.path.exists(local_path) and os.path.getsize(local_path) > 10000: return local_path, f"Using cached firmware: {filename}" print(f"Downloading: {url}") print(f"Saving to: {local_path}") try: req = urllib.request.Request( url, headers={"User-Agent": "micropython-skills/1.0"} ) with urllib.request.urlopen(req, timeout=60) as resp: data = resp.read() with open(local_path, "wb") as f: f.write(data) size_kb = len(data) // 1024 return local_path, f"Downloaded {filename} ({size_kb} KB)" except Exception as e: return None, f"Download failed: {e}" ``` ```python def write_flash(esptool_cmd, port, chip_arg, flash_addr, firmware_path): """Write firmware to flash.""" print(f"Flashing firmware to {flash_addr}...") stdout, stderr, rc = run_cmd( [ esptool_cmd, "--chip", chip_arg, "--port", port, "--baud", "460800", "write_flash", "-z", flash_addr, firmware_path, ], timeout=120 ) ``` ### Technical Analysis Firmware is downloaded over HTTPS from the official MicroPython domain, but the script does not authenticate the firmware artifact itself using a checksum, signed manifest, or digital signature. Transport encryption alone does not protect against a compromised origin, compromised certificate authority, local trust-store manipulation, or cache tampering. The cac ...[truncated 1857 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Verify every firmware image against an authenticated SHA-256 digest or a digital signature from a separately authenticated release manifest. - Perform verification again immediately before invoking `esptool`. - Create a user-private cache directory with mode `0700`. - Verify that the cache directory and artifact are owned by the current user and are not writable by other users. - Reject symlinks and non-regular files using secure file-opening flags where supported. - Download to a securely created temporary file in the destination directory, validate it, flush it, and atomically rename it into place. - Apply an explicit maximum download size and validate the image format and target board metadata. - Do not trust an existing cache entry based only on file size. - Remove or quarantine cache files that fail verification. - Record and display the verified digest before requesting final flash confirmation. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/device_probe.py:210
Finding
Recommended Serial-Port Permission Fix Grants Device Access to Every Local User<![CDATA[ ## Vulnerability Details **File Location**: `scripts/device_probe.py:210-220` **Vulnerability Type**: Overly broad device permissions **Risk Level**: Medium ### Vulnerable Code ```python # Check for permission error if "not readable" in combined.lower() or "permission" in combined.lower(): if sys.platform == "win32": return None, f"Permission denied on {port}. Check Device Manager for driver issues." return None, f"Permission denied on {port}. Run: sudo chmod 666 {port}" ``` The same remediation is recommended by the root skill instructions: ```text status: "permission_denied" → Serial port not accessible. On Linux: sudo chmod 666 /dev/ttyACM0. ``` ### Technical Analysis Mode `0666` grants read and write access to the serial device for the owner, group, and all other local users. This violates least privilege because resolving access for one intended operator does not require granting every local account permission to communicate with the device. Serial access commonly permits interactive REPL use, device file manipulation, reset operations, firmware flashing, credential extraction, and actuator control. The command is run through `sudo`, so the recommendation uses administrative privilege to establish an unnecessarily broad access boundary. Although device-node permissions may reset after reconnection, the exposure remains active while the permission is applied. ### Attack Path 1. A user encounters a serial-port permission error. 2. Following the skill's recommendation, the user executes `sudo chmod 666` on the device node. 3. Another unprivileged local account opens the serial port. 4. That account interacts with MicroPython REPL or invokes compatible tooling. 5. The attacker reads device information, changes files, resets the board, controls connected hardware, or flashes new firmware. ### Impact Assessment Any local user can obtain the same serial-device access as the legitimate operator. Depending on device state, this ...[truncated 390 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `chmod 666` guidance with membership in the platform's serial-access group, commonly `dialout` or `uucp`. - Alternatively, use a narrowly scoped udev rule that grants access only to a designated group for the relevant vendor and product identifiers. - For temporary access, use a per-user ACL such as `setfacl -m u:$USER:rw <device>` rather than world permissions. - Verify the selected serial port before changing permissions. - Explain that the user may need to log out and back in after a group-membership change. - Avoid recommending administrative commands without describing their security effect. ]]>

T08 · Insecure Dependencies

Warning
Location
references/connections.md:52
Finding
Unpinned Dependency Installation and Arbitrary Remote Package Source Guidance<![CDATA[ ## Vulnerability Details **File Location**: `references/connections.md:52-57` **Vulnerability Type**: Unpinned third-party dependencies and arbitrary remote package installation **Risk Level**: Medium ### Vulnerable Code ```bash # Install from micropython-lib mpremote mip install umqtt.simple mpremote mip install bme280 # Install from URL mpremote mip install github:user/repo/package.py ``` The root skill also recommends unpinned host-side installation: ```text - mpremote — pip install mpremote - esptool — pip install esptool - pyserial — pip install pyserial - websocket-client — pip install websocket-client ``` ### Technical Analysis The project recommends installing host and device dependencies without pinning reviewed versions or validating cryptographic hashes. This makes the effective dependency content mutable over time and prevents the audited skill package from defining a reproducible dependency set. The connection reference additionally demonstrates installation from an arbitrary GitHub repository. If an agent substitutes user-controlled repository values, follows manipulated instructions, or selects an unreviewed package, remote code can be deployed to the microcontroller after the skill itself has been audited. No lock file, approved-source allowlist, checksum policy, or explicit dependency-review workflow is provided. ### Attack Path 1. A required module is missing on the host or device. 2. The agent follows the documented installation command without a pinned version or digest. 3. A compromised upstream release, dependency-confusion event, mutable remote reference, or attacker-provided GitHub location supplies malicious code. 4. Host-side package code executes with the invoking user's privileges, or device-side package code is installed into the MicroPython filesystem. 5. The malicious component can execute during later imports or tool invocation. ### Impact Assessment For host-side Python dependencies, malicious installation ...[truncated 441 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin host dependencies to reviewed versions in a requirements or lock file. - Require hashes for host-side package installation, such as pip hash-checking mode. - Pin device packages to immutable release versions or commit identifiers. - Maintain an allowlist of approved package names, repositories, and expected digests. - Remove the generic arbitrary-GitHub installation example or explicitly require source review and user confirmation before using it. - Prefer official package registries and signed release artifacts. - Document dependency update and re-audit procedures. - Run installation and device tooling with the minimum necessary privileges and in an isolated environment where practical. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (53)

Ae1

High
Category
analysis-evasion
Content
- "SKILL.md"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- "scripts/firmware_flash.py"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- "scripts/firmware_flash.py"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- "scripts/firmware_flash.py"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- "scripts/firmware_flash.py"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- `status: "ok"` → Device has MicroPython, proceed to step 2
   - `status: "no_firmware"` → ESP chip detected but no MicroPython. Ask user to confirm, then flash: `python3 {SKILL_DIR}/scripts/firmware_flash.py --port PORT --yes`
   - `status: "no_device"` → No device connected. Guide user to connect hardware.
   - `status: "permission_denied"` → Serial port not accessible. On Linux: `sudo chmod 666 /dev/ttyACM0`. On Windows: check Device Manager for driver issues.
2. **Connect** — Default: USB via mpremote. Optional: WiFi via WebREPL (user must request)
3. **Execute** — Generate MicroPython code and push to device
4. **Parse** — Scan stdout for tagged lines (RESULT:/ERROR:/STATUS:/LOG:)
Confidence
99% confidence
Finding
The suggested parameter `666` is itself the problem: it applies overly permissive access to the serial device and turns a troubleshooting step into a broad host-side security bypass. In a tool-using agent setting, hardcoded unsafe parameters are especially dangerous because they can be repeated automatically and without nuanced operator review.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if "not readable" in combined.lower() or "permission" in combined.lower():
        if sys.platform == "win32":
            return None, f"Permission denied on {port}. Check Device Manager for driver issues."
        return None, f"Permission denied on {port}. Run: sudo chmod 666 {port}"

    # Check for connection error
    if rc != 0 and ("failed" in combined.lower() or "error" in combined.lower()):
Confidence
93% confidence
Finding
The tool guidance embeds a dangerous parameter choice, 'chmod 666', that weakens host security controls as part of normal troubleshooting. Even though the script does not execute the command itself, the skill can induce insecure operator behavior, which is especially risky for an AI-assisted hardware-management workflow.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
"fix": (
                "Check Device Manager — the COM port driver may need reinstalling"
                if sys.platform == "win32"
                else f"Run: sudo chmod 666 {port}"
            ),
        }
        print(json.dumps(result, indent=2))
Confidence
93% confidence
Finding
This second instance repeats the same unsafe tool-parameter recommendation in structured output intended to guide the user. Because users may copy-paste remediation text verbatim, this increases the likelihood of insecure host configuration.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
if "not readable" in combined.lower() or "permission" in combined.lower():
        if sys.platform == "win32":
            return None, f"Permission denied on {port}. Check Device Manager for driver issues."
        return None, f"Permission denied on {port}. Run: sudo chmod 666 {port}"

    chip_match = re.search(r"Chip is (ESP\S+)", combined, re.IGNORECASE)
    if chip_match:
Confidence
98% confidence
Finding
This is a true parameter-abuse/safety issue because the tool encourages a dangerously overbroad permission change on a user-supplied device path. Even though it is only printed as advice, in practice users often copy-paste such remediation, and on a firmware-flashing tool that can erase and rewrite hardware, the resulting access expansion materially increases the attack surface.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill describes capabilities that include shell execution, file reads/writes, and network access, but it does not declare any explicit tool scope or permission boundary. In an agent environment, that increases the chance the skill is invoked with broader-than-necessary privileges, enabling unintended host actions beyond the microcontroller workflow.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger description is extremely broad and includes many generic hardware and networking terms, making accidental activation likely. Over-broad activation can cause the agent to enter a workflow that performs device probing, shell commands, network activity, or code generation when the user's intent was only conversational or informational.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "scripts/firmware_flash.py"
      - "scripts/wifi_setup.py"
      - "scripts/webrepl_exec.py"
      - "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "scripts/firmware_flash.py"
      - "scripts/wifi_setup.py"
      - "scripts/webrepl_exec.py"
      - "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "scripts/wifi_setup.py"
      - "scripts/webrepl_exec.py"
      - "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "scripts/wifi_setup.py"
      - "scripts/webrepl_exec.py"
      - "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "scripts/webrepl_exec.py"
      - "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
      - "references/connections.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "scripts/webrepl_exec.py"
      - "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
      - "references/connections.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
      - "references/connections.md"
      - "references/esp32.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "skills/sensor/SKILL.md"
      - "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
      - "references/connections.md"
      - "references/esp32.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
      - "references/connections.md"
      - "references/esp32.md"
      - "references/safety.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Skill Enumeration

Medium
Category
Agent Snooping
Content
- "skills/actuator/SKILL.md"
      - "skills/network/SKILL.md"
      - "skills/diagnostic/SKILL.md"
      - "skills/algorithm/SKILL.md"
      - "references/connections.md"
      - "references/esp32.md"
      - "references/safety.md"
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `status: "ok"` → Device has MicroPython, proceed to step 2
   - `status: "no_firmware"` → ESP chip detected but no MicroPython. Ask user to confirm, then flash: `python3 {SKILL_DIR}/scripts/firmware_flash.py --port PORT --yes`
   - `status: "no_device"` → No device connected. Guide user to connect hardware.
   - `status: "permission_denied"` → Serial port not accessible. On Linux: `sudo chmod 666 /dev/ttyACM0`. On Windows: check Device Manager for driver issues.
2. **Connect** — Default: USB via mpremote. Optional: WiFi via WebREPL (user must request)
3. **Execute** — Generate MicroPython code and push to device
4. **Parse** — Scan stdout for tagged lines (RESULT:/ERROR:/STATUS:/LOG:)
Confidence
98% confidence
Finding
Using `chmod 666` on a serial device is an over-broad permission change that can expose the port to unintended local access. Because this skill already has shell-oriented behavior and hardware control scope, recommending this command materially increases the risk of unsafe host modification during normal operation.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
- `status: "ok"` → Device has MicroPython, proceed to step 2
   - `status: "no_firmware"` → ESP chip detected but no MicroPython. Ask user to confirm, then flash: `python3 {SKILL_DIR}/scripts/firmware_flash.py --port PORT --yes`
   - `status: "no_device"` → No device connected. Guide user to connect hardware.
   - `status: "permission_denied"` → Serial port not accessible. On Linux: `sudo chmod 666 /dev/ttyACM0`. On Windows: check Device Manager for driver issues.
2. **Connect** — Default: USB via mpremote. Optional: WiFi via WebREPL (user must request)
3. **Execute** — Generate MicroPython code and push to device
4. **Parse** — Scan stdout for tagged lines (RESULT:/ERROR:/STATUS:/LOG:)
Confidence
98% confidence
Finding
Using `chmod 666` on a serial device is an over-broad permission change that can expose the port to unintended local access. Because this skill already has shell-oriented behavior and hardware control scope, recommending this command materially increases the risk of unsafe host modification during normal operation.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
All user-facing prompt examples in this file are presented in Chinese, which may imply a fixed language expectation without offering user choice. Under the language/locale policy, forcing a specific language without opt-in can be a natural-language policy violation unless clearly justified as region-specific.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The documentation includes commands that delete or overwrite files on the microcontroller without clearly warning that these actions can permanently alter device behavior or erase user code. In a hardware-programming skill, users may copy-paste commands directly, so omission of a data-loss warning increases the chance of accidental device disruption.

Static analysis

No suspicious patterns detected.