Unit test for query_parameters(), fix bugs in query_parameters() found by the unit test
This commit is contained in:
@@ -50,10 +50,14 @@ function query_parameters()
|
|||||||
|
|
||||||
$data = func_get_args();
|
$data = func_get_args();
|
||||||
$query = $data[0];
|
$query = $data[0];
|
||||||
$tokens = split("[\&\?\~]", $query);
|
$tokens = split("[&?~]", $query); /* NOTE: no need to escape characters inside of [] in regex */
|
||||||
$preparedquery = $tokens[0];
|
$preparedquery = $tokens[0];
|
||||||
$count = strlen($tokens[0]);
|
$count = strlen($tokens[0]);
|
||||||
|
|
||||||
|
/* do we have the correct number of tokens to the number of parameters provided? */
|
||||||
|
if(count($tokens) != count($data))
|
||||||
|
return NULL; /* count mismatch, return NULL */
|
||||||
|
|
||||||
for ($i=1; $i < count($tokens); $i++)
|
for ($i=1; $i < count($tokens); $i++)
|
||||||
{
|
{
|
||||||
$char = substr($query, $count, 1);
|
$char = substr($query, $count, 1);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
/* TODO: test the rest of the classes we have */
|
/* TODO: test the rest of the classes we have */
|
||||||
|
|
||||||
include_once("test_user.php");
|
include_once("test_user.php");
|
||||||
|
echo "\n";
|
||||||
|
include_once("test_db.php");
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
10
unit_test/test_common.php
Normal file
10
unit_test/test_common.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* common functions used in appdb unit tests */
|
||||||
|
|
||||||
|
function test_start($sFunctionName)
|
||||||
|
{
|
||||||
|
echo $sFunctionName."() starting\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
127
unit_test/test_db.php
Normal file
127
unit_test/test_db.php
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/* unit tests for functions in include/db.php */
|
||||||
|
|
||||||
|
require_once("path.php");
|
||||||
|
require_once("test_common.php");
|
||||||
|
|
||||||
|
/* test query_parameters() */
|
||||||
|
/* query_parameters() is key to protecting sql queries from injection attacks */
|
||||||
|
/* while also being simple and easy to use */
|
||||||
|
function test_query_parameters()
|
||||||
|
{
|
||||||
|
test_start(__FUNCTION__);
|
||||||
|
|
||||||
|
/* see that queries without parameters work properly */
|
||||||
|
$sQuery = "SELECT count(*) as count from user_list";
|
||||||
|
$hResult = query_parameters($sQuery);
|
||||||
|
if(!$hResult)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' failed but should have succeeded\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see that '?' marks in queries are replaced with parameters */
|
||||||
|
$sQuery = "SELECT count(*) as count from ?";
|
||||||
|
$hResult = query_parameters($sQuery, "user_list");
|
||||||
|
if(!$hResult)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' failed but should have succeeded\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
$iUserCount = $oRow->count;
|
||||||
|
|
||||||
|
/* see that '~' strings are replaced with parameters */
|
||||||
|
/* NOTE: if the second parameter is quoted this query will produce */
|
||||||
|
/* a different number of results than if it was */
|
||||||
|
/* NOTE: The second parameter is an example of a SQL injection attack */
|
||||||
|
$sQuery = "SELECT count(*) as count from ~ where userid='~'";
|
||||||
|
$hResult = query_parameters($sQuery, "user_list", "1' OR 1='1");
|
||||||
|
if($hResult)
|
||||||
|
{
|
||||||
|
$oRow = mysql_fetch_object($hResult);
|
||||||
|
if($iUserCount != $oRow->count)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' returned ".$oRow->count." entries instead of the expected ".$iUserCount."\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($iUserCount == 0)
|
||||||
|
{
|
||||||
|
echo "Because iUserCount is zero we can't know if this test passed or failed\n";
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' failed but should have succeeded\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test that '&' in a query is properly replaced by the contents from a file
|
||||||
|
*/
|
||||||
|
/* write to a file that we will use for the test of '&' */
|
||||||
|
$sFilename = "/tmp/test_db.txt";
|
||||||
|
$hFile = fopen($sFilename, "wb");
|
||||||
|
if($hFile)
|
||||||
|
{
|
||||||
|
fwrite($hFile, "user_list");
|
||||||
|
fclose($hFile);
|
||||||
|
|
||||||
|
$sQuery = "SELECT count(*) as count from &";
|
||||||
|
$hResult = query_parameters($sQuery, $sFilename);
|
||||||
|
unlink($sFilename); /* delete the temporary file we've created */
|
||||||
|
if(!$hResult)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' failed but should have succeeded\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
echo "Could not open '".$sFilename."' for writing to complete the '&' test\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test that queries with slashes in them are parameterized properly
|
||||||
|
* NOTE: this test exists because query_parameters() at one point did not work
|
||||||
|
* properly with slashes in the query, they were incorrectly being recognized
|
||||||
|
* as tokens that should be replaced with parameters
|
||||||
|
*/
|
||||||
|
$sQuery = "SELECT count(*) as count, '".mysql_real_escape_string("\r\n")."' as x from ?";
|
||||||
|
$hResult = query_parameters($sQuery, "user_list");
|
||||||
|
if(!$hResult)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' failed but should have succeeded\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test that queries with too many parameters are rejected */
|
||||||
|
$sQuery = "SELECT count(*) as count from ?";
|
||||||
|
$hResult = query_parameters($sQuery, "user_list", "12");
|
||||||
|
if($hResult)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' succeeded but should have failed for too many parameters\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* test that queries with too few parameters are rejected */
|
||||||
|
$sQuery = "SELECT count(*) as count from ?";
|
||||||
|
$hResult = query_parameters($sQuery);
|
||||||
|
if($hResult)
|
||||||
|
{
|
||||||
|
echo "sQuery of '".$sQuery."' succeeded but should have failed for too few parameters\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(!test_query_parameters())
|
||||||
|
echo "test_query_parameters() failed!\n";
|
||||||
|
else
|
||||||
|
echo "test_query_parameters() passed!\n";
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
/* unit tests for user class */
|
/* unit tests for user class */
|
||||||
|
|
||||||
require_once("path.php");
|
require_once("path.php");
|
||||||
|
require_once("test_common.php");
|
||||||
require_once(BASE."include/incl.php");
|
require_once(BASE."include/incl.php");
|
||||||
require_once(BASE."include/user.php");
|
require_once(BASE."include/user.php");
|
||||||
|
|
||||||
@@ -11,11 +12,6 @@ require_once(BASE."include/user.php");
|
|||||||
$test_email = "testemail@somesite.com";
|
$test_email = "testemail@somesite.com";
|
||||||
$test_password = "password";
|
$test_password = "password";
|
||||||
|
|
||||||
function test_start($sFunctionName)
|
|
||||||
{
|
|
||||||
echo $sFunctionName."() starting\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: test_user_login() relies on this function leaving the test user */
|
/* NOTE: test_user_login() relies on this function leaving the test user */
|
||||||
/* in the database */
|
/* in the database */
|
||||||
function test_user_create()
|
function test_user_create()
|
||||||
|
|||||||
Reference in New Issue
Block a user