Back to skill

Security audit

zsquadbot

Security checks for vulnerabilities and agentic risk

Overview

The skill matches quadruped robot development, but it can activate real robot motors with weak safety controls and includes an unsafe test script that runs code from a local path.

Review carefully before installing or running on real hardware. Use the simulator first, do not connect a physical robot unless it is secured with an emergency stop available, and fix the motor-control code to require explicit arming, validated limits, telemetry health checks, and a real torque-disable stop. Do not run simple_test.py unchanged; replace its hardcoded exec path with a normal package-relative import.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (4)

T09 · Insecure Skill Coding Practices

Warning
Location
simple_test.py:4
Finding

Arbitrary Python Execution from an Unverified External File

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/motor_control.py:136
Finding

Unsafe Automatic Motor Activation and Unrestricted Actuator Commands

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/motor_control.py:86
Finding

Incorrect Motor Telemetry Frame Length Causes Safety Monitoring Failure

Content
View full analysis
Dict[str, float]: """ Read motor status and telemetry. Returns: Dictionary with position, velocity, temperature, voltage, and errors """ if not self.ser: raise RuntimeError("Motor not connected") # Request status status_cmd = struct.pack('!BB', 0xFF, self.motor_id) self.ser.write(status_cmd) time.sleep(0.05) # Read response (12 bytes) if self.ser.in_waiting >= 12: data = self.ser.read(12) unpacked = struct.unpack('!fffiiii', data) return { 'position': unpacked[0], # degrees 'velocity': unpacked[1], # rad/s 'force': unpacked[2], # N 'temperature': unpacked[3] / 10.0, # °C (scaled) 'voltage': unpacked[4] / 1000.0, # V 'error_flags': unpacked[5:7] # error codes } return {} ``` ### Technical Analysis The code reads 12 bytes but attempts to unpack the data using `!fffiiii`. This format consists of three four-byte floats and four four-byte integers, requiring 28 bytes in total. Whenever at least 12 bytes are available, the function reads exactly 12 bytes and passes them to a format requiring 28 bytes. Python's `struct.unpack()` therefore raises an exception rather than returning telemetry. There is no local exception handling, frame synchronization, checksum validation, or safe fallback that disables motors when telemetry becomes unavailable. The caller may consequently lose access to position, temperature, voltage, and error-flag information while actuators remain enabled. ### Attack Path 1. The robot is connected and its motors are enabled. 2. The application calls `get_status()` to monitor motor health. 3. The serial device provides at l ...[truncated 878 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/imu_reader.py:70
Finding

IMU Parser Reads a Shorter Frame Than It Subsequently Accesses

Content
View full analysis
= 43: data = self.ser.read(43) # Parse headers header1, header2, packet_type = struct.unpack('!BBB', data[:3]) # Check for valid IMU packet if header1 != 0x55 or header2 != 0xAA: return None # Unpack data fields accel_x, accel_y, accel_z = struct.unpack('!fff', data[3:12]) gyro_x, gyro_y, gyro_z = struct.unpack('!fff', data[12:21]) quat_x, quat_y, quat_z, quat_w = struct.unpack('!ffff', data[21:37]) temperature = struct.unpack('!f', data[37:41])[0] timestamp = struct.unpack('!I', data[41:45])[0] checksum = struct.unpack('!B', data[45:46])[0] # Validate checksum (simple XOR) calculated_checksum = 0 for i in range(45): calculated_checksum ^= data[i] if checksum != calculated_checksum: print("Warning: IMU checksum mismatch") return None ``` ### Technical Analysis The parser waits for and reads 43 bytes, but its field layout accesses data through byte offset 46: - The timestamp slice `data[41:45]` requires bytes through offset 44. - The checksum slice `data[45:46]` requires byte 45. - The checksum loop indexes `data[0]` through `data[44]`. A 43-byte buffer cannot satisfy these accesses. The timestamp unpack operation first receives only two bytes from `data[41:45]` and raises `struct.error`; if execution reached the checksum loop, indexing beyond the buffer would also fail. Although two header bytes are checked, `packet_type` is parsed and never validated. There is no frame resynchronization or fail-safe response when IMU data cannot be authenticated and decoded. ### Attack Path 1. The IMU monitoring process sends a data request. 2. The serial interface makes at least 43 bytes ava ...[truncated 1049 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (21)

exec() call detected

High
Category
Dangerous Code Execution
Confidence
99% confidence
Finding

The script reads Python source from a filesystem path and executes it with exec(), which allows arbitrary code execution if that file is modified, replaced, or sourced from an untrusted workspace. In a skill environment, this is especially dangerous because the path points into a user-writable skills/workspace directory rather than a trusted packaged module.

Content

Scanner excerpt · simple_test.py (reported line 7)May include surrounding context.

python
import time

# 直接读取并执行 sim_state.py
exec(open('/Users/liuxing/.openclaw/workspace/skills/quadruped/scripts/sim_state.py').read())

sim = QuadrupedSimulator()
sim.dt = 0.01

Direct flow: open (file read) → exec (code execution)

High
Category
Data Flow
Confidence
99% confidence
Finding

This is a direct taint-style flow from file read to code execution: open(...).read() feeds directly into exec(...), creating an immediate arbitrary-code-execution sink. Any attacker who can influence the referenced file or its path gains execution in the current process with the script's privileges.

