Back to skill

Security audit

Ros1 Noetic General

Security checks for vulnerabilities and agentic risk

Overview

The skill is transparent about ROS operations, but its bundled lifecycle scripts have unsafe state-file and process-control behavior that needs review before use.

Use this only in a controlled ROS environment after reviewing or fixing the lifecycle scripts. Do not run health or stop commands against untrusted state files, avoid real hardware motion unless explicitly confirmed with a working stop path, and do not expose rosbridge beyond a trusted network boundary.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ros1_runtime_health_check.sh:112
Finding

Arbitrary Shell Command Execution Through Untrusted State Files

Content
View full analysis
&2; exit 2; } state_file="${state_file:A}" [[ -f "$state_file" ]] || { echo "[ERROR] state file not found: $state_file" >&2; exit 1; } # shellcheck disable=SC1090 source "$state_file" require_state_var "WORKSPACE_ROOT" require_state_var "ROS_SETUP" require_state_var "LOG_FILE" ``` ```zsh # scripts/ros1_runtime_health_check.sh:157-160 # shellcheck disable=SC1090 source "$ros_setup" if [[ -n "$workspace_setup" && -f "$workspace_setup" ]]; then # shellcheck disable=SC1090 source "$workspace_setup" fi ``` ```zsh # scripts/ros1_stop_target.sh:67-75 [[ -n "$state_file" ]] || { echo "[ERROR] --state-file is required" >&2; exit 2; } state_file="${state_file:A}" [[ -f "$state_file" ]] || { echo "[ERROR] state file not found: $state_file" >&2; exit 1; } # shellcheck disable=SC1090 source "$state_file" pid="${PID:-}" pgid="${PGID:-}" ros_home="${ROS_HOME:-}" ``` ### Technical Analysis Both lifecycle scripts accept an arbitrary path through `--state-file` and execute that file with the shell `source` built-in. A state file is therefore treated as executable code rather than inert metadata. The scripts only verify that the supplied path points to an existing file. They do not verify that: - The file was generated by `ros1_start_target.sh`. - The file is located beneath an approved state directory. - The file and its parent directories have trusted ownership and permissions. - The file contains only an allowlisted set of data assignments. - Values are free from shell substitutions, functions, redirections, or commands. Any shell statement in t ...[truncated 1899 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/ros1_stop_target.sh:73
Finding

Untrusted Process Metadata Can Terminate Unrelated Processes or Process Groups

