| 1 |
#!/usr/bin/php
|
| 2 |
<?php
|
| 3 |
// $Id: code-checker.php,v 1.5 2006/10/24 18:34:20 killes Exp $
|
| 4 |
// Usage: ./code-checker.php <modules/my-module> <my-report.txt>
|
| 5 |
|
| 6 |
/**
|
| 7 |
* Check SQL query prefixes.
|
| 8 |
*/
|
| 9 |
function check_sql_prefix($lineno, $line) {
|
| 10 |
if ((strstr($line, 'db_query(') && strstr($line, 'pager_query(') && strstr($line, 'db_query_range(')) && // begin of SQL query
|
| 11 |
strstr($line, ');') && // end of SQL query
|
| 12 |
(strstr($line, 'INSERT') || strstr($line, 'UPDATE') || strstr($line, 'DELETE')) &&
|
| 13 |
!strstr($line, '{')) { // check for '{'
|
| 14 |
report(" - The SQL query at line $lineno does not wrap its table names in '{}' and will fail to work with database prefixing.\n $line\n");
|
| 15 |
}
|
| 16 |
}
|
| 17 |
|
| 18 |
/**
|
| 19 |
* Check SQL query prefixes.
|
| 20 |
*/
|
| 21 |
function check_camel_case($lineno, $line) {
|
| 22 |
if (word($line, 0) == 'function') {
|
| 23 |
// Extract the function name:
|
| 24 |
$name = substr(word($line, 1), 0, strpos(word($line, 1), '('));
|
| 25 |
|
| 26 |
// Check the function name:
|
| 27 |
if (ereg("[^a-z0-9_\$()]", $name)) {
|
| 28 |
report(" - The name of the function '$name' uses 'camel case'-style which does not conform Drupal's standards. Use lower-case names and separate words using an underscore.\n\n");
|
| 29 |
}
|
| 30 |
}
|
| 31 |
}
|
| 32 |
|
| 33 |
function check_else_clause($lineno, $line) {
|
| 34 |
if (strstr($line, '} else')) {
|
| 35 |
report(" - The else-clause at line $lineno puts '}' and 'else' on the same line. To conform Drupal's coding conventions they should be on separate lines.\n\n");
|
| 36 |
}
|
| 37 |
}
|
| 38 |
|
| 39 |
function check_db_wrapper($lineno, $line) {
|
| 40 |
if (preg_match('|["\']SELECT|', $line)) {
|
| 41 |
preg_match_all('|(["\'])SELECT [[:print:]]*;|', $line, $select, PREG_SET_ORDER);
|
| 42 |
if (isset($select[0])) {
|
| 43 |
if(preg_match_all("|FROM ([[:print:]]*) WHERE|U", $select[0][0], $from, PREG_SET_ORDER)) {
|
| 44 |
if (strstr($from[0][1], '$')) {
|
| 45 |
report(" - The SELECT query at line $lineno is potentially insecure as it contains a variable in the FROM statement and does not use our database query wrapper properly.\n $line\n");
|
| 46 |
}
|
| 47 |
}
|
| 48 |
else if(preg_match_all("|FROM ([[:print:]]*)". $select[0][1] ."|U", $select[0][0], $from, PREG_SET_ORDER)) {
|
| 49 |
if (strstr($from[0][1], '$')) {
|
| 50 |
report(" - The SELECT query at line $lineno is potentially insecure as it contains a variable in the FROM statement and does not use our database query wrapper properly.\n $line\n");
|
| 51 |
}
|
| 52 |
}
|
| 53 |
if(preg_match_all("|WHERE ([[:print:]]*)". $select[0][1] ."|U", $select[0][0], $where, PREG_SET_ORDER)) {
|
| 54 |
if (strstr($where[0][1], '$')) {
|
| 55 |
report(" - The SELECT query at line $lineno is potentially insecure as it contains a variable in the WHERE statement and does not use our database query wrapper properly.\n $line\n");
|
| 56 |
}
|
| 57 |
}
|
| 58 |
}
|
| 59 |
}
|
| 60 |
else if (preg_match('|["\']INSERT|', $line)) {
|
| 61 |
preg_match_all('|["\']INSERT INTO [[:print:]]*;|', $line, $insert, PREG_SET_ORDER);
|
| 62 |
if (isset($insert[0])) {
|
| 63 |
preg_match_all("|\(.*\)|U", $insert[0][0], $matches, PREG_SET_ORDER);
|
| 64 |
if (!isset($matches[0])) return; // Some nonstandard query
|
| 65 |
if (!isset($matches[1])) {
|
| 66 |
report(" - The INSERT query at line $lineno is potentially invalid.\n $line\n");
|
| 67 |
}
|
| 68 |
else {
|
| 69 |
$fields = count(explode(',', $matches[0][0]));
|
| 70 |
$arguments = count(explode(',', $matches[1][0]));
|
| 71 |
if ($fields !== $arguments) {
|
| 72 |
report(" - The INSERT query at line $lineno does not contain the same number of fields as number of arguments.\n $line\n");
|
| 73 |
}
|
| 74 |
if (strstr(implode('', $matches[1]), '$')) {
|
| 75 |
report(" - The INSERT query at line $lineno is potentially insecure as it does not use our database query wrapper properly.\n $line\n");
|
| 76 |
}
|
| 77 |
}
|
| 78 |
}
|
| 79 |
}
|
| 80 |
else if (preg_match('|["\']UPDATE|', $line)) {
|
| 81 |
preg_match_all('|["\']UPDATE [[:print:]]*;|', $line, $update, PREG_SET_ORDER);
|
| 82 |
if (isset($update[0])) {
|
| 83 |
if(preg_match_all("|SET ([[:print:]]*) WHERE|U", $update[0][0], $set, PREG_SET_ORDER)) {
|
| 84 |
if (strstr($set[0][1], '$')) {
|
| 85 |
report(" - The UPDATE query at line $lineno is potentially insecure as it contains a variable in the SET statement and does not use our database query wrapper properly.\n $line\n");
|
| 86 |
}
|
| 87 |
}
|
| 88 |
else if(preg_match_all("|SET ([[:print:]]*);|U", $update[0][0], $set, PREG_SET_ORDER)) {
|
| 89 |
if (strstr($set[0][1], '$')) {
|
| 90 |
report(" - The UPDATE query at line $lineno is potentially insecure as it contains a variable in the SET statement and does not use our database query wrapper properly.\n $line\n");
|
| 91 |
}
|
| 92 |
}
|
| 93 |
if(preg_match_all("|WHERE ([[:print:]]*),|U", $update[0][0], $where, PREG_SET_ORDER)) {
|
| 94 |
if (strstr($where[0][1], '$')) {
|
| 95 |
report(" - The UPDATE query at line $lineno is potentially insecure as it contains a variable in the WHERE statement and does not use our database query wrapper properly.\n $line\n");
|
| 96 |
}
|
| 97 |
}
|
| 98 |
}
|
| 99 |
}
|
| 100 |
else if (preg_match('|["\']DELETE|', $line)) {
|
| 101 |
preg_match_all('|["\']DELETE FROM [[:print:]]*;|', $line, $delete, PREG_SET_ORDER);
|
| 102 |
if (isset($delete[0])) {
|
| 103 |
if(preg_match_all("|WHERE ([[:print:]]*),|U", $delete[0][0], $where, PREG_SET_ORDER)) {
|
| 104 |
if (strstr($where[0][1], '$')) {
|
| 105 |
report(" - The DELETE query at line $lineno is potentially insecure as it contains a variable in the WHERE statement and does not use our database query wrapper properly.\n $line\n");
|
| 106 |
}
|
| 107 |
}
|
| 108 |
}
|
| 109 |
}
|
| 110 |
}
|
| 111 |
|
| 112 |
/**
|
| 113 |
* Report an error.
|
| 114 |
*/
|
| 115 |
function report($message) {
|
| 116 |
global $argv;
|
| 117 |
static $fd;
|
| 118 |
|
| 119 |
if ($fd == NULL) {
|
| 120 |
$fd = fopen($argv[2], 'w');
|
| 121 |
}
|
| 122 |
fwrite($fd, wordwrap($message));
|
| 123 |
}
|
| 124 |
|
| 125 |
/**
|
| 126 |
* Return the $arg-th word in the line. If $arg is NULL, the last word is returned.
|
| 127 |
*/
|
| 128 |
function word($line, $arg = NULL) {
|
| 129 |
$words = preg_split('[\s]', $line, -1, PREG_SPLIT_NO_EMPTY);
|
| 130 |
if (count($words)) {
|
| 131 |
if ($arg == -1) $arg = count($words) - 1; // -1 == last element
|
| 132 |
return is_null($arg) ? $words : $words[$arg];
|
| 133 |
}
|
| 134 |
return FALSE;
|
| 135 |
}
|
| 136 |
|
| 137 |
/**
|
| 138 |
* Parse a file and check its consistency.
|
| 139 |
*/
|
| 140 |
function parse_file($file) {
|
| 141 |
global $data;
|
| 142 |
|
| 143 |
report("Checking file $file ...\n\n");
|
| 144 |
|
| 145 |
if ($lines = file($file)) {
|
| 146 |
for ($i = 0; $i < count($lines); $i++) {
|
| 147 |
check_sql_prefix($i, $lines[$i]);
|
| 148 |
check_camel_case($i, $lines[$i]);
|
| 149 |
check_else_clause($i, $lines[$i]);
|
| 150 |
check_db_wrapper($i, $lines[$i]);
|
| 151 |
}
|
| 152 |
}
|
| 153 |
}
|
| 154 |
|
| 155 |
|
| 156 |
function parse_directory($dir) {
|
| 157 |
report("Checking directory $dir ...\n\n");
|
| 158 |
|
| 159 |
$fd = opendir($dir);
|
| 160 |
while ($file = readdir($fd)) {
|
| 161 |
if (is_dir("$dir/$file") && $file != '.' && $file != '..' && $file != 'CVS') {
|
| 162 |
parse_directory("$dir/$file");
|
| 163 |
}
|
| 164 |
else if (strstr($file, '.module') || strstr($file, '.theme') || strstr($file, '.inc') || strstr($file, '.php')) {
|
| 165 |
parse_file("$dir/$file");
|
| 166 |
}
|
| 167 |
}
|
| 168 |
fclose($fd);
|
| 169 |
}
|
| 170 |
|
| 171 |
report(date('D F j, Y, G:i:s') ."\n\n");
|
| 172 |
|
| 173 |
parse_directory($argv[1]);
|
| 174 |
|
| 175 |
?>
|