array_diff(array, string) does(n't) work as expected

This is an old problem, quite a few security researchers already wrote about. But repeating things, helps learning them (especially me).

In MageSetup we read posted agreements (terms and conditions) and compare them with all the agreements we expect.

This looks like that:

$requiredAgreements = $this->_getCustomerCreateAgreements();
$controller = $observer->getEvent()->getControllerAction();
$postedAgreements = array_keys($controller->getRequest()->getPost('agreement', array()));

if ($diff = array_diff($requiredAgreements, $postedAgreements)) {  
    // ERROR
}

Today I discovered a nasty bug: if you submit no array for agreement, you end up with:

array_keys(1);
array_diff([1,2,3,4], null);

Which throws two warnings:

Warning: arraykeys() expects parameter 1 to be array, string given Warning: arraydiff(): Argument #2 is not an array

If developer mode is on, the warning is transformed into an exception and thrown, which "solves" the problem. But if dev mode is off, array_keys returns null first, and then array_diff() does the same.

So we don't have the expected check against our $requiredAgreements.

Please make sure, you are handling arrays, before passing them into array_* functions!