#!/usr/bin/php
<?php
//error_reporting(0);

/***
 * Configuration options
 */
$sub1 = 5; // header no in doc e.g. "5 Discovered Vulnerabilities"

/***
 * Main program - Don't edit below
 */
echo "_____ _____ _____ 2\n||_// ||==  ||_// \n|| \\  ||___ ||    \n\n";

foreach (glob("classes/*.php") as $filename)
    include $filename;

$definitions = new \Clapp\CommandLineArgumentDefinition(
    array(
        "help|h"            => "Shows help message",
        "doc|d=s"           => "/path/to/doc.odt to use",
    )
);

$filter = new \Clapp\CommandArgumentFilter($definitions, $argv);

if ($filter->getParam('h') === true || $argc < 2) {
    fwrite(STDERR, $definitions->getUsage());
    exit(0);
} 

// see if doc exists 
if ($filter->getParam("doc") == false)
	die("[-] no doc set\n");

echo "[!] doc: ".$filter->getParam("doc")."\n";
if(!file_exists($filter->getParam("doc")))
	die("[-] no such file! \n");

// extract doc and get contents
$rand = uniqid();
mkdir("/tmp/$rand");
if(unzipFolder($filter->getParam("doc"), "/tmp/$rand/")) {
    $source = file_get_contents("/tmp/$rand/content.xml");
    echo "[+] doc extracted\n";
} else {
	die("[-] unable to extract doc\n");
}
	
// Parse Doc's XML
$line = array();
$reader = new XMLReader();
if (!$reader->open("/tmp/$rand/content.xml")) die("[-] Failed to open 'content.xml'\n");

// font checker
/***
 * FUTURE FEATURE PERHAPS
 */
$fonts = array();
while ($reader->read()){ 
	//print_r($reader->name);
    if ($reader->nodeType == XMLREADER::ELEMENT && ($reader->name === 'style:font-face')) {  
    	//if(!empty($reader->name)){
    		//echo "here2\n";
        	//$line[] = $reader->expand()->textContent; // Put the text into array in correct order...
    		$fonts[] = $reader->getAttribute("style:name");
        	//echo $font;echo "\n"; // DEBUG
        //}
    }
}
$reader->close();
//die();
echo "[=] fonts found: ".sizeof($fonts)."\n";

// get template version used
$reader = new XMLReader();
if (!$reader->open("/tmp/$rand/meta.xml")) die("[-] Failed to open 'meta.xml'\n");
$templateVer = 0.0;
while ($reader->read()) {
  if($reader->name == "dc:version"){
    $reader->read();
    $templateVer = number_format(floatval($reader->value), 2);
    break;
  }
}

// step through text:h and text:p elements to put them into an array
$reader = new XMLReader();
if (!$reader->open("/tmp/$rand/content.xml")) die("[-] Failed to open 'content.xml'\n");
while ($reader->read()){ 
    if ($reader->nodeType == XMLREADER::ELEMENT && ($reader->name === 'text:h' || $reader->name === 'text:p' || $reader->name === 'text:bookmark')) {  
    	if(!empty($reader->expand()->textContent))
        	$line[] = $reader->expand()->textContent; // Put the text into array in correct order...
        	//echo $reader->expand()->textContent;echo "\n"; // DEBUG
    }
}
$reader->close();

// find the content we want
$start = 0; $end = 0;
foreach($line as $key => $val){
	//if(strpos($val, "Discovered Vulnerabilities") === 0){ $start = $key; }
	if(preg_match('/Discovered *\w* Vulnerabilities/', $val, $matches) === true){ $start = $key; }
	//if(strpos($val, "This section provides a quick guide to plan your remediation for the vulnerabilities discovered during the test.") === 0){ $end = $key-2; }
	if(strpos($val, "Observed Hosts and Services") === 0){ $end = $key-2; }
	$line[$key] = trim($val);
}

