<?php
/*
* Clean all cookies
*/
function clean_cookies($test)
{
$path = __DIR__ . '/jars';
if (!file_exists($path))
return;
$cookie_jar = $path . '/' . $test;
rg_log('Cleaning cookies from [' . $cookie_jar . ']...');
@unlink($cookie_jar);
}
/*
* This is called at the begining of all tests
*/
function prepare_http($info)
{
clean_cookies($info['id']);
}
/*
* Data is an array
*/
function do_req($info, $url, &$data, &$headers)
{
global $cookie_jar;
static $http_handles = array();
global $_testns;
global $rg_log_sid;
$id = $info['id'];
if (!isset($info['referer']))
$info['referer'] = '';
if (!isset($info['ua']))
$info['ua'] = 'curl';
if (!isset($info['test']))
$info['test'] = $_testns;
$path = __DIR__ . '/jars';
if (!file_exists($path))
mkdir($path);
$cookie_jar = $path . '/' . $id;
if (is_null($data))
$data = array();
if (!is_array($headers)) {
rg_log("Headers is not an array, reset it.");
$headers = array();
}
// to easy identify requests in the logs
if (!strstr($url, '?'))
$url .= '?tid=' . rg_id(10);
else
$url .= '&tid=' . rg_id(10);
$url .= '&_testns=' . $_testns;
$url .= '&rg_log_sid=' . $rg_log_sid;
rg_log_ml('do_req url: ' . $url . "\n"
. 'info=' . print_r($info, TRUE) . "\n"
. 'data=' . print_r($data, TRUE) . "\n"
. 'headers=' . print_r($headers, TRUE));
$c = FALSE;
if (isset($http_handles[$id]))
$c = $http_handles[$id];
if ($c === FALSE) {
$c = curl_init();
$http_handles[$id] = $c;
}
curl_setopt($c, CURLOPT_URL, $url);
if (!empty($data)) {
curl_setopt($c, CURLOPT_POST, 1);
curl_setopt($c, CURLOPT_POSTFIELDS, $data);
} else {
curl_setopt($c, CURLOPT_POST, 0);
}
curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($c, CURLOPT_HEADER, 1);
curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
curl_setopt($c, CURLOPT_USERAGENT, $info['ua']);
curl_setopt($c, CURLOPT_REFERER, $info['referer']);
curl_setopt($c, CURLOPT_CERTINFO, TRUE);
curl_setopt($c, CURLOPT_VERBOSE, TRUE);
curl_setopt($c, CURLOPT_ENCODING , 'gzip');
curl_setopt($c, CURLOPT_COOKIEJAR, $cookie_jar);
curl_setopt($c, CURLOPT_COOKIEFILE, $cookie_jar);
// HTTP/2
if (curl_version()['features'] & CURL_VERSION_HTTP2)
curl_setopt($c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
$err = @fopen('php://temp', 'w');
if ($err !== FALSE) {
curl_setopt($c, CURLOPT_STDERR, $err);
} else {
rg_log('Cannot open stderr redirection!');
}
$r = curl_exec($c);
if ($err !== FALSE) {
rewind($err);
$xerr = @fread($err, 16 * 4096);
fclose($err);
rg_log_ml($xerr);
}
if ($r === FALSE) {
rg_log_ml("Cannot load (url=$url), data: "
. print_r($data, TRUE));
rg_log("curl error: " . curl_error($c));
exit(1);
}
$ret = array();
$ret['rg_debug_html'] = array();
$ret['ci'] = @curl_getinfo($c);
if ($ret['ci'] === FALSE) {
rg_log('Cannot call curl_getinfo!');
exit(1);
}
//rg_log_debug('ci: ' . print_r($ret['ci'], TRUE));
if ($ret['ci']['http_code'] == 500)
return $ret;
$header_size = $ret['ci']['header_size'];
//rg_log_debug('r (len=' . strlen($r) . '): ' . $r);
//rg_log_debug('header_size: ' . $header_size);
$ret['header'] = substr($r, 0, $header_size);
$ret['body'] = substr($r, $header_size);
if ($ret['ci']['http_code'] != 200)
return $ret;
$x = explode(' --rg_debug_html-- ', $ret['body']);
if (isset($x[2])) {
$j = @json_decode($x[1], TRUE);
if ($j === FALSE) {
rg_log('Cannot decode rg_debug_html json: ' . $x[1]);
exit(1);
}
$ret['rg_debug_html'] = $j;
if (!empty($ret['rg_debug_html']))
rg_log_ml('rg_debug_html: '
. print_r($ret['rg_debug_html'], TRUE));
$ret['body'] = $x[0] . $x[2];
}
if (stristr($ret['header'], 'Content-Type: text/html')) {
// Check for XSS
if (stristr($ret['body'], '<xss>')) {
file_put_contents('http_xss.out', $ret['body']);
rg_log("Found <xss> token! Check http_xss.out. Not good!");
exit(1);
}
}
// TODO: should we compress the file downloads?
if (stristr($ret['header'], 'Content-Disposition: attachment'))
return $ret;
if (!stristr($ret['header'], 'X-RocketGit-skip-compression: 1')
&& (!stristr($ret['header'], 'Content-Encoding: gzip'))) {
rg_log('Content is not compressed!');
exit(1);
}
if (!stristr($ret['header'], 'X-RocketGit-skip-etag: 1')
&& (!stristr($ret['header'], "\n" . 'ETag: '))) {
rg_log('ETag is not present!');
exit(1);
}
// Check with tidy
if (!stristr($ret['header'], 'Content-Type: ')) {
// do nothing
} else if (stristr($ret['header'], 'Content-Type: application/octet-stream')) {
// do nothing
} else if (stristr($ret['header'], 'Content-Type: application/x-rpm')) {
// do nothing
} else if (stristr($ret['header'], 'Content-Type: text/xml')) {
// do nothing - TODO: should we parse it?
} else if (stristr($ret['header'], 'Content-Type: text/html')) {
// some fixes
$ret['body'] = str_replace('autocomplete="off"', '', $ret['body']);
$ret['body'] = str_replace('<xss>', '|xss|', $ret['body']);
$_tf = $_testns . '.tidy';
file_put_contents($_tf . '.in', $ret['body']);
$cmd = 'tidy -errors -utf8 -file ' . escapeshellarg($_tf)
. '.out ' . escapeshellarg($_tf) . '.in';
$r = rg_exec($cmd, '', FALSE, FALSE, FALSE);
if ($r['ok'] != 1) {
rg_log_ml('body: ' . $ret['body']);
rg_log_ml('tidy error: ' . $r['stderr']);
rg_log_ml(file_get_contents($_tf . '.out'));
exit(1);
}
@unlink($_tf . '.in');
@unlink($_tf . '.out');
} else if (stristr($ret['header'], 'Content-Type: application/json')) {
$ret['json'] = @json_decode($ret['body'], TRUE);
if ($ret['json'] === NULL) {
rg_log('body: ' . $ret['body']);
rg_log('Cannot decode JSON: ' . json_last_error_msg() . '!');
exit(1);
}
rg_log_ml('Decoded JSON: ' . print_r($ret['json'], TRUE));
} else {
rg_log('I do not know how to deal with this content-type!');
exit(1);
}
// Check if a '@@' is present
if (strstr($ret['body'], '@@')) {
$t = explode('@@', $ret['body']);
$t = explode('@@', $t[1]);
if (!strstr($t[0], ' ')) {
rg_log_ml('body: ' . $ret['body']);
rg_log("We have unresolved variables: [" . $t[0] . "]!");
exit(1);
}
}
// Find cookies
$ret['cookies'] = array();
$x = preg_match_all('/[sS]et-[cC]ookie: (.*?)=(.*?)[;]/',
$ret['header'], $matches, PREG_SET_ORDER);
if ($x !== FALSE) {
foreach ($matches as $junk => $info) {
$k = $info[1];
$v = $info[2];
$ret['cookies'][$k] = $v;
}
}
//rg_log_ml('ret[cookies]: ' . print_r($ret['cookies'], TRUE));
$ret['sid'] = '';
if (isset($ret['cookies']['sidu']))
$ret['sid'] = $ret['cookies']['sidu'];
if (isset($ret['cookies']['sids']))
$ret['sid'] = $ret['cookies']['sids'];
$ret['tokens'] = array();
$x = preg_match_all('/ name="token" value="([a-zA-Z0-9_:]*)"/',
$ret['body'], $matches);
//rg_log_debug('tokens matches: ' . print_r($matches, TRUE));
if (($x === FALSE) || (!isset($matches[1]))) {
//rg_log("CHECK: no token found");
} else {
foreach ($matches[1] as $m) {
$t = explode(':', $m);
if (!isset($t[1])) {
rg_log_ml('body: ' . print_r($ret['body'], TRUE));
rg_log_ml('matches: ' . print_r($matches[1], TRUE));
rg_log('Invalid debug token (no prefix): ' . $m);
exit(1);
}
$ret['tokens'][$t[1]] = $t[0];
}
}
rg_log('DEBUG ret[tokens]: ' . rg_array2string($ret['tokens']) . '.');
// Collect '<input>' tags
$ret['inputs'] = array();
$x = preg_match_all('/<input .* name="(.*?)" .*value="(.*?)"/uD',
$ret['body'], $matches);
//rg_log_debug('inputs matches: ' . print_r($matches, TRUE));
if (($x === FALSE) || (!isset($matches[1]))) {
//rg_log("CHECK: no token found");
} else {
foreach ($matches[1] as $i => $d)
$ret['inputs'][$d] = $matches[2][$i];
}
//rg_log_ml('DEBUG ret[inputs]: ' .print_r($ret['inputs'], TRUE));
// find logout token
$x = preg_match('/logout\?token=([a-zA-Z0-9:]*)"/', $ret['body'], $matches);
//rg_log_debug('matches[logout]: ' . print_r($matches, TRUE));
if (($x === FALSE) || (!isset($matches[1]))) {
$ret['tokens']['logout'] = '';
} else {
$t = explode(':', $matches[1]);
$ret['tokens']['logout'] = $t[0];
}
$x = preg_match_all('/ class="secret_token">([A-Z0-9]*)</', $ret['body'], $matches);
if (($x !== FALSE) && (isset($matches[1])) && isset($matches[1][0])) {
$ret['totp_secret'] = $matches[1][0];
rg_log('DEBUG ret[totp_secret]=' . $ret['totp_secret']);
}
@rename('http-last.out', 'http-prev.out');
file_put_contents('http-last.out', $ret['body']);
return $ret;
}
/*
* Helper function that will do the login
*/
function test_login($url, $rg_ui)
{
$info = array('id' => $rg_ui['username']);
// First we need to load the form so we can get the token
$data = array();
$r = do_req($info, $url . "/op/login", $data, $headers);
if ($r === FALSE) {
rg_log('Cannot load login form!');
return FALSE;
}
if (!isset($r['tokens']['login'])) {
rg_log_ml('r: ' . print_r($r, TRUE));
rg_log('Login token not returned!');
return FALSE;
}
$good_token = $r['tokens']['login'];
// Now, post login form
rg_log("Do the real login post request");
$data = array(
"doit" => 1,
"token" => $good_token,
"user" => $rg_ui['username'],
"pass" => $rg_ui['pass'],
"lock_ip" => 1
);
if (isset($rg_ui['t']))
$data['t'] = $rg_ui['t'];
$headers = array();
$r = do_req($info, $url . "/op/login", $data, $headers);
if ($r === FALSE) {
rg_log_ml("Cannot login: " . print_r($r, TRUE));
return FALSE;
}
if (strstr($r['body'], "invalid user")) {
rg_log_ml(print_r($r, TRUE));
rg_log("Login invalid. Check above!");
return FALSE;
}
return $r;
}
/*
* Restore password aaaa for user catab
*/
function test_restore($db)
{
$salt = 'd0a41957b835fbf7bfe63b750db15108cc048259';
$pass = 'aaaa';
$pass = rg_user_pass($salt, $pass);
$sql = "UPDATE users SET salt = '$salt'"
. ", pass = '$pass'"
. ", session_time = 3600"
. " WHERE username = 'catab'";
$res = rg_sql_query($db, $sql);
if ($res == FALSE) {
rg_log("Cannot update (" . rg_sql_error() . ")!");
exit(1);
}
rg_sql_free_result($res);
rg_cache_unset('user::4::info', RG_SOCKET_NO_WAIT);
}