{
  "root_cause_equivalence": "confirmed",
  "root_cause_function": "Bo::isParticipant()",
  "root_cause_file": "smallpart/src/Bo.php",
  "root_cause_lines": "2339-2370",
  "parent_trigger": "SmallPartMediaRecorder::ajax_upload() — isTeacher($data['video']['course_id']) with course_id as a PHP array containing participants[].participant_role=3",
  "variant_trigger": "Overlay::ajax_write() — aclCheck($data['course_id'], true) → isParticipant($data['course_id']) / isTeacher($data['course_id']) with course_id as a PHP array containing participants[].participant_role=3",
  "shared_root_cause": "Bo::isParticipant() caches the attacker-supplied participant_role from a request-supplied participants[] array in $this->course_acl[$course['course_id']], then uses that cached value instead of reading from the database for authorization decisions.",
  "shared_sink": "The authorization decision (isTeacher/isParticipant/isAdmin returning true for a non-privileged user)",
  "code_path_comparison": {
    "parent": "ajax_upload → isTeacher($data['video']['course_id']) → isParticipant(array, ROLE_TEACHER) → caches role → returns true → videoPath($data['video']) → copy() writes to header.inc.php → RCE",
    "variant": "ajax_write → aclCheck($data['course_id'], true) → isParticipant(array) → caches role → isTeacher(array) → returns true via cache → aclCheck PASSES → write() throws InvalidArgumentException (pre-existing type guard blocks array)"
  },
  "fix_coverage": {
    "parent_path_fixed": true,
    "variant_path_fixed": false,
    "root_cause_fixed": false,
    "fix_description": "The fix casts course_id to (int) in ajax_upload() (hardening the caller) and adds basename() guards in videoPath() (defense in depth). It does NOT modify isParticipant() itself."
  },
  "isParticipant_unchanged_across_versions": {
    "26.2.20260216": "736702baa2c0f5bc7660821436b5ffe947d46d2c",
    "26.2.20260224": "b8754b6b8ec41d94807e2eecbeded3dc8b7f834a",
    "26.7.20260702": "latest tag checked — isParticipant() byte-identical via git diff"
  },
  "equivalence_confidence": "high"
}
