moo.core.tests.test_security_model_mail
Security tests: Message and MessageRecipient model permission checks.
The mail tables are reachable from any verb that calls send_message or
get_message; without these guards a non-wizard could rewrite or hard-
delete other players’ mail.
- Covers:
Message.save() blocks edits to existing rows for non-wizards (INSERT is allowed so send_message still works)
Message.delete() requires wizard (cascades wipe other recipients)
MessageRecipient.save() allows the recipient to mark read / soft-delete but blocks recipient-FK rebinding and saves by non-recipients
MessageRecipient.delete() (the hard-delete bypass for soft-delete) is wizard-only
Functions
Wizard can hard-delete a Message (and its cascade). |
|
Attack path: verb code gets a MessageRecipient from get_message(), traverses mr.message to the parent Message object, and calls .delete() to cascade-delete the message for ALL recipients — bypassing the soft-delete mechanism entirely. |
|
Wizard can hard-delete a MessageRecipient row. |
|
|
Attack path: verb code calls get_message(player, 1) and then mr.delete(). |
The legitimate mark_read() and delete_message() SDK functions call mr.save() on behalf of the recipient. |
|
A non-wizard verb code that is NOT the recipient of the message cannot call mr.save() on that row, even without modifying mr.recipient. |
|
Attack path: verb code calls get_message(player, 1), sets mr.recipient to a different player object, and calls mr.save(). |
|
|
Wizard can save changes to an existing Message — regression for admin use. |
Attack path: verb code receives a Message instance from send_message(), modifies msg.sender to a wizard object, and calls msg.save(). |
|
send_message() creates a new Message row (pk is None → INSERT). |