You are here

function _coder_review_security_callback in Coder 7

Same name and namespace in other branches
  1. 7.2 coder_review/includes/coder_review_security.inc \_coder_review_security_callback()

Define the rule callbacks.

1 string reference to '_coder_review_security_callback'
coder_review_security_reviews in coder_review/includes/coder_review_security.inc
Implements hook_reviews().

File

coder_review/includes/coder_review_security.inc, line 222
This include file implements coder functionality for Drupal Standards.

Code

function _coder_review_security_callback(&$coder_args, $review, $rule, $lines, &$results) {
  $argex = '(((\\$?)[a-zA-Z_]+((\\([^)]*\\))|\\[[^\\]]*\\])?)|[0-9]+(\\.[0-9]*)?|\'\'|"")';
  $allphp_argex = '(((\\$?)[a-zA-Z_]+((\\([^)]*\\))|\\[[^\\]]*\\])?)|[0-9]+(\\.[0-9]*)?|\'[^\']+\'|"[^"]+")';
  $sanitize_argex = '((t|st|\\$t|check_plain|format_plural|check_markup|field_filter_xss|filter_xss|filter_xss_admin)\\s*\\([^\\)]+?\\))';
  $severity_name = _coder_review_severity_name($coder_args, $review, $rule);
  $ignores = $coder_args['#ignore_lines'];
  $ignores = empty($ignores) || !isset($ignores[$review['#review_name']]) ? array() : $ignores[$review['#review_name']];

  /*
  if (!isset($coder_args['#tokens'])) {
    $source = implode('', $lines);
    $coder_args['#tokens'] = token_get_all($source);
  }
  */

  // Check for calls to trigger_error(), confirm_form(), drupal_set_title(),
  // drupal_set_message(), form_error() and form_set_error() on $vars without a
  // sanitizing function. If found, look back up within current function.
  $this_function = '';
  $function_paren = 0;
  $forward_matches = array();
  $function_name = '';
  foreach ($coder_args['#all_lines'] as $lineno => $line) {

    // Start of a function, store the line.
    if (preg_match('/function (\\w+)\\(/', $line, $match)) {
      $this_function = $line;
      $function_name = $match[1];
    }
    elseif ($function_paren > 0) {
      $this_function .= $line;
    }

    // Check to see if we're still in a function by counting number of {} chars.
    $tmp_line = preg_replace(array(
      '/([^\\\\])\'.+?[^\\\\]\'/',
      '/([^\\\\])".+?[^\\\\]"/',
    ), '$1', $line);
    if (preg_match('/([{}])/', $tmp_line, $match)) {
      $function_paren += $match[0] == '{' ? 1 : -1;

      // If we've just exited a function, run forward match checks.
      if ($function_paren <= 0) {

        // db_rewrite_sql()

        /*
        if (isset($forward_matches['db_rewrite_sql'])) {
          $after_never_regex = '/[\s\(]db_rewrite_sql\s*\(\s*\$' . $forward_matches['db_rewrite_sql'] . '[\s,\)]/';
          if (!preg_match($after_never_regex, $this_function, $sanitized_matches)) {
            $rule = _coder_review_security_db_rewrite_sql_warning();
            _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
          }
        }
        */

        // FAPI #value
        if (isset($forward_matches['fapi_markup_value'])) {
          $find_match = '/[\\s=]array\\s*\\(\\s*([^\\)]*?([\'"]#value[\'"]\\s*=>\\s*.*?\\$' . $forward_matches['fapi_markup_value'] . '.*?))\\);/';
          if (preg_match($find_match, $this_function, $sanitized_matches)) {
            if (!preg_match('/[\'"]#type[\'"]/', $sanitized_matches[1], $markup_matches) || preg_match('/[\'"]#type[\'"]\\s*=>\\s*[\'"]markup[\'"]/', $sanitized_matches[1], $markup_matches)) {
              $rule = _coder_review_security_fapi_markup_value_warning();
              _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
            }
          }
        }
        $forward_matches = array();
        $function_name = '';
      }
    }

    // If we're not in a function, continue.
    if ($function_paren < 0 || $this_function == '') {
      continue;
    }

    // Run our multi-line reviews.
    // trigger_error()
    $regex = '/[\\s\\(]trigger_error\\s*\\(\\s*\\$(\\w+)\\s*[,\\)]/';
    $never_regex = '/(^function\\s|trigger_error\\s*\\(\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*$)/';
    if (preg_match($regex, $line, $matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[1] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_trigger_error_filter_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }

    // drupal_set_title()

    /*
    $regex = '/[\s\(]drupal_set_title\s*\(\s*\$(\w+)\s*[,\)]/';
    $never_regex = '/(^function\s|drupal_set_title\s*\(\s*(((st|t|\$t)\s*\(((\s*[\'"][^!]+?[\'"]\s*,)|(.*?array\([^!]+\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\s*\().*$)/';
    if (preg_match($regex, $line, $matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\s]\$' . $matches[1] . '\s*=\s*(((st|t|\$t)\s*\(((\s*[\'"][^!]+?[\'"]\s*,)|(.*?array\([^!]+\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\s*\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_drupal_set_title_filter_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }
    */

    // drupal_set_message()
    $regex = '/[\\s\\(]drupal_set_message\\s*\\(\\s*\\$(\\w+)\\s*[,\\)]/';
    $never_regex = '/(^function\\s|drupal_set_message\\s*\\(\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*$)/';
    if (preg_match($regex, $line, $matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[1] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_drupal_set_message_filter_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }

    // form_set_error() and form_error()
    $regex = '/[\\s\\(](form_set_error|form_error)\\s*\\(\\s*' . $argex . '\\s*,\\s*\\$(\\w+)\\s*[,\\)]/';
    $never_regex = '/(^function\\s|(form_set_error|form_error)\\s*\\(\\s*' . $argex . '\\s*,\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*$)/';
    if (preg_match($regex, $line, $matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[8] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_form_set_error_filter_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }

    // confirm_form()
    $regex = '/[\\s\\(]confirm_form\\s*\\(\\s*' . $argex . '\\s*,\\s*\\$(\\w+)\\s*[,\\)]/';
    $never_regex = '/(^function\\s|confirm_form\\s*\\(\\s*' . $argex . '\\s*,\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*$)/';
    if (preg_match($regex, $line, $matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[7] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_confirm_form_filter_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }

    // confirm_form() - 4th, 5th and 6th args
    $regex = '/[\\s\\(]confirm_form\\s*\\(\\s*((' . $allphp_argex . '|' . $sanitize_argex . ')\\s*,\\s*){3,5}\\$(\\w+)\\s*[,\\)]/';
    $never_regex = '/(^function\\s|confirm_form\\s*\\(\\s*((' . $allphp_argex . '|' . $sanitize_argex . ')\\s*,\\s*){3,5}(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*$)/';
    if (preg_match($regex, $line, $matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[11] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_confirm_form_filter_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }

    // db_rewrite_sql()
    $regex = '/\\$(\\w+)\\s*=\\s*[\'"]SELECT\\s+(.*)\\s+(FROM|JOIN)\\s+\\{node\\}(.*)?/';
    if (preg_match($regex, $line, $matches) && !preg_match('/COUNT\\s*\\(/', $matches[2]) && !preg_match('/[\\s\\.]nid\\s*=\\s*%d/', $matches[4])) {
      $forward_matches['db_rewrite_sql'] = $matches[1];
    }

    // FAPI #title and #description
    $regex = '/[\'"]#(title|description)[\'"]\\s*=>\\s*.*?\\$(\\w+)\\s*.*?[,\\)]/';
    $never_regex = '/([\'"]#(title|description)[\'"]\\s*=>\\s*((((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*))/';
    if (preg_match($regex, $line, $matches) && preg_match('/_form(_alter)*$/', $function_name, $function_matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[1] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $rule = _coder_review_security_fapi_title_description_warning();
          _coder_review_error($results, $rule, $severity_name, $lineno, $line, $ignores);
        }
      }
    }

    // FAPI #value
    $regex = '/[\'"]#value[\'"]\\s*=>\\s*.*?\\$(\\w+)\\s*.*?[,\\)]/';
    $never_regex = '/([\'"]#value[\'"]\\s*=>\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\().*)/';
    if (preg_match($regex, $line, $matches) && preg_match('/_form(_alter)*$/', $function_name, $function_matches)) {
      if (!preg_match($never_regex, $coder_args['#all_lines'][$lineno])) {
        $before_never_regex = '/[\\s]\\$' . $matches[1] . '\\s*=\\s*(((st|t|\\$t)\\s*\\(((\\s*[\'"][^!]+?[\'"]\\s*,)|(.*?array\\([^!]+\\))))|(format_plural|field_filter_xss|filter_xss|filter_xss_admin|check_plain|check_markup)\\s*\\()/';
        if (!preg_match($before_never_regex, $this_function, $sanitized_matches)) {
          $forward_matches['fapi_markup_value'] = $matches[1];
        }
      }
    }
  }
}