Content

Scanner excerpt · simple_test.py (reported line 7)May include surrounding context.

python
import time

# 直接读取并执行 sim_state.py
exec(open('/Users/liuxing/.openclaw/workspace/skills/quadruped/scripts/sim_state.py').read())

sim = QuadrupedSimulator()
sim.dt = 0.01

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The motor-control examples show direct actuator enablement and position commands against a real hardware interface without an immediate, explicit warning that running the snippet can move physical joints. In a quadruped robotics skill, that omission is safety-relevant because users may copy and run the code on connected hardware, causing unintended motion, pinching, tip-over, or equipment damage if the robot is not secured or tested in simulation first.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The CLI examples for calibration and motor testing present commands that may change motor state or trigger movement, but they do not warn that these operations can affect physical hardware. Because these are copy-pasteable commands in a robotics skill, users may execute them on a live robot without securing the platform or understanding the consequences, leading to unsafe actuation or altered calibration state.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The user-facing comments and echoed output throughout the script are entirely in Chinese, which imposes a specific language on users. Under the policy rule, this is a natural-language locale choice that should either be optional, user-selectable, or clearly justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file contains multiple user-facing print strings in Chinese, including the main test banner and gait labels. The policy requires avoiding forced language or locale constraints unless the skill offers user choice or clearly documents that it is region-specific, which is not present here.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The code checks for only 43 available bytes and then reads 43 bytes, but immediately parses and indexes the buffer as if it contains 46 bytes. A malformed or normal short packet from the serial device can therefore trigger struct unpack errors or crash the reader, causing a denial of service in any process relying on this IMU input. In a robotics context, loss of sensor ingestion can degrade stability, monitoring, or control decisions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This Python file contains multiple user-facing print strings in Chinese in the main execution path. Because the skill does not offer an opt-in language choice or explain that it is intended only for a Chinese-speaking context, it violates the language/locale policy for natural-language content.

Content

No source excerpt is available for this finding.

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/imu_reader.py (reported line 49)May include surrounding context.

python
True if connection successful
        """
        try:
            self.ser = serial.Serial(self.port, self.baudrate, timeout=0.1)
            self.motor_id = motor_id
            time.sleep(0.1)  # Allow connection to stabilize
            return True

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/motor_control.py (reported line 41)May include surrounding context.

python
True if connection successful
        """
        try:
            self.ser = serial.Serial(self.port, self.baudrate, timeout=0.1)
            self.motor_id = motor_id
            time.sleep(0.1)  # Allow connection to stabilize
            return True

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/motor_control.py (reported line 139)May include surrounding context.

python
True if connection successful
        """
        try:
            self.ser = serial.Serial(self.port, self.baudrate, timeout=0.1)
            self.motor_id = motor_id
            time.sleep(0.1)  # Allow connection to stabilize
            return True

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The status parser is internally inconsistent: the code checks for 12 bytes but then attempts to unpack the buffer with the format '!fffiiii', which requires 28 bytes. This can cause exceptions, misparsed telemetry, or unsafe control decisions based on invalid status data, which is particularly risky in a robot motor-control context where feedback integrity matters.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The connect path automatically iterates through all 12 motors, connects to each, and immediately enables them without any explicit safety interlock, operator confirmation, or warning about physical motion risk. In a quadruped robot context, unexpected enablement can result in sudden actuation, pinching, falls, or equipment damage even if the code is not malicious.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The CLI test and interactive modes issue live position, velocity, and force commands directly to motors, but provide no clear safety disclosure, environment checks, or operational guardrails. Because this skill controls real actuators, a user can trigger hazardous movement during testing or interactive use without being prompted to secure the robot or confirm intent.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The monitor banner at L098 uses Chinese text in a user-facing interface, and similar Chinese-only status text appears elsewhere in the file. The skill does not offer localization, language selection, or any justification that it is intended only for a Chinese-speaking or region-specific context, which makes this a natural-language locale policy issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

Lines L163, L187, and L189 emit Chinese-only runtime messages to the user. Because the file provides no mechanism for selecting language and no documented reason for a fixed Chinese locale, this violates the rule against forcing a specific language without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The file’s human-facing text is entirely in Chinese, including the module docstring and all printed output. This imposes a specific language/locale on users without offering any choice or documenting a justified locale restriction, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This shell script includes user-facing natural-language text exclusively in Chinese, including status messages and usage guidance. For an organizational language/locale policy, forcing a specific language without user opt-in can violate the requirement to offer language choice or clearly justify the locale restriction.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The docstring and all printed user-facing strings are in Chinese, which imposes a specific language on users. The file does not provide any opt-in, fallback, or explanation that this is intentionally limited to a Chinese-speaking context.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The method documentation presents duration as part of creating a complete gait profile, implying the output should reflect the requested time span. In implementation, duration is never used, so the function does not actually create a duration-specific profile and instead just delegates to gait generators and returns their raw output with a frequency value.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The monitor UI documents 'S - Stretch pose' in the controls, implying uppercase S is a valid command. However, input is normalized with .lower() before dispatch, so uppercase 'S' becomes 's' and triggers set_static_pose() instead; set_stretch_pose() is therefore unreachable via the documented command path.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.