// add to sexy array's
$vuln = array();
$vulnPlace = 0;
$switch = 0;
$sub2 = 0;
$sub3 = 1;
echo "[=] Template $templateVer used\n"; 
if(number_format($templateVer, 2) >= number_format(1.0, 2)){
	
	for ($i=$start; $i <= $end ; $i++) { 
		// change state (action to take)
		/***
		 * ~states~
		 * 0 = do nothing
		 * 1 = next is title
		 * 2 = next is description
		 * 3 = next is solution
		 * 4 = next is remediation
		 * 5 = next is cvss no
		 * 6 = next is risk level
		 * 7 = next is hosts
		 * 8 = next possibly title
		 */
		
		switch ($line[$i]) {
			case (preg_match('/Discovered *\w* Vulnerabilities/', $val, $matches) === true):
				$switch = 0;
				break;
			case 'Serious Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'High Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Medium Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Low Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Informational Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Description':
				$switch = 2;
				break;
			case 'Solution':
				$switch = 3;
				break;
			case 'Remediation':
				$switch = 4;
				break;
			case 'CVSS Base Score':
				$switch = 5;
				break;
			case 'Risk Analysis':
				$switch = 6;
				break;
			case 'Vulnerabilities Exist On':
				$switch = 7;
				break;
			case 'Potential Impact':
				$switch = 9;
				break;
			default:
				# code...
				break;
		}

		//take action
		switch ($switch) {
			case 1:
				$i++;
				$vuln[$vulnPlace]['title'] = $line[$i];
				$vuln[$vulnPlace]['ref'] = "$sub1.$sub2.$sub3";
				$sub3++;
				$switch = 0;
				break;
			case 2:
				@$vuln[$vulnPlace]['desc'] .= $line[$i];
				break;
			case 3:
				@$vuln[$vulnPlace]['fix'] .= $line[$i];
				break;
			case 4:
				$i++;
				//$vuln[$vulnPlace]['rem'] = trim(strtok($line[$i], " "));
				$switch = 0;
				break;
			case 5:
				$i++;
				$vuln[$vulnPlace]['cvss'] = $line[$i];
				$switch = 0;
				break;
			case 6:
				$i++;
				$line[$i+3] = str_replace("Vulnerability Img", "", $line[$i+3]);
				$vuln[$vulnPlace]['risk'] = strstr(trim($line[$i+3]), ":", true);
				$vuln[$vulnPlace]['owasp'] = trim(substr($line[$i+3], strpos($line[$i+3], ":") + 1));  
				$vuln[$vulnPlace]['impact'] = trim(strtok($line[$i+4], " "));  
				$vuln[$vulnPlace]['rem'] = trim(strtok($line[$i+5], " ")); 
				$switch = 0;
				break;
			case 7:
				$i++;
				$vuln[$vulnPlace]['hosts'] = $line[$i];
				$switch = 8;
				$vulnPlace++;
				break;
			case 8:
				$vuln[$vulnPlace]['title'] = trim($line[$i]);
				$vuln[$vulnPlace]['ref'] = "$sub1.$sub2.$sub3";
				$sub3++;
				$switch = 0;
				break;
			case 9:
				@$vuln[$vulnPlace]['impact'] .= $line[$i];
				break;
			default:
				# code...
				break;
		}

		//echo $line[$i]."\n"; // DEBUG
	}
	$first_desc = explode("Description", $vuln[0]['desc']);

	$vuln[0]['desc'] = $first_desc[sizeof($first_desc)-1];
}else{ // old template or Dave's format
	echo "[!] Daves Template\n";
	for ($i=$start; $i <= $end ; $i++) { 
		// change state (action to take)
		/***
		 * ~states~
		 * 0 = do nothing
		 * 1 = next is title
		 * 2 = next is description
		 * 3 = next is solution
		 * 4 = next is remediation
		 * 5 = next is cvss no
		 * 6 = next is risk level
		 * 7 = next is hosts
		 * 8 = next possibly title
		 */
		echo $line[$i]."\n";
		switch ($line[$i]) {
			case 'Discovered Vulnerabilities':
				$switch = 0;
				break;
			case 'Serious Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'High Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Medium Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Low Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Informational Risk Vulnerabilities':
				$sub2++; $sub3 = 1;
				$switch = 1;
				break;
			case 'Description':
				$switch = 2;
				break;
			case 'Solution':
				$switch = 3;
				break;
			case 'Remediation':
				$switch = 4;
				break;
			case 'CVSS Base Score':
				$switch = 5;
				break;
			case 'Risk Level':
				$switch = 6;
				break;
			case 'Vulnerabilities Exist On':
				$switch = 7;
				break;
			case 'Potential Impact':
				$switch = 9;
				break;
			default:
				# code...
				break;
		}

		//take action
		switch ($switch) {
			case 1:
				$i++;
				$vuln[$vulnPlace]['title'] = $line[$i];
				$vuln[$vulnPlace]['ref'] = "$sub1.$sub2.$sub3";
				$sub3++;
				$switch = 0;
				break;
			case 2:
				@$vuln[$vulnPlace]['desc'] .= $line[$i];
				break;
			case 3:
				@$vuln[$vulnPlace]['fix'] .= $line[$i];
				break;
			case 4:
				$i++;
				$vuln[$vulnPlace]['rem'] = trim(strtok($line[$i], " "));
				$switch = 0;
				break;
			case 5:
				$i++;
				$vuln[$vulnPlace]['cvss'] = $line[$i];
				$switch = 0;
				break;
			case 6:
				$i++;
				$vuln[$vulnPlace]['risk'] = trim(strtok($line[$i], " "));
				$vuln[$vulnPlace]['owasp'] = trim(substr($line[$i], strpos($line[$i], ":") + 1));    
				$switch = 0;
				break;
			case 7:
				$i++;
				$vuln[$vulnPlace]['hosts'] = $line[$i];
				$switch = 8;
				$vulnPlace++;
				break;
			case 8:
				$vuln[$vulnPlace]['title'] = trim($line[$i]);
				$vuln[$vulnPlace]['ref'] = "$sub1.$sub2.$sub3";
				$sub3++;
				$switch = 0;
				break;
			case 9:
				@$vuln[$vulnPlace]['impact'] .= $line[$i];
				break;
			default:
				# code...
				break;
		}

		//echo $line[$i]."\n"; // DEBUG
	}
}

