CVE-2022-0238: Cross-Site Request Forgery (CSRF) (CWE-352)
value: phoronix-test-suite is vulnerable to Cross-Site Request Forgery (CSRF)
CWE description: The web application does not, or can not, sufficiently verify whether a well-formed, valid, consistent request was intentionally provided by the user who submitted the request.
CVSS v3 base score: 4.3
Published: 2022-01-16T11:15Z
Repository: https://github.com/phoronix-test-suite/phoronix-test-suite
Language: PHP
Files changed: pts-core/phoromatic/pages/phoromatic_benchmark.php, pts-core/phoromatic/pages/phoromatic_r_add_test_details.php, pts-core/phoromatic/pages/phoromatic_r_basic_suite_details.php, pts-core/phoromatic/pages/phoromatic_schedules.php, pts-core/phoromatic/pages/phoromatic_system_claim.php, pts-core/phoromatic/pages/phoromatic_systems.php, pts-core/phoromatic/phoromatic_functions.php
Commit date: 2022-01-15 03:56:13 -0600
Commit message: phoromatic: Additional input sanitization / validation

Diff: diff --git a/pts-core/phoromatic/pages/phoromatic_benchmark.php b/pts-core/phoromatic/pages/phoromatic_benchmark.php
index 809f081c46..5c64cf711c 100644
--- a/pts-core/phoromatic/pages/phoromatic_benchmark.php
+++ b/pts-core/phoromatic/pages/phoromatic_benchmark.php
@@ -77,27 +77,27 @@ class phoromatic_benchmark implements pts_webui_interface
 			$result = $stmt->execute();
 			$row = $result->fetchArray();
 
 			if(!empty($row))
 			{
-				if(isset($_GET['remove']))
+				if(isset($_GET['remove']) && verify_submission_token())
 				{
 					$stmt = phoromatic_server::$db->prepare('DELETE FROM phoromatic_benchmark_tickets WHERE AccountID = :account_id AND TicketID = :ticket_id');
 					$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 					$stmt->bindValue(':ticket_id', $PATH[0]);
 					$result = $stmt->execute();
 					header('Location: /?benchmark');
 				}
-				else if(isset($_GET['repeat']))
+				else if(isset($_GET['repeat']) && verify_submission_token())
 				{
 					$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_benchmark_tickets SET TicketIssueTime = :new_ticket_time, State = 1 WHERE AccountID = :account_id AND TicketID = :ticket_id');
 					$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 					$stmt->bindValue(':ticket_id', $PATH[0]);
 					$stmt->bindValue(':new_ticket_time', time());
 					$result = $stmt->execute();
 				}
-				else if(isset($_GET['disable']))
+				else if(isset($_GET['disable']) && verify_submission_token())
 				{
 					$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_benchmark_tickets SET State = 0 WHERE AccountID = :account_id AND TicketID = :ticket_id');
 					$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 					$stmt->bindValue(':ticket_id', $PATH[0]);
 					$result = $stmt->execute();
@@ -105,11 +105,11 @@ class phoromatic_benchmark implements pts_webui_interface
 
 				$main = null;
 				$main .= '<h1>' . $row['Title'] . '</h1>';
 				$main .= '<h3>' . $row['Description'] . '</h3>';
 				$main .= '<p>This benchmark ticket was created on <strong>' . date('j F Y \a\t H:i', strtotime($row['LastModifiedOn'])) . '</strong> by <strong>' . $row['LastModifiedBy'] . '. The ticket was last issued for testing at ' . date('j F Y \a\t H:i', $row['TicketIssueTime']) . '</strong>.';
-				$main .= '<p> <a href="/?benchmark/' . $PATH[0] . '/&repeat">Repeat Ticket</a> &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&remove">Remove Ticket</a>' . (!isset($_GET['disable']) && $row['State'] > 0 ? ' &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&disable">End Ticket</a>' : null) . '</p>';
+				$main .= '<p> <a href="/?benchmark/' . $PATH[0] . '/&repeat' . append_token_to_url('') . '">Repeat Ticket</a> &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&remove' . append_token_to_url('') . '">Remove Ticket</a>' . (!isset($_GET['disable']) && $row['State'] > 0 ? ' &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&disable' . append_token_to_url('') . '">End Ticket</a>' : null) . '</p>';
 
 				if(!empty($row['RunTargetSystems']))
 				{
 					$main .= '<hr /><h1>System Targets</h1><ol>';
 					foreach(explode(',', $row['RunTargetSystems']) as $system_id)
diff --git a/pts-core/phoromatic/pages/phoromatic_r_add_test_details.php b/pts-core/phoromatic/pages/phoromatic_r_add_test_details.php
index 9edb5fa9c5..5cbc43ade5 100644
--- a/pts-core/phoromatic/pages/phoromatic_r_add_test_details.php
+++ b/pts-core/phoromatic/pages/phoromatic_r_add_test_details.php
@@ -35,10 +35,11 @@ class phoromatic_r_add_test_details implements pts_webui_interface
 	{
 		return true;
 	}
 	public static function render_page_process($PATH)
 	{
+		phoromatic_quit_if_invalid_input_found(array('tp'));
 		$test_profile = new pts_test_profile($_GET['tp']);
 		$name = $test_profile->get_title();
 		$description = $test_profile->get_description();
 
 		echo '<h2>' . $name . ' [' . $test_profile->get_identifier() . '] </h2>';
diff --git a/pts-core/phoromatic/pages/phoromatic_r_basic_suite_details.php b/pts-core/phoromatic/pages/phoromatic_r_basic_suite_details.php
index 6a8323ca97..5ae916aca9 100644
--- a/pts-core/phoromatic/pages/phoromatic_r_basic_suite_details.php
+++ b/pts-core/phoromatic/pages/phoromatic_r_basic_suite_details.php
@@ -34,10 +34,11 @@ class phoromatic_r_basic_suite_details implements pts_webui_interface
 	{
 		return true;
 	}
 	public static function render_page_process($PATH)
 	{
+		phoromatic_quit_if_invalid_input_found(array('ts'));
 		$ts = $_GET['ts'];
 		$ts_file = phoromatic_server::find_suite_file($_SESSION['AccountID'], $ts);
 		$test_suite = new pts_test_suite($ts_file);
 		$name = $test_suite->get_title();
 		$description = $test_suite->get_description();
diff --git a/pts-core/phoromatic/pages/phoromatic_schedules.php b/pts-core/phoromatic/pages/phoromatic_schedules.php
index 9424a65577..333221d4b3 100644
--- a/pts-core/phoromatic/pages/phoromatic_schedules.php
+++ b/pts-core/phoromatic/pages/phoromatic_schedules.php
@@ -55,11 +55,11 @@ class phoromatic_schedules implements pts_webui_interface
 			else
 			{
 				if(!PHOROMATIC_USER_IS_VIEWER)
 				{
 
-					if(isset($_POST['add_to_schedule_select_test']))
+					if(isset($_POST['add_to_schedule_select_test']) && verify_submission_token())
 					{
 						phoromatic_quit_if_invalid_input_found(array('add_to_schedule_select_test'));
 						$name = $_POST['add_to_schedule_select_test'];
 						$args = array();
 						$args_name = array();
@@ -87,11 +87,11 @@ class phoromatic_schedules implements pts_webui_interface
 							$stmt->bindValue(':test_description', $args_name);
 							$result = $stmt->execute();
 							phoromatic_add_activity_stream_event('tests_for_schedule', $PATH[0], 'added');
 						}
 					}
-					else if(isset($_POST['suite_add']))
+					else if(isset($_POST['suite_add']) && verify_submission_token())
 					{
 						$test_suite = phoromatic_server::find_suite_file($_SESSION['AccountID'], $_POST['suite_add']);
 						if(is_file($test_suite))
 						{
 							$test_suite = new pts_test_suite($test_suite);
@@ -152,21 +152,21 @@ class phoromatic_schedules implements pts_webui_interface
 						$stmt->bindValue(':new_state', $new_state);
 						$result = $stmt->execute();
 						$row['State'] = $new_state;
 						phoromatic_add_activity_stream_event('schedule', $PATH[0], $PATH[1]);
 					}
-					else if(isset($_POST['do_manual_test_run']))
+					else if(isset($_POST['do_manual_test_run']) && verify_submission_token())
 					{
 						$stmt = phoromatic_server::$db->prepare('INSERT INTO phoromatic_schedules_triggers (AccountID, ScheduleID, Trigger, TriggeredOn) VALUES (:account_id, :schedule_id, :trigger, :triggered_on)');
 						$stmt->bindValue(':account_id',	$_SESSION['AccountID']);
 						$stmt->bindValue(':schedule_id', $PATH[0]);
 						$stmt->bindValue(':trigger', $_SESSION['UserName'] . ' - Manual Test Run - ' . date('H:i j M Y'));
 						$stmt->bindValue(':triggered_on', phoromatic_server::current_time());
 						$stmt->execute();
 						$main .= '<h2 style="color: red;">Manual Test Run Triggered</h2>';
 					}
-					else if(isset($_POST['skip_current_ticket']))
+					else if(isset($_POST['skip_current_ticket']) && verify_submission_token())
 					{
 						$stmt = phoromatic_server::$db->prepare('INSERT INTO phoromatic_schedules_trigger_skips (AccountID, ScheduleID, Trigger) VALUES (:account_id, :schedule_id, :trigger)');
 						$stmt->bindValue(':account_id',	$_SESSION['AccountID']);
 						$stmt->bindValue(':schedule_id', $PATH[0]);
 						$stmt->bindValue(':trigger', date('Y-m-d'));
@@ -249,14 +249,14 @@ class phoromatic_schedules implements pts_webui_interface
 				{
 					$trigger_url = 'http://' . phoromatic_web_socket_server_ip() . '/event.php?type=trigger&user=' . $_SESSION['UserName'] . '&public_key=' . $row['PublicKey'] . '&trigger=XXX';
 					$main .= '<p>This test schedule can be manually triggered to run at any time by calling <strong>' . $trigger_url . '</strong> where <em>XXX</em> is the trigger value to be used (if relevant, such as a time-stamp, Git/SVN commit number or hash, etc). There\'s also the option of sub-targeting system(s) part of this schedule. One option is appending <em>&sub_target_this_ip</em> if this URL is being called from one of the client test systems to only sub-target the triggered testing on that client, among other options.</p>';
 					$main .= '<p>If you wish to run this test schedule now, click the following button and the schedule will be run on all intended systems at their next earliest possible convenience.</p>';
 					$main .= '<p><form action="?schedules/' . $PATH[0] . '" name="manual_run" method="post">';
-					$main .= '<input type="hidden" name="do_manual_test_run" value="1" /><input type="submit" value="Run Test Schedule Now" onclick="return confirm(\'Run this test schedule now?\');" />';
+					$main .= write_token_in_form() . '<input type="hidden" name="do_manual_test_run" value="1" /><input type="submit" value="Run Test Schedule Now" onclick="return confirm(\'Run this test schedule now?\');" />';
 					$main .= '</form></p>';
 					$main .= '<p><form action="?schedules/' . $PATH[0] . '" name="skip_run" method="post">';
-					$main .= '<input type="hidden" name="skip_current_ticket" value="1" /><input type="submit" value="Skip Current Test Ticket" onclick="return confirm(\'Skip any currently active test ticket on all systems?\');" />';
+					$main .= write_token_in_form() . '<input type="hidden" name="skip_current_ticket" value="1" /><input type="submit" value="Skip Current Test Ticket" onclick="return confirm(\'Skip any currently active test ticket on all systems?\');" />';
 					$main .= '</form></p>';
 				}
 
 				$main .= '<hr />';
 
@@ -327,11 +327,11 @@ class phoromatic_schedules implements pts_webui_interface
 
 				if(!PHOROMATIC_USER_IS_VIEWER)
 				{
 					$main .= '<hr /><h2>Add A Test</h2>';
 					$main .= '<form action="?schedules/' . $PATH[0] . '" name="add_test" id="add_test" method="post">';
-					$main .= '<select name="add_to_schedule_select_test" id="add_to_schedule_select_test" onchange="phoromatic_schedule_test_details(\'\');">';
+					$main .= write_token_in_form() . '<select name="add_to_schedule_select_test" id="add_to_schedule_select_test" onchange="phoromatic_schedule_test_details(\'\');">';
 					$dc = pts_client::download_cache_path();
 					$dc_exists = is_file($dc . 'pts-download-cache.json');
 					if($dc_exists)
 					{
 						$cache_json = file_get_contents($dc . 'pts-download-cache.json');
@@ -374,11 +374,11 @@ class phoromatic_schedules implements pts_webui_interface
 					}
 					$official_suites = pts_test_suites::suites_on_disk(false, true);
 
 					$main .= '<hr /><h2>Add A Suite:</h2>';
 					$main .= '<form action="?schedules/' . $PATH[0] . '" name="add_suite" id="add_suite" method="post">';
-					$main .= '<p><select name="suite_to_run" id="suite_to_run_identifier" onchange="phoromatic_show_basic_suite_details(\'\');">';
+					$main .= write_token_in_form() . '<p><select name="suite_to_run" id="suite_to_run_identifier" onchange="phoromatic_show_basic_suite_details(\'\');">';
 					foreach(array_merge($local_suites, $official_suites) as $title => $id)
 					{
 						$main .= '<option value="' . $id . '">' . $title . '</option>';
 					}
 					$main .= '</select></p>';
diff --git a/pts-core/phoromatic/pages/phoromatic_system_claim.php b/pts-core/phoromatic/pages/phoromatic_system_claim.php
index cdf2c8a866..1cc075115a 100644
--- a/pts-core/phoromatic/pages/phoromatic_system_claim.php
+++ b/pts-core/phoromatic/pages/phoromatic_system_claim.php
@@ -40,11 +40,11 @@ class phoromatic_system_claim implements pts_webui_interface
 		$main = null;
 
 		if(!PHOROMATIC_USER_IS_VIEWER)
 		{
 
-			if(function_exists('ssh2_connect') && isset($_POST['ip']) && isset($_POST['port']) && isset($_POST['password']) && isset($_POST['username']))
+			if(function_exists('ssh2_connect') && isset($_POST['ip']) && isset($_POST['port']) && isset($_POST['password']) && isset($_POST['username']) && verify_submission_token())
 			{
 				$connection = ssh2_connect($_POST['ip'], $_POST['port']);
 
 				if(ssh2_auth_password($connection, $_POST['username'], $_POST['password']))
 				{
@@ -70,31 +70,31 @@ class phoromatic_system_claim implements pts_webui_interface
 					ssh2_exec($connection, 'chmod +x ' . $tmp_remote_file);
 					ssh2_exec($connection, './' . $tmp_remote_file);
 					ssh2_exec($connection, 'rm' . $tmp_remote_file);
 				}
 			}
-			if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) && isset($_POST['ping']))
+			if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) && isset($_POST['ping']) && verify_submission_token())
 			{
 				$ip_ping = ip2long($_POST['ip_claim']) !== false ? $_POST['ip_claim'] : null;
 				if($ip_ping)
 				{
 					echo '<h3>Ping Test: ' . $ip_ping . '</h3>';
 					echo '<pre>';
 					echo shell_exec('ping -c 1 ' . $ip_ping);
 					echo '</pre>';
 				}
 			}
-			else if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) || (isset($_POST['mac_claim']) && !empty($_POST['mac_claim'])))
+			else if(((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) || (isset($_POST['mac_claim']) && !empty($_POST['mac_claim']))) && verify_submission_token())
 			{
 				$stmt = phoromatic_server::$db->prepare('INSERT INTO phoromatic_system_association_claims (AccountID, IPAddress, NetworkMAC, CreationTime) VALUES (:account_id, :ip_address, :mac_address, :creation_time)');
 				$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 				$stmt->bindValue(':ip_address', pts_strings::simple($_POST['ip_claim']));
 				$stmt->bindValue(':mac_address', pts_strings::simple($_POST['mac_claim']));
 				$stmt->bindValue(':creation_time', phoromatic_server::current_time());
 				$result = $stmt->execute();
 			}
-			if(isset($_POST['remove_claim']) && !empty($_POST['remove_claim']))
+			if(isset($_POST['remove_claim']) && !empty($_POST['remove_claim']) && verify_submission_token())
 			{
 				list($ipc, $macc) = explode(',', $_POST['remove_claim']);
 				$stmt = phoromatic_server::$db->prepare('DELETE FROM phoromatic_system_association_claims WHERE AccountID = :account_id AND NetworkMAC = :mac_address AND IPAddress = :ip_address');
 				$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 				$stmt->bindValue(':ip_address', pts_strings::simple($ipc));
@@ -107,11 +107,11 @@ class phoromatic_system_claim implements pts_webui_interface
 			<p>If your Phoromatic client systems are SSH-enabled, you can specify their SSH connection information below. In doing so, the Phoromatic Server will do a one-time connection to it immediately to pre-seed the system with the Phoromatic Server account information for this account. This should allow the client systems to then find the server automatically next time the phoronix-test-suite is run. This command assumes the Phoronix Test Suite is already pre-installed on the client system in your desired configuration.</p>';
 
 			if(function_exists('ssh2_connect'))
 			{
 				$main .= '<h3>Phoromatic Client SSH Information:</h3>';
-				$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="ssh_connect" method="post">
+				$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="ssh_connect" method="post">' . write_token_in_form() . '
 				<p><strong>IP Address:</strong> <input type="text" name="ip" /></p>
 				<p><strong>SSH Port:</strong> <input type="text" name="port" value="22" /></p>
 				<p><strong>Username:</strong> <input type="text" name="username" /></p>
 				<p><strong>Password:</strong> <input type="password" name="password" /></p>
 				<p><input name="submit" value="Seed Phoromatic Server Account Information" type="submit" /></p>
@@ -122,11 +122,11 @@ class phoromatic_system_claim implements pts_webui_interface
 				$main .= '<h3>PHP SSH2 Must Be Installed For This Feature</h3>';
 			}
 			$main .= '<hr />';
 			$main .= '<h2>Add Phoromatic Server Info Via IP/MAC</h2>
 			<p>If deploying a Phoromatic Server within an organization, you can attempt for automatic configuration of Phoromatic clients if you know the system\'s IP or MAC addresses. When specifying either of these fields, if a Phoromatic client attempts to connect to this Phoromatic system without being associated to an account, it will be claimed by this account as long as no other Phoromatic accounts are attempting to claim the IP/MAC. This method can be particularly useful if running the Phoromatic client as a systemd/Upstart service where it will continually poll every 90 seconds auto-detected Phoromatic Servers on the LAN via zero-conf networking. For this feature to work, the zero-conf networking (Avahi) support must be enabled and working.</p>';
-			$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="auto_associate" method="post">
+			$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="auto_associate" method="post">' . write_token_in_form() . '
 			<p><strong>IP Address Claim:</strong> <input type="text" name="ip_claim" /></p>
 			<p><strong>MAC Address Claim:</strong> <input type="text" name="mac_claim" /></p>
 			<p><input name="ping" value="Ping Test" type="submit" /> &nbsp; <input name="submit" value="Submit Claim" type="submit" /></p>
 			</form>';
 
@@ -145,11 +145,11 @@ class phoromatic_system_claim implements pts_webui_interface
 			$main .= '</p>';
 
 			if(!empty($claims))
 			{
 				$main .= '<hr /><h2>Remove Claim</h2><p>Removing a claimed IP / MAC address.</p>';
-				$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="remove_claim" method="post"><select name="remove_claim" id="remove_claim">';
+				$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="remove_claim" method="post"><select name="remove_claim" id="remove_claim">' . write_token_in_form();
 
 				foreach($claims as $claim)
 				{
 					$main .= '<option value="' . $claim . '">' . str_replace(',', ' ', $claim) . '</option>';
 				}
diff --git a/pts-core/phoromatic/pages/phoromatic_systems.php b/pts-core/phoromatic/pages/phoromatic_systems.php
index 55fb345419..f550490af3 100644
--- a/pts-core/phoromatic/pages/phoromatic_systems.php
+++ b/pts-core/phoromatic/pages/phoromatic_systems.php
@@ -50,11 +50,11 @@ class phoromatic_systems implements pts_webui_interface
 			$stmt->bindValue(':description', pts_strings::sanitize($_POST['system_description']));
 			$stmt->bindValue(':state', $_POST['system_state']);
 			$stmt->bindValue(':block_power_offs', $_POST['block_power_offs']);
 			$stmt->execute();
 		}
-		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['maintenance_mode']))
+		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['maintenance_mode']) && verify_submission_token())
 		{
 			$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_systems SET MaintenanceMode = :maintenance_mode WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 			$stmt->bindValue(':system_id', $PATH[0]);
 			$stmt->bindValue(':maintenance_mode', $_POST['maintenance_mode']);
@@ -65,19 +65,19 @@ class phoromatic_systems implements pts_webui_interface
 			$stmt = phoromatic_server::$db->prepare('DELETE FROM phoromatic_system_client_errors WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 			$stmt->bindValue(':system_id', $PATH[0]);
 			$stmt->execute();
 		}
-		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_reboot']))
+		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_reboot']) && verify_submission_token())
 		{
 			$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_systems SET TickThreadEvent = :event WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 			$stmt->bindValue(':system_id', $PATH[0]);
 			$stmt->bindValue(':event', time() . ':reboot');
 			$stmt->execute();
 		}
-		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_halt']))
+		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_halt']) && verify_submission_token())
 		{
 			$stmt = phoromatic_server::$db->prepare('UPDATE phoromatic_systems SET TickThreadEvent = :event WHERE AccountID = :account_id AND SystemID = :system_id');
 			$stmt->bindValue(':account_id', $_SESSION['AccountID']);
 			$stmt->bindValue(':system_id', $PATH[0]);
 			$stmt->bindValue(':event', time() . ':halt-testing');
@@ -90,10 +90,11 @@ class phoromatic_systems implements pts_webui_interface
 			$stmt->bindValue(':system_id', $PATH[0]);
 			$stmt->execute();
 		}
 		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['system_var_names'])&& isset($_POST['system_var_values']))
 		{
+			phoromatic_quit_if_invalid_input_found(array('system_var_names', 'system_var_values'));
 			$vars = array();
 			foreach($_POST['system_var_names'] as $i => $name)
 			{
 				if(isset($_POST['system_var_values'][$i]))
 				{
@@ -197,17 +198,17 @@ class phoromatic_systems implements pts_webui_interface
 						$mm_str = 'Enter Maintenance Mode';
 						$mm_val = 1;
 						$mm_onclick = 'return confirm(\'Enter maintenance mode now?\');';
 					}
 
-					$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="maintenance_mode" value="' . $mm_val . '" /><input type="submit" value="' . $mm_str . '" onclick="' . $mm_onclick . '" style="float: left; margin: 0 20px 5px 0;" /></form> Putting the system into maintenance mode will power up the system (if supported and applicable) and cause the Phoronix Test Suite Phoromatic client to idle and block all testing until the mode has been disabled. If a test is already running on the system, the maintenance mode will not be entered until after the testing has completed. The maintenance mode can be used if wishing to update the system software or carry out other tasks without interfering with the Phoromatic client process. Once disabled, the Phoronix Test Suite will continue to function as normal.</p>';
+					$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="maintenance_mode" value="' . $mm_val . '" /><input type="submit" value="' . $mm_str . '" onclick="' . $mm_onclick . '" style="float: left; margin: 0 20px 5px 0;" /></form> Putting the system into maintenance mode will power up the system (if supported and applicable) and cause the Phoronix Test Suite Phoromatic client to idle and block all testing until the mode has been disabled. If a test is already running on the system, the maintenance mode will not be entered until after the testing has completed. The maintenance mode can be used if wishing to update the system software or carry out other tasks without interfering with the Phoromatic client process. Once disabled, the Phoronix Test Suite will continue to function as normal.</p>';
 
 					if($row['CoreVersion'] >= 5730)
 					{
-						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="tick_thread_reboot" value="1" /><input type="submit" value="Reboot System" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and connected to the Phoromatic Server, this will send a message to the system to issue a reboot -- in case the system is hung on a test or you wish to otherwise manually reboot the server.</p>';
+						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="tick_thread_reboot" value="1" /><input type="submit" value="Reboot System" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and connected to the Phoromatic Server, this will send a message to the system to issue a reboot -- in case the system is hung on a test or you wish to otherwise manually reboot the server.</p>';
 
-						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="tick_thread_halt" value="1" /><input type="submit" value="Halt Testing" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and running a test/benchmark via the Phoromatic Server, this will tell the system to halt the testing prematurely as soon as the currently-active test has finished. The results successfully ran will then be uploaded to the Phoromatic Server.</p>';
+						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="tick_thread_halt" value="1" /><input type="submit" value="Halt Testing" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and running a test/benchmark via the Phoromatic Server, this will tell the system to halt the testing prematurely as soon as the currently-active test has finished. The results successfully ran will then be uploaded to the Phoromatic Server.</p>';
 					}
 				}
 
 				$main .= '<hr /><h2>System Variables</h2><p>System variables allow for providing per-system information in an easy-to-use manner for other parts of the Phoromatic system. Initially these named variables can be used for the results identifier when <a href="/?benchmark">creating a benchmark ticket</a> and in the future the system variables may be used elsewhere. Examples of system variables could include providing a <em>.SERIAL</em> variable to acknowledge the system\'s serial number that may not be presented elsewhere by the Phoronix Test Suite, <em>.ADMIN</em> for the system\'s local administrator, etc. Variable names can only be alpha-numeric strings while their values are also alpha-numeric strings but with spaces allowed. System variables are always prefixed by a period. These system variables are also automatically transferred to the Phoromatic clients and set as environment variables prior to running any scheduled tests/process via Phoromatic.</p>';
 
diff --git a/pts-core/phoromatic/phoromatic_functions.php b/pts-core/phoromatic/phoromatic_functions.php
index 45820488b6..ffbef1c5ed 100644
--- a/pts-core/phoromatic/phoromatic_functions.php
+++ b/pts-core/phoromatic/phoromatic_functions.php
@@ -525,13 +525,13 @@ function phoromatic_oldest_result_for_schedule($schedule_id)
 }
 function write_token_in_form()
 {
 	return '<input type="hidden" name="token_submit" value="' . $_SESSION['Token'] . '" />';
 }
