All Low add-ons are now owned by EEHarbor. Read the blog post.

Support archive

FreeForm validation fails.

FingerPaint Developers 6 Aug 2012 21:28 problem, active

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

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

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

  3. 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;
    }