T09 · Insecure Skill Coding Practices
- Location
simple_test.py:4- Finding
Arbitrary Python Execution from an Unverified External File
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
simple_test.py:4Arbitrary Python Execution from an Unverified External File
scripts/motor_control.py:136Unsafe Automatic Motor Activation and Unrestricted Actuator Commands
scripts/motor_control.py:86Incorrect Motor Telemetry Frame Length Causes Safety Monitoring Failure
scripts/imu_reader.py:70IMU Parser Reads a Shorter Frame Than It Subsequently Accesses
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.
import time
# 直接读取并执行 sim_state.py
exec(open('/Users/liuxing/.openclaw/workspace/skills/quadruped/scripts/sim_state.py').read())
sim = QuadrupedSimulator()
sim.dt = 0.01
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.
import time
# 直接读取并执行 sim_state.py
exec(open('/Users/liuxing/.openclaw/workspace/skills/quadruped/scripts/sim_state.py').read())
sim = QuadrupedSimulator()
sim.dt = 0.01
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.
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.
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.
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.
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.
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.
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.
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
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.
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
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
No suspicious patterns detected.