Content
View full analysis
/dev/null; then related_pids=("${(@f)$(related_pids_for_ros_home "$ros_home")}") if (( ${#related_pids[@]} == 0 )); then append_or_replace_state "STATUS" "stopped" echo "[ros1-stop] status=already_stopped" exit 0 fi for child_pid in "${related_pids[@]}"; do kill -TERM "$child_pid" 2>/dev/null || true done sleep 2 remaining=("${(@f)$(related_pids_for_ros_home "$ros_home")}") if (( ${#remaining[@]} > 0 )); then for child_pid in "${remaining[@]}"; do kill -KILL "$child_pid" 2>/dev/null || true ``` ```zsh # scripts/ros1_stop_target.sh:116-125 target="-$pgid" if [[ -z "$pgid" ]]; then target="$pid" fi kill -TERM "$target" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true deadline=$((SECONDS + timeout_seconds)) while kill -0 "$pid" 2>/dev/null; do if (( SECONDS >= deadline )); then kill -KILL "$target" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true ``` The fallback process search is: ```zsh related_pids_for_ros_home() { local ros_home="$1" [[ -n "$ros_home" ]] || return 0 ps -eo pid=,args= | awk -v needle="$ros_home" 'index($0, needle) {print $1}' } ``` ### Technical Analysis The stop operation trusts `PID`, `PGID`, and `ROS_HOME` values loaded from the state file. It performs no proof that the selected process belongs to a run created by `ros1_start_target.sh`. Missing controls include: - Numeric and range validation for PID and PGID values. - Process ownership verification. - Executable or command identity verification. - Process start-time ve ...[truncated 2127 chars]
Remediation
View remediation
/stat`. - Executable path. - Launcher path. - UID. - Run-directory identifier. 4. Before signaling, verify that the current process matches every recorded identity property. 5. Confirm that the target is a descendant of the recorded launcher or belongs to a dedicated, verified process group. 6. Refuse process-group signaling unless the group was explicitly created by the launcher and `PGID == launcher PID` is confirmed. 7. Remove command-line substring matching through `ps | awk`. 8. If descendant recovery is required, use verified parent-process relationships or a dedicated supervisor/cgroup. 9. Revalidate process identity immediately before both `SIGTERM` and `SIGKILL` to address PID reuse. 10. Return an error and request manual review whenever target ownership or identity cannot be proven. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ros1_start_target.sh:252
Finding

Launcher Does Not Isolate Its Process Group Before Group-Wide Termination

Content
View full analysis
"$log_file" 2>&1 < /dev/null & pid="$!" sleep 0.2 pgid="$(ps -o pgid= -p "$pid" | tr -d ' ' || true)" append_state_line "PID" "$pid" append_state_line "PGID" "$pgid" ``` ```zsh # scripts/ros1_stop_target.sh:116-125 target="-$pgid" if [[ -z "$pgid" ]]; then target="$pid" fi kill -TERM "$target" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true deadline=$((SECONDS + timeout_seconds)) while kill -0 "$pid" 2>/dev/null; do if (( SECONDS >= deadline )); then kill -KILL "$target" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true ``` ### Technical Analysis The launcher backgrounds a process with `nohup`, but `nohup` only changes signal disposition and redirects output; it does not guarantee creation of a new session or process group. The script records whichever PGID the child inherited. In non-interactive shells and some Agent execution environments, the launcher can share a process group with its invoking shell and sibling processes. The stop script later converts that PGID into a negative target, causing `kill` to signal every member of the group. The implementation does not verify that: - A dedicated process group was created. - The launcher is the leader of that group. - `PGID` equals the launcher PID. - The group contains only processes associated with the ROS run. - The caller or Agent runtime is outside the target group. This creates a dangerous normal-operation path independent of state-file tampering. ### Attack Path 1. The Agent starts a ROS target from a shell environment where background children inherit the caller’s process group: ```zsh zsh scripts/ros1_start_target.sh \ --workspace /path/to/ws \ --packag ...[truncated 1194 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/move_forward_by_odom.py:13
Finding

Robot Motion Script Accepts Non-Finite and Excessive Control Parameters

Content
View full analysis
argparse.Namespace: p = argparse.ArgumentParser(description="Closed-loop forward movement by odometry") p.add_argument("--cmd-topic", default="/cmd_vel") p.add_argument("--odom-topic", default="/odom") p.add_argument("--distance", type=float, default=1.0) p.add_argument("--speed", type=float, default=0.2) p.add_argument("--rate", type=float, default=20.0) p.add_argument("--timeout", type=float, default=20.0) return p.parse_args() def main() -> int: args = parse_args() if args.distance <= 0: print("ERROR: --distance must be > 0", file=sys.stderr) return 2 if args.speed <= 0: print("ERROR: --speed must be > 0", file=sys.stderr) return 2 rospy.init_node("move_forward_by_odom", anonymous=True) pub = rospy.Publisher(args.cmd_topic, Twist, queue_size=10) try: first = rospy.wait_for_message(args.odom_topic, Odometry, timeout=5.0) except Exception as e: # noqa: BLE001 print(f"ERROR: failed to read odometry from {args.odom_topic}: {e}", file=sys.stderr) return 3 sx = first.pose.pose.position.x sy = first.pose.pose.position.y cmd = Twist() cmd.linear.x = args.speed ``` ### Technical Analysis The script validates only that `distance` and `speed` are greater than zero. Python’s `float` parser accepts special values such as `inf` and `nan`. In particular, positive infinity passes the `<= 0` checks and is assigned directly to `cmd.linear.x`. There are also no upper bounds for distance or speed, and no finite or valid-range checks for `rate` and `timeout`. Consequently: - Excessive speed values can be published directly to `/cmd_vel`. - `inf` can ...[truncated 1778 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill is described as a general ROS assistance skill, but it also includes direct robot actuation guidance such as publishing to /cmd_vel and running closed-loop motion scripts. That mismatch is dangerous because users or orchestrators may invoke it expecting informational or routine project help, while the skill can transition into physical-world control with safety consequences.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill is described as a general ROS assistance skill, but it also includes direct robot actuation guidance such as publishing to /cmd_vel and running closed-loop motion scripts. That mismatch is dangerous because users or orchestrators may invoke it expecting informational or routine project help, while the skill can transition into physical-world control with safety consequences.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill is described as a general ROS assistance skill, but it also includes direct robot actuation guidance such as publishing to /cmd_vel and running closed-loop motion scripts. That mismatch is dangerous because users or orchestrators may invoke it expecting informational or routine project help, while the skill can transition into physical-world control with safety consequences.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 47)May include surrounding context.

md
- Resolve `scripts/...` and `references/...` relative to this `SKILL.md` file before using them.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script directly sources the attacker-controllable state file, which causes any shell code inside that file to execute with the privileges of the user running the health check. Because --state-file is an input parameter and the script is meant for local project operations, a forged or tampered state file can turn a read-only health check into arbitrary code execution.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script sources ROS setup scripts from paths loaded out of the untrusted state file, so an attacker can point ROS_SETUP or WORKSPACE_SETUP at a malicious shell script and gain code execution. In this skill context, where scripts may be run against local ROS workspaces and automation state, this substantially increases risk because users are encouraged to run operational commands on their machines.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill explicitly instructs the agent to run shell commands and bundled scripts, but it does not declare any tool scope, permissions, or allowed-tools boundary. This creates an authorization gap where a broadly triggered skill can perform local command execution without a clearly constrained capability contract, increasing the chance of unintended or unsafe execution.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger conditions are extremely broad, covering common ROS terms and generic bilingual requests, which makes accidental invocation likely. In a skill that can lead to shell execution, builds, launches, and even robot control, overbroad routing increases the risk that sensitive actions are selected in contexts where a safer general-response path should have been used.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The example phrases include highly ambiguous requests like 'help me look at ROS' or 'start ROS' without requiring context, target, or confirmation. Because this skill contains operational workflows, ambiguous natural-language examples can cause the agent to escalate from discussion into local execution or runtime manipulation too easily.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
60% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · SKILL.md (reported line 187)May include surrounding context.

md
- Fail fast when telemetry is stale or missing.
- Report exact measured outcome (not only “success”).
- When starting long-running processes, persist pid/log/state metadata so later health checks and stop actions can use the same handle.
- Ask for confirmation before commands that install packages, require `sudo`, change system configuration, or move hardware in the real world.
- Prefer dry-run inspection before any destructive or stateful ROS action.

## Step 6: References

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The profile includes rosbag record and related data-capture commands without any warning that telemetry, images, maps, robot state, or environment data may be written to disk and retained. In a ROS operations skill, this is a real security/privacy issue because users may run the command in production or around sensitive environments and unintentionally collect or persist sensitive operational data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The rosbridge section instructs users to start rosbridge_websocket and shows publish/subscribe/service-call templates, but it does not explicitly warn that this exposes ROS control and telemetry over a network-accessible websocket. In the context of a ROS operations skill, this is especially dangerous because rosbridge can provide remote command and observation paths into a live robot system; without strong warnings and restrictions, users may expose unsafe control surfaces or sensitive telemetry.

Content

No source excerpt is available for this finding.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/official-docs.md (reported line 63)May include surrounding context.

zsh
# one-time init, only with user approval
# sudo rosdep init

# update as normal user (no sudo)
rosdep update

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/ros1-full-scope.md (reported line 58)May include surrounding context.

zsh
# one-time init, only with user approval
# sudo rosdep init

# update as normal user (no sudo)
rosdep update

Sudo/Root Execution

Medium
Category
Privilege Escalation
Confidence
70% confidence
Finding

Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Content

Scanner excerpt · references/ros1-full-scope.md (reported line 69)May include surrounding context.

md
Notes:

- ROS wiki explicitly warns: do **not** run `rosdep update` with sudo
- `rosdep init` is machine-level setup, not a routine per-workspace step
- prefer apt installation paths for Noetic tooling where available

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/move_forward_by_odom.py (reported line 59)May include surrounding context.

python
try:
        while not rospy.is_shutdown():
            try:
                od = rospy.wait_for_message(args.odom_topic, Odometry, timeout=0.5)
            except Exception as e:  # noqa: BLE001
                print(f"ERROR: odometry timeout: {e}", file=sys.stderr)
                break

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script executes source on ROS_SETUP and workspace_setup, where at least workspace_setup is derived from the inspected workspace path. Sourcing a setup file runs arbitrary shell code in the current process, so a malicious or untrusted workspace can execute commands during a seemingly harmless 'preflight' check, which is especially risky because this skill is designed to operate on local ROS projects and workspaces.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
65% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · scripts/ros1_start_target.sh (reported line 259)May include surrounding context.

sh
append_state_line "STATUS" "starting"
append_state_line "STARTED_AT" "$(date -Iseconds)"

nohup "$launcher_file" >"$log_file" 2>&1 < /dev/null &

pid="$!"
sleep 0.2

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The script executes ROS CLI commands via eval on L29, L35, and L37, but provides no comment, docstring, or explicit user-facing disclosure beyond basic usage text. Because subprocess execution is one of the safety-relevant operations in scope for code files, the lack of any warning or explanatory notice is a quality/safety gap.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

The helper updates or appends keys directly into the provided state file via sed and shell redirection. Although state management is part of the script's function, this file-writing behavior is not disclosed with a confirmation prompt, user-facing warning at write time, or explanatory comment near the write operations.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.