Back to skill

Security audit

xgorobot

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent XGO robot controller, but it includes unsafe shell execution and under-disclosed privileged hardware commands that need review before installation.

Install only after review or remediation. The core robot-control purpose is clear, but users should avoid untrusted URLs, filenames, and target text, should expect camera/audio data to be sent to DashScope for AI features, and should remove shell=True/os.system patterns and sudo-backed helper calls before using this on a real robot.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (6)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/audio/play_http.py:14
Finding
Shell Command Injection Through an Untrusted Audio URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audio/play_http.py:14-15` **Vulnerability Type**: OS command injection through `shell=True` **Risk Level**: High ### Vulnerable Code ```python cmd = f'mplayer "{args.url}"' subprocess.run(cmd, shell=True, check=True) ``` ### Technical Analysis The user-controlled `--url` argument is interpolated into a command string and executed through a system shell. Quoting the value does not make it safe because a malicious value can contain quote characters and shell metacharacters that terminate the intended argument and introduce additional commands. The program does not validate the URL scheme, destination, or characters before invoking the shell. Shell interpretation is unnecessary because `mplayer` can be executed directly with an argument array. ### Attack Path 1. An attacker causes the Skill or its controlling agent to invoke `play_http.py` with a crafted `--url`. 2. The crafted value is inserted into `cmd` without escaping. 3. `subprocess.run(..., shell=True)` passes the complete string to the shell. 4. The shell interprets attacker-controlled syntax as additional commands. 5. Those commands execute with the privileges of the Skill process. ### Impact Assessment Successful exploitation permits arbitrary command execution under the account running the Skill. The attacker could read or modify files available to that account, access attached robot hardware, invoke other installed programs, interfere with robot operation, or use accessible credentials such as environment-provided API keys. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Do not invoke a shell. Pass arguments as a list: ```python from urllib.parse import urlparse import subprocess parsed = urlparse(args.url) if parsed.scheme != "https": raise ValueError("Only HTTPS audio URLs are allowed") subprocess.run(["mplayer", args.url], check=True) ``` - Restrict accepted schemes to HTTPS. - If URLs are expected only from known services, enforce a hostname allowlist. - Consider blocking loopback, link-local, and private network destinations to reduce server-side request forgery and local-service access risks. - Apply timeouts and resource limits to the media player process. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/audio/play.py:13
Finding
Shell Command Injection and Path Traversal Through Local Audio Filename<![CDATA[ ## Vulnerability Details **File Location**: `scripts/audio/play.py:13` **Vulnerability Type**: OS command injection and unrestricted file path construction **Risk Level**: High ### Vulnerable Code ```python os.system(f"mplayer /home/pi/Music/{args.filename}") ``` ### Technical Analysis The user-controlled `--filename` value is concatenated directly into an `os.system()` command. Shell metacharacters in the filename can introduce additional commands. The code also does not normalize or constrain the resulting path, so path traversal sequences may reference media or other files outside `/home/pi/Music`. `os.system()` is unnecessary for launching a media player and creates an avoidable command-execution boundary. ### Attack Path 1. An attacker supplies a crafted value through `--filename`. 2. The value is appended to `/home/pi/Music/` without path validation. 3. `os.system()` passes the resulting string to the shell. 4. Shell syntax embedded in the filename is interpreted as commands, or traversal components select a file outside the intended directory. 5. The injected command executes with Skill-process privileges. ### Impact Assessment Command injection provides arbitrary code execution as the Skill account. Path traversal can expose local files to the media player and may disclose information through error messages or parser behavior. Since this Skill controls physical robot hardware, exploitation could also cause unauthorized movement or other unsafe hardware actions. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Resolve the requested path and ensure it remains beneath the intended media directory. - Execute `mplayer` using an argument array rather than a shell: ```python from pathlib import Path import subprocess music_root = Path("/home/pi/Music").resolve() audio_path = (music_root / args.filename).resolve() if music_root not in audio_path.parents: raise ValueError("The audio file must be inside /home/pi/Music") if not audio_path.is_file(): raise FileNotFoundError(audio_path) subprocess.run(["mplayer", str(audio_path)], check=True) ``` - Optionally restrict allowed file extensions and reject symbolic links. - Run the media player with minimal filesystem and device permissions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/combo/ai_find_step.py:88
Finding
Shell Command Injection Through Target-Derived Speech Text<![CDATA[ ## Vulnerability Details **File Location**: `scripts/combo/ai_find_step.py:88-91` **Vulnerability Type**: OS command injection through speech text **Risk Level**: High ### Vulnerable Code ```python cmd = f'espeak -v zh -s {speed} -p 50 "{text}"' subprocess.run(cmd, shell=True, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) ``` The vulnerable function is called with speech text derived from the user-controlled target: ```python speech_text = f"这个,是,{args.target}" text_to_speech(speech_text, args.speed) ``` ### Technical Analysis The `text` argument is inserted into a shell command inside double quotes. In this workflow, that text contains the user-supplied `--target` value. A target containing quote characters and shell syntax can escape the intended `espeak` argument and introduce an additional command. The `speed` argument is parsed as an integer, but that does not protect the independently controlled `text` value. The use of `shell=True` is unnecessary. ### Attack Path 1. An attacker supplies a crafted `--target` value. 2. The target is incorporated into `speech_text`. 3. `text_to_speech()` interpolates the text into the `espeak` command string. 4. The shell parses attacker-controlled metacharacters. 5. The injected command executes when the robot reaches the speech stage of the workflow. ### Impact Assessment An attacker can execute arbitrary commands with the privileges of the Skill process. This may permit access to user files, API credentials, camera and audio devices, serial-connected robot controls, and other resources available to the runtime account. Exploitation occurs within a physical movement workflow, increasing the potential safety impact. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions Pass each argument directly to `espeak` without a shell: ```python subprocess.run( ["espeak", "-v", "zh", "-s", str(speed), "-p", "50", text], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) ``` Additional hardening should include: - Enforce a reasonable maximum target length. - Validate the numeric speed against a safe operational range. - Avoid logging sensitive target text unless needed. - Ensure robot movement stops safely if speech or any preceding operation fails. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ai/text_to_speech.py:61
Finding
Command Injection Through a Network-Provided Text-to-Speech Audio URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ai/text_to_speech.py:61-66` **Vulnerability Type**: OS command injection across a network-to-shell trust boundary **Risk Level**: High ### Vulnerable Code ```python audio_url = result["output"]["audio"]["url"] edu.lcd_clear() edu.lcd_text(5, 5, "播放中...", "GREEN", 14) subprocess.run(f'mplayer -really-quiet "{audio_url}"', shell=True, check=True) ``` ### Technical Analysis The `audio_url` value is obtained from a remote API response and inserted into a shell command. The code assumes the service always returns a benign URL and does not validate the URL or prevent shell interpretation. A compromised API service, compromised network trust path, unexpected upstream response, or test endpoint could return a value containing shell syntax. Because `shell=True` is used, that response becomes a command-execution input. ### Attack Path 1. The Skill sends a text-to-speech request to the external service. 2. An attacker capable of controlling or altering the service response supplies a malicious `audio.url` value. 3. The Skill places that value into a shell command without validation. 4. The shell interprets embedded syntax rather than treating the entire value as one URL argument. 5. The attacker-controlled command executes locally. ### Impact Assessment Successful exploitation gives the party controlling the relevant API response arbitrary command execution with Skill-process privileges. The reachable scope can include local files, environment variables, camera and audio resources, robot-control interfaces, and network resources accessible from the device. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Remove shell processing: ```python subprocess.run( ["mplayer", "-really-quiet", audio_url], check=True, ) ``` - Parse the URL and require HTTPS. - Enforce an allowlist of expected media hostnames where operationally possible. - Reject URLs containing credentials, fragments, control characters, or unexpected schemes. - Consider downloading the file with a hardened HTTP client, enforcing size and content-type limits, and then playing a verified local temporary file. - Store temporary audio with restrictive permissions and remove it after playback. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
lib/edulib.py:419
Finding
Unsafe Shell-Backed Media and Recording Methods in the Shared Library<![CDATA[ ## Vulnerability Details **File Location**: `lib/edulib.py:419-475` **Vulnerability Type**: OS command injection, unsafe path construction, and potentially privileged command execution **Risk Level**: High ### Vulnerable Code ```python def xgoSpeaker(self,filename): path="/home/pi/xgoMusic/" os.system("mplayer"+" "+path+filename) def xgoVideoAudio(self,filename): path="/home/pi/xgoVideos/" time.sleep(0.2) cmd="sudo mplayer "+path+filename+" -novideo" os.system(cmd) ``` The recording method contains the same unsafe construction: ```python def xgoAudioRecord(self,filename="record",seconds=5): path="/home/pi/xgoMusic/" if not os.path.exists(path): os.makedirs(path) command1 = "sudo arecord -d" command2 = "-f S32_LE -r 8000 -c 1 -t wav" cmd=command1+" "+str(seconds)+" "+command2+" "+path+filename print(cmd) os.system(cmd) ``` ### Technical Analysis These library methods concatenate caller-controlled filenames into shell command strings. `xgoAudioRecord()` also concatenates `seconds` unless callers consistently supply a trusted numeric type. No canonical-path validation or shell-safe argument separation is applied. Two methods prefix their commands with `sudo`. If the runtime account has passwordless permission for these commands, injected shell syntax may execute in a privileged context depending on sudo policy and shell parsing. Even where elevation is not achieved, arbitrary commands can execute as the Skill account. Because these methods are exposed through the shared `XGOEDU` library, any generated custom code or future script calling them may inherit the vulnerability. ### Attack Path 1. A caller passes an attacker-controlled filename to one of the affected library methods. 2. The method concatenates the filename into a command string. 3. `os.system()` invokes the system shell. 4. Shell metacharacters in the filename introduce additional commands. 5. Commands execute as the Skill a ...[truncated 504 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace every `os.system()` call with `subprocess.run()` using argument arrays. - Remove `sudo` from library methods. - Configure only the required device permissions through groups, udev rules, or a narrowly scoped privileged helper. - Resolve each media path and verify it remains under its expected base directory. - Validate recording duration as an integer within a safe range. - Avoid printing full command lines or sensitive paths. Example: ```python from pathlib import Path import subprocess def xgoSpeaker(self, filename): root = Path("/home/pi/xgoMusic").resolve() media = (root / filename).resolve() if root not in media.parents or not media.is_file(): raise ValueError("Invalid media path") subprocess.run(["mplayer", str(media)], check=True) def xgoAudioRecord(self, filename="record.wav", seconds=5): duration = int(seconds) if not 1 <= duration <= 300: raise ValueError("Invalid recording duration") root = Path("/home/pi/xgoMusic").resolve() output = (root / filename).resolve() if root not in output.parents: raise ValueError("Invalid output path") subprocess.run( [ "arecord", "-d", str(duration), "-f", "S32_LE", "-r", "8000", "-c", "1", "-t", "wav", str(output), ], check=True, ) ``` ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
lib/edulib.py:194
Finding
Implicit Privileged GPIO Operations Violate Least Privilege<![CDATA[ ## Vulnerability Details **File Location**: `lib/edulib.py:194-197` and `lib/edulib.py:382-406` **Vulnerability Type**: Unnecessary privileged operations and undocumented privileged side effects **Risk Level**: Medium ### Vulnerable Code Object construction automatically executes privileged GPIO configuration: ```python os.system("sudo pinctrl set 24 ip") os.system("sudo pinctrl set 23 ip") os.system("sudo pinctrl set 17 ip") os.system("sudo pinctrl set 22 ip") ``` Button reads also invoke `sudo`: ```python if button == "d": result = subprocess.run(["sudo", "pinctrl", "level", "24"], capture_output=True, text=True).stdout elif button == "c": result = subprocess.run(["sudo", "pinctrl", "level", "23"], capture_output=True, text=True).stdout elif button == "a": result = subprocess.run(["sudo", "pinctrl", "level", "17"], capture_output=True, text=True).stdout elif button == "b": result = subprocess.run(["sudo", "pinctrl", "level", "22"], capture_output=True, text=True).stdout ``` ### Technical Analysis Constructing `XGOEDU` immediately attempts to execute privileged GPIO commands, including in scripts that only need display, camera, or AI functionality. This broadens the privilege boundary beyond the minimum required for each operation. The behavior also conflicts with the Skill documentation, which explicitly instructs users not to use `sudo`. Repeated privileged subprocess calls can block on password prompts, create inconsistent runtime behavior, and increase the consequences of command-execution flaws elsewhere in the Skill. Although the fixed argument arrays used for button reads are not themselves command-injection vulnerabilities, automatic privilege use is an unnecessary and insufficiently isolated escalation of authority. ### Attack Path 1. A script initializes `XGOEDU`, including scripts unrelated to GPIO button handling. 2. The constructor automatically invokes `sudo pinctrl`. 3. If passwordless sudo is configured, GPIO ...[truncated 735 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove all runtime `sudo` invocations from the library. - Grant narrowly scoped GPIO access through an appropriate device group, udev rule, GPIO character-device permissions, or a dedicated helper with a minimal interface. - Make button/GPIO initialization explicit rather than performing it in the general `XGOEDU` constructor. - Initialize only the pins required by the requested operation. - Fail safely with a clear permissions error rather than attempting interactive elevation. - Align implementation and documentation so that the documented prohibition on `sudo` is enforced by the code. - Review local sudoers rules to ensure the Skill account does not have broad passwordless command access. ]]>
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (173)

Tainted flow: 'audio_url' from requests.post (line 61, network input) → subprocess.run (code execution)

Critical
Category
Data Flow
Content
edu.lcd_clear()
            edu.lcd_text(5, 5, "播放中...", "GREEN", 14)
            
            subprocess.run(f'mplayer -really-quiet "{audio_url}"', shell=True, check=True)
            print(f"语音合成完成: {args.text}")
            print(f"音色: {args.voice} ({VOICE_OPTIONS.get(args.voice, '')})")
        else:
Confidence
99% confidence
Finding
There is a direct tainted data flow from requests.post response content to subprocess.run, creating a classic remote-to-code-execution path. Because this runs on a robot control platform, successful exploitation could allow arbitrary commands on the host and potentially affect attached hardware or broader system trust.

Vague Triggers

High
Confidence
96% confidence
Finding
The activation text is extremely broad, covering generic terms like movement, camera, AI, speech, and detection, which can cause the skill to trigger in many unrelated conversations. Because this skill has shell, network, sensing, and physical actuation implications, overbroad routing materially raises the chance of accidental invocation and unsafe real-world actions.

Ae1

High
Category
analysis-evasion
Content
| `take_photo.py` | 拍照 | `--filename photo.jpg` | 照片已保存: {path} |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `camera_preview.py` | 摄像头预览 | `--duration 10` | 预览窗口显示 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `face_detect.py` | 人脸检测 | `--continuous` (持续模式) | 检测到人脸: x=, y=, w=, h= 或 未检测到人脸 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `face_count.py` | 人脸计数 | 无 | 共检测到 N 张人脸 + 每张人脸位置 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `gesture_detect.py` | 手势识别 | `--continuous` | 识别到手势: {手势} 位置=({x},{y}) 或 无 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `color_detect.py` | 颜色识别 | `--color R` (R/G/B/Y) `--continuous` | 检测到{颜色}: 位置=({x},{y}), 半径={r} |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `line_detect.py` | 巡线检测 | `--color K` (K黑/W白/R/G/B/Y) `--continuous` | 巡线: x={x}, angle={角度} |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `qr_scan.py` | 二维码扫描 | `--continuous` | 二维码内容: {内容} 或 无 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `yolo_detect.py` | 目标检测 | `--continuous` | 检测到: {类别} 位置=({x},{y}) 或 无 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `emotion_detect.py` | 情绪识别 | `--continuous` | 情绪: {情绪} 位置=({x},{y}) 或 无 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `find_ball.py` | 寻找小球 | `--color R` `--timeout 30` | ✓ 找到{颜色}色小球 或 ✗ 超时未找到 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `find_person.py` | 寻找人类 | `--timeout 45` | ✓ 找到人类 或 ✗ 超时未找到 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `catch_ball.py` | 抓取小球 | `--color R` `--timeout 60` | ✓ 抓取成功 或 ✗ 抓取失败 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `read_imu.py` | 读取IMU | `--axis all` (roll/pitch/yaw/all) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `read_motor.py` | 读取舵机角度 | 所有舵机当前角度 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `move.py` | 前后移动 | `--speed 0.5` `--runtime 3` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `roll.py` | Roll姿态 | `--angle 10` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `height.py` | 身高调整 | `--height 90` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `reset_odom.py` | 重置里程计 | 无参数 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `balance_roll.py` | Roll自平衡 | `--mode 1` (0=关, 1=开) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `perform.py` | 表演模式 | `--mode 1` (0=关, 1=开) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `calibration.py` | 软件标定 | `--state start/end` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `periodic_roll.py` | 周期Roll | `--period 1.5` `--duration 5` |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Static analysis

No suspicious patterns detected.