// get all vulns.json if there are any
$currentPath = dirname($filter->getParam("doc"));
$jsonVuln = array();
$jsonFiles = glob($currentPath."/*.json");
foreach($jsonFiles as $finding){
    $placeholder = json_decode(file_get_contents($finding), true);
    if(isset($placeholder['title'])) // is a vuln and not config file
    	$jsonVuln[] = $placeholder;
}

// minor tidying of arrays
for ($i=0; $i < sizeof($vuln) ; $i++) { 
	if (strpos($vuln[$i]['desc'], "Description") === 0) $vuln[$i]['desc'] = substr($vuln[$i]['desc'], strlen("Description"));
	if (strpos($vuln[$i]['fix'], "Solution") === 0) $vuln[$i]['fix'] = substr($vuln[$i]['fix'], strlen("Solution"));
	$vuln[$i]['risk'] = rtrim($vuln[$i]['risk'], ":");
	// remove html encoding
	foreach($vuln[$i] as $key => $val){
		$vuln[$i][$key] = mb_convert_encoding($val, "UTF-8", 'UTF-8');
	}

	// fixing summary tables
	$descExpl = explode(".", $vuln[$i]['desc']);
	$fixExpl = explode(".", $vuln[$i]['fix']);
	$vuln[$i]['desc'] = $descExpl[0].".";
	$vuln[$i]['fix'] = $fixExpl[0].".";
	foreach($jsonVuln as $id => $jsonIssue){	
		if($jsonIssue['title'] == $vuln[$i]['title']){

			if((isset($jsonIssue['summary_issue']) && $jsonIssue['summary_issue'] <> "") || (isset($jsonIssue['summary_solution']) && $jsonIssue['summary_solution'] <> "")){
				echo "[+] summary found for: ".$vuln[$i]['title']."\n";
				$vuln[$i]['desc'] = $jsonIssue['summary_issue'];
				$vuln[$i]['fix'] = $jsonIssue['summary_solution'];
			}
		}
	}	
}

//print_r($vuln); // DEBUG
echo "[+] vulnerabilities identified\n";

delTree("/tmp/$rand");
echo "[+] temp files removed\n";

