--- id: "bug-1560" status: "fixed" status_updated: "2026-09-24" status_note: "" component: "scheduler" severity: "security" timestamp: "2026-08-15T22:54:35Z" last_seen: "2026-09-19T09:00:00Z" occurrences: 5 files: ["src/scheduler/testsuite/test_admin_request.c", "src/scheduler/src/recording.c"] tags: ["scheduler", "recording", "symlink", "test-failure", "coverage-gap-discovery", "xce-build.sh", "set-e-propagation", "test-only-bug", "not-environment-dependent"] related: ["bug-1559"] legacy: true --- # bug-1560: test_recordstart_symlink_escape_replies_error (src/scheduler/testsuite/test_admin_request.c:850)… ## Description test_recordstart_symlink_escape_replies_error (src/scheduler/testsuite/test_admin_request.c:850) FAILED: Expected 'invalid record file name' Was 'permission denied accessing record file'. Surfaced only now because bug-1559's fix made xce-build.sh's scheduler case run the full `meson test --suite scheduler` for the first time -- this test was never reached by the old sched_test-only invocation. ## Environment _Not recorded (migrated from buglog.json)._ ## Reproduction _Not recorded (migrated from buglog.json)._ ## Root cause Not yet investigated. Test plants a symlink inside record_dir whose target resolves outside record_dir, then issues a recordStart admin request expecting rejection with error string 'invalid record file name' (the canonical-path containment check). Instead the admin path is returning a 'permission denied accessing record file' error, implying some permission-check code path is being hit and short-circuiting before (or instead of) the containment check in this sandbox environment (uid 1000, non-root) -- ordering between the two checks in recording.c not yet confirmed by direct read. Unrelated to component-failover-adoption / bug-1548/1549/1558/1559 -- discovered purely as a side effect of bug-1559's coverage-gap fix making this test reachable. [UPDATE 2026-08-30] ROOT-CAUSED, confirmed by direct code reading and cross-checked against a sibling unit test. sched_validate_record_filename() (recording.c)'s canonical-path containment check (path_is_contained() branch, hit when a pre-existing symlink inside record_dir resolves outside it) deliberately returns -EACCES, not -EINVAL -- -EINVAL is reserved for Step 1's malformed-filename check (bare '/' or '..' in the filename itself, rejected before any filesystem access). This is NOT environment-dependent (not a uid-1000-vs-root permission-check ordering issue as originally suspected) -- test_recording_path_validation.c's own direct unit test for the identical scenario (test_symlink_inside_record_dir_escaping_outside_is_rejected) already asserts TEST_ASSERT_EQUAL_INT(-EACCES, rc) and has always passed. The bug was purely in test_admin_request.c's test_recordstart_symlink_escape_replies_error, which asserted the wrong expected error string ("invalid record file name", the -EINVAL string) against production code that has always, correctly, returned -EACCES ("permission denied accessing record file") for this exact scenario. ## Fix FIXED (2026-08-30). Corrected test_recordstart_symlink_escape_replies_error's expected error string in src/scheduler/testsuite/test_admin_request.c from "invalid record file name" to "permission denied accessing record file", matching the already-correct, already-independently-verified production behavior. No production code changed -- this was purely a wrong test assertion, not a real product bug (the symlink-escape IS correctly rejected; only the test's expectation of WHICH rejection string was wrong). Verified: full `meson test --suite scheduler` run clean, Ok:55 Fail:0, all 55 test binaries pass including sched_admin_request_test (16/16 cases in test_admin_request.c PASS). ## Verification _Not recorded (migrated from buglog.json)._ ## References - Related: bug-1559