-function append_token_to_url()
+function append_token_to_url($prefix = '/')
 {
-	return '/&token_submit=' . $_SESSION['Token'];
+	return $prefix . '&token_submit=' . $_SESSION['Token'];
 }
 function verify_submission_token()
 {
 	return isset($_REQUEST['token_submit']) && $_REQUEST['token_submit'] == $_SESSION['Token'];
 }

Vulnerable code: 				if(isset($_GET['remove']))
				else if(isset($_GET['repeat']))
				else if(isset($_GET['disable']))
				$main .= '<p> <a href="/?benchmark/' . $PATH[0] . '/&repeat">Repeat Ticket</a> &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&remove">Remove Ticket</a>' . (!isset($_GET['disable']) && $row['State'] > 0 ? ' &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&disable">End Ticket</a>' : null) . '</p>';
					if(isset($_POST['add_to_schedule_select_test']))
					else if(isset($_POST['suite_add']))
					else if(isset($_POST['do_manual_test_run']))
					else if(isset($_POST['skip_current_ticket']))
					$main .= '<input type="hidden" name="do_manual_test_run" value="1" /><input type="submit" value="Run Test Schedule Now" onclick="return confirm(\'Run this test schedule now?\');" />';
					$main .= '<input type="hidden" name="skip_current_ticket" value="1" /><input type="submit" value="Skip Current Test Ticket" onclick="return confirm(\'Skip any currently active test ticket on all systems?\');" />';
					$main .= '<select name="add_to_schedule_select_test" id="add_to_schedule_select_test" onchange="phoromatic_schedule_test_details(\'\');">';
					$main .= '<p><select name="suite_to_run" id="suite_to_run_identifier" onchange="phoromatic_show_basic_suite_details(\'\');">';
			if(function_exists('ssh2_connect') && isset($_POST['ip']) && isset($_POST['port']) && isset($_POST['password']) && isset($_POST['username']))
			if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) && isset($_POST['ping']))
			else if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) || (isset($_POST['mac_claim']) && !empty($_POST['mac_claim'])))
			if(isset($_POST['remove_claim']) && !empty($_POST['remove_claim']))
				$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="ssh_connect" method="post">
			$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="auto_associate" method="post">
				$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="remove_claim" method="post"><select name="remove_claim" id="remove_claim">';
		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['maintenance_mode']))
		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_reboot']))
		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_halt']))
					$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="maintenance_mode" value="' . $mm_val . '" /><input type="submit" value="' . $mm_str . '" onclick="' . $mm_onclick . '" style="float: left; margin: 0 20px 5px 0;" /></form> Putting the system into maintenance mode will power up the system (if supported and applicable) and cause the Phoronix Test Suite Phoromatic client to idle and block all testing until the mode has been disabled. If a test is already running on the system, the maintenance mode will not be entered until after the testing has completed. The maintenance mode can be used if wishing to update the system software or carry out other tasks without interfering with the Phoromatic client process. Once disabled, the Phoronix Test Suite will continue to function as normal.</p>';
						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="tick_thread_reboot" value="1" /><input type="submit" value="Reboot System" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and connected to the Phoromatic Server, this will send a message to the system to issue a reboot -- in case the system is hung on a test or you wish to otherwise manually reboot the server.</p>';
						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post"><input type="hidden" name="tick_thread_halt" value="1" /><input type="submit" value="Halt Testing" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and running a test/benchmark via the Phoromatic Server, this will tell the system to halt the testing prematurely as soon as the currently-active test has finished. The results successfully ran will then be uploaded to the Phoromatic Server.</p>';