$resultsFolder = substr($filter->getParam("doc"), 0, strrpos( $filter->getParam("doc"), '/') )."/rep2";
if(!file_exists($resultsFolder."/")){
	mkdir($resultsFolder."/");
	echo "[+] created directory $resultsFolder/\n";
}else{
	$i = 1;
	while (file_exists($resultsFolder."_$i/"))
		$i++;
	mkdir($resultsFolder."_$i/");
	$resultsFolder .= "_$i";
	echo "[+] created directory $resultsFolder/\n";
}

if(writeIssueTable($vuln, "Serious", $resultsFolder."/findings_serious.csv"))
	echo "[+] serious issues: $resultsFolder/findings_serious.csv\n";
if(writeIssueTable($vuln, "High", $resultsFolder."/findings_high.csv"))
	echo "[+] high issues: $resultsFolder/findings_high.csv\n";
if(writeIssueTable($vuln, "Medium", $resultsFolder."/findings_medium.csv"))
	echo "[+] medium issues: $resultsFolder/findings_medium.csv\n";
if(writeIssueTable($vuln, "Low", $resultsFolder."/findings_low.csv"))
	echo "[+] low issues: $resultsFolder/findings_low.csv\n";
if(writeIssueTable($vuln, "Info", $resultsFolder."/findings_info.csv"))
	echo "[+] info issues: $resultsFolder/findings_info.csv\n";

$order = array('title', 'ref', 'desc', 'fix','rem','cvss','risk','impact','owasp','hosts');
$orderedArray = array();
foreach($vuln as $vn_no =>$vn){
	foreach ($order as $key) {
		//echo $key."\n";
	    $orderedArray[$vn_no][$key] = $vuln[$vn_no][$key];
	}
}
if(writeAllTable($orderedArray, $resultsFolder."/findings_all.csv"))
	echo "[+] all issues: $resultsFolder/findings_all.csv\n";

if(writeRemediationTable($vuln, $resultsFolder."/remediation.csv"))
	echo "[+] remediation table: $resultsFolder/remediation.csv\n";

if(writeOWASPTable($vuln, $resultsFolder."/owasp.csv"))
	echo "[+] OWASP table: $resultsFolder/owasp.csv\n";

viewVulns($vuln);

function unzipFolder($zipInputFile, $outputFolder) {
    $zip = new ZipArchive;
    $res = $zip->open($zipInputFile);
    if ($res === true) {
        $zip->extractTo($outputFolder);
        $zip->close();
        return true;
    }
    else {
        return false;
    }
}

function XML2Array(SimpleXMLElement $parent){
    $array = array();

    foreach ($parent as $name => $element) {
        ($node = & $array[$name])
            && (1 === count($node) ? $node = array($node) : 1)
            && $node = & $node[];

        $node = $element->count() ? XML2Array($element) : trim($element);
    }

    return $array;
}

function delTree($dir){ 
    $files = array_diff(scandir($dir), array('.', '..')); 

    foreach ($files as $file) { 
        (is_dir("$dir/$file")) ? delTree("$dir/$file") : unlink("$dir/$file"); 
    }

    return rmdir($dir); 
} 

function viewVulns($vuln){
	$s = $h = $m = $l = $i = 0 ;
	for ($j=0; $j < sizeof($vuln) ; $j++) { 
		switch ($vuln[$j]['risk']) {
			case 'Serious':
				$s++;
				break;
			case 'High':
				$h++;
				break;
			case 'Medium':
				$m++;
				break;
			case 'Low':
				$l++;
				break;
			case 'Info':
				$i++;
				break;
		}
	}
	echo "[=] Serious = $s, High = $h, Medium = $m, Low = $l, Info = $i\n";

echo"
Ref    | Title                             |  Risk  |  CVSS  |  Remediation  |  OWASP
-------|-----------------------------------|--------|--------|---------------|------------------------------\n";
for ($i=0; $i < sizeof($vuln) ; $i++) { 
	$ref = str_pad($vuln[$i]['ref'], 7);
	$title = str_pad($vuln[$i]['title'], 35);
	$risk = str_pad($vuln[$i]['risk'], 8);
		$cvs1 = explode("-", $vuln[$i]['cvss']);
	$cvss = str_pad($cvs1[0], 8);
	$rem = str_pad($vuln[$i]['rem'], 15);
	$owasp = str_pad($vuln[$i]['owasp'], 30);

	echo substr($ref, 0, 7); echo "|";
	echo substr($title, 0, 35); echo "|";
	echo substr($risk, 0, 8); echo "|";
	echo substr($cvss, 0, 8); echo "|";
	echo substr($rem, 0, 15); echo "|";
	echo substr($owasp, 0, 30); echo "\n";
}
}

