Found in the adversarial review of PR #536 (#443 work). Pre-existing, not introduced there.
spp.change.request.registrant_id (models/change_request.py, field near line 79) is not in _FROZEN_ON_SUBMIT_FIELDS (line ~676) and has no write guard, so a user with write on their own change request can change the target registrant after submission and after approval-but-before-apply. Combined with the detail-level freeze protecting only the proposed values, the approver reviews a proposal against one registrant and apply writes it onto another. The detail's registrant_id is a stored related off the CR, so it follows.
Mitigating factors: auto_apply_on_approve defaults True, so the approved-but-unapplied window is normally nil; the pending window still shows the retargeted registrant in the live preview. The residual window is apply_error set (apply raised, request left approved).
Suggested fix: add registrant_id to _FROZEN_ON_SUBMIT_FIELDS (the same _alters_frozen_field machinery), with a test that a submitted request refuses a registrant change and a draft one accepts it. Check spp_farmer_registry_cr / spp_studio_change_requests / spp_cr_type_assign_program for writers of registrant_id post-submit before enabling.
Related: #443 (where the review happened), the detail create()+bind gap filed alongside this one.
Found in the adversarial review of PR #536 (#443 work). Pre-existing, not introduced there.
spp.change.request.registrant_id(models/change_request.py, field near line 79) is not in_FROZEN_ON_SUBMIT_FIELDS(line ~676) and has no write guard, so a user with write on their own change request can change the target registrant after submission and after approval-but-before-apply. Combined with the detail-level freeze protecting only the proposed values, the approver reviews a proposal against one registrant andapplywrites it onto another. The detail'sregistrant_idis a stored related off the CR, so it follows.Mitigating factors:
auto_apply_on_approvedefaults True, so the approved-but-unapplied window is normally nil; the pending window still shows the retargeted registrant in the live preview. The residual window isapply_errorset (apply raised, request left approved).Suggested fix: add
registrant_idto_FROZEN_ON_SUBMIT_FIELDS(the same_alters_frozen_fieldmachinery), with a test that a submitted request refuses a registrant change and a draft one accepts it. Checkspp_farmer_registry_cr/spp_studio_change_requests/spp_cr_type_assign_programfor writers ofregistrant_idpost-submit before enabling.Related: #443 (where the review happened), the detail
create()+bind gap filed alongside this one.