FreeForm validation fails.
ext.low_nospam.php, function freeform_module_validate_end does not pass the content array to the is_spam function and therefore does not check the spamminess of the $content variable. Unless the email address of the registrant is a spam email address, the check always returns false for spam and lets all spam comments through. The same is true in the following functions:
forum_submit_post_start
edit_wiki_article_end
member_member_register_start
user_register_error_checking
zoo_visitor_register_validation_start
Replies
Low 6 Aug 2012 21:33
The method is_spam($array) will check $this->input if no array is given. $this->input is set in all methods you listed.
However, I haven't tested it with the latest version of Freeform (4). That might cause issues.
FingerPaint Developers 7 Aug 2012 02:27
Thanks for the quick reply! Upon further investigation, the problem is that the $data variable that is passed to the freeform_module_validate_end function is an empty array. I'm looking into this on the FreeForm end at the moment. Using Freeform 3.1.0.
FingerPaint Developers 7 Aug 2012 15:51
Okay, I had some time this morning to chase this down in FreeForm. The first thing I did was upgrade to FreeForm 3.1.4 (didn't realize I was out of date until last night).
This forum post from over a year ago demonstrates another user having the same problem with this FreeForm hook, which was never designed properly in the first place:
http://www.solspace.com/forums/viewth...
Greg from SolSpace recommends using the $_POST variable collection for form validation, since the $data array isn't built until later, and the $errors array is always empty at the point where the freeform_module_validate_end is called. To your credit, your implementation of the hook follows SolSpace's documentation on how to use the hook - but how the hook behaves in the wild is different from the description in their documentation.
Specifically, this is what is going on:
mod.freeform.php:
Line 920: Initialize $errors as empty array
Lines 948-970: Add error labels & messages to $errors array for missing required fields or fields not in the field list
Line 1016: If there are errors, return them as output
Line 1037: Function call to freeform_module_validate_end hook will always include empty errors array, because if array is not empty, line 1016 will return and end function
What should be passed to the freeform_module_validate_end hook as the second parameter should actually be the $data array after it is built on line 1204, similar to how freeform_module_insert_begin is called on line 1225.
So, barring an update from the SolSpace people to restructure how they are building the $data array and calling the freeform_module_validate_end hook, I would recommend rewriting the Freeform hook section of NoSpam to utilize the $_POST variable collection instead.
In my local copy, I modified line 561 of ext.low_nospam.php to run the foreach loop on $_POST instead of $data, and on several other lines where $data was used, and I added the following entries to your $ignore array, matching values from Freeform's ignore list ($exclude array on line 1152 of mod.freeform.php):
'ACT', 'RET', 'URI', 'PRV', 'XID', 'return', 'ee_notify', 'ee_required', 'submit'
Plus additional freeform values that don't need to be utilized:
'status', 'redirect_on_duplicate', 'ajax_request', 'params_id'
I would also recommend restructuring this function, because the name field is stripped in favor of the logged-in user. We have a contact form on our website where unauthenticated general public users enter their name, which is not included anywhere in the call to Akismet, despite being a probable spam trigger.
Here is my final modified function (feel free to fold into the extension and modify as necessary):
public function freeform_module_validate_end($data)
{
$last_call = ( isset( $this->EE->extensions->last_call ) AND is_array($this->EE->extensions->last_call) ) ? $this->EE->extensions->last_call : $data;
// check settings to see if comment needs to be verified
if ($this->settings['check_freeform_entries'] == 'y')
{
// Don't send these values to the service
$ignore = array(
'accept_terms',
'ACT',
'ajax_request',
'author_id',
'edit_date',
'ee_notify',
'ee_required',
'email' ,
'entry_date',
'form_name',
'FROM',
'group_id',
'name',
'params_id',
'password',
'password_confirm',
'PRV',
'redirect_on_duplicate',
'RET',
'return',
'rules',
'site_id',
'status',
'submit',
'URI',
'url',
'username',
'website',
'XID',
);
// Init content var
$content = '';
// Loop through posted data, add to content var
foreach ($_POST AS $key => $val)
{
if (in_array($key, $ignore)) continue;
$content .= $val . "\n";
}
//url could come from a lot of places
$url = isset($_POST['url']) ? $_POST['url'] : (isset($_POST['website']) ? $_POST['website'] : $this->EE->input->get_post('url'));
// Negotiation for comment_author
$comment_author = '';
if (isset($_POST['name'])) {
$comment_author = $_POST['name'];
} elseif (isset($this->EE->session->userdata['username'])) {
$comment_author = $this->EE->session->userdata['username'];
}
$this->input = array(
'user_ip' => $this->EE->session->userdata['ip_address'],
'user_agent' => $this->EE->session->userdata['user_agent'],
'comment_author' => $comment_author,
'comment_author_email' => isset($_POST['email']) ? $_POST['email'] : '',
'comment_author_url' => $url,
'comment_content' => $content
);
// Check it!
if ($this->is_spam())
{
// Exit if spam
$this->abort();
}
}
//this needs to be returned either way
return $last_call;
}