function writeIssueTable($vuln, $issue, $path){
	$towrite = array();
	for ($i=0; $i < sizeof($vuln) ; $i++) { 
		if($vuln[$i]['risk'] == $issue){
			$towrite[$i]['desc'] = $vuln[$i]['title']." - ".$vuln[$i]['desc'];
			$towrite[$i]['fix'] = $vuln[$i]['fix'];
			$towrite[$i]['ref'] = $vuln[$i]['ref'];
			$towrite[$i]['hosts'] = $vuln[$i]['hosts'];
		}
	}
	if(sizeof($towrite) > 0){
		$fp = fopen($path, 'w');
		fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
		foreach ($towrite as $fields) {
	    	fputcsv($fp, $fields);
		}
		fclose($fp);
		return true;
	}else{
		return false;
	} 
}

function writeAllTable($vuln, $path){
	if(sizeof($vuln) > 0){
		$fp = fopen($path, 'w');
		fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
		fputcsv($fp, array("Title", "Ref", "Description", "Solution", "Remediation", "CVSS", "Risk","Impact", "OWASP", "Affected"));

		//print_r($orderedArray);
		foreach ($vuln as $fields) {
	    	fputcsv($fp, $fields);
		}
		fclose($fp);
		return true;
	}else{
		return false;
	} 
}

function writeRemediationTable($vuln, $path){
	$towrite = array();
	for ($i=0; $i < sizeof($vuln) ; $i++) { 
		$towrite[$i]['hosts'] = $vuln[$i]['hosts'];
		$towrite[$i]['ref'] = $vuln[$i]['ref'];
		$towrite[$i]['p'] = " ";	
		$towrite[$i]['c'] = " ";	
		$towrite[$i]['d'] = " ";	
		$towrite[$i]['u'] = " ";	
		switch ($vuln[$i]['rem']) {
			case 'Patch':
				$towrite[$i]['p'] = $vuln[$i]['risk'][0];
				break;
			case 'Configuration':
				$towrite[$i]['c'] = $vuln[$i]['risk'][0];
				break;
			case 'Development':
				$towrite[$i]['d'] = $vuln[$i]['risk'][0];
				break;
			case 'Upgrade':
				$towrite[$i]['u'] = $vuln[$i]['risk'][0];
				break;
		}	
	}
	if(sizeof($towrite) > 0){
		$fp = fopen($path, 'w');
		fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
		fputcsv($fp, array("Host", "Ref", "P", "C", "D", "U"));
		foreach ($towrite as $fields) {
	    	fputcsv($fp, $fields);
		}
		fclose($fp);
		return true;
	}else{
		return false;
	} 
}

function writeOWASPTable($vuln, $path){
	$towrite = array();
	for ($i=0; $i < sizeof($vuln) ; $i++) { 
		if(in_array($vuln[$i]['owasp'], array_column($towrite, 'owaspId'))){
			$towrite[$vuln[$i]['owasp']]['no']++;
		}else{
			$towrite[$vuln[$i]['owasp']]['owaspId'] = $vuln[$i]['owasp'];
			$towrite[$vuln[$i]['owasp']]['no'] = 1;
		}
	}
	if(sizeof($towrite) > 0){
		$fp = fopen($path, 'w');
		fprintf($fp, chr(0xEF).chr(0xBB).chr(0xBF));
		foreach ($towrite as $fields) {
	    	fputcsv($fp, $fields);
		}
		fclose($fp);
		return true;
	}else{
		return false;
	} 
}

?>