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

test_message_delete_allowed_for_wizard(...)

Wizard can hard-delete a Message (and its cascade).

test_message_delete_blocked_for_non_wizard(...)

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.

test_message_recipient_hard_delete_allowed_for_wizard(...)

Wizard can hard-delete a MessageRecipient row.

test_message_recipient_hard_delete_blocked_for_non_wizard(...)

Attack path: verb code calls get_message(player, 1) and then mr.delete().

test_message_recipient_save_allowed_for_recipient(...)

The legitimate mark_read() and delete_message() SDK functions call mr.save() on behalf of the recipient.

test_message_recipient_save_blocked_for_non_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.

test_message_recipient_save_blocks_redirection(...)

Attack path: verb code calls get_message(player, 1), sets mr.recipient to a different player object, and calls mr.save().

test_message_save_allowed_for_wizard(t_init, ...)

Wizard can save changes to an existing Message — regression for admin use.

test_message_save_blocked_for_non_wizard(...)

Attack path: verb code receives a Message instance from send_message(), modifies msg.sender to a wizard object, and calls msg.save().

test_send_message_still_works_for_non_wizard(...)

send_message() creates a new Message row (pk is None → INSERT).