added a comment - - edited
how someone can issue a merge request for a $new_host that's a zombie, as that host isn't associated with the requesting user's account, making it (GUI-wise) hopefully unavailable for merging in the first place, right?
When you visit a host, path: host/NNN, the host id is NNN. When you click merge button, you go to path: host/NNN/merge which is boincwork_host_merge_form()
First thing this function does is call boincwork_host_user_is_owner() on this host, NNN.
- This function should prevent the selected host ($current_host) from being a zombie, i.e, its userid cannot be zero. host->userid is compared to account->boincuser_id, the latter cannot be zero under normal circumstances.
Then the function puts NNN as 'current_host_id', which is then passed as parameter '$new_host' in the boincwork_host_merge() function, lines L919 and L927.
Conclusion: as written the code should prevent cases where $new_host, the target host, has userid=0.
Additionally, you are correct: you only see the 'merge' button on a page where you own the host. If you go to a host's merge form path for a host do not own, e.g., the path '/host/42/merge', you will not be allowed to merge the host, and will be kicked back to path 'host/42'.
What about the other case: where $new_host's host.rpc_seqno points back to $old_host?
There is no check for this in the code. Function boincwork_host_get_compatible_hosts queries the database for hosts compatible with $host_id NNN, which will eventually be $new_host. The resulting query is an array of hosts. These will be looped over, each one being merge with $new_host and is parameter $old_host. There is no check here for rpc_seqno.