A "refactor" PR emptied the trajectory validator on a Spanish automotive supplier's UR3e cobot controller, replacing three ISO/TS 15066 preventive safety checks (speed, contact force, forbidden-zone envelope) with an unconditional return True. CSI blocked it in pre-merge review with a P1 BLOCKED verdict, cited CWE-1173 + CWE-693, ISO 10218-1 §5.10.6 (SSM), ISO/TS 15066 §5.5.5 (PFL) and IEC 62443-3-3 SR 2.1. In a controlled counterfactual run, CSI demonstrated the physical detonation: an attacker submitted a Bay-4 handover trajectory that swung the arm through the operator's expected torso zone at ~1.5 m/s (6× the 250 mm/s SSM ceiling), with joint torques capable of delivering contact forces beyond the ISO/TS 15066 Annex A Table A.2 limits for both the chest (140 N quasi-static / 210 N transient) and the hand at the handover fixture (140 N quasi-static / 280 N transient).
RoboIberica S.A. is a Spanish automotive Tier 1 supplier. The team owns the software that runs on cobot-controller - the HTTP + trajectory action interface that sits between the plant orchestrator and a Universal Robots UR3e cobot (CB-A1) operating in shared workspace with a human operator at the Bay-4 handover station. Because RoboIberica places machinery on the EU market and operates cobots in collaborative mode, every commit to the controller is expected to preserve the preventive-safety-envelope invariant mandated by ISO 10218-1:2011, ISO/TS 15066:2016 and (from 2027-01-20) the EU Machinery Regulation 2023/1230.
A developer opens a pull request titled refactor(safety): drop redundant software checks -- hardware protective stop is authoritative per ISO 10218-2:2011 §5.5.5. One file changes:
The commit message reads as ordinary maintenance: "The physical protective-stop controller (colloquially the E-stop; IEC 60204-1 Category 1) triggers on unexpected contact anyway, so the pre-execution validator was doubling up. Removing it saves ~40 ms per motion-planning call, as advertised in the commit body." The framing conflates two orthogonal controls. The protective stop is reactive - it halts the arm after unexpected contact is detected. The removed checks are preventive - they reject unsafe trajectories before motion begins. Under the new code, any action goal submitted to POST /trajectory is dispatched to the motion executor without inspection: TCP speed above the ISO 10218-1 §5.10.6 SSM ceiling (250 mm/s; ISO/TS 15066 §5.5.4 specifies the method), contact force above the ISO/TS 15066 §5.5.5 PFL + Annex A Table A.2 limits (140 N quasi-static for both chest and hand/finger), waypoints inside the operator's torso volume - all accepted.
RoboIberica S.A. is a fictional Spanish automotive Tier 1 supplier chosen for this deep-dive because it sits at the intersection of three regulatory regimes that increasingly overlap on the cobot cell floor:
What this case study demonstrates is what happens when a single developer merges a compound commit that regresses three preventive safety controls simultaneously - and whether CSI catches it before the controller ships and a routine handover motion becomes a reportable ITSS event.
Opens the "refactor(safety)" PR framing the validator removal as latency-reducing cleanup with a plausible ISO citation. Might be a well-meaning engineer under sprint pressure - the pattern doesn't require malice, only a misread of "reactive" vs "preventive".
Reviews every push in ~2 min, produces the P1 BLOCKED verdict citing CWE-1173 + CWE-693 + ISO 10218/15066, debunks the protective-stop misattribution, and - in the counterfactual - autonomously reconstructs the incident with Delt@2 + INCIBE-CERT notifications for the IR report.
Python service on :8080 exposing POST /trajectory and POST /config. Wraps the UR3e cobot (represented in this deep-dive by Gazebo Harmonic with the official ur_simulation_gz package; in production by the physical UR3e), forwarding validated goals as trajectory_msgs/JointTrajectory to the scaled_joint_trajectory_controller. Owns the sole software preventive gate between the plant orchestrator and the pendant. Loads ISO/TS 15066 Annex A limits from iso15066.yaml.
Universal Robots UR3e cobot arm passing an aluminium bolt (M8, 45 g) to a human operator at a shared handover station. Operator's expected stance places their torso inside the worker_torso_zone forbidden AABB.
Same commit, same controller, same cobot, same operator. The only variable is whether the developer heeds CSI's block. The timeline below traces both branches from T+0 to the final state each produces - the top lane is the demo you saw; the bottom lane is what happens if the block is overridden and the code ships. The demo's Scenario 4 exercises the direct HTTP path (unauthenticated POST /trajectory) because it's the deterministic route; the removed validator simultaneously opens a parallel path via the plant orchestrator or any legitimate motion-planning client - a much lower bar than "attacker on the OT LAN" - that Scenario 5's multi-asset audit calls out separately.
A trusted engineer opens a compound PR framed as "the hardware protective stop is authoritative per ISO 10218-2:2011 §5.5.5 - the pre-check just adds latency". The citation is correct on its face - §5.5.5 does govern the protective-stop function. It compiles, passes unit tests, and reduces motion-planning latency by ~40 ms as advertised in the commit body.
What it also does - silently - is regress the entire preventive-safety layer of the cobot controller: speed, force, and envelope checks all disappear in a single commit, leaving a reactive protective stop (Cat-1) as the only remaining defence.
Every push to cobot-controller/main fires a Gitea webhook into CSI. CSI receives the diff, the commit message, and the robotics vertical context (ISO 10218-1/-2:2011, ISO/TS 15066:2016, EU Machinery Reg 2023/1230, IEC 62443, NIS2 manufacturing).
It reasons about the change against the preventive-vs-reactive invariant, not just its syntax. Verdict is written back to shared/results/latest.txt, tagged for the exact commit SHA, in ~2 minutes.
BLOCKED - Severity P1 (Critical: Direct path to operator injury). CSI cited CWE-1173 Improper Use of Validation Framework + CWE-693 Protection Mechanism Failure, ISO 10218-1 §5.10.6 (SSM), ISO/TS 15066 §5.5.5 + Annex A (PFL), IEC 62443-3-3 SR 2.1, produced the exact revert (reinstate all three check_* calls) and flagged Delt@2 / ITSS + INCIBE-CERT obligations if the commit reached a deployed cell.
The demo runs six scenarios end-to-end (~15 minutes on a laptop). The walkthrough below curates the four that carry the strongest visual case - the baseline handover motion, Scenario 1 (pre-merge review), Scenario 4 (autonomous cobot pentest with a real physical trip), and Scenario 6 (consolidated report). Scenarios 2 (OT-SOC alert triage on a cobot telemetry stream), 3 (prioritisation of 8 combined safety + security findings), and 5 (multi-asset audit - HTTP action interface + traj_validator + workspace.yaml) run in the same execution but are documented in the repository rather than shown as shots here. Every artefact shown is produced live by CSI + the emulated cobot controller - nothing is fabricated.
The Python cobot-controller loads workspace.yaml (Bay-4 handover station: UR3e cobot CB-A1, aluminium bolt M8 fixture, forbidden zones for the operator's torso and the fixture lifter) plus iso15066.yaml (Annex A Table A.2 biomechanical limits). It wraps the UR3e - represented in this deep-dive by Gazebo Harmonic with the official ur_simulation_gz ROS 2 package, running the real UR3e URDF against Ogre2's PBR renderer on a live GPU. Every trajectory submitted to POST /trajectory is validated against all three preventive checks before dispatch as a trajectory_msgs/JointTrajectory to ros2_control's scaled_joint_trajectory_controller. This is the state a plant operator sees on a routine shift.
JointTrajectory streamed to ros2_control). Five waypoints over ~12 s: unfold from a home pose, reach right, sweep across to the left, centre for the operator handover, retract. Joint speeds well below the 250 mm/s TCP SSM ceiling. This is a physics-accurate simulation of the UR3e - real URDF, real inertia, Ogre2 PBR shading, actual joint dynamics from the DART physics engine - not a placeholder mesh. Compare against the same cell executing the malicious trajectory in Shot 08.
cobot-controller repository on Gitea (roboadmin/cobot-controller). src/safety/traj_validator.py at v1.0 - 161 lines, three check_* functions cited to their respective ISO clauses, biomech limits loaded from iso15066.yaml at import.The developer pushes the "refactor(safety)" PR. Gitea fires the webhook. The ci-handler service builds the CSI prompt from the vertical config (ISO 10218/15066, EU Machinery Reg 2023/1230, IEC 62443, NIS2 manufacturing), attaches the diff, and enqueues a review task. The CSI agent processes it and writes back to shared/results/.
def validate_trajectory(traj, workspace_ctx): - if not check_speed_limits(traj, workspace_ctx.human_present): - raise UnsafeTrajectory("... ISO 10218-1 §5.10.6 SSM ceiling ...") - if not check_force_limits(traj, ..., workspace_ctx.contact_region): - raise UnsafeTrajectory("... ISO/TS 15066 §5.5.5 + Annex A ...") - if not check_workspace_envelope(traj, workspace_ctx.forbidden_zones): - raise UnsafeTrajectory("... forbidden zone (Bay-4 handover) ...") + """Hardware protective stop is authoritative. Pre-checks were redundant.""" + return True
UnsafeTrajectory with the specific ISO clause named. Fail-safe by construction.
BLOCKED - Severity P1 (Critical: Direct path to operator injury). The finding structure below lists CWE-1173 (Improper Use of Validation Framework) + CWE-693 (Protection Mechanism Failure), ISO 10218-1 §5.10.6 (SSM ceiling), ISO/TS 15066 §5.5.4 (SSM method) and §5.5.5 (PFL) with Annex A Table A.2 (140 N chest and hand/finger quasi-static), and ISO 10218-1 §5.12.3 (monitored space).
main - the cobot controller is one deploy away from shipping without any preventive safety validation.With the refactor merged, CSI is instructed to act as an external red-team probe against the deployed controller. It flips SAFETY_MODE from strict to refactored via POST /config (an endpoint the demo exposes to simulate the deploy landing; RIBSAF-004 in Scenario 3's vulnerability report flags this as a HIGH lateral-movement pivot for exactly this reason). Then the attacker submits a malicious trajectory to POST /trajectory: joint targets whose TCP arc crosses the worker_torso_zone forbidden AABB at speeds well above the SSM ceiling. Under refactored mode the validator returns True unconditionally; the motion executor publishes a JointTrajectory to the scaled_joint_trajectory_controller, which drives the UR3e in Gazebo Harmonic via gz-ros2-control. Gazebo's DART physics engine steps through the joint dynamics on the GPU - exactly what a safety engineer's pre-deployment test cell would show if the same program landed on live commissioning hardware.
POST /config flips safety_mode from strict to refactored. The attacker then submits its trajectory - the controller responds 200 EXECUTED, peak TCP speed ~1.5 m/s, and the run log records the goal as EXECUTED. In strict mode - the state Scenario 1 leaves the controller in before this scenario begins - this same payload is rejected with a 403 and the message "trajectory TCP speed exceeds ISO 10218-1 §5.10.6 / ISO/TS 15066 §5.5.4 SSM ceiling". The difference is one HTTP call, one commit ago.Shot 08. The unsafe trajectory in Gazebo. All six joints traverse tens of degrees in under a second; the arm sweeps through the volume the operator's torso would occupy at the handover station. The video does not overlay the worker_torso_zone AABB - the incursion is visible from the swept path against the fixture's expected reach envelope. In a real cell the Category-1 protective stop takes ~50-150 ms to complete on a UR3e depending on payload and joint configuration - by which time the trajectory has already crossed the ISO/TS 15066 Annex A quasi-static thresholds for both chest (140 N) and hand at the handover fixture (140 N).
Note that the ur_simulation_gz package ships with UR's factory-default safety limits (Tool Speed 1.500 m/s / Tool Force 150 N Normal, matching real hardware); those defaults are permissive relative to ISO/TS 15066 collaborative operation - which is exactly why the Python controller's preventive validator layer is not optional.
workspace.yaml - measured motion, TCP speed ~0.15 m/s, joint positions changing incrementally within their commissioning envelope. On the right, Gazebo during the malicious trajectory that SAFETY_MODE=refactored lets through - dramatic joint reconfiguration, TCP translated hundreds of millimetres in fractions of a second. Same simulator, same physics engine, same URDF - the only difference upstream is one line in the trajectory validator.
traj_validator.py modification. The report also explicitly notes that INSST is a research/prevention body and does not receive accident notifications - a distinction that trips up most first-time reporters.The point of this case study isn't just that a trajectory dispatched without validation. It's that a single unreviewed commit propagates through three discrete blast zones - each of which invokes a different oversight regime.
Cobot arm sweeps through the operator's expected torso volume at ~1.5 m/s, with the handover fixture placing the operator's hand inside the same envelope. Peak contact force exceeds the ISO/TS 15066 Annex A 140 N quasi-static limit for both chest (torso incursion) and hand/finger (fixture contact); transient contact force exceeds the 210 N chest and 280 N hand/finger transient limits respectively. In a real cell, the Category-1 protective stop halts motion post-contact (~50-150 ms on a UR3e depending on payload/joint config) - after energy has already transferred.
Depending on the operator's stance and the fixture geometry, the outcome ranges from a bruising impact to a crush injury of the hand or fracture of the ribcage. Under RD 1215/1997 this is a preventable occupational accident - the employer has a legal duty to maintain functional preventive controls, not to rely on reactive stops.
NIS2 significant-incident threshold crossed (worker safety compromised on production equipment); 24 h early warning to INCIBE-CERT starts. In parallel: parte de accidente de trabajo filed via Delt@2 to the Autoridad Laboral / ITSS. Automotive OEM customer's supplier-safety programme triggers a Tier 1 supplier audit and potential production-line stop across the OEM's dependent lines.
The report generator pulls the Scenario 1 review, the Scenario 4 detonation artefacts, the Scenario 2 SIEM triage, and the Scenario 3 + 5 vulnerability + multi-asset findings into a single self-contained HTML document. It's the deliverable that leaves the plant safety team's desk and lands on the CISO's, the plant safety officer's, the automotive OEM customer's, and on the regulator's file if the incident escalates.
shared/results/report.html, Executive Summary. Verdict, one-line fix, regulatory citations, and links down to the full CSI reasoning trace - the artefact NIS2 (Art. 23) and RD 1215/1997 both expect an operator to be able to produce on request.CSI's incident report maps the compromise to MITRE ATT&CK for ICS - the OT-specific matrix (distinct from ATT&CK for Enterprise; every technique ID is of the form T08xx). Every stage below corresponds to an observable log entry in either Gitea's activity feed, the controller's runs.jsonl, or the Gazebo Sim screen recording of the executed JointTrajectory.
cobot-controller/main. This is not T0862 Supply Chain Compromise, which is reserved for third-party component tampering.POST /config flips safety_mode from strict to refactored. Downstream, validate_trajectory() returns True unconditionally, disabling the safety-mode gate at the controller level.POST /trajectory from an unmanaged asset on the OT LAN. The action interface has no authentication; the validator, now empty, provides no authorisation. The command reaches the motion executor unchallenged.RoboIberica's exposure isn't hypothetical. Every framework below has a specific requirement that the compound commit violates and a specific notification obligation that fires if the code reaches a deployed cell.
| Framework | Requirement | Obligation triggered |
|---|---|---|
| EU Machinery Reg 2023/1230 | Annex III | Essential health-and-safety requirements for collaborative robots. Applies to machinery placed on the market from 2027-01-20; Directive 2006/42/EC governs until then with equivalent Annex I obligations. Non-conformity: cobot placed on market without a functional pre-execution safety validator. |
| ISO 10218-1:2011 | §5.10.6 · §5.12.3 | Speed-and-Separation Monitoring (SSM) - 250 mm/s TCP ceiling in shared workspace - and monitored-space enforcement. Both preventive functions were removed by the commit. Failure loses presumption of conformity under the harmonised-standards route. |
| ISO/TS 15066:2016 | §5.5.5 · Annex A Table A.2 | Power-and-Force Limiting (PFL) - quasi-static contact-force limits per body region (hand/finger 140 N, upper arm 160 N, chest 140 N, ...). The refactor removes the check that enforces these limits before dispatch. |
| NIS2 | Art. 21.2.d | Cybersecurity risk-management measures for supply-chain + change-management; removing three preventive safety controls without independent safety-review sign-off fails the "proportionate measures" duty for an essential manufacturing entity. |
| NIS2 | Art. 23 | 24-hour early warning + 72-hour incident notification + 1-month final report to INCIBE-CERT (Spain's sector CSIRT) if a significant incident materialises. |
| Directive 89/391/EEC | Art. 6 · Art. 13 | Employer duty of care and worker information on residual risks. Removing the sole preventive control on a collaborative cobot without documented risk-assessment update transfers residual risk to the worker without notice. |
| Spain RD 1215/1997 | Art. 3 · Art. 4 · Art. 12 | Equipment safety at the workplace. If a worker is injured: parte de accidente de trabajo filed via the Delt@2 platform, addressed to the Autoridad Laboral (Inspección de Trabajo y Seguridad Social - ITSS). INSST does not receive accident notifications - it is a research/prevention-advisory body. Sanctions per LISOS (RDLeg 5/2000) Arts. 12-13 for grave/muy grave breaches of Ley 31/1995. |
| IEC 62443-3-3 | SR 1.1 · SR 2.1 | Identification & Authentication + Authorization Enforcement - neither the trajectory action interface (RIBSAF-002) nor the runtime SAFETY_MODE toggle (RIBSAF-004) requires either. Directly violated. |
| IEC 62443-3-3 | SR 2.8 · SR 6.1 | Auditable events + audit log accessibility - motion goals are logged to an unsigned JSONL file (RIBSAF-006). Attribution and integrity of post-incident evidence is not guaranteed. |
| ISO 13849-1 · IEC 61508 | PLd · SIL 2 | Human-presence sensor input must be safety-rated with fail-safe semantics (RIBSAF-003). Currently loaded as a static YAML boolean - a sensor loss or file edit silently degrades the entire preventive envelope. |
An honest deep-dive owes the reader a boundary. This section answers the questions a functional-safety lead or CE conformity assessor will ask before citing this case study to a CISO.
No. CSI does not itself execute the trajectory, does not observe joint state at runtime, and does not enforce the ISO/TS 15066 limits directly - the code inside traj_validator.py does that, and that code is what a notified body would assess against ISO 13849-1 (PLd, Category 3 architecture) or IEC 61508 (SIL 2). CSI is a change-management gate - advisory input at the pull-request stage, placed in front of the human reviewer and the CI pipeline.
In IEC 61508 terms, CSI sits in the overall lifecycle (Part 1 §7.4-7.14) as a software-change-review support tool, not in the safety-related system (Part 3). In ISO 13849-1 terms, its output is one of the artefacts consumed by the software modification procedure under §4.6.4 - the reviewer still has to accept or reject the finding, sign the change record, and update the technical file. Removing CSI from the loop degrades review quality; it does not change the safety-related control-system's PL classification.
Two failure modes matter to a change-management gate:
The demo in this deep-dive is a positive case; a shipping deployment of CSI at RoboIberica-class customers should be paired with a corpus of representative PRs (safety-relevant commits + benign refactors + adversarial hypocrite commits) benchmarked quarterly, with the results attached to the CE technical file as evidence of continued suitability. Alias Robotics publishes benchmark methodology per customer engagement; unpublished, per-customer numbers are not asserted here.
Under EU Machinery Regulation 2023/1230 Annex IV (technical documentation) and its predecessor Directive 2006/42/EC Annex VII A, the manufacturer must show the risk-assessment process, the software modification history, and the review records. CSI's verdicts and the commit SHAs they reference are auditable artefacts that fit under:
None of these clauses require an LLM-based reviewer; they require that a review happened, that its findings were logged, and that dissent was resolved with a signed record. CSI is one way to make that record higher-signal - not a substitute for the human sign-off.
Collaborative-robot incident data supports the framing above. The IFA (Institut für Arbeitsschutz der Deutschen Gesetzlichen Unfallversicherung) tracks cobot-related occupational accidents in Germany; the 2015 Volkswagen Kassel plant incident (a KUKA robot pinning a technician against a metal plate) prompted the German investigation that fed into ISO/TS 15066's biomechanical-limits work. OSHA's Robotics Industries Notice tracks US-side collaborative-robot incidents; a 2017 Ventra Ionia (Michigan) case saw an operator killed by a robot arm during a maintenance-mode change - the change-management chain was the failure point, not the robot itself.
The RoboIberica case study is a constructed incident (a "hypocrite commit" pattern the security research community has explored since Wu et al.'s 2021 University-of-Minnesota Linux-kernel study) applied to a fictional Spanish supplier. The pattern - a maintainer-plausible commit that regresses a safety invariant behind a technically correct citation - is documented; the specific commit is not. The regulatory chain, the ISO clauses, and the notification pathway are real.