Ryan Prather
21082c7513
finding.inc - removed ID property to prevent duplicate findings from being added to the table host_list.inc - deleted unused constructor import.inc - formatting db_schema.json - removed sagacity.findings.id field (making tgt_id and pdi_id new primary keys), and updated references Database_Baseline.zip - updated routines for above change background_results.php - fixed bug #19 export-ckl.php - performance adjustments parse_excel_echecklist.php - performance improvements, ensure duplicate findings are not created, make eChecklist true status, update for removing findings.id field parse_nvd_json_cve.php - convert reading json to array instead of object for reading CPEs (which were updated to CPE 2.3 instead of 2.2) parse_* - remove findings.id field database.inc - formatting, and update for removing findings.id field index.php - ensure user can't import a host list without uploading a host list file Fixed: #65, #51, #28, #27, #10
606 lines
13 KiB
PHP
606 lines
13 KiB
PHP
<?php
|
|
|
|
/**
|
|
* File: finding.inc
|
|
* Author: Ryan Prather
|
|
* Purpose: Represents a finding
|
|
* Created: Sep 12, 2013
|
|
*
|
|
* Portions Copyright 2016-2018: Cyber Perspectives, LLC, All rights reserved
|
|
* Released under the Apache v2.0 License
|
|
*
|
|
* Portions Copyright (c) 2012-2015, Salient Federal Solutions
|
|
* Portions Copyright (c) 2008-2011, Science Applications International Corporation (SAIC)
|
|
* Released under Modified BSD License
|
|
*
|
|
* See license.txt for details
|
|
*
|
|
* Change Log:
|
|
* - Sep 12, 2013 - File created
|
|
* - Sep 1, 2016 - Updated Copyright and added comments to finding_status class
|
|
* - Nov 7, 2016 - Added finding::inc_Finding_Count function to increment counter
|
|
* - May 25, 2017 - Fixed bug of get_Category method returning empty severity (defaults to II if empty)
|
|
* - Jan 10, 2018 - Formatting
|
|
* - May 24, 2018 - Simplified get_Finding_Status_ID method
|
|
* - Nov 6, 2018 - Removed ID property to keep from duplicate findings
|
|
*/
|
|
|
|
/**
|
|
* Represents a finding
|
|
*
|
|
* @author Ryan Prather
|
|
*
|
|
*/
|
|
class finding {
|
|
|
|
/**
|
|
* Target ID
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $tgt_id = null;
|
|
|
|
/**
|
|
* PDI ID
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $pdi_id = null;
|
|
|
|
/**
|
|
* Scan ID
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $scan_id = null;
|
|
|
|
/**
|
|
* Finding Status ID
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $finding_status_id = null;
|
|
|
|
/**
|
|
* Updated category for the finding
|
|
*
|
|
* @var int
|
|
*/
|
|
protected $cat = null;
|
|
|
|
/**
|
|
* Array of ia controls that apply to this finding
|
|
*
|
|
* @var array:string
|
|
*/
|
|
protected $ia_controls = array();
|
|
|
|
/**
|
|
* Notes
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $notes = null;
|
|
|
|
/**
|
|
* Change ID
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $change_id = null;
|
|
|
|
/**
|
|
* Original source
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $orig_src = null;
|
|
|
|
/**
|
|
* Finding iteration (incremented if finding is updated
|
|
*
|
|
* @var integer
|
|
*/
|
|
protected $finding_itr = null;
|
|
|
|
/**
|
|
* Array of statuses
|
|
*
|
|
* @var array:string
|
|
*/
|
|
protected $STATUS = [
|
|
1 => 'Not Reviewed',
|
|
2 => 'Not a Finding',
|
|
3 => 'Open',
|
|
4 => 'Not Applicable',
|
|
5 => 'No Data',
|
|
6 => 'Exception',
|
|
7 => 'False Positive'
|
|
];
|
|
|
|
/**
|
|
* Constant for no change
|
|
*
|
|
* @var integer
|
|
*/
|
|
const NC = 0;
|
|
|
|
/**
|
|
* Constant for change ID::TO_OPEN
|
|
*
|
|
* @var integer
|
|
*/
|
|
const TO_OPEN = 1;
|
|
|
|
/**
|
|
* Constant for change ID::TO_NF
|
|
*
|
|
* @var integer
|
|
*/
|
|
const TO_NF = 2;
|
|
|
|
/**
|
|
* Constant for change ID::TO_NA
|
|
*
|
|
* @var integer
|
|
*/
|
|
const TO_NA = 3;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param integer $int_Tgt_ID
|
|
* @param integer $int_PDI_ID
|
|
* @param integer $int_Scan_ID
|
|
* @param integer|string $Finding_Status
|
|
* @param string $str_Notes
|
|
* @param integer $int_Change_ID
|
|
* @param string $str_Orig_Src
|
|
* @param integer $int_Finding_Itr
|
|
*/
|
|
public function __construct($int_Tgt_ID, $int_PDI_ID, $int_Scan_ID, $Finding_Status, $str_Notes, $int_Change_ID, $str_Orig_Src, $int_Finding_Itr) {
|
|
$this->tgt_id = $int_Tgt_ID;
|
|
$this->pdi_id = $int_PDI_ID;
|
|
$this->scan_id = $int_Scan_ID;
|
|
if (is_numeric($Finding_Status)) {
|
|
$this->finding_status_id = $Finding_Status;
|
|
}
|
|
else {
|
|
$this->finding_status_id = $this->get_Finding_Status_ID($Finding_Status);
|
|
}
|
|
$this->notes = $str_Notes;
|
|
$this->change_id = $int_Change_ID;
|
|
$this->orig_src = $str_Orig_Src;
|
|
$this->finding_itr = $int_Finding_Itr;
|
|
}
|
|
|
|
/**
|
|
* Getter function for target ID
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function get_Tgt_ID() {
|
|
return $this->tgt_id;
|
|
}
|
|
|
|
/**
|
|
* Setter function for target ID
|
|
*
|
|
* @param integer $int_Tgt_ID
|
|
*/
|
|
public function set_Tgt_ID($int_Tgt_ID) {
|
|
$this->tgt_id = $int_Tgt_ID;
|
|
}
|
|
|
|
/**
|
|
* Getter function for PDI ID
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function get_PDI_ID() {
|
|
return $this->pdi_id;
|
|
}
|
|
|
|
/**
|
|
* Setter function for PDI ID
|
|
*
|
|
* @param integer $int_PDI_ID
|
|
*/
|
|
public function set_PDI_ID($int_PDI_ID) {
|
|
$this->pdi_id = $int_PDI_ID;
|
|
}
|
|
|
|
/**
|
|
* Getter function for Scan ID
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function get_Scan_ID() {
|
|
return $this->scan_id;
|
|
}
|
|
|
|
/**
|
|
* Setter function for Scan ID
|
|
*
|
|
* @param integer $int_Scan_ID
|
|
*/
|
|
public function set_Scan_ID($int_Scan_ID) {
|
|
$this->scan_id = $int_Scan_ID;
|
|
}
|
|
|
|
/**
|
|
* Getter function for Finding status ID
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function get_Finding_Status() {
|
|
return $this->finding_status_id;
|
|
}
|
|
|
|
/**
|
|
* Getter function for Finding status ID based on string
|
|
*
|
|
* @param string $status
|
|
* @return integer
|
|
*/
|
|
public function get_Finding_Status_ID($status) {
|
|
$arr_flip = array_flip($this->STATUS);
|
|
if(isset($arr_flip[$status])) {
|
|
return $arr_flip[$status];
|
|
}
|
|
else {
|
|
return $arr_flip['Not Reviewed'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Getter function for finding status string
|
|
*
|
|
* @param integer $int_Status_ID
|
|
* @return string
|
|
*/
|
|
public function get_Finding_Status_String($int_Status_ID = null) {
|
|
if ($int_Status_ID) {
|
|
return $this->STATUS[$int_Status_ID];
|
|
}
|
|
else {
|
|
return $this->STATUS[$this->finding_status_id];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setter function for finding status
|
|
*
|
|
* @param integer $int_Finding_Status_ID
|
|
*/
|
|
public function set_Finding_Status($int_Finding_Status_ID) {
|
|
$this->finding_status_id = $int_Finding_Status_ID;
|
|
}
|
|
|
|
/**
|
|
* Setter function for finding status
|
|
*
|
|
* @param string $str_New_Status
|
|
*/
|
|
public function set_Finding_Status_By_String($str_New_Status) {
|
|
$this->finding_status_id = $this->get_Finding_Status_ID($str_New_Status);
|
|
}
|
|
|
|
/**
|
|
* Getter function for notes
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_Notes() {
|
|
return $this->notes;
|
|
}
|
|
|
|
/**
|
|
* Setter function for notes
|
|
*
|
|
* @param string $str_Notes
|
|
*/
|
|
public function set_Notes($str_Notes) {
|
|
$this->notes = $str_Notes;
|
|
}
|
|
|
|
/**
|
|
* Function to prepend notes to the existing list
|
|
*
|
|
* @param string $str_Notes
|
|
*/
|
|
public function prepend_Notes($str_Notes) {
|
|
$this->notes = $str_Notes . PHP_EOL . $this->notes;
|
|
}
|
|
|
|
/**
|
|
* Function to append notes
|
|
*
|
|
* @param string $str_Notes
|
|
* @param boolean $merge
|
|
*/
|
|
public function append_Notes($str_Notes, $merge = false) {
|
|
$this->notes .= PHP_EOL . ($merge ? "(Merged Target)" . PHP_EOL : "") . $str_Notes;
|
|
}
|
|
|
|
/**
|
|
* Getter function for change ID
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function get_Change_ID() {
|
|
if ($this->change_id) {
|
|
return $this->change_id;
|
|
}
|
|
else {
|
|
return $this::NC;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setter function for change ID
|
|
*
|
|
* @param integer $int_Change_ID
|
|
*/
|
|
public function set_Change_ID($int_Change_ID) {
|
|
$this->change_id = $int_Change_ID;
|
|
}
|
|
|
|
/**
|
|
* Getter function for original source
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_Original_Source() {
|
|
return $this->orig_src;
|
|
}
|
|
|
|
/**
|
|
* Setter function for original source
|
|
*
|
|
* @param string $str_Original_Source
|
|
*/
|
|
public function set_Original_Source($str_Original_Source) {
|
|
$this->orig_src = $str_Original_Source;
|
|
}
|
|
|
|
/**
|
|
* Getter function for finding iteration
|
|
*
|
|
* @return integer
|
|
*/
|
|
public function get_Finding_Iteration() {
|
|
return $this->finding_itr;
|
|
}
|
|
|
|
/**
|
|
* Setter function for finding iteration
|
|
*
|
|
* @param integer $int_Finding_Iteration
|
|
*/
|
|
public function set_Finding_Iteration($int_Finding_Iteration) {
|
|
$this->finding_itr = $int_Finding_Iteration;
|
|
}
|
|
|
|
/**
|
|
* Increment the finding count by 1
|
|
*/
|
|
public function inc_Finding_Count() {
|
|
$this->finding_itr++;
|
|
}
|
|
|
|
/**
|
|
* Getter function for deconflicted status
|
|
*
|
|
* @param string $str_New_Status
|
|
* @return string
|
|
*/
|
|
public function get_Deconflicted_Status($str_New_Status) {
|
|
// must get original status first!
|
|
return deconflict_status::$DECONFLICTED_STATUS[$this->get_Finding_Status_String()][$str_New_Status];
|
|
}
|
|
|
|
/**
|
|
* Getter function for category
|
|
*
|
|
* @return int
|
|
*/
|
|
public function get_Category() {
|
|
if (empty($this->cat)) {
|
|
return 2;
|
|
}
|
|
return $this->cat;
|
|
}
|
|
|
|
/**
|
|
* Setter function for category
|
|
*
|
|
* @param mixed $cat_in
|
|
*/
|
|
public function set_Category($cat_in) {
|
|
if (is_numeric($cat_in)) {
|
|
$this->cat = $cat_in;
|
|
}
|
|
elseif (is_string($cat_in)) {
|
|
$this->cat = substr_count($cat_in, "I");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Getter function for IA controls
|
|
*
|
|
* @return array:string
|
|
*/
|
|
public function get_IA_Controls() {
|
|
return $this->ia_controls;
|
|
}
|
|
|
|
/**
|
|
* Getter function for IA Controls
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_IA_Controls_String() {
|
|
return implode(" ", $this->ia_controls);
|
|
}
|
|
|
|
/**
|
|
* Setter function for the IA Controls
|
|
*
|
|
* @param mixed $ia_controls_in
|
|
*/
|
|
public function set_IA_Controls($ia_controls_in) {
|
|
if (is_array($ia_controls_in)) {
|
|
$this->ia_controls = $ia_controls_in;
|
|
}
|
|
elseif (is_string($ia_controls_in)) {
|
|
$this->ia_controls = explode(" ", $ia_controls_in);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function to add an IA control the the array
|
|
*
|
|
* @param string $ia_control_in
|
|
*/
|
|
public function add_IA_Control($ia_control_in) {
|
|
$add = true;
|
|
foreach ($this->ia_controls as $ia) {
|
|
if ($ia == $ia_control_in) {
|
|
$add = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($add) {
|
|
$this->ia_controls[] = $ia_control_in;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* The finding status
|
|
*
|
|
* @author Ryan Prather
|
|
*/
|
|
class finding_status {
|
|
|
|
/**
|
|
* The database ID of the finding status
|
|
*
|
|
* @var int
|
|
*/
|
|
public $id = 0;
|
|
|
|
/**
|
|
* The status of the finding
|
|
*
|
|
* @var string
|
|
*/
|
|
public $status = '';
|
|
|
|
}
|
|
|
|
/**
|
|
* Class to deconflict statuses
|
|
*
|
|
* @author Ryan Prather
|
|
*/
|
|
class deconflict_status {
|
|
|
|
/**
|
|
* Stores the matrix of current -> new statuses
|
|
*
|
|
* @var array:string
|
|
*
|
|
* / Finding Definitions
|
|
* Open: The finding is valid for this host - the host does not meet the requirements
|
|
* Not a Finding: The finding is not valid for this host - the host meets the requirements
|
|
* Not Applicable: The requirement does not apply to this host - prerequisites do not exist.
|
|
* Not Reviewed: The finding has not yet been reviewed.
|
|
* Exception: (A type of Open) - The finding is valid, but the system cannot comply for a valid reason
|
|
* False Positive: (A type of Not a Finding) - The scanning tool incorrectly reported Open.
|
|
* No Data: Because dissimilar checklists were merged, there is no data available for this item (Uncommon)
|
|
*
|
|
* General Precedence Order: E, FP, O, NF, NA, NR, ND
|
|
* Exception - the newest E or FP always take precedence (security engineer input)
|
|
*
|
|
* Decision Table:
|
|
* orig\new | E | FP | O | NF | NA | NR | ND
|
|
* E | E | FP | E | E | E | E | E
|
|
* FP | E | FP | FP | FP | FP | FP | FP
|
|
* O | E | FP | O | O | O | O | O
|
|
* NF | E | FP | O | NF | NF | NF | NF
|
|
* NA | E | FP | O | NF | NA | NA | NA
|
|
* NR | E | FP | O | NF | NA | NR | NR
|
|
* ND | E | FP | O | NF | NA | NR | ND
|
|
*/
|
|
static $DECONFLICTED_STATUS = [
|
|
'Exception' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'Exception',
|
|
'Not a Finding' => 'Exception',
|
|
'Not Applicable' => 'Exception',
|
|
'Not Reviewed' => 'Exception',
|
|
'No Data' => 'Exception'
|
|
],
|
|
'False Positive' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'False Positive',
|
|
'Not a Finding' => 'False Positive',
|
|
'Not Applicable' => 'False Positive',
|
|
'Not Reviewed' => 'False Positive',
|
|
'No Data' => 'False Positive'
|
|
],
|
|
'Open' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'Open',
|
|
'Not a Finding' => 'Open',
|
|
'Not Applicable' => 'Open',
|
|
'Not Reviewed' => 'Open',
|
|
'No Data' => 'Open'
|
|
],
|
|
'Not a Finding' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'Open',
|
|
'Not a Finding' => 'Not a Finding',
|
|
'Not Applicable' => 'Not a Finding',
|
|
'Not Reviewed' => 'Not a Finding',
|
|
'No Data' => 'Not a Finding'
|
|
],
|
|
'Not Applicable' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'Open',
|
|
'Not a Finding' => 'Not a Finding',
|
|
'Not Applicable' => 'Not Applicable',
|
|
'Not Reviewed' => 'Not Applicable',
|
|
'No Data' => 'Not Applicable'
|
|
],
|
|
'Not Reviewed' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'Open',
|
|
'Not a Finding' => 'Not a Finding',
|
|
'Not Applicable' => 'Not Applicable',
|
|
'Not Reviewed' => 'Not Reviewed',
|
|
'No Data' => 'Not Reviewed'
|
|
],
|
|
'No Data' => [
|
|
'Exception' => 'Exception',
|
|
'False Positive' => 'False Positive',
|
|
'Open' => 'Open',
|
|
'Not a Finding' => 'Not a Finding',
|
|
'Not Applicable' => 'Not Applicable',
|
|
'Not Reviewed' => 'Not Reviewed',
|
|
'No Data' => 'No Data'
|
|
]
|
|
];
|
|
|
|
}
|