function append_token_to_url()
	return '/&token_submit=' . $_SESSION['Token'];
Fixed code: 				if(isset($_GET['remove']) && verify_submission_token())
				else if(isset($_GET['repeat']) && verify_submission_token())
				else if(isset($_GET['disable']) && verify_submission_token())
				$main .= '<p> <a href="/?benchmark/' . $PATH[0] . '/&repeat' . append_token_to_url('') . '">Repeat Ticket</a> &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&remove' . append_token_to_url('') . '">Remove Ticket</a>' . (!isset($_GET['disable']) && $row['State'] > 0 ? ' &nbsp; &nbsp; &nbsp; <a href="/?benchmark/' . $PATH[0] . '/&disable' . append_token_to_url('') . '">End Ticket</a>' : null) . '</p>';
		phoromatic_quit_if_invalid_input_found(array('tp'));
		phoromatic_quit_if_invalid_input_found(array('ts'));
					if(isset($_POST['add_to_schedule_select_test']) && verify_submission_token())
					else if(isset($_POST['suite_add']) && verify_submission_token())
					else if(isset($_POST['do_manual_test_run']) && verify_submission_token())
					else if(isset($_POST['skip_current_ticket']) && verify_submission_token())
					$main .= write_token_in_form() . '<input type="hidden" name="do_manual_test_run" value="1" /><input type="submit" value="Run Test Schedule Now" onclick="return confirm(\'Run this test schedule now?\');" />';
					$main .= write_token_in_form() . '<input type="hidden" name="skip_current_ticket" value="1" /><input type="submit" value="Skip Current Test Ticket" onclick="return confirm(\'Skip any currently active test ticket on all systems?\');" />';
					$main .= write_token_in_form() . '<select name="add_to_schedule_select_test" id="add_to_schedule_select_test" onchange="phoromatic_schedule_test_details(\'\');">';
					$main .= write_token_in_form() . '<p><select name="suite_to_run" id="suite_to_run_identifier" onchange="phoromatic_show_basic_suite_details(\'\');">';
			if(function_exists('ssh2_connect') && isset($_POST['ip']) && isset($_POST['port']) && isset($_POST['password']) && isset($_POST['username']) && verify_submission_token())
			if((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) && isset($_POST['ping']) && verify_submission_token())
			else if(((isset($_POST['ip_claim']) && !empty($_POST['ip_claim'])) || (isset($_POST['mac_claim']) && !empty($_POST['mac_claim']))) && verify_submission_token())
			if(isset($_POST['remove_claim']) && !empty($_POST['remove_claim']) && verify_submission_token())
				$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="ssh_connect" method="post">' . write_token_in_form() . '
			$main .= '<form action="' . $_SERVER['REQUEST_URI'] . '" name="auto_associate" method="post">' . write_token_in_form() . '
				$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="remove_claim" method="post"><select name="remove_claim" id="remove_claim">' . write_token_in_form();
		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['maintenance_mode']) && verify_submission_token())
		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_reboot']) && verify_submission_token())
		if(!PHOROMATIC_USER_IS_VIEWER && !empty($PATH[0]) && isset($_POST['tick_thread_halt']) && verify_submission_token())
			phoromatic_quit_if_invalid_input_found(array('system_var_names', 'system_var_values'));
					$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="maintenance_mode" value="' . $mm_val . '" /><input type="submit" value="' . $mm_str . '" onclick="' . $mm_onclick . '" style="float: left; margin: 0 20px 5px 0;" /></form> Putting the system into maintenance mode will power up the system (if supported and applicable) and cause the Phoronix Test Suite Phoromatic client to idle and block all testing until the mode has been disabled. If a test is already running on the system, the maintenance mode will not be entered until after the testing has completed. The maintenance mode can be used if wishing to update the system software or carry out other tasks without interfering with the Phoromatic client process. Once disabled, the Phoronix Test Suite will continue to function as normal.</p>';
						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="tick_thread_reboot" value="1" /><input type="submit" value="Reboot System" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and connected to the Phoromatic Server, this will send a message to the system to issue a reboot -- in case the system is hung on a test or you wish to otherwise manually reboot the server.</p>';
						$main .= '<p><form action="' . $_SERVER['REQUEST_URI'] . '" name="update_groups" method="post">' . write_token_in_form() . '<input type="hidden" name="tick_thread_halt" value="1" /><input type="submit" value="Halt Testing" style="float: left; margin: 0 20px 5px 0;" /></form> If the system is currently powered up and running a test/benchmark via the Phoromatic Server, this will tell the system to halt the testing prematurely as soon as the currently-active test has finished. The results successfully ran will then be uploaded to the Phoromatic Server.</p>';
function append_token_to_url($prefix = '/')
	return $prefix . '&token_submit=' . $_SESSION['Token'];