sagacity/inc/database.inc
Ryan Prather bb9e2f4adb
fix(eChecklist): Fix bug with eChecklist note duplication
parse_excel_echecklist.php - change preg_match to a stripos method check for notes string and add update_Scan_Host_List call after importing all worksheets
database.inc - add a check for if appending a duplicate string to the checklist notes. add update_Target_Counts call when updating host list

fix #80, #10
2019-01-15 13:08:49 -05:00

13005 lines
432 KiB
PHP

<?php
/**
* File: database.inc
* Author: Ryan Prather
* Purpose: This file will contain all the variables and functions to operate a database
* and for the ST&E Manager to interact with
* Created: Sep 11, 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 11, 2013 - File created
* - Sep 1, 2016 - Copyright Updated, way to many updates to list
* - Oct 24, 2016 - Updated delete_Scan function to check for return of scan array
* Changed SQL updating host list to REPLACE instead (fixes updating the finding count)
* Added get_Running_Script_Status function to return the status of the script
* Added is_Connected function to check the status of the DB connection (and reconnect if there are issues)
* Updated tcp/udp_port variable to use functions instead
* - Nov 7, 2016 - Several changes when getting and saving tcp/udp port data
* - Nov 16, 2016 - Changed error for failed DB connect to include attempted server
* - Jan 30, 2017 - Formatting
* - Jan 31, 2017 - Cleaned up db_helper functionality
* - Feb 15, 2017 - Started process to migrate some query to be created with the db_helper class and fixed a couple bugs
* - Mar 3, 2017 - Added IS and NOT_LIKE defined constants, added check if ` or AS is present, then not adding a `,
* Documentation, Fixed error in db_helper::order method, Started converting db class methods to use db_helper
* - Mar 8, 2017 - Converted a couple more queries to use db_helper object and formatting
* - Mar 13, 2017 - Fixed bugs with db_helper::where functions and added fields function
* - Mar 14, 2017 - Fixed bug in db::get_Software method
* - Mar 20, 2017 - Updated CCE and CCI methods to use db_helper object
* - Mar 22, 2017 - Updated get_Nessus save_Nessus method to use db_helper object,
* Fixed bug in db_helper::debug and db::get_Checklist
* - Apr 5, 2017 - Converted a few other methods to use db_helper class, switch positions for parameters in db::save_Target method,
* Added documentation in a few places, fixed bug with Nessus parsing
* - Apr 7, 2017 - Replaced function get_STE_Category_List with get_STE_Cat_List (and updated required files),
* Converted more methods to use db_helper, fixed a couple typos (add_Source -> add_Sources),
* Changed save_Target to save the short_sw_string as the os_string instead
* - Apr 10, 2017 - More db_helper conversions and bug fixes
* - May 13, 2017 - Always lots of changes here
* Added get_Checklist_By_Filename method
* Converted get_Category_Checklists function to use db_helper class
* Converted save_SV_Rule method to use db_helper class
* Converted over a few other methods to use db_helper class
* - May 19, 2017 - Converted over more methods to use db_helper class
* Fixed typo, added deleting category interview data when deleting category
* - May 22, 2017 - Simplified get_ScanData function to not retrieve all data for target host lists (took too much time)
* Converted save_IA_Controls method to use db_helper class
* - May 25, 2017 - Added 'start' flag to db_helper::flags method to start at a different record (supporting paging in DataTables library)
* Converted over a couple methods to use db_helper class
* - Jun 27, 2017 - Set default target classification if not present, fixed bug #264
* - Jul 13, 2017 - Revised several method to use db_helper class and removed db::add_PDI method and moved to just using save_PDI method
* - Jul 21, 2017 - Cleaned up port saving in save_Interface method
* - Jul 24, 2017 - Added save_EMASS_CCIs() and get_EMASS_CCIs() to create and retrieve the rmf.emass_cci table
* - Aug 28, 2017 - Fixed typo w/cve_reference table #276
* - Aug 28, 2017 - Added delete_Target_Software, fixed error when deleting all software or checklists (wouldn't save correctly), and cleaned up delete_Target method
* - Oct 26, 2017 - Formatting and bug fix for #326
* - Oct 27, 2017 - Change filtering for get_ports, added delete_Interface method
* - Dec 27, 2017 - Migrate methods to use db_helper and syntax updates
* - Jan 1, 2018 - Added get_Software_Id method that gets all the software IDs from a list of CPEs
* - Jan 10, 2018 - Added a couple functions and formatting
* - Jan 15, 2018 - Fixed bug in get_Category_Findings
* - Jan 16, 2018 - Added include for host_list.inc, updated to use host_list class, fixed bug in delete_Scan method
Moved scan deletion here
* - Jan 20, 2018 - Fixed typo in save_STE method
* - May 24, 2018 - Added defaulting where clause operator to '='
* - May 26, 2018 - Updated autocategorization to removed any extranious spaces before or after the string
* - May 31, 2018 - Changes to support renaming sagacity.pdi_catalog.check_content field and scan error detection
* - Jun 2, 2018 - Formatting and added set_Setting_Array method
* - Jun 5, 2018 - Changed set_Setting_Array method to use SQL update instead of replace
* - Sep 5, 2018 - Fix for #8
* - Nov 3, 2018 - Fix for fix #62, commented out last INSERT in post_Processing, jao
* - Nov 8, 2018 - Added a couple methods to support changes for #25
*/
include_once 'base.inc';
include_once 'software.inc';
include_once 'checklist.inc';
include_once 'ste.inc';
include_once 'target.inc';
include_once 'scan.inc';
include_once 'pdi_catalog.inc';
include_once 'port.inc';
include_once 'sites.inc';
include_once 'sources.inc';
include_once 'ste_cat.inc';
include_once 'system.inc';
include_once 'interfaces.inc';
include_once 'script.inc';
include_once 'stigs.inc';
include_once 'golddisk.inc';
include_once 'finding.inc';
include_once 'ia_control.inc';
include_once 'retina.inc';
include_once 'sv_rule.inc';
include_once 'nessus.inc';
include_once 'echecklist.inc';
include_once 'cve.inc';
include_once 'advisories.inc';
include_once 'iavm.inc';
include_once 'oval.inc';
include_once 'cce.inc';
include_once 'proc_ia_controls.inc';
include_once 'error.inc';
include_once 'question.inc';
include_once 'cci.inc';
include_once 'rmf_control.inc';
include_once 'cpe.inc';
include_once 'nasl.inc';
include_once 'uuid.inc';
include_once 'host_list.inc';
// @TODO - Make sure all save functions accept a class object or array of that class that is being saved. Otherwise, only primative types will be passed in.
/**
* Constant to decide if the database queries will run automatically after creating them
*
* @var boolean
*/
define('AUTORUN', false);
/**
* Global to represent an IN statement (e.g.
* WHERE field IN (1,2))
*
* @var int
*/
define('IN', 1);
/**
* Global to represent a NOT IN statement (e.g.
* WHERE field NOT IN (1,2))
*
* @var int
*/
define('NOT_IN', 64);
/**
* Global to represent a BETWEEN statement (e.g.
* WHERE field BETWEEN 1 and 2)
*
* @var int
*/
define('BETWEEN', 2);
/**
* Global to represent a LIKE statement (e.g.
* WHERE field LIKE '%value%')
*
* @var int
*/
define('LIKE', 4);
/**
* Global to represent an IS NOT statement (e.g.
* WHERE field IS NOT NULL)
*
* @var int
*/
define('IS_NOT', 8);
/**
* Global to represent an IS statement (e.g.
* WHERE field IS NULL)
*
* @var int
*/
define('IS', 16);
/**
* Global to represent an NOT LIKE statement (e.g.
* WHERE field NOT LIKE '%value%'
*
* @var int
*/
define('NOT_LIKE', 32);
/**
* Class to help the database
*
* @author Ryan Prather
*/
class db_helper
{
const SELECT = 1;
const SELECT_COUNT = 2;
const CREATE_TABLE = 3;
const DROP = 4;
const DELETE = 5;
const INSERT = 6;
const REPLACE = 7;
const UPDATE = 8;
const EXTENDED_INSERT = 9;
const EXTENDED_REPLACE = 10;
const EXTENDED_UPDATE = 11;
const ALTER_TABLE = 12;
const TRUNCATE = 13;
/**
* The mysqli connection
*
* @var mysqli
*/
public $c;
/**
* To store the SQL statement
*
* @var string
*/
public $sql = null;
/**
* A string to store the type of query that is being run
*
* @var string
*/
public $query_type = null;
/**
* The result of the query
*
* @var mixed
*/
private $result = null;
/**
* Constructor
*
* @param mysqli $dbh
* [by ref]
* mysqli object to perform queries.
*/
public function __construct(&$dbh)
{
if (! is_null($dbh) && is_a($dbh, "mysqli")) {
$this->c = $dbh;
} else {
throw (new Exception("Could not create database helper class", E_ERROR));
}
$this->c->real_query("SET time_zone='+00:00'");
$this->c->real_query("SET sql_mode=''");
}
/**
* Function to execute the statement
*
* @param mixed $return
* [optional]
* MYSQLI constant to control what is returned from the mysqli_result object
* @param string $sql
* [optional]
* Optional SQL query
*
* @return mixed
*/
public function execute($return = MYSQLI_ASSOC, $sql = null)
{
if (! is_null($sql)) {
$this->sql = $sql;
}
if (is_a($this->c, 'mysqli')) {
if (! $this->c->ping()) {
$this->c = null;
$this->c = new mysqli(DB_SERVER, 'web', db::decrypt_pwd(), 'sagacity');
}
} else {
throw (new Exception('Database was not connected', E_ERROR));
}
try {
if (in_array($this->query_type, [
self::SELECT,
self::SELECT_COUNT
])) {
$this->result = $this->c->query($this->sql);
if ($this->c->error) {
$this->debug(E_ERROR);
}
} elseif ($this->query_type == self::DELETE) {
$this->c->real_query($this->sql);
if ($this->c->error) {
return 0;
}
} else {
$this->c->real_query($this->sql);
if ($this->c->error) {
$this->debug(E_ERROR, $this->c->error);
}
}
$this->result = $this->check_results($return);
} catch (Exception $e) {
die($e->getTraceAsString());
}
return $this->result;
}
/**
* Function to check the results and return what is expected
*
* @param mixed $return_type
* [optional]
* Optional return mysqli_result return type
*
* @return mixed
*/
function check_results($return_type = MYSQLI_ASSOC)
{
$res = null;
if ($this->c->error) {
$this->debug(E_ERROR);
} elseif (LOG_LEVEL == E_DEBUG) {
$this->debug(E_DEBUG);
}
switch ($this->query_type) {
case self::SELECT_COUNT:
if (! is_a($this->result, 'mysqli_result')) {
$this->debug(E_ERROR);
}
if ($this->result->num_rows == 1) {
$res = $this->result->fetch_assoc()['count'];
} elseif ($this->result->num_rows > 1) {
$res = $this->result->num_rows;
}
mysqli_free_result($this->result);
return $res;
case self::SELECT:
if (! is_a($this->result, 'mysqli_result')) {
$this->debug(E_ERROR);
}
if ($this->result->num_rows == 1) {
$res = $this->result->fetch_array($return_type);
} elseif ($this->result->num_rows > 1) {
$res = $this->fetch_all($return_type);
}
mysqli_free_result($this->result);
return $res;
case self::INSERT:
if ($this->c->error) {
$this->debug(E_ERROR);
return 0;
}
if ($this->c->insert_id) {
return $this->c->insert_id;
} elseif ($this->c->affected_rows) {
return $this->c->affected_rows;
}
return 1;
case self::EXTENDED_INSERT:
case self::EXTENDED_REPLACE:
case self::EXTENDED_UPDATE:
case self::REPLACE:
case self::UPDATE:
case self::DELETE:
case self::ALTER_TABLE:
if ($this->c->error && $this->c->errno == 1060) {
return ($this->c->affected_rows ? $this->c->affected_rows : true);
} elseif ($this->c->error) {
$this->debug(E_ERROR);
return false;
} elseif ($this->c->affected_rows) {
return $this->c->affected_rows;
} else {
return true;
}
break;
case self::CREATE_TABLE:
case self::DROP:
case self::TRUNCATE:
return true;
}
}
/**
* Function to pass through calling the query function (used for backwards compatibility and for more complex queries that aren't currently supported)
*
* @param string $sql
* [optional]
* Optional query to pass in and execute
*
* @return mysqli_result
*/
public function query($sql = null)
{
if (is_null($sql)) {
return $this->c->query($this->sql);
} else {
return $this->c->query($sql);
}
}
/**
* A function to build a select query
*
* @param string $table_name
* The table to query
* @param array $fields
* [optional]
* Optional array of fields to return (defaults to '*')
* @param array $where
* [optional]
* Optional 2-dimensional array to build where clause from
* @param array $flags
* [optional]
* Optional 2-dimensional array to allow other flags
*
* @see db_helper::where()
* @see db_helper::flags()
*
* @return mixed
*/
public function select($table_name, $fields = null, $where = null, $flags = null)
{
$this->sql = null;
$this->query_type = self::SELECT;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "SELECT " . $this->fields($fields) . " FROM $table_name";
} else {
return null;
}
if (isset($flags['table_joins']) && is_array($flags['table_joins'])) {
$this->sql .= " " . implode(" ", $flags['table_joins']);
}
if (! is_null($where) && is_array($where) && count($where)) {
$this->sql .= $this->where($where);
}
if (! is_null($flags) && is_array($flags) && count($flags)) {
$this->sql .= $this->flags($flags);
}
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to build a query to check the number of rows in a table
*
* @param string $table_name
* The table to query
* @param array $where
* [optional]
* Optional 2-dimensional array to build where clause
* @param array $flags
* [optional]
* Optional 2-dimensional array to add flags
*
* @see db_helper::where()
* @see db_helper::flags()
*
* @return string|NULL
*/
public function select_count($table_name, $where = null, $flags = null)
{
$this->sql = null;
$this->query_type = self::SELECT_COUNT;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "SELECT COUNT(1) AS 'count' FROM $table_name";
} else {
return null;
}
if (isset($flags['table_joins']) && is_array($flags['table_joins'])) {
$this->sql .= " " . implode(" ", $flags['table_joins']);
}
if (! is_null($where) && is_array($where) && count($where)) {
$this->sql .= $this->where($where);
}
if (! is_null($flags) && is_array($flags) && count($flags)) {
$this->sql .= $this->flags($flags);
}
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to build an insert query statement
*
* @param string $table_name
* Table name to query
* @param array $params
* Name/value pair to insert into the table
* @param boolean $to_ignore
* [optional]
* Optional boolean to decide if the "IGNORE" will be added
*
* @return string|NULL
*/
public function insert($table_name, $params, $to_ignore = false)
{
$this->sql = null;
$this->query_type = self::INSERT;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "INSERT " . ($to_ignore ? "IGNORE " : "") . "INTO $table_name " . "(`" . implode("`,`", array_keys($params)) . "`)";
}
$this->sql .= " VALUES (" . implode(",", array_map([
$this,
'_escape'
], array_values($params))) . ")";
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to create an extended insert query statement
*
* @param string $table_name
* The table name that the data is going to be inserted on
* @param array $fields
* An array of field names that each value represents
* @param array $params
* An array of array of values
* @param boolean $to_ignore
* [optional]
* Boolean to decide if we need to use the INSERT IGNORE INTO syntax
*
* @return NULL|string Returns the SQL if AUTORUN is set to false, else it returns the output from running.
*/
public function extended_insert($table_name, $fields, $params, $to_ignore = false)
{
$this->sql = null;
$this->query_type = self::EXTENDED_INSERT;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "INSERT " . ($to_ignore ? "IGNORE " : "") . "INTO $table_name " . "(`" . implode("`,`", $fields) . "`)";
} else {
throw (new Exception("Missing table name in extended_insert", E_ERROR));
}
if (is_array($params) && count($params)) {
$this->sql .= " VALUES ";
if (isset($params[0]) && is_array($params[0])) {
foreach ($params as $p) {
$this->sql .= "(" . implode(",", array_map([
$this,
'_escape'
], array_values($p))) . "),";
}
} else {
if (count($params) != count($fields)) {
throw (new Exception("Inconsistent number of fields in fields and values"));
}
$this->sql .= "(" . implode("),(", array_map([
$this,
'_escape'
], array_values($params))) . "),";
}
} else {
throw new \InvalidArgumentException("Expected array parameters");
}
$this->sql = substr($this->sql, 0, - 1);
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Build a statement to update a table
*
* @param string $table_name
* The table name to update
* @param array $params
* Name/value pairs of the field name and value
* @param array $where
* [optional]
* Two-dimensional array to create where clause
* @param array $flags
* [optional]
* Two-dimensional array to create other flag options (table_joins, order, and group)
*
* @see db_helper::where()
* @see db_helper::flags()
*
* @return NULL|string
*/
public function update($table_name, $params, $where = null, $flags = null)
{
$this->sql = "UPDATE ";
$this->query_type = self::UPDATE;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql .= $table_name;
if (isset($flags['table_joins'])) {
$this->sql .= " " . implode(" ", $flags['table_joins']);
unset($flags['table_joins']);
}
$this->sql .= " SET ";
}
foreach ($params as $f => $p) {
if ((strpos($f, "`") === false) && (strpos($f, ".") === false) && (strpos($f, "*") === false) && (stripos($f, " as ") === false)) {
$f = "`{$f}`";
}
if (! is_null($p)) {
$this->sql .= "$f={$this->_escape($p)},";
} else {
$this->sql .= "$f=NULL,";
}
}
$this->sql = substr($this->sql, 0, - 1);
if (! is_null($where) && is_array($where) && count($where)) {
$this->sql .= $this->where($where);
}
if (! is_null($flags) && is_array($flags) && count($flags)) {
$this->sql .= $this->flags($flags);
}
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to offer an extended updated functionality by using two different tables.
*
* @param string $to_be_updated
* The table that you want to update (alias 'tbu' is automatically added)
* @param string $original
* The table with the data you want to overwrite to_be_updated table (alias 'o' is automatically added)
* @param string $using
* The common index value between them that will join the fields
* @param array|string $params
* If string only a single field is updated (tbu.$params = o.$params)
* If array each element in the array is a field to be updated (tbu.$param = o.$param)
*
* @return mixed
*/
public function extended_update($to_be_updated, $original, $using, $params)
{
$this->sql = "UPDATE ";
$this->query_type = self::EXTENDED_UPDATE;
if (! is_null($to_be_updated) && ! is_null($original) && ! is_null($using)) {
$this->sql .= "$to_be_updated tbu INNER JOIN $original o USING ($using) SET ";
}
if (is_array($params) && count($params)) {
foreach ($params as $param) {
$this->sql .= "tbu.$param = o.$param,";
}
$this->sql = substr($this->sql, 0, - 1);
} elseif (is_string($params)) {
$this->sql .= "tbu.$params = o.$params";
} else {
throw (new Exception("Do not understand datatype of \$params", E_ERROR));
}
if (AUTORUN) {
return $this->execute(MYSQL_BOTH);
}
return $this->sql;
}
/**
* Function to build a replace query
*
* @param string $table_name
* The table to update
* @param array $params
* Name/value pair to insert
*
* @return NULL|string
*/
public function replace($table_name, $params)
{
$this->sql = null;
$this->query_type = self::REPLACE;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "REPLACE INTO $table_name " . "(`" . implode("`,`", array_keys($params)) . "`)";
}
$this->sql .= " VALUES (" . implode(",", array_map(array(
$this,
'_escape'
), array_values($params))) . ")";
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to build an extended replace statement
*
* @param string $table_name
* Table name to update
* @param array $fields
* Array of fields
* @param array $params
* Two-dimensional array of values
*
* @return NULL|string
*/
public function extended_replace($table_name, $fields, $params)
{
$this->sql = null;
$this->query_type = self::EXTENDED_REPLACE;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "REPLACE INTO $table_name " . "(`" . implode("`,`", $fields) . "`)";
} else {
return null;
}
if (is_array($params) && count($params)) {
$this->sql .= " VALUES ";
foreach ($params as $p) {
$this->sql .= "(" . implode(",", array_map(array(
$this,
'_escape'
), array_values($p))) . "),";
}
}
$this->sql = substr($this->sql, 0, - 1);
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to build a delete statement
*
* @param string $table_name
* Table name to act on
* @param array $fields
* [optional]
* Optional list of fields to delete (used when including multiple tables)
* @param array $where
* [optional]
* Optional 2-dimensional array to build where clause from
* @param array $table_joins
* [optional]
* Optional 2-dimensional array to add other flags
*
* @see db_helper::where()
* @see db_helper::flags()
*
* @return string|NULL
*/
public function delete($table_name, $fields = null, $where = null, $table_joins = null)
{
$this->sql = "DELETE";
$this->query_type = self::DELETE;
if (! is_null($fields) && is_array($fields)) {
$this->sql .= " " . implode(",", $fields);
}
if (! is_null($table_name) && is_string($table_name)) {
$this->sql .= " FROM $table_name";
} else {
throw (new Exception("Failed to create delete query, no table name"));
}
if (! is_null($table_joins) && is_array($table_joins) && count($table_joins)) {
$this->sql .= " " . implode(" ", $table_joins);
}
if (! is_null($where) && is_array($where) && count($where)) {
$this->sql .= $this->where($where);
}
if (AUTORUN) {
return $this->execute(MYSQLI_BOTH);
}
return $this->sql;
}
/**
* Function to build a drop table statement (automatically executes)
*
* @param string $schema
* Schema the table resides in
* @param string $table_name
* Table to drop
* @param boolean $is_tmp
* [optional]
* Optional boolean if this is a temporary table
*
* @return string|NULL
*/
public function drop($schema, $table_name, $is_tmp = false)
{
$this->sql = null;
$this->query_type = self::DROP;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "DROP " . ($is_tmp ? "TEMPORARY " : "") . "TABLE IF EXISTS `$schema`.`$table_name`";
}
return $this->execute(MYSQLI_BOTH);
}
/**
* Function to build a truncate table statement (automatically executes)
*
* @param string $table_name
* Table to truncate
*
* @return string|NULL
*/
public function truncate($table_name)
{
$this->sql = null;
$this->query_type = self::TRUNCATE;
if (! is_null($table_name) && is_string($table_name)) {
$this->sql = "TRUNCATE TABLE $table_name";
}
return $this->execute(MYSQLI_BOTH);
}
/**
* Function to build a create temporary table statement
*
* @param string $table_name
* Name to give the table when creating
* @param boolean $is_tmp
* [optional]
* Optional boolean to make the table a temporary table
* @param mixed $select
* [optional]
* Optional parameter if null uses last built statement
* If string, will be made the SQL statement executed to create the table
* If array, 2-dimensional array with "field", "datatype" values to build table fields
*
* @return NULL|string
*/
public function create_table($table_name, $is_tmp = false, $select = null)
{
$this->query_type = self::CREATE_TABLE;
if (is_null($select) && ! is_null($this->sql) && substr($this->sql, 0, 6) == 'SELECT') {
$this->sql = "CREATE " . ($is_tmp ? "TEMPORARY" : "") . " TABLE IF NOT EXISTS $table_name AS ($this->sql)";
}
if (! is_null($table_name) && is_string($table_name) && is_string($select)) {
$this->sql = "CREATE " . ($is_tmp ? "TEMPORARY" : "") . " TABLE IF NOT EXISTS $table_name AS ($select)";
} elseif (! is_null($table_name) && is_string($table_name) && is_array($select)) {
$this->sql = "CREATE " . ($is_tmp ? "TEMPORARY" : "") . " TABLE IF NOT EXISTS $table_name (";
foreach ($select as $field) {
$this->sql .= "{$field['field']} {$field['datatype']}" . (isset($field['default']) ? " {$field['default']}" : '') . (isset($field['option']) ? " {$field['option']}" : '') . ",";
}
$this->sql = substr($this->sql, 0, - 1) . ")";
}
if (AUTORUN) {
return $this->execute();
}
return $this->sql;
}
/**
* Function to create a table using a stdClass object derived from JSON
*
* @param stdClass $json
*/
public function create_table_json($json)
{
$this->query_type = self::CREATE_TABLE;
$this->c->select_db($json->schema);
$this->sql = "CREATE TABLE IF NOT EXISTS `{$json->name}` (";
foreach ($json->fields as $field) {
$this->sql .= "`{$field->name}` {$field->dataType}";
if ($field->dataType == 'enum') {
$this->sql .= "('" . implode("','", $field->values) . "')";
}
if ($field->ai) {
$this->sql .= " AUTO_INCREMENT";
}
if ($field->nn) {
$this->sql .= " NOT NULL";
} else {
if ($field->default === null) {
$this->sql .= " DEFAULT NULL";
} elseif (strlen($field->default)) {
$this->sql .= " DEFAULT '{$field->default}'";
}
}
if ($field != end($json->fields)) {
$this->sql .= ",";
}
}
if (isset($json->index) && is_array($json->index) && count($json->index)) {
foreach ($json->index as $ind) {
$this->sql .= ", " . strtoupper($ind->type) . " `{$ind->id}` (`{$ind->ref}`)";
}
}
if (isset($json->constraints) && is_array($json->constraints) && count($json->constraints)) {
foreach ($json->constraints as $con) {
$this->sql .= ", CONSTRAINT `{$con->id}` " .
"FOREIGN KEY (`{$con->local}`) " .
"REFERENCES `{$con->schema}`.`{$con->table}` (`{$con->field}`) " .
"ON DELETE " . (!isset($con->delete) || is_null($con->delete) ? "NO ACTION" : strtoupper($con->delete)) . " " .
"ON UPDATE " . (!isset($con->update) || is_null($con->update) ? "NO ACTION" : strtoupper($con->update));
}
}
if (isset($json->unique) && is_array($json->unique) && count($json->unique)) {
$this->sql .= ", UNIQUE(`" . implode("`,`", $json->unique) . "`)";
}
if (isset($json->primary_key) && is_array($json->primary_key) && count($json->primary_key)) {
$this->sql .= ", PRIMARY KEY(`" . implode("`,`", $json->primary_key) . "`))";
} else {
$this->sql = substr($this->sql, 0, - 1) . ")";
}
$this->execute();
}
/**
* Function to alter a existing table
*
* @param string $table_name
* Table to alter
* @param string $action
* What action should be taken ('add-column', 'drop-column', 'modify-column')
* @param array $params
* [optional]
* Optional 2-dimensional array of parameters to act on. $action will dictate what parameters need to be present
*
* @return mixed
*/
public function alter_table($table_name, $action, $params)
{
$this->query_type = self::ALTER_TABLE;
$this->sql = "ALTER TABLE $table_name ";
if ($action == 'add-column') {
$nn = ($params->nn ? " NOT NULL" : "");
$default = null;
if ($params->default === null) {
$default = " DEFAULT NULL";
} elseif (strlen($params->default)) {
$default = " DEFAULT '{$params->default}'";
}
$this->sql .= "ADD COLUMN {$params->name} {$params->dataType}" . $nn . $default;
} elseif ($action == 'drop-column') {
$this->sql .= "DROP COLUMN ";
foreach ($params as $col) {
$this->sql .= "{$col['name']},";
}
$this->sql = substr($this->sql, 0, - 1);
} elseif ($action == 'modify-column') {}
$this->debug(E_DEBUG);
return $this->execute();
}
/**
* Check to see if a field in a table exists
*
* @param string $schema
* Schema that contains tables
* @param string $table_name
* Table to check
* @param string $field_name
* Field name to find
*
* @return boolean Returns TRUE if field is found in that schema and table, otherwise FALSE
*/
public function field_exists($schema, $table_name, $field_name)
{
$this->c->select_db($schema);
$fdata = $this->field_data($schema, $table_name);
foreach ($fdata as $field) {
if ($field->name == $field_name) {
return true;
}
}
return false;
}
/**
* Function to get the column data (datatype, flags, defaults, etc)
*
* @param string $schema
* Schema to search for table in
* @param string $table_name
* Table to query
* @param mixed $field
* [optional]
* Optional field to retrieve data (if null, returns data from all fields)
*
* @return array
*/
public function field_data($schema, $table_name, $field = null)
{
$this->c->select_db($schema);
if (is_null($field)) {
$res = $this->c->query("SELECT * FROM $table_name LIMIT 1");
} elseif (is_array($field)) {
$res = $this->c->query("SELECT `" . implode("`,`", $field) . "` FROM $table_name LIMIT 1");
} elseif (is_string($field)) {
$res = $this->c->query("SELECT $field FROM $table_name LIMIT 1");
} else {
return null;
}
$fields = null;
if (is_a($res, 'mysqli_result')) {
$fields = $res->fetch_fields();
foreach ($fields as $i => $f) {
$fields["{$f->name}"] = $f;
unset($fields[$i]);
}
}
return $fields;
}
/**
* Function to check that all field parameters are set correctly
*
* @param object $field_data
* @param object $check
* @param object $pks
* @param object $index
*
* @return array
*/
public function field_check($field_data, $check, $pks, $index)
{
$default = null;
$ret = null;
$nn = ($check->nn ? " NOT NULL" : null);
if ($check->default === null) {
$default = " DEFAULT NULL";
} elseif (strlen($check->default)) {
$default = " DEFAULT '{$check->default}'";
}
if ($field_data->type != $check->type && $check->type != MYSQLI_TYPE_ENUM) {
$this->debug("{$field_data->name} wrong datatype, changing to {$check->dataType}");
$ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}" . "{$nn}{$default}";
} elseif (! is_null($check->length) && $field_data->length != $check->length) {
$this->debug("{$field_data->name} incorrect size ({$field_data->length} != {$check->length})");
$ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}" . "{$nn}{$default}";
} elseif ($check->type == MYSQLI_TYPE_ENUM && ! ($field_data->flags & MYSQLI_ENUM_FLAG)) {
$ret = " CHANGE COLUMN `{$field_data->name}` `{$check->name}` {$check->dataType}('" . implode("','", $check->values) . "')" . "{$nn}{$default}";
}
if (! is_null($index) && is_array($index) && count($index)) {
foreach ($index as $ind) {
if ($check->name == $ind->ref && ! ($field_data->flags & MYSQLI_MULTIPLE_KEY_FLAG)) {
$this->debug("{$field_data->name} is not an index");
$ret .= ($ret ? "," : "") . " ADD INDEX `{$ind->id}` (`{$ind->ref}` ASC)";
}
}
}
if (in_array($check->name, $pks) && ! ($field_data->flags & MYSQLI_PRI_KEY_FLAG)) {
$ret .= ($ret ? "," : "") . " DROP PRIMARY KEY, ADD PRIMARY KEY(`" . implode("`,`", $pks) . "`)";
}
return $ret;
}
/**
* Function to check for the existence of a table within a schema
*
* @param string $schema
* Schema to search for table
* @param string $table_name
* Table to search for
*
* @return boolean Returns TRUE if table is found in that schema, otherwise FALSE
*/
public function table_exists($schema, $table_name)
{
$this->c->select_db($schema);
$res = $this->c->query("SHOW TABLES LIKE '$table_name'");
if ($res->num_rows > 0) {
return true;
}
return false;
}
/**
* Function to detect if string is a JSON object or not
*
* @param string $val
*
* @return boolean
*/
public function isJson($val)
{
json_decode($val);
return (json_last_error() == JSON_ERROR_NONE);
}
/**
* Function to escape SQL characters to prevent SQL injection
*
* @param mixed $val
* Value to escape
*
* @return string Escaped value
*/
public function _escape($val)
{
if (is_null($val)) {
return 'NULL';
} elseif (is_numeric($val) || is_string($val)) {
if ($this->isJson($val)) {
return "'{$this->c->real_escape_string($val)}'";
} elseif (strtolower($val) == 'now()') {
return $val;
} elseif (preg_match("/\.`\w+`/", $val)) {
return $val;
}
return "'{$this->c->real_escape_string($val)}'";
} elseif (is_a($val, 'DateTime')) {
return "'{$val->format(MYSQL_DT_FORMAT)}'";
} elseif (is_bool($val)) {
return $val ? "'1'" : "'0'";
} elseif (gettype($val) == 'object') {
$this->debug(E_ERROR, "Unknown object to escape " . get_class($val) . " in SQL string {$this->sql}");
} else {
$this->debug(E_ERROR, "Unknown datatype to escape in SQL string {$this->sql} " . gettype($val));
}
throw (new Exception("Unknown datatype to escape in SQL string {$this->sql} " . gettype($val), E_ERROR));
}
/**
* Function to retrieve all results
*
* @param string $resulttype
*
* @return mixed
*/
public function fetch_all($resulttype = MYSQLI_ASSOC)
{
$res = [];
if (method_exists('mysqli_result', 'fetch_all')) { // Compatibility layer with PHP < 5.3
$res = $this->result->fetch_all($resulttype);
} else {
while ($tmp = $this->result->fetch_array($resulttype)) {
$res[] = $tmp;
}
}
return $res;
}
/**
* Function to debug the class
*
* @param int $errno
* @param string $errmsg
*/
public function debug($errno = E_NOTICE, $errmsg = null)
{
check_path(LOG_PATH . "/db.log");
check_path(LOG_PATH . "/db.err");
check_path(LOG_PATH . "/db.debug");
$err_lvl = 'NOTICE';
switch ($errno) {
case E_DEBUG:
$err_lvl = 'DEBUG';
break;
case E_WARNING:
$err_lvl = 'WARNING';
break;
case E_ERROR:
$err_lvl = 'FATAL ERROR';
break;
}
$dt = new DateTime();
if (is_null($errmsg)) {
$errmsg = $this->sql;
}
file_put_contents(realpath(LOG_PATH . '/db.log'), "{$dt->format(DATE_ISO8601)}\t" . "$err_lvl\t" . "Executing: $this->query_type\t" . "SQL: {$errmsg}" . PHP_EOL, FILE_APPEND);
if ($errno == E_DEBUG && $this->result && LOG_LEVEL == E_DEBUG && is_a($this->result, 'mysqli_result')) {
file_put_contents(realpath(LOG_PATH . '/db.debug'), print_r($this->result, true), FILE_APPEND);
} elseif ($errno == E_ERROR && $this->c->error) {
file_put_contents(realpath(LOG_PATH . '/db.err'), "{$dt->format(DATE_ISO8601)}\t" . "{$this->c->error}" . PHP_EOL, FILE_APPEND);
error_log($this->c->error);
die($this->c->error);
}
}
/**
* Function to populate the fields for the SQL
*
* @param array $fields
* [optional]
* Optional array of fields to string together to create a field list
*
* @return string
*/
public function fields($fields = null)
{
$str_fields = null;
if (is_array($fields) && count($fields)) {
foreach ($fields as $field) {
if ((strpos($field, '`') === false) && (strpos($field, '.') === false) && (strpos($field, '*') === false) && (stripos($field, ' as ') === false)) {
$str_fields .= "`$field`,";
} else {
$str_fields .= "$field,";
}
}
$str_fields = substr($str_fields, 0, - 1);
} elseif (is_null($fields)) {
$str_fields = "*";
}
return $str_fields;
}
/**
* Function to create the where statement for the SQL
*
* @param array $where
* Two-dimensional array to use to build the where clause
*
* <code>
* array(<br />
* &nbsp;&nbsp;array(<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'field' => 'field_name',<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'op' => '=', // (common operations or IN, BETWEEN, LIKE, NOT_LIKE, IS, & IS_NOT constants)<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'value' => 'field_value',<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'sql_op' => 'AND', // NOT required for first element (common SQL operators AND, OR, NOR)<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'open-paren' => true, // optional to add a paren '(' BEFORE clause<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'close-paren' => true, // optional to add a paren ')' AFTER clause<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'low' => '1', // LOW value only used in BETWEEN clause<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'high' => '100', // HIGH value only used in BETWEEN clause<br />
* &nbsp;&nbsp;&nbsp;&nbsp;'case_insensitive' => true // optional boolean to set the parameters to LOWER to do case insenstive comparison
* &nbsp;&nbsp;),<br />
* &nbsp;&nbsp;array(<br />
* &nbsp;&nbsp;&nbsp;&nbsp;...<br />
* &nbsp;&nbsp;),<br />
* &nbsp;&nbsp;...<br />
* )
* </code>
*
* @return string
*/
public function where($where)
{
$ret = " WHERE";
foreach ($where as $x => $w) {
if (! isset($w['field']) && isset($w['close-paren']) && $w['close-paren']) {
$ret .= ")";
continue;
} elseif (! isset($w['field']) || ($x > 0 && ! isset($w['sql_op']))) {
continue;
}
if ($x > 0) {
$ret .= " {$w['sql_op']}";
}
if (isset($w['open-paren']) && $w['open-paren']) {
$ret .= " (";
}
if ((strpos($w['field'], '`') === false) && (strpos($w['field'], '.') === false) && (strpos($w['field'], '*') === false) && (stripos($w['field'], ' as ') === false)) {
$field = "`{$w['field']}`";
} else {
$field = $w['field'];
}
$not = null;
if (isset($w['op']) && in_array($w['op'], array(
IS_NOT,
NOT_LIKE,
NOT_IN
))) {
$not = ' NOT';
}
if (isset($w['op']) && ($w['op'] == LIKE || $w['op'] == NOT_LIKE)) {
$ret .= " {$field}{$not} LIKE {$w['value']}";
} elseif (isset($w['op']) && ($w['op'] == IN || $w['op'] == NOT_IN) && is_string($w['value'])) {
$ret .= " {$field}{$not} IN " . (strpos($w['value'], '(') !== false ? $w['value'] : "({$w['value']})");
} elseif (isset($w['op']) && ($w['op'] == IN || $w['op'] == NOT_IN) && is_array($w['value'])) {
$ret .= " {$field}{$not} IN (" . implode(",", array_map(array(
$this,
'_escape'
), $w['value'])) . ")";
} elseif (isset($w['op']) && $w['op'] == BETWEEN) {
if (! isset($w['low']) && ! isset($w['high'])) {
continue;
}
$ret .= " {$field} BETWEEN {$this->_escape($w['low'])} AND {$this->_escape($w['high'])}";
} elseif (isset($w['op']) && ($w['op'] == IS || $w['op'] == IS_NOT)) {
$ret .= " {$field} IS{$not} {$this->_escape($w['value'])}";
} else {
$op = "=";
if (isset($w['op'])) {
$op = $w['op'];
}
if (isset($w['case_insensitive']) && $w['case_insensitive']) {
$ret .= " LOWER({$field}) {$op} LOWER({$this->_escape($w['value'])})";
} elseif (preg_match("/\(SELECT/", $w['value'])) {
$ret .= " {$field} {$op} {$w['value']}";
} else {
$ret .= " {$field} {$op} {$this->_escape($w['value'])}";
}
}
if (isset($w['close-paren']) && $w['close-paren']) {
$ret .= ")";
}
}
if (strlen($ret) == 7) {
$ret = '';
}
return $ret;
}
/**
* Function to parse the flags
*
* @param array $flags
* Two-dimensional array to added flags
*
* <code>
* array(
* &nbsp;&nbsp;'table_joins' => array(
* &nbsp;&nbsp;&nbsp;&nbsp;"JOIN table2 t2 ON t2.id=t1.id"
* &nbsp;&nbsp;),
* &nbsp;&nbsp;'group' => 'field',
* &nbsp;&nbsp;'having' => 'field',
* &nbsp;&nbsp;'order' => 'field',
* &nbsp;&nbsp;'start' => 0,
* &nbsp;&nbsp;'limit' => 0
* )
* </code>
*
* @see db_helper::groups()
* @see db_helper::having()
* @see db_helper::order()
*
* @return string
*/
public function flags($flags)
{
$ret = '';
if (isset($flags['group'])) {
$ret .= $this->groups($flags['group']);
}
if (isset($flags['having']) && is_array($flags['having'])) {
$ret .= $this->having($flags['having']);
}
if (isset($flags['order'])) {
$ret .= $this->order($flags['order']);
}
if (isset($flags['limit']) && (is_string($flags['limit']) || is_numeric($flags['limit']))) {
$ret .= " LIMIT ";
if (isset($flags['start']) && (is_string($flags['start']) || is_numeric($flags['start']))) {
$ret .= "{$flags['start']},";
}
$ret .= "{$flags['limit']}";
}
return $ret;
}
/**
* Function to parse SQL GROUP BY statements
*
* @param mixed $groups
*
* @return string
*/
private function groups($groups)
{
$ret = '';
if (is_array($groups)) {
$ret .= " GROUP BY";
foreach ($groups as $grp) {
$ret .= " $grp";
}
} elseif (is_string($groups)) {
$ret .= " GROUP BY {$groups}";
}
return $ret;
}
/**
* Function to parse SQL HAVING statements
*
* @param mixed $having
* return string
*/
private function having($having)
{
$ret = " HAVING";
$x = 0;
foreach ($having as $h) {
if (! isset($h['field']) || ($x > 0 && ! isset($h['sql_op']))) {
continue;
}
if ($x > 0) {
$ret .= " {$h['sql_op']} ";
}
if ($h['op'] == LIKE) {
$ret .= " {$h['field']} LIKE {$h['value']}";
} elseif ($h['op'] == IN && is_string($h['value'])) {
$ret .= " {$h['field']} IN {$h['value']}";
} elseif ($h['op'] == IN && is_array($h['value'])) {
$ret .= " {$h['field']} IN ('" . implode("', '", $h['value']) . "')";
} elseif ($h['op'] == BETWEEN) {
$ret .= " {$h['field']} BETWEEN {$this->_escape($h['low'])} AND {$this->_escape($h['high'])}";
} elseif ($h['op'] == IS) {
$ret .= " {$h['field']} IS {$this->_escape($h['value'])}";
} elseif ($h['op'] == IS_NOT) {
$ret .= " {$h['field']} IS NOT {$this->_escape($h['value'])}";
} else {
$ret .= " {$h['field']} {$h['op']} {$this->_escape($h['value'])}";
}
$x ++;
}
return $ret;
}
/**
* Function to parse SQL ORDER BY statements
*
* @param mixed $order
*
* @return string
*/
private function order($order)
{
$ret = '';
if (is_array($order)) {
$ret .= " ORDER BY";
foreach ($order as $ord) {
$ret .= " {$ord['field']} {$ord['sort']},";
}
$ret = substr($ret, 0, - 1);
} elseif (is_string($order)) {
$ret .= " ORDER BY {$order}";
}
return $ret;
}
/**
*
* @return boolean
*/
public static function run()
{
$args = func_get_args();
$conn = null;
if (is_array($args) && count($args) < 2) {
return;
}
if (! is_a($args[0], "mysqli")) {
return;
} else {
$conn = $args[0];
}
if (! is_string($args[1])) {
return;
} else {
$sql = $args[1];
}
if (strpos($sql, "?") !== false) {
$x = 2;
while (strpos($sql, "?") !== false) {
$sql = str_replace_first("?", "'" . $conn->real_escape_string($args[$x]) . "'", $sql);
}
}
if (! $stmt = $conn->prepare($sql)) {
print $conn->error . PHP_EOL;
return;
}
if (! $stmt->execute()) {
print "Execution of prepared statement failed: (" . $stmt->errno . ") " . $stmt->error . PHP_EOL;
return false;
}
return true;
}
/**
* Function
*
* @return void|mixed
*/
public static function selectrow_array()
{
$args = func_get_args();
$conn = null;
$ret = [];
if (is_array($args) && count($args) < 2) {
return;
}
if (! is_a($args[0], "mysqli")) {
return;
} else {
$conn = $args[0];
}
if (! is_string($args[1])) {
return;
} else {
$sql = $args[1];
}
if (strpos($sql, "?") !== false) {
$x = 2;
while (strpos($sql, "?") !== false) {
$sql = str_replace_first("?", "'" . $conn->real_escape_string($args[$x]) . "'", $sql);
}
}
if (! $stmt = $conn->prepare($sql)) {
print $conn->error . PHP_EOL;
return;
}
if (! $stmt->execute()) {
print "Execution of prepared statement failed: (" . $stmt->errno . ") " . $stmt->error . PHP_EOL;
return;
}
$meta = $stmt->result_metadata();
$fields = $fieldNames = [];
while ($field = $meta->fetch_field()) {
$fieldNames[] = $var = $field->name;
$$var = null;
$fields[$var] = &$$var;
}
$fieldCount = (is_array($fieldNames) ? count($fieldNames) : 0);
call_user_func_array(array(
$stmt,
"bind_result"
), $fields);
$i = 0;
while ($stmt->fetch()) {
for ($r = 0; $r < $fieldCount; $r ++) {
$ret[$i][$fieldNames[$r]] = $fields[$fieldNames[$r]];
}
}
if (is_array($ret) && count($ret) == 1) {
return $ret[0];
} else {
return $ret;
}
}
/**
*
* @return string
*/
public static function mysql_escape_string()
{
$args = func_get_args();
$conn = null;
$sql = '';
if (is_array($args) && count($args) < 3) {
return;
}
if (! is_a($args[0], "mysqli")) {
return;
} else {
$conn = $args[0];
}
if (! is_string($args[1])) {
return;
} else {
$sql = $args[1];
}
if (strpos($sql, "?") !== false) {
$x = 2;
while (strpos($sql, "?") !== false) {
$sql = str_replace_first("?", "'" . $conn->real_escape_string($args[$x]) . "'", $sql);
}
}
return $sql;
}
public function is_constraint($con_id)
{
$res = $this->c->query("SELECT * FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_NAME = '$con_id'");
if ($res->num_rows) {
return true;
}
return false;
}
}
/**
* This file will contain all the variables and functions to operate a database
* and for the ST&E Manager to interact with
*
* @author Ryan Prather
*/
class db
{
/**
* Array of words to be removed
*/
private $DISALLOWED = array(
'the',
'be',
'to',
'of',
'and',
'a',
'in',
'that',
'have',
'I',
'it',
'for',
'not',
'on',
'with',
'he',
'as',
'you',
'do',
'at',
'this',
'but',
'his',
'by',
'from',
'they',
'we',
'say',
'her',
'she',
'or',
'an',
'will',
'my',
'one',
'all',
'would',
'there',
'their',
'what',
'so',
'up',
'out',
'if',
'about',
'who',
'get',
'which',
'go',
'me',
'when',
'make',
'can',
'like',
'time',
'no',
'just',
'him',
'know',
'take',
'people',
'into',
'year',
'your',
'good',
'some',
'could',
'them',
'see',
'other',
'than',
'then',
'now',
'look',
'only',
'come',
'its',
'over',
'think',
'also',
'back',
'after',
'use',
'two',
'how',
'our',
'work',
'first',
'well',
'way',
'even',
'new',
'want',
'because',
'any',
'these',
'give',
'day',
'most',
'us'
);
/**
* The database connection
*
* @var mysqli
* @access protected
*/
protected $conn = null;
/**
* A private database helper
*
* @var db_helper
* @access public
*/
public $help = null;
/**
* Constant for the first column where target finding statuses start
*
* @var integer
*/
const FIRST_ECHECKLIST_HOST_COL = 5;
/**
* Constructor function to instantiate a new DB object and connection
*
* @param bool $persistent
* [optional]
*/
public function __construct($persistent = false)
{
// attempt to create a new database connection
$host = ($persistent ? "p:" : "") . DB_SERVER;
if (class_exists('mysqli')) {
$pwd = self::decrypt_pwd();
$this->conn = new mysqli($host, 'web', $pwd, 'sagacity');
} else {
die("Could not find the mysqli class");
}
// if there is a problem output that
if ($this->conn->connect_errno && $this->conn->connect_errno == 1045) {
die("Invalid database username and/or password");
} elseif ($this->conn->connect_errno) {
error_log("Error connecting to " . DB_SERVER . " " . $this->conn->connect_error);
die("Error connecting to " . DB_SERVER);
}
// set the character set and default database
$this->conn->set_charset("utf8");
$this->conn->real_query("SET sql_mode=''");
$this->help = new db_helper($this->conn);
}
/**
* Function to decrypt the password
*
* @return string
*/
public static function decrypt_pwd()
{
if (! file_exists(DOC_ROOT . "/" . PWD_FILE)) {
die("Cannot connect to the database because the password file does not exist");
}
$enc_pwd = file_get_contents(DOC_ROOT . "/" . PWD_FILE);
$pwd = my_decrypt($enc_pwd);
return $pwd;
}
/**
* Get the ID of the last command that was executed
*
* @return integer The integer of the last primary key id inserted into whatever table
*/
public function get_Last_Insert_ID()
{
return $this->conn->insert_id;
}
// {{{ ADVISORY CLASS FUNCTIONS
/**
* Function to get an advisory from the database
*
* @param string $advisory_id
* [optional]
* String with advisory ID to specifically find
*
* @return array:advisory|NULL Returns array of advisory objects or NULL if nothing is found in the database
*/
public function get_Advisory($advisory_id = null)
{
$ret = [];
if (! is_null($advisory_id)) {
$this->help->select("sagacity.advisories", null, array(
array(
'field' => 'advisory_id',
'op' => '=',
'value' => $advisory_id
)
));
} else {
$this->help->select("sagacity.advisories", null, []);
}
if ($ref = $this->help->execute()) {
foreach ($ref as $row) {
$ret[] = new advisory($row['pdi_id'], $row['advisory_id'], $row['reference'], $row['type'], $row['url']);
}
return $ret;
} else {
Sagacity_Error::sql_handler($this->help->sql);
$this->help->debug(E_ERROR);
}
return null;
}
/**
* Update or insert an advisory
*
* @param array:advisory $advisories
* Array of advisory class objects to save/update to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Advisory($advisories)
{
$values = [];
$fields = [
'pdi_id',
'advisory_id',
'type',
'reference',
'url',
'title',
'impact'
];
foreach ($advisories as $adv) {
$values[] = [
$adv->get_PDI_ID(),
$adv->get_Advisory_ID(),
$adv->get_Type(),
$adv->get_Ref_Text(),
$adv->get_URL(),
$adv->get_Title(),
$adv->get_Impact()
];
}
$this->help->extended_replace("sagacity.advisories", $fields, $values);
if (! $this->help->execute()) {
Sagacity_Error::sql_handler($this->help->sql);
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ CATEGORY CLASS FUNCTIONS
/**
* Get ST&E category data
*
* @param integer $int_Cat_ID
* [optional]
* Grab specific ste_cat from database (default NULL)
*
* @return array:ste_cat|NULL Returns an array of categories that are applicable to the specific ST&E or a specifically requested category
*/
public function get_Category($int_Cat_ID = null)
{
$where = [];
$ret = [];
if ($int_Cat_ID != null) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $int_Cat_ID
];
}
$this->help->select("ste_cat", null, $where);
$cats = $this->help->execute();
if (is_array($cats) && count($cats) && isset($cats['id'])) {
$cats = [
0 => $cats
];
}
if (is_array($cats) && count($cats)) {
foreach ($cats as $cat) {
$tmp = new ste_cat($cat['id'], $cat['ste_id'], $cat['name'], $cat['analysts']);
$this->help->select("ste_cat_sources", [
'src_id'
], [
[
'field' => 'cat_id',
'op' => '=',
'value' => $cat['id']
]
]);
$srcs = $this->help->execute();
if (is_array($srcs) && count($srcs) && isset($srcs['src_id'])) {
$srcs = [
0 => $srcs
];
}
if (is_array($srcs) && count($srcs)) {
foreach ($srcs as $src) {
$tmp->add_Source($this->get_Sources($src['src_id']));
}
}
$this->get_Cat_Count($tmp);
$ret[] = $tmp;
}
}
return $ret;
}
/**
* Function to automatically put targets in categories by operating systems<br />
* Skips generic OS's and targets that already assigned
*
* @param int $ste_id
*/
public function auto_Catorgize_Targets($ste_id)
{
$this->help->select("sagacity.target t", [
't.id',
't.os_string'
], [
[
'field' => 't.ste_id',
'value' => $ste_id
],
[
'field' => 't.cat_id',
'op' => IS,
'value' => null,
'sql_op' => 'AND'
],
[
'field' => 's.cpe',
'op' => '!=',
'value' => 'cpe:/o:generic:generic:-',
'sql_op' => 'AND'
]
], [
'table_joins' => [
'JOIN sagacity.software s ON t.os_id=s.id'
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$id = 0;
$this->help->select("sagacity.ste_cat", [
'id'
], [
[
'field' => 'ste_id',
'value' => $ste_id
],
[
'field' => 'name',
'value' => trim($row['os_string']),
'sql_op' => 'AND'
]
]);
$tmp = $this->help->execute();
if (is_array($tmp) && count($tmp) && isset($tmp['id'])) {
$id = $tmp['id'];
} else {
$this->help->insert("sagacity.ste_cat", [
'ste_id' => $ste_id,
'name' => trim($row['os_string'])
], true);
$id = $this->help->execute();
}
if ($id) {
$this->help->update("sagacity.target", [
'cat_id' => $id
], [
[
'field' => 'id',
'value' => $row['id']
]
]);
$this->help->execute();
}
}
}
}
/**
* Function to save categories
*
* @param ste_cat $ste_cat_in
*
* @return mixed Returns FALSE if failed, otherwise the ID of the newly inserted category
*/
public function save_Category($ste_cat_in)
{
if (is_null($ste_cat_in->get_ID())) {
$this->help->insert("sagacity.ste_cat", array(
'ste_id' => $ste_cat_in->get_STE_ID(),
'name' => $ste_cat_in->get_Name(),
'analysts' => $ste_cat_in->get_Analyst()
));
if (! ($cat_id = $this->help->execute())) {
$this->help->debug(E_ERROR);
return false;
}
$ste_cat_in->set_ID($cat_id);
} else {
$this->help->update("sagacity.ste_cat", array(
'name' => $ste_cat_in->get_Name(),
'analysts' => $ste_cat_in->get_Analyst()
), array(
array(
'field' => 'id',
'op' => '=',
'value' => $ste_cat_in->get_ID()
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
}
if (is_array($ste_cat_in->get_Sources()) && count($ste_cat_in->get_Sources())) {
$this->help->delete("ste_cat_sources", null, [
[
'field' => 'cat_id',
'op' => '=',
'value' => $ste_cat_in->get_ID()
]
]);
$this->help->execute();
$srcs = [];
foreach ($ste_cat_in->get_Sources() as $src) {
$srcs[] = [
$ste_cat_in->get_ID(),
$src->get_ID()
];
}
$this->help->extended_insert("ste_cat_sources", [
'cat_id',
'src_id'
], $srcs);
$this->help->execute();
}
return $ste_cat_in->get_ID();
}
/**
* This function renames a category
*
* @param integer $intOldCat
* Category ID of the category to rename
* @param string $strNewCatName
* New name for the category
*
* @return boolean Return TRUE if successful, otherwise FALSE
*/
public function rename_Cat($intOldCat, $strNewCatName)
{
$this->help->update("sagacity.ste_cat", array(
'name' => $strNewCatName
), array(
array(
'field' => 'id',
'op' => '=',
'value' => $intOldCat
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* This function deletes a category and assigns the targets to "Unassigned"
*
* @param integer $intCat
* ID of the category to delete
*
* @return boolean Return TRUE if successful, otherwise FALSE
*/
public function delete_Cat($intCat)
{
$this->help->update("sagacity.target", array(
'cat_id' => null
), array(
array(
'field' => 'cat_id',
'op' => '=',
'value' => $intCat
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("sagacity.ste_cat_sources", null, array(
array(
'field' => 'cat_id',
'op' => '=',
'value' => $intCat
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("sagacity.category_interview", null, array(
array(
'field' => 'cat_id',
'op' => '=',
'value' => $intCat
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("sagacity.ste_cat", null, array(
array(
'field' => 'id',
'op' => '=',
'value' => $intCat
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* This function sets the analyst that is in charge of this category
*
* @param integer $intCat
* Category ID to update
* @param string $strAnalyst
* Name of the analyst
*
* @return boolean Return TRUE if successful, otherwise FALSE
*/
public function assign_Analyst_To_Category($intCat, $strAnalyst)
{
$analysts = strtolower($strAnalyst) == 'none' ? null : $strAnalyst;
$this->help->update("sagacity.ste_cat", array(
'analysts' => $analysts
), array(
array(
'field' => 'id',
'op' => '=',
'value' => $intCat
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* This function does the move of a tgt to a new category
*
* @param array:integer $arrTgts
* Array of integer ID for each target to move
* @param integer $intCat
* Category ID to reassign them to
*
* @return boolean Return TRUE if successful, otherwise FALSE
*/
public function move_Tgt_To_Cat($arrTgts, $intCat)
{
$this->help->update("sagacity.target", array(
'cat_id' => $intCat
), array(
array(
'field' => 'id',
'op' => IN,
'value' => $arrTgts
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ CCE CLASS FUNCTIONS
/**
* Getter function for CCE
*
* @param string $cce_id
* CCE ID to query for
*
* @return array:cce
*/
public function get_CCE($cce_id = null)
{
$ret = [];
$where = [];
if (! is_null($cce_id)) {
$where[] = array(
'field' => 'cce_id',
'op' => '=',
'value' => $cce_id
);
}
$this->help->select("sagacity.cce", array(
'pdi_id',
'cce_id'
), $where);
$cces = $this->help->execute();
if (is_array($cces) && count($cces) && isset($cces['pdi_id'])) {
$cces = array(
0 => $cces
);
}
if (is_array($cces) && count($cces) && isset($cces[0])) {
foreach ($cces as $cce) {
$ret[] = new cce($cce['pdi_id'], $cce['cce_id']);
}
}
return $ret;
}
/**
* Function to save CCE's to database
*
* @param array:cce|cce $cces
* An array of CCE's that need to be saved
*
* @return boolean Returns TRUE if save was successful, otherwise FALSE
*/
public function save_CCE($cces)
{
$ret = true;
$fields = array(
'pdi_id',
'cce_id'
);
$params = [];
if (is_array($cces)) {
foreach ($cces as $cce) {
$params[] = [
$cce->get_PDI_ID(),
$cce->get_CCE_ID()
];
}
$this->help->extended_replace("sagacity.cce", $fields, $params);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
$ret = false;
}
} else {
$this->help->replace("sagacity.cce", array(
'pdi_id' => $cces->get_PDI_ID(),
'cce_id' => $cces->get_CCE_ID()
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
$ret = false;
}
}
return $ret;
}
// }}}
// {{{ CCI CLASS FUNCTIONS
public function get_CCI($cci_id = null)
{
$ret = [];
$this->help->select("sagacity.cci");
if (! is_null($cci_id)) {
$this->help->select("sagacity.cci", null, array(
array(
'field' => 'cci_id',
'op' => '=',
'value' => $cci_id
)
));
}
$ccis = $this->help->execute();
if (is_array($ccis) && count($ccis)) {
foreach ($ccis as $cci_data) {
$cci = new cci();
$cci->cci_id = $cci_data['cci_id'];
$cci->definition = $cci_data['definition'];
$cci->type = $cci_data['type'];
$cci->param = $cci_data['param'];
$cci->note = $cci_data['note'];
$this->help->select("sagacity.cci_refs", null, array(
array(
'field' => 'cci_id',
'op' => '=',
'value' => $cci_data['cci_id']
)
));
$refs = $this->help->execute();
if (is_array($refs) && count($refs)) {
foreach ($refs as $ref_data) {
$ref = new cci_reference();
$ref->index = $ref_data['index'];
$ref->url = $ref_data['url'];
$ref->title = $ref_data['title'];
$ref->ver = $ref_data['ver'];
$cci->refs[] = $ref;
}
}
$ret[] = $cci;
}
} else {
$this->help->debug(E_ERROR);
}
return $ret;
}
/**
* Get eMASS CCI Map
*
* @author Matt Shuter
*
* @return array Array of CCI-eMASS control mappings
*/
public function get_EMASS_CCIs()
{
$this->help->select("rmf.emass_cci");
$ret = $this->help->execute();
return $ret;
}
/**
* Function to save a CCI
*
* @param array:cci|cci $cci_in
*
* @return boolean
*/
public function save_CCI($cci_in)
{
if (is_array($cci_in)) {
$ccis = [];
foreach ($cci_in as $cci) {
$cci_id = preg_replace("/CCI\-[0]+/", "CCI-", $cci->cci_id);
$ccis[] = [
$cci_id,
$cci->control_id,
$cci->enh_id,
$cci->definition,
$cci->guidance,
$cci->procedure
];
}
$this->help->extended_insert('rmf.cci', array(
'id',
'control_id',
'enh_id',
'def',
'guidance',
'procedure'
), $ccis, true);
} else {
$cci_id = preg_replace("/CCI\-[0]+/", "CCI-", $cci_in->cci_id);
$this->help->insert('rmf.cci', array(
'cci_id' => $cci_id,
'control_id' => $cci_in->control_id,
'enh_id' => $cci_in->enh_id,
'def' => $cci_in->definition,
'guidance' => $cci_in->guidance,
'procedure' => $cci_in->procedure
), true);
}
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to save an array of eMASS-CCI mappings
*
* @param array:cci $cci_in
*
* @return boolean
*/
public function save_EMASS_CCIs($ccis_in)
{
$ret = false;
$columns = array(
'id',
'control'
);
$this->help->extended_insert('rmf.emass_cci', $columns, $ccis_in, true);
if ($this->help->execute()) {
$ret = true;
} else {
$this->help->debug(E_ERROR);
}
return $ret;
}
// }}}
// {{{ CHECKLIST CLASS FUNCTIONS
/**
* Get a checklist
*
* @param mixed $Checklist_ID
* [optional]
* Checklist ID to query for (default NULL)
* @param boolean $ord_desc
* [optional]
* Decide if you want to order to return from newest release
*
* @return array:checklist Returns an array of checklists, or an empty array if none found
*/
public function get_Checklist($Checklist_ID = null, $ord_desc = false)
{
$ret = [];
if ($ord_desc) {
$this->help->select("sagacity.checklist", [
'id',
'checklist_id',
'name',
'description',
'date',
'file_name',
'ver',
"MAX(LPAD(`release`, 0, 2)) AS 'release'",
'type',
'icon'
]);
} else {
$this->help->select("sagacity.checklist", null);
}
$where = [];
if (! is_null($Checklist_ID)) {
if (is_numeric($Checklist_ID)) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $Checklist_ID
];
} elseif (is_array($Checklist_ID)) {
if (isset($Checklist_ID['checklist_id'])) {
$where[] = [
'field' => 'checklist_id',
'op' => '=',
'value' => $Checklist_ID['checklist_id']
];
}
if (isset($Checklist_ID['type'])) {
$where[] = [
'field' => 'type',
'op' => '=',
'value' => $Checklist_ID['type'],
'sql_op' => 'AND'
];
}
if (isset($Checklist_ID['version'])) {
$where[] = [
'field' => 'ver',
'op' => '=',
'value' => $Checklist_ID['version'],
'sql_op' => 'AND'
];
}
if (isset($Checklist_ID['release'])) {
$where[] = [
'field' => 'release',
'op' => '=',
'value' => $Checklist_ID['release'],
'sql_op' => 'AND'
];
}
} else {
$where[] = [
'field' => 'checklist_id',
'op' => '=',
'value' => $Checklist_ID
];
}
}
if (is_array($where) && count($where)) {
$this->help->sql .= $this->help->where($where);
}
$flags = [];
if ($ord_desc) {
$flags = [
'group' => 'type',
'order' => [
[
'field' => 'name',
'sort' => 'asc'
],
[
'field' => 'type',
'sort' => 'asc'
],
[
'field' => 'ver',
'sort' => 'desc'
],
[
'field' => 'LPAD(`release`,2,0)',
'sort' => 'desc'
]
]
];
} else {
$flags = [
'order' => [
[
'field' => 'name',
'sort' => 'asc'
],
[
'field' => 'type',
'sort' => 'asc'
],
[
'field' => 'ver',
'sort' => 'asc'
],
[
'field' => 'LPAD(`release`,2,0)',
'sort' => 'asc'
]
]
];
}
if (is_array($flags) && count($flags)) {
$this->help->sql .= $this->help->flags($flags);
}
$rows = $this->help->execute();
if (isset($rows['id'])) {
$rows = array(
0 => $rows
);
}
if (is_array($rows) && count($rows)) {
foreach ($rows as $row) {
$chk = new checklist($row['id'], $row['checklist_id'], $row['name'], $row['description'], $row['date'], $row['file_name'], $row['ver'], $row['release'], $row['type'], $row['icon']);
/*
* $this->help->select("sagacity.checklist_software_lookup", array('sw_id'), array(
* array(
* 'field' => 'chk_id',
* 'op' => '=',
* 'value' => $row['id']
* )
* ));
* $sw_rows = $this->help->execute();
* if (count($sw_rows)) {
* if (isset($sw_rows['sw_id'])) {
* $sw_rows = array(0 => $sw_rows);
* }
*
* foreach ($sw_rows as $row2) {
* $chk->add_SW($this->get_Software($row2['sw_id']));
* }
* }
*/
$ret[] = $chk;
}
}
return $ret;
}
/**
* Function to get the checklists by the checklist name in an eChecklist file
*
* @param array:string $chk_arr
*
* @return array:checklist
*/
public function get_Checklist_By_Name($chk_arr)
{
$ret = [];
$this->help->select("checklist", null, [
[
'field' => "CONCAT(`name`, ' V', `ver`, 'R', `release`, ' (', `type`, ')')",
'op' => IN,
'value' => $chk_arr,
'backticks' => false
]
]);
$rows = $this->help->execute();
if(is_array($rows) && count($rows) && isset($rows[0])) {
foreach($rows as $row) {
$ret[] = new checklist($row['id'], $row['checklist_id'], $row['name'], $row['description'], new DateTime($row['date']), $row['file_name'], $row['ver'], $row['release'], $row['type'], $row['icon']);
}
} elseif(is_array($rows) && count($rows) && isset($rows['id'])) {
$ret = new checklist($rows['id'], $rows['checklist_id'], $rows['name'], $rows['description'], new DateTime($rows['date']), $rows['file_name'], $rows['ver'], $rows['release'], $rows['type'], $rows['icon']);
}
return $ret;
}
/**
* Function to get the checklist based on the checklist filename
*
* @param string $fname
*
* @return mixed
*/
public function get_Checklist_By_File($fname)
{
$ret = [];
$this->help->select("sagacity.checklist", null, [
[
'field' => 'file_name',
'value' => $fname
]
]);
$rows = $this->help->execute();
if (isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$chk = new checklist($row['id'], $row['checklist_id'], $row['name'], $row['description'], $row['date'], $row['file_name'], $row['ver'], $row['release'], $row['type'], $row['icon']);
/* */
$this->help->select("sagacity.checklist_software_lookup", [
'sw_id'
], [
[
'field' => 'chk_id',
'value' => $row['id']
]
]);
$sw_rows = $this->help->execute();
if (is_array($sw_rows) && count($sw_rows)) {
if (isset($sw_rows['sw_id'])) {
$sw_rows = [
0 => $sw_rows
];
}
foreach ($sw_rows as $row2) {
$chk->add_SW($this->get_Software($row2['sw_id']));
}
}
$ret[] = $chk;
}
}
return $ret;
}
/**
* Function to retrieve the most current checklist for a given software package
*
* @param software $software
* Software of which to look for checklists
*
* @return array:checklist Returns an array of checklists that this software ties to. Otherwise, an empty array
*/
public function get_Latest_Checklist_By_Software($software)
{
$ret = [];
$this->help->create_table("c", true, $this->help->select("sagacity.checklist", null, [], array(
'order' => '`ver` DESC, CONVERT(`release`, DECIMAL(4,2)) DESC'
)));
if (! $this->help->execute()) {
return $ret;
}
$this->help->select("c", array(
'c.id'
), array(
array(
'field' => 'csl.sw_id',
'op' => '=',
'value' => $software->get_ID()
)
), array(
'table_joins' => array(
"JOIN sagacity.checklist_software_lookup csl ON csl.chk_id=c.id"
),
'group' => 'c.name,c.type',
'order' => 'c.name'
));
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = array(
0 => $rows
);
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$chk = $this->get_Checklist($row['id']);
if (is_array($chk) && count($chk) && isset($chk[0])) {
$ret[] = $chk[0];
}
}
}
return $ret;
}
/**
* Get a summary of checklist stats
*
* @param integer $cat_id
* Integer category ID to get the summary on
*
* @return NULL|array:targets,checklist,string Returns an associative array of target (id & name), checklists, and a summary that joins the two
*/
public function get_Checklist_Summary($cat_id)
{
$where = array(
array(
'field' => 't.cat_id',
'op' => (is_null($cat_id) ? IS : '='),
'value' => $cat_id
)
);
$tgts = [];
$chklsts = [];
$summary = [];
$this->help->select("sagacity.target_checklist tc", null, $where, array(
'table_joins' => array(
"LEFT JOIN sagacity.target t ON t.id=tc.tgt_id"
),
'group' => 't.id'
));
$tgt_rows = $this->help->execute();
if (is_array($tgt_rows) && count($tgt_rows) && isset($tgt_rows['name'])) {
$tgt_rows = array(
0 => $tgt_rows
);
}
if (is_array($tgt_rows) && count($tgt_rows) && isset($tgt_rows[0])) {
foreach ($tgt_rows as $row) {
$tgts[$row['id']] = $row['name'];
}
} else {
$this->help->debug(E_ERROR);
}
$this->help->select("sagacity.target_checklist tc", array(
'c.id',
'c.name',
'c.type',
'c.ver',
'c.`release`'
), $where, array(
'table_joins' => array(
"LEFT JOIN sagacity.checklist c ON tc.chk_id=c.id",
"LEFT JOIN sagacity.target t ON tc.tgt_id=t.id"
),
'group' => 'c.id',
'order' => 'c.name'
));
$chk_rows = $this->help->execute();
if (is_array($chk_rows) && count($chk_rows) && isset($chk_rows['id'])) {
$chk_rows = array(
0 => $chk_rows
);
}
if (is_array($chk_rows) && count($chk_rows) && isset($chk_rows[0])) {
foreach ($chk_rows as $row) {
$chklsts[$row['id']] = "{$row['name']} V{$row['ver']}R{$row['release']} (" . ($row['type'] == 'iavm' ? 'IAVM' : ucfirst($row['type'])) . ")";
}
} else {
$this->help->debug(E_ERROR);
}
$this->help->select("sagacity.findings f", array(
"COUNT(1) as 'cnt'",
'c.id',
'c.name',
'c.ver',
'c.`release`'
), array(
array(
'field' => 't.cat_id',
'op' => (is_null($cat_id) ? IS : '='),
'value' => $cat_id
),
array(
'field' => 'c.name',
'op' => '=',
'value' => 'Orphan',
'sql_op' => 'AND'
)
), array(
'table_joins' => array(
"LEFT JOIN sagacity.target t ON t.id=f.tgt_id",
"LEFT JOIN sagacity.pdi_checklist_lookup pcl ON pcl.pdi_id=f.pdi_id",
"LEFT JOIN sagacity.checklist c ON pcl.checklist_id=c.id"
)
));
$chk_rows2 = $this->help->execute();
if (is_array($chk_rows2) && count($chk_rows2) && isset($chk_rows2['cnt'])) {
if ($chk_rows2['cnt'] > 0) {
$chklsts[$chk_rows2['id']] = "{$chk_rows2['name']} V{$chk_rows2['ver']}R{$chk_rows2['release']}";
}
}
foreach ($chklsts as $chk_key => $chk) {
foreach ($tgts as $host_key => $host) {
if ($chk != 'Orphan') {
$this->help->select_count("sagacity.target_checklist tc", [
[
'field' => 'tc.tgt_id',
'op' => '=',
'value' => $host_key
],
[
'field' => 'tc.chk_id',
'op' => '=',
'value' => $chk_key,
'sql_op' => 'AND'
]
]);
} else {
$this->help->select("sagacity.findings f", [
"IF(COUNT(1) > 0, '1', '0')"
], [
[
'field' => 'f.tgt_id',
'op' => '=',
'value' => $host_key
],
[
'field' => 'c.name',
'op' => '=',
'value' => 'Orphan',
'sql_op' => 'AND'
]
], [
'table_joins' => [
"LEFT JOIN sagacity.pdi_checklist_lookup pcl ON pcl.pdi_id=f.pdi_id",
"LEFT JOIN sagacity.checklist c ON c.id=pcl.checklist_id"
]
]);
}
$summary[$chk_key][$host_key] = $this->help->execute();
}
}
return [
'tgts' => $tgts,
'checklists' => $chklsts,
'summary' => $summary
];
}
/**
* Get all checklist & targets in a category
*
* @param integer $cat_id
* Category ID to pull the checklists from
*
* @return NULL|array:string checklist
*/
public function get_Category_Checklists($cat_id)
{
$chklsts = [];
$this->help->select("sagacity.target_checklist tc", array(
'tc.tgt_id',
'tc.chk_id'
), array(
array(
'field' => 't.cat_id',
'op' => '=',
'value' => $cat_id
)
), array(
'table_joins' => array(
"LEFT JOIN sagacity.target t ON tc.tgt_id = t.id",
"LEFT JOIN sagacity.checklist c ON tc.chk_id = c.id"
),
'order' => 'c.name'
));
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['tgt_id'])) {
$rows = array(
0 => $rows
);
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$chk = $this->get_Checklist($row['chk_id']);
if (is_array($chk) && count($chk) && isset($chk[0]) && is_a($chk[0], 'checklist')) {
$chk = $chk[0];
} else {
continue;
}
$tgts = isset($chklsts[$chk->get_ID()]['tgts']) ? $chklsts[$chk->get_ID()]['tgts'] : null;
$chklsts[$chk->get_ID()] = array(
'tgts' => $tgts . $row['tgt_id'] . ",",
'checklist' => $chk
);
}
}
$this->help->select_count("sagacity.target t", array(
array(
'field' => 't.cat_id',
'op' => '=',
'value' => $cat_id
),
array(
'field' => 'pcl.checklist_id',
'op' => '=',
'value' => "(SELECT c.id FROM sagacity.checklist c WHERE c.name='Orphan')",
'sql_op' => 'AND'
)
), array(
'table_joins' => array(
"LEFT JOIN sagacity.findings f ON t.id=f.tgt_id",
"LEFT JOIN sagacity.pdi_checklist_lookup pcl ON pcl.pdi_id=f.pdi_id"
)
));
$count = $this->help->execute();
if ($count) {
$this->help->select("sagacity.target t", array(
"t.id AS 'tgt_id'",
"pcl.checklist_id AS 'chk_id'"
), array(
array(
'field' => 't.cat_id',
'op' => '=',
'value' => $cat_id
),
array(
'field' => 'pcl.checklist_id',
'op' => '=',
'value' => "(SELECT c.id FROM sagacity.checklist c WHERE c.name='Orphan')",
'sql_op' => 'AND'
)
), array(
'table_joins' => array(
"LEFT JOIN sagacity.findings f ON t.id=f.tgt_id",
"LEFT JOIN sagacity.pdi_checklist_lookup pcl ON pcl.pdi_id=f.pdi_id"
),
'group' => 't.id,pcl.checklist_id'
));
$rows2 = $this->help->execute();
if (is_array($rows2) && count($rows2) && isset($rows2['tgt_id'])) {
$rows2 = array(
0 => $rows2
);
}
if (is_array($rows2) && count($rows2) && isset($rows2[0])) {
foreach ($rows2 as $row2) {
$chk = $this->get_Checklist($row2['chk_id']);
if (is_array($chk) && count($chk) && isset($chk[0]) && is_a($chk[0], 'checklist')) {
$chk = $chk[0];
} else {
continue;
}
$tgts = isset($chklsts[$chk->get_ID()]['tgts']) ? $chklsts[$chk->get_ID()]['tgts'] : "";
$chklsts[$chk->get_ID()] = array(
'tgts' => $tgts . $row2['tgt_id'] . ",",
'checklist' => $chk
);
}
}
}
return $chklsts;
}
/**
* Get array of checklists for a target
*
* @param integer $tgt_id
* The target ID of the target we want checklists from
*
* @return array:checklist |NULL
* Returns an array of checklists that are assigned to the requested target
*/
public function get_Target_Checklists($tgt_id)
{
$this->help->select("sagacity.target_checklist tc", [
'c.id',
'tc.class'
], [
[
'field' => 'tc.tgt_id',
'op' => '=',
'value' => $tgt_id
]
], [
'table_joins' => [
"LEFT JOIN sagacity.checklist c ON c.id=tc.chk_id"
],
'order' => 'c.name'
]);
$chk = [];
$chks = $this->help->execute();
if (isset($chks['id'])) {
$chks = [
0 => $chks
];
}
if (is_array($chks) && count($chks) && isset($chks[0])) {
foreach ($chks as $row) {
/** @var checklist $checklist */
$checklist = $this->get_Checklist($row['id'])[0];
$checklist->set_Classification($row['class']);
$chk[$checklist->get_ID()] = $checklist;
}
}
// get the orphan checklist ID
$this->help->select("sagacity.checklist", [
'id'
], [
[
'field' => 'name',
'op' => '=',
'value' => 'Orphan'
]
]);
$orphan = $this->help->execute();
// check to see if this target has findings from the Orphan checklist
$this->help->select_count("sagacity.pdi_checklist_lookup pcl", [
[
'field' => 'pcl.checklist_id',
'op' => '=',
'value' => $orphan['id']
],
[
'field' => 'f.tgt_id',
'op' => '=',
'value' => $tgt_id,
'sql_op' => 'AND'
]
], [
'table_joins' => [
"RIGHT JOIN sagacity.findings f ON pcl.pdi_id=f.pdi_id"
]
]);
$cnt = $this->help->execute();
// add the orphan checklist if findings exist
if ($cnt) {
$chk[] = $this->get_Checklist($orphan['id'])[0];
}
return $chk;
}
/**
* Function for getting eChecklist data to export
*
* @param int $cat_id
* @param array $chk_host_list
* @param string $status
* @param int $category
*
* @return array
*/
public function get_Category_Findings($cat_id, $chk_host_list = [], $status = null, $category = null)
{
$ret = [];
$stigs = [];
$tgt_ids = [];
$where = [
[
'field' => 'gcf.cat_id',
'op' => (is_null($cat_id) ? IS : '='),
'value' => $cat_id
]
];
if (! is_null($status)) {
$where[] = [
'field' => 'gcf.status',
'op' => '=',
'value' => $status,
'sql_op' => 'AND',
'open-paren' => true
];
if ($status == 'Not Reviewed') {
$where[] = [
'field' => 'gcf.status',
'op' => IS,
'value' => null,
'sql_op' => 'OR',
'close-paren' => true
];
} else {
unset($where[1]['open-paren']);
}
}
if (! is_null($category)) {
$where[] = [
'field' => 'gcf.cat',
'op' => '=',
'value' => $category,
'sql_op' => 'AND'
];
}
$this->help->select("sagacity.get_cat_findings gcf", null, $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['tgt_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
if (is_null($row['chk_icon']) || $row['chk_icon'] == '') {
$worksheet_name = '(Unknown)';
} else {
$worksheet_name = substr($row['chk_icon'], 0, - 4);
}
if (! isset($ret[$worksheet_name])) {
$ret[$worksheet_name] = [
'target_list' => [],
'checklists' => [],
'stigs' => []
];
$where2 = [
[
'field' => 't.cat_id',
'op' => '=',
'value' => $cat_id
],
[
'field' => 'c.icon',
'op' => LIKE,
'value' => "'$worksheet_name%'",
'sql_op' => 'AND'
]
];
if (is_array($tgt_ids) && count($tgt_ids)) {
$where2[] = [
'field' => 't.id',
'op' => IN,
'value' => $tgt_ids,
'sql_op' => 'AND'
];
}
$this->help->select("target t", [
't.class'
], $where2, [
'table_joins' => [
"LEFT JOIN target_checklist tc ON tc.tgt_id = t.id",
"LEFT JOIN checklist c ON c.id=tc.chk_id"
],
'group' => 't.class',
'order' => "FIELD(t.class, 'S', 'FOUO', 'U')"
]);
$rows2 = $this->help->execute();
if (is_array($rows2) && count($rows2) && isset($rows2['class'])) {
$ret[$worksheet_name]['highest_class'] = $rows2['class'];
}
}
if (! in_array($row['chk_id'], $ret[$worksheet_name]['checklists'])) {
$ret[$worksheet_name]['checklists'][] = $row['chk_id'];
}
if (! isset($ret[$worksheet_name]['target_list'][$row['tgt_name']])) {
$ret[$worksheet_name]['target_list']["{$row['tgt_name']}"] = count($ret[$worksheet_name]['target_list']) + 6;
}
if (! isset($ret[$worksheet_name]['stigs'][$row['stig_id']])) {
if (! empty($row['finding_ia_controls'])) {
$ia = explode(' ', $row['finding_ia_controls']);
} else {
$ia = explode(' ', $row['ia_controls']);
}
$echk = new echecklist($row['stig_id'], $row['vms_id'], (empty($row['finding_cat']) ? $row['cat'] : $row['finding_cat']), $ia, $row['short_title'], null, $row['notes'], $row['check_contents'], null);
$echk->set_PDI_ID($row['pdi_id']);
$ret[$worksheet_name]['stigs'][$row['stig_id']] = [
'echecklist' => $echk,
"{$row['tgt_name']}" => $row['finding_status'],
'chk_id' => $row['chk_id']
];
if (! in_array($row['stig_id'], $stigs)) {
$stigs[] = $row['stig_id'];
}
} else {
$ret[$worksheet_name]['stigs'][$row['stig_id']][$row['tgt_name']] = $row['finding_status'];
if(stripos($ret[$worksheet_name]['stigs'][$row['stig_id']]['echecklist']->get_Notes(), $row['notes']) === false) {
$ret[$worksheet_name]['stigs'][$row['stig_id']]['echecklist']->append_Notes($row['notes'] . PHP_EOL);
}
}
if ($row['chk_type'] == 'manual') {
$ret[$worksheet_name]['stigs'][$row['stig_id']]['chk_id'] = $row['chk_id'];
}
}
}
/*
$where = [
[
'field' => 'gof.cat_id',
'op' => (is_null($cat_id) ? IS : '='),
'value' => $cat_id
]
];
if (is_array($stigs) && count($stigs) && isset($stigs[0]) && is_a($stigs[0], 'stig')) {
$where[] = [
'field' => 'gof.stig_id',
'op' => NOT_IN,
'value' => $stigs,
'sql_op' => 'AND'
];
}
if (! is_null($status)) {
$where[] = [
'field' => 'gof.status',
'op' => '=',
'value' => $status,
'sql_op' => 'AND',
'open-paren' => true
];
if ($status == 'Not Reviewed') {
$where[] = [
'field' => 'gof.status',
'op' => IS,
'value' => null,
'sql_op' => 'OR',
'close-paren' => true
];
} else {
unset($where[2]['open-paren']);
}
}
if (! is_null($category)) {
$where[] = [
'field' => 'gof.cat',
'op' => '=',
'value' => $category,
'sql_op' => 'AND'
];
}
$this->help->select("sagacity.get_orphan_findings gof", null, $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['tgt_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
$worksheet_name = "Orphan";
$class = [
'U' => 1,
'FOUO' => 2,
'S' => 3
];
foreach ($rows as $row) {
if (! isset($ret[$worksheet_name])) {
$ret[$worksheet_name] = [
'target_list' => [],
'checklists' => [],
'stigs' => [],
'highest_class' => 'U'
];
}
if (! in_array($row['chk_id'], $ret[$worksheet_name]['checklists'])) {
$ret[$worksheet_name]['checklists'][] = $row['chk_id'];
}
if (! isset($ret[$worksheet_name]['target_list'][$row['tgt_name']])) {
$ret[$worksheet_name]['target_list'][$row['tgt_name']] = (is_array($ret[$worksheet_name]['target_list']) ? count($ret[$worksheet_name]['target_list']) + 6 : 7);
$sql2 = "SELECT t.`class` " . "FROM `sagacity`.`target` t " . "WHERE t.`name` = '" . $this->conn->real_escape_string($row['tgt_name']) . "'";
if ($res2 = $this->conn->query($sql2)) {
$row2 = $res2->fetch_assoc();
if (isset($class[$row2['class']]) && isset($class[$ret[$worksheet_name]['highest_class']])) {
if ($class[$row2['class']] > $class[$ret[$worksheet_name]['highest_class']]) {
$ret[$worksheet_name]['highest_class'] = $row2['class'];
}
}
}
}
if (! isset($ret[$worksheet_name]['stigs'][$row['stig_id']])) {
if (! empty($row['finding_ia_controls'])) {
$ia = explode(" ", $row['finding_ia_controls']);
} else {
$ia = explode(" ", $row['ia_controls']);
}
$echk = new echecklist($row['stig_id'], $row['vms_id'], (empty($row['finding_cat']) ? $row['cat'] : $row['finding_cat']), $ia, $row['short_title'], null, $row['notes'], $row['check_contents'], null);
$echk->set_PDI_ID($row['pdi_id']);
$ret[$worksheet_name]['stigs'][$row['stig_id']] = [
'echecklist' => $echk,
$row['tgt_name'] => $row['finding_status'],
'chk_id' => $row['chk_id']
];
} else {
$ret[$worksheet_name]['stigs'][$row['stig_id']][$row['tgt_name']] = $row['finding_status'];
$ret[$worksheet_name]['stigs'][$row['stig_id']]['echecklist']->append_Notes($row['notes'] . "\r");
}
}
}
*/
return $ret;
}
/**
* Add a checklist to database
*
* @param checklist $checklist_in
* The checklist that we want to add to the database
*
* @return integer Returns the id of the checklist inserted, or 0 if failed
*/
public function save_Checklist($checklist_in)
{
if (empty($checklist_in->id)) {
$this->help->insert("sagacity.checklist", array(
'checklist_id' => $checklist_in->checklist_id,
'name' => $checklist_in->name,
'description' => $checklist_in->description,
'date' => $checklist_in->date,
'file_name' => $checklist_in->file_name,
'release' => $checklist_in->release,
'ver' => $checklist_in->ver,
'type' => $checklist_in->type,
'icon' => $checklist_in->icon
), true);
if (! $this->help->execute()) {
Sagacity_Error::sql_handler($this->help->sql);
$this->help->debug(E_ERROR);
} else {
$chk_id = $this->conn->insert_id;
}
if (is_array($checklist_in->sw) && count($checklist_in->sw)) {
$fields = [
'chk_id',
'sw_id'
];
$params = [];
foreach ($checklist_in->sw as $sw) {
if (is_a($sw, 'software') && $sw->get_ID()) {
$params[] = [
$chk_id,
$sw->get_ID()
];
}
}
if (count($params)) {
$this->help->extended_insert('checklist_software_lookup', $fields, $params, true);
if (! $this->help->execute()) {
Sagacity_Error::sql_handler($this->help->sql);
$this->help->debug(E_ERROR);
}
}
}
} else {
$this->help->update('checklist', [
'checklist_id' => $checklist_in->checklist_id,
'name' => $checklist_in->name,
'description' => $checklist_in->description,
'date' => $checklist_in->date,
'file_name' => $checklist_in->file_name,
'release' => $checklist_in->release,
'ver' => $checklist_in->ver,
'type' => $checklist_in->type,
'icon' => $checklist_in->icon
], [
[
'field' => 'id',
'op' => '=',
'value' => $checklist_in->id
]
]);
$chk_id = $checklist_in->id;
if (! $this->help->execute()) {
Sagacity_Error::sql_handler($this->help->sql);
$this->help->debug(E_ERROR);
}
if (is_array($checklist_in->sw) && count($checklist_in->sw)) {
$this->help->delete("checklist_software_lookup", [
[
'field' => 'chk_id',
'op' => '=',
'value' => $checklist_in->id
]
]);
$this->help->execute();
$field = [
'chk_id',
'sw_id'
];
$params = [];
foreach ($checklist_in->sw as $sw) {
if ($sw->get_ID()) {
$params[] = [
$chk_id,
$sw->get_ID()
];
}
}
if (is_array($params) && count($params)) {
$this->help->extended_insert("checklist_software_lookup", $field, $params, true);
if (! $this->help->execute()) {
Sagacity_Error::sql_handler($this->help->sql);
$this->help->debug(E_ERROR);
}
}
}
}
return $chk_id;
}
// }}}
// {{{ CVE CLASS FUNCTIONS
/**
* Function to retrieve CVE object
*
* @param string $cve_id
* CVE to query from the database
*
* @return cve|NULL Returns CVE and associated references or null is nothing found
*/
public function get_CVE($cve_id)
{
$cve = null;
$this->help->select("sagacity.cve_db", array(
"cve_db.cve_id",
"cve.pdi_id",
"cve_db.seq",
"cve_db.status",
"cve_db.phase",
"cve_db.phase_date",
"cve_db.desc"
), array(
array(
'field' => 'cve_db.cve_id',
'op' => '=',
'value' => $cve_id
)
), array(
'table_joins' => array(
"LEFT JOIN sagacity.cve ON cve.cve_id=cve_db.cve_id"
)
));
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['cve_id'])) {
$cve_id = $row['cve_id'];
$cve = new cve($row['pdi_id'], $row['cve_id']);
$cve->set_Sequence($row['seq']);
$cve->set_Status($row['status']);
$cve->set_Phase($row['phase']);
$cve->set_Phase_Date($row['phase_date']);
$cve->set_Description($row['desc']);
$this->help->select("sagacity.iavm_to_cve itc", array(
"itc.noticeId"
), array(
array(
'field' => "itc.cve_id",
'op' => '=',
'value' => $cve_id
)
));
$iavm_rows = $this->help->execute();
if (is_array($iavm_rows) && count($iavm_rows) && isset($iavm_rows['noticeId'])) {
$iavm_rows = array(
0 => $iavm_rows
);
}
if (is_array($iavm_rows) && count($iavm_rows) && isset($iavm_rows[0])) {
foreach ($iavm_rows as $iavm) {
$cve->add_IAVM($iavm['noticeId']);
}
}
$this->help->select("sagacity.cve_references", array(
'id',
'source',
'url',
'val'
), array(
array(
'field' => 'cve_seq',
'op' => '=',
'value' => $cve_id
)
));
$ref_rows = $this->help->execute();
if (is_array($ref_rows) && count($ref_rows) && isset($ref_rows['id'])) {
$ref_rows = array(
0 => $ref_rows
);
}
if (is_array($ref_rows) && count($ref_rows) && isset($ref_rows[0])) {
foreach ($ref_rows as $ref) {
$cve->add_Reference(new cve_reference($ref['id'], $ref['source'], $ref['url'], $ref['val']));
}
}
}
return $cve;
}
/**
* Getter function to retrieve CVE's by their link to a PDI
*
* @param integer $pdi_id
* PDI ID that we want to find CVE's for
*
* @return NULL|array:cve Returns an array of CVEs for each one found that links to a PDI or NULL if none found
*/
public function get_CVEs_By_PDI($pdi_id)
{
$ret = [];
$sql = "SELECT " . "cve_db.`cve_id`,cve.`pdi_id`,cve_db.`seq`,cve_db.`status`," . "cve_db.`phase`,cve_db.`phase_date`,cve_db.`desc` " . "FROM `sagacity`.`cve_db` " . "LEFT JOIN `sagacity`.`cve` ON cve.`cve_id` = cve_db.`cve_id` " . "WHERE cve.`pdi_id` = " . $this->conn->real_escape_string($pdi_id);
if ($res = $this->conn->query($sql)) {
if (! $res->num_rows) {
return null;
}
while ($row = $res->fetch_assoc()) {
$cve_id = $row['cve_id'];
$cve = new cve($row['pdi_id'], $row['cve_id']);
$cve->set_Sequence($row['seq']);
$cve->set_Status($row['status']);
$cve->set_Phase($row['phase']);
$cve->set_Phase_Date($row['phase_date']);
$cve->set_Description($row['desc']);
$sql = "SELECT itc.`noticeId` " . "FROM `sagacity`.`iavm_to_cve` itc " . "WHERE itc.`cve_id`='" . $this->conn->real_escape_string($cve_id) . "'";
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
while ($row2 = $res2->fetch_assoc()) {
$cve->add_IAVM($row2['noticeId']);
}
}
}
$sql = "SELECT `id`,`source`,`url`,`val` " . "FROM `sagacity`.`cve_references` " . "WHERE `cve_seq`='" . $this->conn->real_escape_string($cve_id) . "'";
if ($res2 = $this->conn->query($sql)) {
while ($row2 = $res2->fetch_assoc()) {
$cve->add_Reference(new cve_reference($row2['id'], $row2['source'], $row2['url'], $row2['val']));
}
$ret[] = $cve;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
}
return $ret;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return null;
}
/**
* Get a CVE from a external reference
*
* @param string $ext
* String of the external reference we are looking for
*
* @return cve|NULL Returns the CVE that references that external data point or NULL if none found
*/
public function get_CVE_From_External($ext)
{
$sql = "SELECT `cve_seq` " . "FROM `sagacity`.`cve_references` " . "WHERE `url` LIKE '%" . $this->conn->real_escape_string($ext) . "%' OR " . "`val` LIKE '%" . $this->conn->real_escape_string($ext) . "%' " . "GROUP BY `cve_seq` " . "ORDER BY `cve_seq` DESC";
if ($res = $this->conn->query($sql)) {
if ($res->num_rows) {
$row = $res->fetch_assoc();
$cve = $this->get_CVE($row['cve_seq']);
return $cve;
}
}
return null;
}
/**
* Update or insert a CVE
*
* @param array:cve $cves
* Array of CVEs to save to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_CVE($cves)
{
if (is_array($cves) && isset($cves[0]) && is_a($cves[0], 'cve')) {
foreach ($cves as $cve) {
$db_cve = $this->get_CVE($cve->get_CVE());
if (! is_null($db_cve) && is_a($db_cve, 'cve')) {
$this->help->update("sagacity.cve_db", array(
'status' => $cve->get_Status(),
'phase' => $cve->get_Phase(),
'phase_date' => $cve->get_Phase_Date(),
'desc' => $cve->get_Description()
), array(
array(
'field' => 'cve_id',
'op' => '=',
'value' => $cve->get_CVE()
)
));
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
if (! $db_cve->get_PDI_ID() && $cve->get_PDI_ID()) {
$this->help->insert("sagacity.cve", array(
'pdi_id' => $cve->get_PDI_ID(),
'cve_id' => $cve->get_CVE()
));
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
$vals = [];
foreach ($cve->get_References() as $ref) {
if (! $db_cve->ref_Exists($ref->get_Value())) {
$vals[] = [
$cve->get_CVE(),
$ref->get_Source(),
$ref->get_URL(),
$ref->get_Value()
];
}
}
if (is_array($vals) && count($vals)) {
$this->help->extended_insert("cve_references", [
'cve_seq',
'source',
'url',
'val'
], $vals, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
} else {
$this->help->insert("cve_db", [
'cve_id' => $cve->get_CVE(),
'seq' => $cve->get_Sequence(),
'status' => $cve->get_Status(),
'phase' => $cve->get_Phase(),
'phase_date' => $cve->get_Phase_Date(),
'desc' => $cve->get_Description()
], true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
if ($cve->get_PDI_ID()) {
$this->help->insert("sagacity.cve", [
'pdi_id' => $cve->get_PDI_ID(),
'cve_id' => $cve->get_CVE()
], true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
if (is_array($cve->get_References()) && count($cve->get_References())) {
$ref_vals = [];
foreach ($cve->get_References() as $ref) {
$ref_vals[] = [
$cve->get_CVE(),
$ref->get_Source(),
$ref->get_URL(),
$ref->get_Value()
];
}
if (is_array($ref_vals) && count($ref_vals)) {
$this->help->extended_insert("cve_references", [
'cve_seq',
'source',
'url',
'val'
], $ref_vals, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
}
if ($cve->get_XML()) {
$this->help->insert("cve_web", [
'cve_id' => $cve->get_CVE(),
'xml' => $cve->get_XML()
], true);
$this->help->execute();
}
}
}
}
return true;
}
// }}}
// {{{ ECHECKLIST CLASS FUNCTIONS
/**
* Get an eChecklist for a checklist and list of targets
*
* @param mixed $ref
* The reference to search for (can consist of any data that is referenced in an eChecklist line
* @param integer $chk_id
*
* @return NULL|echecklist Returns eChecklist for associated checklists and reference
*/
public function get_eChecklist($ref, $chk_id)
{
$ret = null;
$where = [];
if (is_a($ref, "stig")) {
$where[] = [
'field' => 's.stig_id',
'op' => '=',
'value' => $ref->get_ID()
];
} elseif (is_a($ref, "golddisk")) {
$where[] = [
'field' => 'v.vms_id',
'op' => '=',
'value' => $ref->get_ID()
];
} elseif (is_a($ref, "pdi")) {
$where[] = [
'field' => 'pdi.id',
'op' => '=',
'value' => $ref->get_ID()
];
} else {
error_log("No reference to search for");
return $ret;
}
$this->help->select("pdi_catalog pdi", [
"pdi.id AS 'pdi_id'",
"s.stig_id",
"v.vms_id",
"pdi.short_title",
"IF(pdi.cat=1,'I',IF(pdi.cat=2,'II',IF(pdi.cat=3,'III',''))) as 'cat'"
], $where, [
'table_joins' => [
"LEFT JOIN stigs s ON s.pdi_id = pdi.id",
"LEFT JOIN golddisk v ON v.pdi_id = pdi.id"
],
'group' => 's.stig_id'
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['pdi_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret = new echecklist($row['stig_id'], $row['vms_id'], $row['cat'], null, $row['short_title'], null, null, null, null);
$ret->set_PDI_ID($row['pdi_id']);
$this->help->select("pdi_checklist_lookup pcl", [
'pcl.check_contents'
], [
[
'field' => 'pcl.checklist_id',
'op' => IN,
'value' => (is_array($chk_id) ? implode(",", $chk_id) : $chk_id)
],
[
'field' => 'pcl.pdi_id',
'op' => '=',
'value' => $row['pdi_id'],
'sql_op' => 'AND'
]
], [
'table_joins' => [
"JOIN sagacity.checklist c ON c.id = pcl.checklist_id"
],
'order' => "FIELD(c.`type`, 'manual', 'iavm', 'policy', 'benchmark')"
]);
$row2 = $this->help->execute();
if (is_array($row2) && count($row2) && isset($row2['check_contents'])) {
$ret->set_Check_Contents($row2['check_contents']);
}
}
}
return $ret;
}
// }}}
// {{{ FILTER CLASS FUNCTIONS
/**
* Getter function for search filters
*
* @param string $type
*
* @return array:string
*/
public function get_Filters($type, $name = null)
{
$ret = [];
$sql = "SELECT `type`, `name`, `criteria` " . "FROM `sagacity`.`search_filters` " . "WHERE `type` = '" . $this->conn->real_escape_string($type) . "'";
if (! is_null($name)) {
$sql .= " AND `name` = '" . $this->conn->real_escape_string($name) . "'";
}
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$ret[] = array(
'type' => $row['type'],
'name' => $row['name'],
'criteria' => $row['criteria']
);
}
return $ret;
} else {
error_log($this->conn->error);
Sagacity_Error::sql_handler($sql);
}
return [];
}
/**
* Save function for a search filter
*
* @param string $type
* @param string $name
* @param string $criteria
*
* @return string
*/
public function save_Filter($type, $name, $criteria)
{
$this->help->insert("sagacity.search_filters", [
'name' => $name,
'type' => $type,
'criteria' => $criteria
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
return true;
}
// }}}
// {{{ FINDING CLASS FUNCTIONS
/**
* Get finding(s) for a specific target from the database
*
* @param target $tgt
* The target that we want findings for
* @param stig|golddisk|iavm|nessus $ref
* [optional]
* Get a finding associated with a specific PDI (default null)
* @param scan $scan
* [optional]
* Get findings associated with a specific scan (default null)
* @param boolean $orphan_only
* [optional]
* Only retrieve orphaned findings (default false)
* @param string $status
* [optional]
* Limit the retrieval to findings with this status (default null)
*
* @return array:finding|NULL Returns array of findings
*/
public function get_Finding($tgt, $ref = null, $scan = null, $orphan_only = false, $status = null)
{
$ret = null;
$where = [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt->get_ID()
]
];
if (! is_null($scan)) {
$where[] = [
'field' => 'scan_id',
'op' => '=',
'value' => $scan->get_ID(),
'sql_op' => 'AND'
];
}
if (! is_null($ref) && method_exists($ref, 'get_PDI_ID')) {
$where[] = [
'field' => 'pdi_id',
'op' => '=',
'value' => $ref->get_PDI_ID(),
'sql_op' => 'AND'
];
}
$this->help->select("sagacity.findings", null, $where);
if (! is_null($status)) {
$this->help->sql = "SELECT " .
"f.`id`, {$tgt->get_ID()} as 'tgt_id', pdi.`id` as 'pdi_id', f.`scan_id`, " .
"f.`notes`, f.`change_id`, f.`orig_src`, f.`finding_itr`, f.`cat`, " .
"IF(f.`findings_status_id` IS NOT NULL, " .
"f.`findings_status_id`, " .
"(SELECT fs.`id` " .
"FROM `sagacity`.`findings_status` fs " .
"WHERE fs.`status` = '{$this->conn->real_escape_string($status)}')" .
") as 'findings_status' " .
"FROM `sagacity`.`pdi_catalog` pdi " .
"LEFT JOIN `sagacity`.`pdi_checklist_lookup` lookup ON lookup.`pdi_id` = pdi.`id` " .
"LEFT JOIN `sagacity`.`target_checklist` tc ON tc.`chk_id` = lookup.`checklist_id` " .
"LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pdi.`id` AND f.`tgt_id` = {$tgt->get_ID()} " .
"WHERE tc.`tgt_id` = {$tgt->get_ID()} AND " .
"(f.`findings_status_id` = (" .
"SELECT fs.`id` " .
"FROM `sagacity`.`findings_status` fs " .
"WHERE fs.`status` = '{$this->conn->real_escape_string($status)}'" . ") OR " .
"f.`findings_status_id` IS NULL) " .
"GROUP BY pdi.id";
}
if ($orphan_only) {
$this->help->select("sagacity.findings f", [
'f.*'
], [
[
'field' => 'f.tgt_id',
'op' => '=',
'value' => $tgt->get_ID()
],
[
'field' => 'c.name',
'op' => '=',
'value' => 'Orphan',
'sql_op' => 'AND'
]
], [
'table_joins' => [
"LEFT JOIN pdi_checklist_lookup pcl ON f.pdi_id=pcl.pdi_id",
"LEFT JOIN target_checklist tc ON tc.chk_id=pcl.checklist_id",
"LEFT JOIN checklist c ON pcl.checklist_id=c.id"
]
]);
}
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['pdi_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$find = new finding($row['tgt_id'], $row['pdi_id'], $row['scan_id'], $row['findings_status_id'], $row['notes'], $row['change_id'], $row['orig_src'], $row['finding_itr']);
$find->set_Category($row['cat']);
$this->get_Finding_Notes($find);
$this->help->select("finding_controls", [
'ia_control'
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $row['tgt_id']
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $row['pdi_id'],
'sql_op' => 'AND'
]
]);
$rows2 = $this->help->execute();
if (is_array($rows2) && count($rows2) && isset($rows2['ia_control'])) {
$rows2 = [
0 => $rows2
];
}
if (is_array($rows2) && count($rows2) && isset($rows2[0])) {
foreach ($rows2 as $row2) {
$find->add_IA_Control($row2['ia_control']);
}
} else {
$this->help->select("ia_controls", [
"CONCAT(`type`, '-', `type_id`) AS 'ia_control'"
], [
[
'field' => 'pdi_id',
'op' => '=',
'value' => $row['pdi_id']
]
]);
$rows2 = $this->help->execute();
if (is_array($rows2) && count($rows2) && isset($rows2['ia_control'])) {
$rows2 = [
0 => $rows2
];
}
if (is_array($rows2) && count($rows2) && isset($rows2[0])) {
foreach ($rows2 as $row2) {
$find->add_IA_Control($row2['ia_control']);
}
}
}
$ret[$find->get_PDI_ID()] = $find;
}
}
return $ret;
}
/**
* Function to get the findings that are assigned to specific controls
*
* @param ste $ste
* @param proc_ia_controls $ia_ctrl
* @param string $status
* @return array:finding |NULL
*/
public function get_Findings_by_Control($ste, $ia_ctrl, $status = null)
{
if (! is_null($status)) {
if ($status == "Open") {
$status = " AND (fs.`status` = 'Open' OR fs.`status` = 'Exception')";
} else {
$status = " AND fs.`status` = '" . $this->conn->real_escape_string($status) . "'";
}
}
$sql = <<<EOQ
SELECT
f.`tgt_id`, f.`pdi_id`, f.`scan_id`, f.`findings_status_id` as 'findings_status',
f.`notes`, f.`change_id`, f.`orig_src`, f.`finding_itr`, f.`cat`
FROM `sagacity`.`findings` f
JOIN `sagacity`.`findings_status` fs ON f.`findings_status_id` = fs.`id`
JOIN `sagacity`.`stigs` s ON s.`pdi_id` = f.`pdi_id`
JOIN `sagacity`.`target` t ON t.`id` = f.`tgt_id`
JOIN `sagacity`.`finding_controls` fc ON fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id`
WHERE
t.`ste_id` = {$ste->get_ID()} AND
fc.`ia_control` = '{$this->conn->real_escape_string($ia_ctrl->get_Control_ID())}'
$status
GROUP BY f.`pdi_id`
ORDER BY f.`cat`, s.`stig_id`
EOQ;
if ($res = $this->conn->query($sql)) {
$ret = [];
while ($row = $res->fetch_assoc()) {
$find = new finding($row['tgt_id'], $row['pdi_id'], $row['scan_id'], $row['findings_status'], $row['notes'], $row['change_id'], $row['orig_src'], $row['finding_itr']);
$find->set_Category($row['cat']);
$sql2 = "SELECT `ia_control` FROM `sagacity`.`finding_controls` WHERE `tgt_id` = {$row['tgt_id']} AND `pdi_id` = {$row['pdi_id']}";
if ($res2 = $this->conn->query($sql2)) {
if ($res2->num_rows) {
while ($row2 = $res2->fetch_assoc()) {
$find->add_IA_Control($row2['ia_control']);
}
} else {
$sql2 = "SELECT CONCAT(`type`, '-', `type_id`) AS 'ia_control' FROM `sagacity`.`ia_controls` WHERE `pdi_id` = " . $row['pdi_id'];
if ($res2 = $this->conn->query($sql2)) {
while ($row2 = $res2->fetch_assoc()) {
$find->add_IA_Control($row2['ia_control']);
}
}
}
}
$ret[] = $find;
}
return $ret;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return null;
}
/**
* Function to return the host name of the targets that have this finding
*
* @param ste $ste
* @param pdi $pdi
*
* @return string
*/
public function get_Affected_Hosts_by_PDI($ste, $pdi)
{
$sql = <<<EOQ
SELECT (
SELECT GROUP_CONCAT(DISTINCT t.`name` SEPARATOR ', ')) AS 'name'
FROM `sagacity`.`findings` f
JOIN `sagacity`.`target` t ON f.`tgt_id` = t.`id`
WHERE
t.`ste_id` = {$ste->get_ID()} AND
f.`pdi_id` = {$pdi->get_ID()}
EOQ;
if ($res = $this->conn->query($sql)) {
return $res->fetch_assoc()['name'];
}
return '';
}
/**
* Function to return stigs that are not in the systems MAC and Classification
*
* @param ste $ste
*
* @return array:stig
*/
public function get_Findings_Not_in_System($ste)
{
$ret = [];
$this->help->create_table("unaccounted_for_findings", [
[
'field' => 'pdi_id',
'datatype' => 'int(11)',
'option' => 'UNIQUE NOT NULL'
]
]);
$this->help->execute();
$sql = "INSERT IGNORE INTO `unaccounted_for_findings` (`pdi_id`) SELECT DISTINCT(f.`pdi_id`) " .
"FROM `findings` f JOIN `target` t ON t.`id` = f.`tgt_id` " .
"WHERE t.`ste_id` = {$ste->get_ID()}";
$this->conn->real_query($sql);
$class = 'cl';
if ($ste->get_System()->get_Classification() == 'Public') {
$class = 'pub';
} elseif ($ste->get_System()->get_Classification() == 'Sensitive') {
$class = 'sen';
}
$sql = "DELETE FROM `unaccounted_for_findings` WHERE `pdi_id` IN (SELECT ia.`pdi_id` " .
"FROM `proc_level_type` plt " .
"JOIN `ia_controls` ia ON CONCAT(ia.`type`, '-', ia.`type_id`) = plt.`proc_control` " .
"WHERE " .
"plt.`level` = {$ste->get_System()->get_MAC()} AND " .
"plt.`class` = '$class')";
$this->conn->real_query($sql);
$sql = "SELECT s.`stig_id` FROM `unaccounted_for_findings` uaf JOIN `stigs` s ON s.`pdi_id` = uaf.`pdi_id`";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$stig = $this->get_Stig($row['stig_id']);
if (is_array($stig) && count($stig) && isset($stig[0]) && is_a($stig[0], 'stig')) {
$stig = $stig[0];
}
if (! preg_match("/^\d{5}$/", $stig->get_ID())) {
$ret[] = $stig;
}
}
}
return $ret;
}
/**
* Get count of all findings with the status passed in
*
* @param integer $cat_id
* The category we are searching
* @param string $status
* The status to look for
* @param integer $cat
* [optional]
* The CAT/severity level
* @param proc_ia_controls $ctrl
* [optional]
* A IA control to filter for
*
* @return integer Returns the number of findings in the category that have the passed in status, severity, and control
*/
public function get_Finding_Count_By_Status($cat_id, $status, $cat = null, $ctrl = null)
{
$joins = [
"JOIN target_checklist tc ON t.id = tc.tgt_id",
"JOIN pdi_checklist_lookup pcl ON pcl.checklist_id = tc.chk_id",
"LEFT JOIN findings f ON f.pdi_id = pcl.pdi_id AND t.id = f.tgt_id",
"LEFT JOIN findings_status fs ON fs.id = f.findings_status_id"
];
if (! is_null($ctrl)) {
$joins[] = "JOIN finding_controls fc ON fc.tgt_id = f.tgt_id AND fc.pdi_id = f.pdi_id";
}
$where = [
[
'field' => 't.cat_id',
'value' => $cat_id
],
[
'field' => 'fs.status',
'value' => $status,
'sql_op' => 'AND',
'open-paren' => true
]
];
if ($status == 'Not Reviewed') {
$where[] = [
'field' => 'fs.status',
'op' => IS,
'value' => null,
'sql_op' => 'OR',
'close-paren' => true
];
} else {
$where[] = [
'close-paren' => true
];
}
if (! is_null($cat) && is_numeric($cat)) {
$where[] = [
'field' => 'f.cat',
'value' => $cat,
'sql_op' => 'AND'
];
}
if (! is_null($ctrl) && is_a($ctrl, 'proc_ia_controls')) {
$where[] = [
'field' => 'fc.ia_control',
'value' => $ctrl->get_Control_ID(),
'sql_op' => 'AND'
];
}
$field = ($status == 'Not Reviewed' ? "COUNT(DISTINCT(pcl.pdi_id)) AS 'count'" : "COUNT(DISTINCT(f.id)) AS 'count'");
$this->help->select_count("target t", $where, [
'table_joins' => $joins
]);
$this->help->sql = str_replace("COUNT(1) AS 'count'", $field, $this->help->sql);
$cnt = $this->help->execute();
$joins = [
"JOIN pdi_checklist_lookup pcl ON pcl.checklist_id = c.id",
"JOIN findings f ON f.pdi_id = pcl.pdi_id",
"LEFT JOIN findings_status fs ON f.findings_status_id = fs.id",
"JOIN target t ON t.id = f.tgt_id"
];
if (! is_null($ctrl) && is_a($ctrl, 'proc_ia_controls')) {
$joins[] = "JOIN finding_controls fc ON fc.tgt_id = f.tgt_id AND fc.pdi_id = f.pdi_id";
}
$where = [
[
'field' => 't.cat_id',
'value' => $cat_id
],
[
'field' => 'c.name',
'value' => 'Orphan',
'sql_op' => 'AND'
],
[
'field' => 'fs.status',
'value' => $status,
'sql_op' => 'AND',
'open-paren' => true
]
];
if ($status == 'Not Reviewed') {
$where[] = [
'field' => 'fs.status',
'op' => IS,
'value' => null,
'sql_op' => 'OR',
'close-paren' => true
];
} else {
$where[] = [
'close-paren' => true
];
}
if (! is_null($cat) && is_numeric($cat)) {
$where[] = [
'field' => 'f.cat',
'value' => $cat,
'sql_op' => 'AND'
];
}
if (! is_null($ctrl) && is_a($ctrl, 'proc_ia_controls')) {
$where[] = [
'field' => 'fc.ia_control',
'value' => $ctrl->get_Control_ID(),
'sql_op' => 'AND'
];
}
$this->help->select_count("checklist c", $where, array(
'table_joins' => $joins
));
$this->help->sql = str_replace("COUNT(1) AS 'count'", $field, $this->help->sql);
$cnt += $this->help->execute();
return $cnt;
}
/**
* Get count of all findings with the status passed in
*
* @param ste $ste
* The category we are searching
* @param string $status
* The status to look for
* @param integer $cat
* The CAT/severity level
* @param proc_ia_controls $ctrl
* A IA control to filter for
*
* @return integer Returns the number of findings with status
*/
public function get_STE_Finding_Count_By_Status($ste, $status, $cat = null, $ctrl = null)
{
$field = ($status == 'Not Reviewed' ? "SELECT COUNT(DISTINCT(pcl.`pdi_id`))" : "SELECT COUNT(DISTINCT(f.`id`))");
$ctrl_join = (! is_null($ctrl) ? "JOIN `sagacity`.`finding_controls` fc ON fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id` " : "");
$status_clause = ($status == 'Not Reviewed' ? " OR fs.`status` IS NULL" : "");
$cat_clause = (! is_null($cat) ? "AND f.`cat` = $cat " : "");
$ctrl_clause = (! is_null($ctrl) ? "AND fc.`ia_control` = '{$ctrl->get_Control_ID()}' " : "");
$sql = <<<EOQ
SELECT (
$field
FROM `sagacity`.`target` t
LEFT JOIN `sagacity`.`target_checklist` tc ON t.`id` = tc.`tgt_id`
LEFT JOIN `sagacity`.`pdi_checklist_lookup` pcl ON pcl.`checklist_id` = tc.`chk_id`
LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pcl.`pdi_id` AND t.`id` = f.`tgt_id`
LEFT JOIN `sagacity`.`findings_status` fs ON fs.`id` = f.`findings_status_id`
$ctrl_join
WHERE
t.`ste_id` = {$this->conn->real_escape_string($ste->get_ID())} AND
(
fs.`status` = '{$this->conn->real_escape_string($status)}'
$status_clause
)
$cat_clause
$ctrl_clause
)
+
(
$field
FROM `sagacity`.`checklist` c
LEFT JOIN `sagacity`.`pdi_checklist_lookup` pcl ON pcl.`checklist_id` = c.`id`
LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pcl.`pdi_id`
LEFT JOIN `sagacity`.`findings_status` fs ON f.`findings_status_id` = fs.`id`
JOIN `sagacity`.`target` t ON t.`id` = f.`tgt_id`
$ctrl_join
WHERE
t.`ste_id` = {$this->conn->real_escape_string($ste->get_ID())} AND
c.`name` = 'Orphan' AND
(
fs.`status` = '{$this->conn->real_escape_string($status)}'
$status_clause
)
$cat_clause
$ctrl_clause
) AS 'sum_count'
EOQ;
if ($res = $this->conn->query($sql)) {
return $res->fetch_assoc()['sum_count'];
} else {
return 0;
}
}
/**
* Get count of all findings with the status passed in
*
* @param target $tgt
* The target we are searching
* @param string $status
* The status to look for
* @param integer $cat
* [optional]
* The CAT/severity level
* @param proc_ia_controls $ctrl
* [optional]
* A IA control to filter for
* @param array $chk_ids
* [optional]
* @param boolean $is_orphan
* [optional]
*
* @return integer Returns the number of findings with status 'False Positives'
*/
public function get_Host_Finding_Count_By_Status($tgt, $status, $cat = null, $ctrl = null, $chk_ids = null, $is_orphan = false)
{
if (! $is_orphan) {
$sql = "SELECT (SELECT COUNT(DISTINCT(pcl.`pdi_id`)) " .
"FROM `sagacity`.`target` t " .
"LEFT JOIN `sagacity`.`target_checklist` tc ON t.`id` = tc.`tgt_id` " .
"LEFT JOIN `sagacity`.`pdi_checklist_lookup` pcl ON pcl.`checklist_id` = tc.`chk_id` " .
"LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pcl.`pdi_id` AND t.`id` = f.`tgt_id` " .
"LEFT JOIN `sagacity`.`findings_status` fs ON fs.`id` = f.`findings_status_id` " .
(! is_null($ctrl) ? "JOIN `sagacity`.`finding_controls` fc ON fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id` " : "") .
"WHERE t.`id` = {$this->conn->real_escape_string($tgt->get_ID())} AND " .
"(fs.`status` = '{$this->conn->real_escape_string($status)}' " .
($status == 'Not Reviewed' ? " OR fs.`status` IS NULL" : "") . ") " .
(! is_null($cat) ? "AND f.`cat` = $cat " : "") .
(! is_null($ctrl) ? "AND fc.`ia_control` = '{$ctrl->get_Control_ID()}' " : "") .
(! is_null($chk_ids) ? "AND pcl.`checklist_id` IN (" . implode(", ", $chk_ids) . ") " : "") .
")";
} else {
$sql = "SELECT (SELECT COUNT(DISTINCT(pcl.`pdi_id`)) " .
"FROM `sagacity`.`checklist` c " .
"LEFT JOIN `sagacity`.`pdi_checklist_lookup` pcl ON pcl.`checklist_id` = c.`id` " .
"LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pcl.`pdi_id` " .
"LEFT JOIN `sagacity`.`findings_status` fs ON f.`findings_status_id` = fs.`id` " .
"JOIN `sagacity`.`target` t ON t.`id` = f.`tgt_id` " .
(! is_null($ctrl) ? "JOIN `sagacity`.`finding_controls` fc ON fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id` " : "") .
"WHERE t.`id` = {$this->conn->real_escape_string($tgt->get_ID())} AND " .
"c.`name` = 'Orphan' AND " .
"(fs.`status` = '{$this->conn->real_escape_string($status)}' " .
($status == 'Not Reviewed' ? " OR fs.`status` IS NULL" : "") . ") " .
(! is_null($cat) ? "AND f.`cat` = $cat " : "") .
(! is_null($ctrl) ? "AND fc.`ia_control` = '{$ctrl->get_Control_ID()}' " : "") .
")";
}
$sql .= " AS 'sum_count'";
if ($res = $this->conn->query($sql)) {
return $res->fetch_assoc()['sum_count'];
} else {
return 0;
}
}
/**
* Function for getting number of targets that have a finding in this control
*
* @param ia_control $ctrl
* @param ste $ste
* @param string $status
*
* @return int
*/
public function get_Control_Finding_Count($ctrl, $ste, $status, $cat = null)
{
$sql = "SELECT " .
"IFNULL((SELECT COUNT(1) " .
"FROM `target` t " .
"LEFT JOIN `target_checklist` tc ON t.`id` = tc.`tgt_id` " .
"LEFT JOIN `pdi_checklist_lookup` pcl ON pcl.`checklist_id` = tc.`chk_id` " .
"LEFT JOIN `findings` f ON f.`pdi_id` = pcl.`pdi_id` AND t.`id` = f.`tgt_id` " .
"LEFT JOIN `findings_status` fs ON fs.`id` = f.`findings_status_id` " .
"LEFT JOIN `finding_controls` fc ON fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id` " .
"WHERE " .
"(fs.`status` = '$status' " .
($status == 'Open' ? " OR fs.`status` = 'Exception'" : "") .
($status == 'Not a Finding' ? " OR fs.`status` = 'Not Applicable'" : "") .
($status == 'Not Reviewed' ? " OR fs.`status` IS NULL" : "") .
") AND " .
(! is_null($cat) ? "f.`cat` = $cat AND " : "") .
(! is_null($ctrl) ? "fc.`ia_control` = '{$ctrl->get_Control_ID()}' AND " : "") .
"t.`ste_id` = $ste " .
"GROUP BY f.`pdi_id`" . "), 0)" .
" + " .
"IFNULL((SELECT COUNT(1) " .
"FROM `checklist` c " .
"LEFT JOIN `pdi_checklist_lookup` pcl ON pcl.`checklist_id` = c.`id` " .
"LEFT JOIN `findings` f ON f.`pdi_id` = pcl.`pdi_id` " .
"LEFT JOIN `findings_status` fs ON f.`findings_status_id` = fs.`id` " .
"LEFT JOIN `target` t ON t.`id` = f.`tgt_id` " .
"LEFT JOIN `finding_controls` fc ON fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id` " .
"WHERE " .
"c.`name` = 'Orphan' AND " .
"(fs.`status` = '$status' " .
($status == 'Open' ? " OR fs.`status` = 'Exception'" : "") .
($status == 'Not a Finding' ? " OR fs.`status` = 'Not Applicable'" : "") .
($status == 'Not Reviewed' ? " OR fs.`status` IS NULL" : "") . ") AND " .
(! is_null($cat) ? "f.`cat` = $cat AND " : "") .
(! is_null($ctrl) ? "fc.`ia_control` = '{$ctrl->get_Control_ID()}' AND " : "") .
"t.`ste_id` = $ste " .
"GROUP BY f.`pdi_id`" . "), 0) AS 'sum_count'";
if ($res = $this->conn->query($sql)) {
return $res->fetch_assoc()['sum_count'];
} else {
return 0;
}
}
/**
* Getter method to get finding notes
*
* @param finding $find
*/
public function get_Finding_Notes(finding &$find)
{
$this->help->select("analyst_notes", ['note'], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $find->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $find->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
$row = $this->help->execute();
if(isset($row['note'])) {
$find->set_Analyst_Notes($row['note']);
}
$this->help->select("scan_notes sn", ['sn.note', 'src.name'], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $find->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $find->get_PDI_ID(),
'sql_op' => 'AND'
],
[
'field' => 'scan_id',
'op' => '=',
'value' => $find->get_Scan_ID(),
'sql_op' => 'AND'
]
], [
'table_joins' => [
"JOIN scans s ON sn.scan_id = s.id",
"JOIN sources src ON src.id = s.src_id"
]
]);
$rows = $this->help->execute();
if(is_array($rows) && count($rows) && isset($rows[0])) {
$notes = $find->get_Scanner_Notes();
foreach($rows as $row) {
$notes .= $row['name'] . ":\r" . $row['note'];
}
$find->set_Scanner_Notes($notes);
} elseif(isset($rows['note'])) {
$notes = $find->get_Scanner_Notes();
$notes .= $rows['name'] . ":\r" . $rows['note'];
$find->set_Scanner_Notes($notes);
}
}
/**
* Function to determine how pervasive a finding is across all targets
*
* @todo - FINISH
*
* @param ste $ste
* @param proc_ia_controls $ia_ctrl
* @param string $status
*/
public function get_Finding_Pervasivity_by_Control($ste, $ia_ctrl, $status = null)
{}
/**
* Function to return all the possible finding statuses
*
* @return array:finding_status
*/
public function get_Finding_Statuses()
{
$this->help->select("sagacity.findings_status", null);
$rows = $this->help->execute();
$ret = [];
if(is_array($rows) && count($rows) && isset($rows[0])) {
foreach($rows as $row) {
$s = new finding_status();
$s->id = $row['id'];
$s->status = $row['status'];
$ret[] = $s;
}
} elseif(is_array($rows) && count($rows) && isset($rows['id'])) {
$s = new finding_status();
$s->id = $rows['id'];
$s->status = $rows['status'];
$ret[] = $s;
}
return $ret;
}
/**
* Function to compare the findings from two different targets
*
* @param target $left_tgt
* @param target $right_tgt
*
* @return array
*/
public function get_Finding_Comparrison($left_tgt, $right_tgt)
{
$ret = [];
$left_sql = "SELECT " .
"s.`stig_id`, pcl.`check_contents`, " .
"tgt.`id` AS 'tgt_id', tgt.`name` AS 'tgt_name', " .
"IF(f.`cat` IS NULL, pdi.`cat`, f.`cat`) AS 'cat', f.`notes`, " .
"IF(f.`findings_status_id` IS NULL, 'Not Reviewed', fs.`status`) AS 'finding_status', " .
"(SELECT GROUP_CONCAT(fc.`ia_control` SEPARATOR ' ') " .
"FROM `sagacity`.`finding_controls` fc " .
"WHERE fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id`) AS 'finding_ia_controls', " .
"(SELECT GROUP_CONCAT(DISTINCT CONCAT(ia.`type`, '-', ia.`type_id`) SEPARATOR ' ') " .
"FROM `sagacity`.`ia_controls` ia " .
"WHERE ia.`pdi_id` = pcl.`pdi_id`) AS 'ia_controls' " .
"FROM `sagacity`.`checklist` chk " .
"JOIN `sagacity`.`target_checklist` tc ON tc.`chk_id` = chk.`id` " .
"JOIN `sagacity`.`target` tgt ON tgt.`id` = tc.`tgt_id` " .
"LEFT JOIN `sagacity`.`pdi_checklist_lookup` pcl ON pcl.`checklist_id` = chk.`id` " .
"LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pcl.`pdi_id` AND f.`tgt_id` = tgt.`id` " .
"LEFT JOIN `sagacity`.`findings_status` fs ON fs.`id` = f.`findings_status_id` " .
"LEFT JOIN `sagacity`.`stigs` s ON s.`pdi_id` = pcl.`pdi_id` " .
"LEFT JOIN `sagacity`.`pdi_catalog` pdi ON pdi.`id` = pcl.`pdi_id` " .
"WHERE tgt.`id` = {$left_tgt->get_ID()} " .
"GROUP BY s.`stig_id`, tgt.`name` " .
"ORDER BY s.`stig_id`, FIELD(chk.`type`, 'manual', 'iavm', 'policy', 'benchmark')";
$right_sql = "SELECT " .
"s.`stig_id`, pcl.`check_contents`, " .
"tgt.`id` AS 'tgt_id', tgt.`name` AS 'tgt_name', " .
"IF(f.`cat` IS NULL, pdi.`cat`, f.`cat`) AS 'cat', f.`notes`, " .
"IF(f.`findings_status_id` IS NULL, 'Not Reviewed', fs.`status`) AS 'finding_status', " .
"(SELECT GROUP_CONCAT(fc.`ia_control` SEPARATOR ' ') " .
"FROM `sagacity`.`finding_controls` fc " .
"WHERE fc.`tgt_id` = f.`tgt_id` AND fc.`pdi_id` = f.`pdi_id`) AS 'finding_ia_controls', " .
"(SELECT GROUP_CONCAT(DISTINCT CONCAT(ia.`type`, '-', ia.`type_id`) SEPARATOR ' ') " .
"FROM `sagacity`.`ia_controls` ia " .
"WHERE ia.`pdi_id` = pcl.`pdi_id`) AS 'ia_controls' " .
"FROM `sagacity`.`checklist` chk " .
"JOIN `sagacity`.`target_checklist` tc ON tc.`chk_id` = chk.`id` " .
"JOIN `sagacity`.`target` tgt ON tgt.`id` = tc.`tgt_id` " .
"LEFT JOIN `sagacity`.`pdi_checklist_lookup` pcl ON pcl.`checklist_id` = chk.`id` " .
"LEFT JOIN `sagacity`.`findings` f ON f.`pdi_id` = pcl.`pdi_id` AND f.`tgt_id` = tgt.`id` " .
"LEFT JOIN `sagacity`.`findings_status` fs ON fs.`id` = f.`findings_status_id` " .
"LEFT JOIN `sagacity`.`stigs` s ON s.`pdi_id` = pcl.`pdi_id` " .
"LEFT JOIN `sagacity`.`pdi_catalog` pdi ON pdi.`id` = pcl.`pdi_id` " .
"WHERE tgt.`id` = {$right_tgt->get_ID()} " .
"GROUP BY s.`stig_id`, tgt.`name` " .
"ORDER BY s.`stig_id`, FIELD(chk.`type`, 'manual', 'iavm', 'policy', 'benchmark')";
if ($res = $this->conn->query($left_sql)) {
while ($row = $res->fetch_assoc()) {
$ret['left'][$row['stig_id']] = array(
'stig_id' => $row['stig_id'],
'cat' => $row['cat'],
'ia_controls' => (! empty($row['finding_ia_controls']) ? $row['finding_ia_controls'] : $row['ia_controls']),
'status' => $row['finding_status'],
'notes' => $row['notes']
);
}
}
if ($res = $this->conn->query($right_sql)) {
while ($row = $res->fetch_assoc()) {
$ret['right'][$row['stig_id']] = array(
'stig_id' => $row['stig_id'],
'cat' => $row['cat'],
'ia_controls' => (! empty($row['finding_ia_controls']) ? $row['finding_ia_controls'] : $row['ia_controls']),
'status' => $row['finding_status'],
'notes' => $row['notes']
);
if (! isset($ret['left'][$row['stig_id']])) {
$ret['left'][$row['stig_id']] = null;
}
}
}
return $ret;
}
// @TODO - modify to accept finding and array:finding
/**
* Add a finding
*
* @param scan $scan
* Scan that found this item
* @param array:target|target $tgts
* Array of targets or a single target that have this finding
* @param array|finding $finding_data
* Array of data associated with the finding<br />
* [0] => 'stig id'<br />
* [1] => 'vms id'<br />
* [2] => 'category level (I, II, III)'<br />
* [3] => 'ia controls (space delimited)'<br />
* [4] => 'short title'<br />
* [5...n] => 'target status'<br />
* [n+1] => 'notes'<br />
* [n+2] => 'check contents'<br />
* [n+3] => 'missing pdi'
*/
public function add_Finding($scan, $tgts, $finding_data)
{
global $cmd;
set_time_limit(0);
$host_count = 0;
$ref = null;
if (is_array($tgts)) {
$host_count = count($tgts);
} else {
$host_count ++;
}
if (preg_match('/\d\.\d+/', $finding_data[0])) {
$finding_data[0] = str_pad($finding_data[0], 5, "0");
}
$stig_id = $finding_data[0];
$vms_id = preg_replace("/V0+/i", "V-", $finding_data[1]);
$cat_lvl = substr_count($finding_data[2], 'I');
$ia_controls = $finding_data[3];
$short_title = $finding_data[4];
$notes = $finding_data[self::FIRST_ECHECKLIST_HOST_COL + $host_count];
if (preg_match('/SV\-.*_rule/', $stig_id)) {
$ref = $this->get_SV_Rule(null, $stig_id);
} elseif (preg_match('/CVE\-\d{4}\-\d+/', $stig_id)) {
$ref = [
0 => $this->get_CVE($stig_id)
];
} elseif (preg_match('/\d{4}\-[ABT]\-\d+/', $stig_id)) {
$ref = [
0 => $this->get_IAVM($stig_id)
];
}
if (is_null($ref) && $stig_id != 'No Reference') {
$ref = $this->get_Stig($stig_id);
}
if (is_null($ref) && $vms_id != 'No Reference') {
$ref = $this->get_GoldDisk($vms_id);
}
if (is_array($ref) && count($ref) && isset($ref[0])) {
$ref = $ref[0];
} else {
// add a new checklist entry
$pdi = new pdi(null, $cat_lvl, 'NOW', $short_title, $short_title);
$pdi_id = $this->save_PDI($pdi);
$stig = new stig($pdi_id, $stig_id, $short_title);
$ref = $stig;
$this->add_Stig($stig);
$golddisk = new golddisk($pdi_id, $vms_id, $short_title);
if ($vms_id != 'No Reference') {
$this->save_GoldDisk($golddisk);
}
}
if (is_array($tgts)) {
$updated_finding = [];
$new_finding = [];
$x = 0;
foreach ($tgts as $tgt) {
switch (strtolower(str_replace('_', ' ', $finding_data[self::FIRST_ECHECKLIST_HOST_COL + $x]))) {
case 'not reviewed':
case 'not a finding':
case 'open':
case 'not applicable':
case 'no data':
case 'exception':
case 'false positive':
$status = $finding_data[self::FIRST_ECHECKLIST_HOST_COL + $x];
break;
default:
$status = 'Not Reviewed';
}
$current_finding = $this->get_Finding($tgt, $ref);
if (is_array($current_finding) && count($current_finding) > 0) {
$current_finding = $current_finding[0];
}
$current_status = '';
if ($current_finding != null) {
$current_status = $current_finding->get_Finding_Status_String();
// $current_source = $current_finding->get();
if ($current_status != $status) {
$current_notes = $current_finding->get_Notes();
if (! $current_notes) {
$current_finding->set_Notes($notes);
} else {
if ($notes && stristr($current_notes, $notes) === false) {
$current_finding->prepend_Notes($current_notes);
}
}
if (($current_status == 'Open' || $status == 'Open') && ($current_status == 'Not Applicable' || $current_status == 'Not a Finding' || $status == 'Not Applicable' || $status == 'Not a Finding')) {
$current_finding->set_Notes("OPEN CONFLICT: $current_status/$status\n$notes");
$current_finding->set_Change_ID(finding::TO_OPEN);
} elseif (($current_status == 'Not a Finding' || $current_status == 'Not Applicable') && ($status == 'Not a Finding' || $status == 'Not Applicable')) {
$current_finding->set_Notes("NF/NA CONFLICT: $current_status/$status\n$notes");
$current_finding->set_Change_ID(finding::TO_NF);
} else {
$current_finding->set_Change_ID(finding::NC);
}
$new_status = $current_finding->get_Deconflicted_Status($status);
$new_status_id = $current_finding->get_Finding_Status_ID($new_status);
$current_finding->set_Finding_Status($new_status_id);
$current_finding->set_Original_Source($scan->get_Source()
->get_Name());
$current_finding->set_Finding_Iteration($current_finding->get_Finding_Iteration() + 1);
$current_finding->set_Scan_ID($scan->get_ID());
$current_finding->set_Category($cat_lvl);
$current_finding->set_IA_Controls($ia_controls);
$updated_finding[] = $current_finding;
} else {
$current_notes = $current_finding->get_Notes();
if (! $current_notes) {
$current_finding->set_Notes($notes);
} else {
if ($notes && stristr($current_notes, $notes) === false) {
$current_finding->set_Notes($current_notes . PHP_EOL . $notes);
}
}
$current_finding->set_Change_ID(finding::NC);
$current_finding->set_Original_Source($scan->get_Source()
->get_Name());
$current_finding->set_Finding_Iteration($current_finding->get_Finding_Iteration() + 1);
$current_finding->set_Scan_ID($scan->get_ID());
$current_finding->set_Category($cat_lvl);
$current_finding->set_IA_Controls($ia_controls);
$updated_finding[] = $current_finding;
}
} else {
$new = new finding($tgt->get_ID(), $ref->get_PDI_ID(), $scan->get_ID(), $status, $notes, 0, null, 1);
$new->set_Category($cat_lvl);
$new->set_IA_Controls($ia_controls);
$new_finding[] = $new;
}
if ($status == 'False Positive') {
$match = [];
if (preg_match("/\(FP\-([a-zA-Z \-]+)\)/i", $notes, $match)) {
$src = $match[1];
// $src = str_replace("_", " ", $match[1]);
$sql = "REPLACE INTO `false_positives` (`pdi_id`, `src_id`, `notes`) VALUES (" . $this->conn->real_escape_string($ref->get_PDI_ID()) . ", " . "(SELECT `id` FROM `sources` WHERE `name` = '" . $this->conn->real_escape_string($src) . "'), " . "'Common FP for $src')";
if (! $this->conn->real_query($sql)) {
error_log($this->conn->error);
Sagacity_Error::sql_handler($sql);
}
if (isset($cmd['debug'])) {
Sagacity_Error::err_handler("Added " . $ref->get_PDI_ID() . " to FP list for $src");
}
}
}
if ($status == 'Exception') {
$ste = $this->get_STE($tgt->get_STE_ID())[0];
$sql = "REPLACE INTO `exceptions` (`pdi_id`, `sys_id`, `notes`) VALUES (" . $this->conn->real_escape_string($ref->get_PDI_ID()) . ", " . $this->conn->real_escape_string($ste->get_System()
->get_ID()) . ", " . "'')";
if (! $this->conn->real_query($sql)) {
error_log($this->conn->error);
Sagacity_Error::sql_handler($sql);
}
if (isset($cmd['debug'])) {
Sagacity_Error::err_handler("Added exception " . $ref->get_PDI_ID());
}
}
$x ++;
}
$notes = (isset($current_finding) && is_array($current_finding) && count($current_finding) ? $current_finding->get_Notes() . " " . $notes : $notes);
if (isset($updated_finding) && is_array($updated_finding) && count($updated_finding) > 0) {
/** @var finding $finding */
foreach ($updated_finding as $finding) {
$this->help->update('findings', [
'scan_id' => $finding->get_Scan_ID(),
'findings_status_id' => $finding->get_Finding_Status(),
'notes' => $finding->get_Notes(),
'change_id' => $finding->get_Change_ID(),
'orig_src' => $finding->get_Original_Source(),
'finding_itr' => $finding->get_Finding_Iteration(),
'cat' => $finding->get_Category()
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $finding->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $finding->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("finding_controls", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $finding->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $finding->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
$this->help->execute();
$params = [];
foreach ($finding->get_IA_Controls() as $ia) {
$params[] = [
$finding->get_Tgt_ID(),
$finding->get_PDI_ID(),
$ia
];
}
$this->help->extended_insert('finding_controls', ['tgt_id', 'pdi_id', 'ia_control'], $params, true);
$this->help->execute();
}
}
if (isset($new_finding) && count($new_finding) > 0) {
$finding_params = [];
$finding_control_params = [];
foreach ($new_finding as $finding) {
/** @var finding $finding */
$finding_params[] = [
$finding->get_Tgt_ID(),
$finding->get_PDI_ID(),
$finding->get_Scan_ID(),
$finding->get_Finding_Status(),
$finding->get_Category(),
$finding->get_Notes()
];
foreach ($finding->get_IA_Controls() as $ia) {
$finding_control_params[] = [
$finding->get_Tgt_ID(),
$finding->get_PDI_ID(),
$ia
];
}
}
if(is_array($finding_params) && count($finding_params)) {
$this->extended_insert('findings', ['tgt_id', 'pdi_id', 'scan_id', 'findings_status_id', 'cat', 'notes'], $finding_params, true);
if($this->help->execute()) {
$this->help->extended_insert('finding_controls', ['tgt_id', 'pdi_id', 'ia_control'], $finding_control_params, true);
$this->help->execute();
}
}
}
return true;
} else {
$updated_finding = null;
$new_finding = null;
switch (strtolower(str_replace('_', ' ', $finding_data[self::FIRST_ECHECKLIST_HOST_COL]))) {
case 'not reviewed':
case 'not a finding':
case 'open':
case 'not applicable':
case 'no data':
case 'exception':
case 'false positive':
$status = str_replace('_', ' ', $finding_data[self::FIRST_ECHECKLIST_HOST_COL]);
break;
default:
$status = 'Not Reviewed';
}
$current_finding = $this->get_Finding($tgts, $ref);
if (is_array($current_finding) && count($current_finding) > 0) {
$current_finding = current($current_finding);
}
$current_status = '';
if (is_a($current_finding, 'finding')) {
$current_status = $current_finding->get_Finding_Status_String();
if ($current_status != $status) {
$current_notes = $current_finding->get_Notes();
if (! $current_notes) {
$current_finding->set_Notes($notes);
} else {
if ($notes && stristr($current_notes, $notes) === false) {
$current_finding->set_Notes($current_notes . PHP_EOL . $notes);
}
}
if (($current_status == 'Open' || $status == 'Open') && ($current_status == 'Not Applicable' || $current_status == 'Not a Finding' || $status == 'Not Applicable' || $status == 'Not a Finding')) {
$current_finding->set_Notes("OPEN CONFLICT: $current_status/$status\n$notes");
$current_finding->set_Change_ID(finding::TO_OPEN);
} elseif (($current_status == 'Not a Finding' || $current_status == 'Not Applicable') && ($status == 'Not a Finding' || $status == 'Not Applicable')) {
$current_finding->set_Notes("NF/NA CONFLICT: $current_status/$status\n$notes");
$current_finding->set_Change_ID(finding::TO_NF);
} else {
$current_finding->set_Change_ID(finding::NC);
}
$new_status = $current_finding->get_Deconflicted_Status($status);
$new_status_id = $current_finding->get_Finding_Status_ID($new_status);
$current_finding->set_Finding_Status($new_status_id);
$current_finding->set_Original_Source($scan->get_Source()
->get_Name());
$current_finding->set_Finding_Iteration($current_finding->get_Finding_Iteration() + 1);
$current_finding->set_Scan_ID($scan->get_ID());
$updated_finding = $current_finding;
} else {
$current_notes = $current_finding->get_Notes();
if (! $current_notes) {
$current_finding->set_Notes($notes);
} else {
if ($notes && stristr($current_notes, $notes) === false) {
$current_finding->set_Notes($current_notes . PHP_EOL . $notes);
}
}
$current_finding->set_Change_ID(finding::NC);
$current_finding->set_Original_Source($scan->get_Source()
->get_Name());
$current_finding->set_Finding_Iteration($current_finding->get_Finding_Iteration() + 1);
$current_finding->set_Scan_ID($scan->get_ID());
$updated_finding = $current_finding;
}
} else {
$new_finding = new finding($tgts->get_ID(), $ref->get_PDI_ID(), $scan->get_ID(), $status, $notes, 0, null, 1);
$new_finding->set_Category($cat_lvl);
$new_finding->set_IA_Controls($ia_controls);
}
$notes = (isset($current_finding) && is_array($current_finding) && count($current_finding) ? $current_finding->get_Notes() . " " . $notes : $notes);
if (isset($updated_finding) && ! is_null($updated_finding)) {
$this->help->update("findings", [
'scan_id' => $updated_finding->get_Scan_ID(),
'findings_status_id' => $updated_finding->get_Finding_status(),
'notes' => $updated_finding->get_Notes(),
'change_id' => $updated_finding->get_Change_ID(),
'orig_src' => $updated_finding->get_Original_Source(),
'finding_itr' => $updated_finding->get_Finding_Iteration(),
'cat' => $updated_finding->get_Category()
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $updated_finding->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $updated_finding->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("finding_controls", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $updated_finding->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $updated_finding->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
$this->help->execute();
$params = [];
foreach ($updated_finding->get_IA_Controls() as $ia) {
$params[] = [
$updated_finding->get_Tgt_ID(),
$updated_finding->get_PDI_ID(),
$ia
];
}
$this->help->extended_insert("finding_controls", ['tgt_id', 'pdi_id', 'ia_control'], $params, true);
$this->help->execute();
}
if (isset($new_finding) && ! is_null($new_finding)) {
$this->help->insert("findings", [
'tgt_id' => $new_finding->get_Tgt_ID(),
'pdi_id' => $new_finding->get_PDI_ID(),
'scan_id' => $new_finding->get_Scan_ID(),
'findings_status_id' => $new_finding->get_Finding_Status(),
'notes' => $new_finding->get_Notes(),
'cat' => $new_finding->get_Category()
], true);
if(!$this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$params = [];
foreach ($new_finding->get_IA_Controls() as $ia) {
$params[] = [
$new_finding->get_Tgt_ID(),
$new_finding->get_PDI_ID(),
$ia
];
}
$this->help->extended_insert('finding_controls', ['tgt_id', 'pdi_id', 'ia_control'], $params, true);
$this->help->execute();
}
return true;
}
}
/**
* Function to add findings to the database
*
* @param array:finding $updated_findings
* Array of findings to update
* @param array:finding $added_findings
* Array of findings to add to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function add_Findings_By_Target($updated_findings, $added_findings)
{
$fields = [
'pdi_id',
'tgt_id',
'scan_id',
'findings_status_id',
'notes',
'cat'
];
$ins_arr = [];
if (is_array($added_findings) && count($added_findings) && is_a(current($added_findings), 'finding')) {
$scan_id = current($added_findings)->get_Scan_ID();
foreach ($added_findings as $finding) {
/** @var finding $finding */
$ins_arr[] = [
$finding->get_PDI_ID(),
$finding->get_Tgt_ID(),
$finding->get_Scan_ID(),
$finding->get_Finding_Status(),
$finding->get_Notes(),
$finding->get_Category()
];
}
if (is_array($ins_arr) && count($ins_arr)) {
$this->help->extended_insert('findings', $fields, $ins_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
}
$this->help->sql = "INSERT IGNORE INTO `finding_controls` (`tgt_id`, `pdi_id`, `ia_control`) " .
"(SELECT f.`tgt_id`, f.`pdi_id`, " .
"(SELECT CONCAT(ia.`type`, '-', ia.`type_id`) " .
"FROM `ia_controls` ia " .
"WHERE ia.`pdi_id` = f.`pdi_id`) " .
"FROM `findings` f " .
"WHERE f.`scan_id` = $scan_id)";
$this->help->query_type = db_helper::INSERT;
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
$this->help->delete("finding_controls", null, [
[
'field' => 'ia_control',
'op' => '=',
'value' => ''
],
[
'field' => 'ia_control',
'op' => '=',
'value' => '-',
'sql_op' => 'OR'
],
[
'field' => 'ia_control',
'op' => IS,
'value' => null,
'sql_op' => 'OR'
]
]);
$this->help->execute();
}
if (is_array($updated_findings) && count($updated_findings) && is_a(current($updated_findings), 'finding')) {
$this->help->create_table("tmp_findings", true, [
[
'field' => 'tgt_id',
'datatype' => 'int(11)'
],
[
'field' => 'pdi_id',
'datatype' => 'int(11)'
],
[
'field' => 'scan_id',
'datatype' => 'int(11)'
],
[
'field' => 'findings_status_id',
'datatype' => 'int(11)'
],
[
'field' => 'change_id',
'datatype' => 'int(11)'
],
[
'field' => 'finding_itr',
'datatype' => 'int(5)'
],
[
'field' => 'cat',
'datatype' => 'int(1)'
],
[
'field' => 'notes',
'datatype' => 'text'
],
[
'field' => 'orig_src',
'datatype' => 'varchar(10)'
]
]);
$this->help->execute();
$upd_arr = [];
$update_fields = [
'tgt_id',
'pdi_id',
'scan_id',
'findings_status_id',
'change_id',
'finding_itr',
'cat',
'notes',
'orig_src'
];
foreach ($updated_findings as $finding) {
/** @var finding $finding */
$upd_arr[] = [
$finding->get_Tgt_ID(),
$finding->get_PDI_ID(),
$finding->get_Scan_ID(),
$finding->get_Finding_Status(),
$finding->get_Change_ID(),
$finding->get_Finding_Iteration(),
$finding->get_Category(),
$finding->get_Notes(),
$finding->get_Original_Source()
];
}
if (is_array($upd_arr) && count($upd_arr)) {
$this->help->extended_insert("tmp_findings", $update_fields, $upd_arr, true);
$this->help->execute();
$this->help->extended_update('findings', 'tmp_findings', '`tgt_id`,`pdi_id`', $update_fields);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
}
}
return true;
}
/**
* Function to update a finding status and notes
*
* @param finding $find
* The finding to update
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function update_Finding($find)
{
$this->help->replace("sagacity.findings", [
'tgt_id' => $find->get_Tgt_ID(),
'pdi_id' => $find->get_PDI_ID(),
'scan_id' => $find->get_Scan_ID(),
'findings_status_id' => $find->get_Finding_Status(),
'notes' => $find->get_Notes(),
'cat' => $find->get_Category()
]);
if ($this->help->execute()) {
$this->help->delete("sagacity.finding_controls", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $find->get_Tgt_ID()
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $find->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
$ia_arr = [];
foreach ($find->get_IA_Controls() as $ia) {
$ia_arr[] = [
$find->get_Tgt_ID(),
$find->get_PDI_ID(),
$ia
];
}
if (is_array($ia_arr) && count($ia_arr) && isset($ia_arr[0])) {
$this->help->extended_insert("sagacity.finding_controls", [
'tgt_id',
'pdi_id',
'control_id'
], $ia_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
}
} else {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Get count of open category I findings for a target
*
* @param integer $checklist_id
* Checklist ID to query for quantity
* @param target $tgt
* Target to query
*
* @return integer Returns the number of findings that are Cat I and with a status of 'Open' for a specific host
*/
public function get_Host_Open_Cat_1($checklist_id, $tgt)
{
$this->help->select_count("sagacity.pdi_catalog pdi", array(
array(
'field' => 'lu.checklist_id',
'op' => '=',
'value' => $checklist_id
),
array(
'field' => 'f.tgt_id',
'op' => '=',
'value' => $tgt->get_ID(),
'sql_op' => 'AND'
),
array(
'field' => 'fs.status',
'op' => '=',
'value' => 'Open',
'sql_op' => 'AND'
),
array(
'field' => 'pdi.cat',
'op' => '=',
'value' => 1,
'sql_op' => 'AND'
)
), array(
'table_joins' => array(
"JOIN sagacity.pdi_checklist_lookup lu ON lu.pdi_id=pdi.id",
"JOIN sagacity.findings f ON f.pdi_id=pdi.id",
"LEFT JOIN sagacity.stigs s ON s.pdi_id=pdi.id",
"LEFT JOIN sagacity.findings_status fs ON fs.id=f.findings_status_id"
)
));
return $this->help->execute();
}
/**
* Get count of not reviewed findings for a target
*
* @param integer $checklist_id
* Checklist ID to query for quantity
* @param target $tgt
* Target to query
*
* @return integer Returns the number of findings with a status of 'Not Reviewed' for a specific host
*/
public function get_Host_Not_Reviewed($checklist_id, $tgt)
{
$this->help->select_count("sagacity.pdi_catalog pdi", array(
array(
'field' => 'lu.checklist_id',
'op' => '=',
'value' => $checklist_id
),
array(
'field' => 'f.tgt_id',
'op' => '=',
'value' => $tgt->get_ID(),
'sql_op' => 'AND'
),
array(
'field' => 'fs.status',
'op' => '=',
'value' => 'Not Reviewed',
'sql_op' => 'AND'
)
), array(
'table_joins' => array(
"JOIN sagacity.pdi_checklist_lookup lu ON lu.pdi_id=pdi.id",
"JOIN sagacity.findings f ON f.pdi_id=pdi.id",
"LEFT JOIN sagacity.stigs s ON s.pdi_id=pdi.id",
"LEFT JOIN sagacity.findings_status fs ON fs.id=f.findings_status_id"
)
));
return $this->help->execute();
}
// }}}
// {{{ GOLDDISK CLASS FUNCTIONS
/**
* Get GoldDisk data
*
* @param string $str_VMS_ID
* [optional]
* The VMS id of the golddisk object (default null)
*
* @return array:golddisk |NULL
* Returns an array of golddisk objects, or null if none found
*/
public function get_GoldDisk($str_VMS_ID = null)
{
$ret = [];
$where = [];
if ($str_VMS_ID != null) {
$where[] = array(
'field' => 'vms_id',
'op' => '=',
'value' => $str_VMS_ID
);
}
$this->help->select("sagacity.golddisk", null, $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['pdi_id'])) {
$rows = array(
0 => $rows
);
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = new golddisk($row['pdi_id'], $row['vms_id'], $row['short_title']);
}
}
return $ret;
}
/**
* Function for retrieving a VMS using the PDI
*
* @param integer $pdi_id
* The PDI ID of the golddisk to grab
*
* @return array:golddisk |NULL
* Returns an array of golddisk, or null if none found
*/
public function get_GoldDisk_By_PDI($pdi_id)
{
$ret = [];
$this->help->select("golddisk", null, [
[
'field' => 'pdi_id',
'op' => '=',
'value' => $pdi_id
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['pdi_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = new golddisk($row['pdi_id'], $row['vms_id'], $row['short_title']);
}
} else {
$this->help->debug(E_ERROR);
}
return $ret;
}
/**
* Function to add GoldDisk to database
*
* @param golddisk $new_GoldDisk
* The golddisk object to add to database
*
* @return boolean Returns TRUE if successful, otherwise false
*/
public function save_GoldDisk($new_GoldDisk)
{
$this->help->insert("sagacity.golddisk", array(
'pdi_id' => $new_GoldDisk->get_PDI_ID(),
'vms_id' => $new_GoldDisk->get_ID(),
'short_title' => $new_GoldDisk->get_Short_Title()
), true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ IA_CONTROL CLASS FUNCTIONS
/**
* Function to get IA control from DB
*
* @param ia_control $ia
* IA Control to retrieve from the database
*
* @return ia_control|NULL Returns IA_Control object, or null if none found
*/
public function get_IA_Controls($ia)
{
$sql = "SELECT `pdi_id`, `type`, `type_id` " . "FROM `sagacity`.`ia_controls` " . "WHERE " . "`pdi_id` = " . $this->conn->real_escape_string($ia->get_PDI_ID()) . " AND " . "`type` = '" . $this->conn->real_escape_string($ia->get_Type()) . "' AND " . "`type_id` = " . $this->conn->real_escape_string($ia->get_Type_ID());
$res = $this->conn->query($sql);
if ($res->num_rows > 0) {
$row = $res->fetch_assoc();
return new ia_control($row['pdi_id'], $row['type'], $row['type_id']);
}
return null;
}
/**
* Function for retrieving all the IA controls by mac and classification
*
* @param system $sys
*
* @return array:ia_control
*/
public function get_IA_Controls_By_Mac_Class($sys)
{
$class = 'cl';
if ($sys->get_Classification() == 'Public') {
$class = 'pub';
} elseif ($sys->get_Classification() == 'Sensitive') {
$class = 'sen';
}
$ret = [];
$sql = "SELECT `proc_control` " . "FROM `sagacity`.`proc_level_type` " . "WHERE " . "`level` = " . $sys->get_MAC() . " AND " . "`class` = '$class'";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$ret[] = new ia_control(null, explode('-', $row['proc_control'])[0], explode('-', $row['proc_control'])[1]);
}
} else {
error_log($this->conn->error);
Sagacity_Error::sql_handler($sql);
}
return $ret;
}
/**
* Function for retrieving IA Controls by PDI
*
* @param integer $pdi_id
* PDI ID used to query
*
* @return array:ia_control |NULL
* Returns array of ia_controls associated with a specific PDI, or null if none found
*/
public function get_IA_Controls_By_PDI($pdi_id)
{
$sql = "SELECT " . "`pdi_id`, `type`, `type_id` " . "FROM `sagacity`.`ia_controls` " . "WHERE `pdi_id` = " . $this->conn->real_escape_string($pdi_id);
if ($res = $this->conn->query($sql)) {
$ret = [];
while ($row = $res->fetch_assoc()) {
$ret[] = new ia_control($row['pdi_id'], $row['type'], $row['type_id']);
}
return $ret;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return null;
}
/**
* Function to get the icon that represents the IA control status
*
* @param ste $ste
* @param proc_ia_controls $ctrl
*
* @return string
*/
public function get_IA_Control_Icon($ste, $ctrl)
{
$cats = $this->get_STE_Cat_List($ste->get_ID());
$total = 0;
foreach ($cats as $cat) {
$total += $this->get_Finding_Count_By_Status($cat->get_ID(), "Open", null, $ctrl);
$total += $this->get_Finding_Count_By_Status($cat->get_ID(), "Exception", null, $ctrl);
}
if ($total) {
return "error.png";
}
if (false) {
$ctrl = new proc_ia_controls();
}
if (empty($ctrl->finding->vul_desc)) {
return "exclamation.png";
} elseif (empty($ctrl->finding->mitigations)) {
return "exclamation.png";
}
return "Under_construction.svg";
}
/**
* Update an IA control
*
* @param ia_control|array:ia_control $ia_Controls
* Array of IA Controls to update
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_IA_Control($ia_Controls)
{
$params = [];
if (is_array($ia_Controls) && count($ia_Controls) && isset($ia_Controls[0]) && is_a($ia_Controls[0], 'ia_control')) {
foreach ($ia_Controls as $ia) {
$params[] = array(
$ia->get_PDI_ID(),
$ia->get_Type(),
$ia->get_Type_ID()
);
}
} elseif (is_a($ia_Controls, 'ia_control')) {
$params[] = array(
$ia_Controls->get_PDI_ID(),
$ia_Controls->get_Type(),
$ia_Controls->get_Type_ID()
);
} else {
return false;
}
$this->help->extended_replace("sagacity.ia_controls", array(
'pdi_id',
'type',
'type_id'
), $params);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ IAVM CLASS FUNCTIONS
/**
* Function for retrieving an IAVM
*
* @param integer|string $iavm_ID
* The IAVM ID to look for
*
* @return iavm|NULL Returns IAVM object, otherwise null if none found
*/
public function get_IAVM($iavm_ID)
{
$sql = "SELECT " . "iavm.`noticeId`, iavm.`pdi_id`, iavm.`xmlUrl`, iavm.`htmlUrl`, iavm.`iavmNoticeNumber`, iavm.`title`, " . "iavm.`type`, iavm.`state`, iavm.`lastUpdated`, iavm.`releaseDate`, iavm.`supersedes`, " . "iavm.`executiveSummary`, iavm.`fixAction`, iavm.`note`, iavm.`vulnAppsSysAndCntrmsrs`, " . "iavm.`stigFindingSeverity`, iavm.`knownExploits`, iavm.`file_name` " . "FROM `sagacity`.`iavm_notices` iavm";
if (is_numeric($iavm_ID)) {
$sql .= " WHERE iavm.`noticeId` = " . $this->conn->real_escape_string($iavm_ID);
} else {
$sql .= " WHERE iavm.`iavmNoticeNumber` = '" . $this->conn->real_escape_string($iavm_ID) . "'";
}
if ($res = $this->conn->query($sql)) {
if (! $res->num_rows) {
return null;
}
$notice_row = $res->fetch_assoc();
$noticeId = $notice_row['noticeId'];
$iavm = new iavm($notice_row['noticeId'], $notice_row['pdi_id'], $notice_row['xmlUrl'], $notice_row['htmlUrl'], $notice_row['iavmNoticeNumber'], $notice_row['title'], $notice_row['type'], $notice_row['state'], $notice_row['lastUpdated'], $notice_row['releaseDate'], $notice_row['supersedes'], $notice_row['executiveSummary'], $notice_row['fixAction'], $notice_row['note'], $notice_row['vulnAppsSysAndCntrmsrs'], $notice_row['stigFindingSeverity'], $notice_row['knownExploits']);
$sql = "SELECT `cve_id` FROM `sagacity`.`iavm_to_cve` WHERE `noticeId` = " . $notice_row['noticeId'];
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
while ($row2 = $res2->fetch_assoc()) {
$iavm->add_CVE($row2['cve_id']);
}
}
}
$sql = "SELECT `id`, `title`, `url` FROM `sagacity`.`iavm_references` " . "WHERE `iavm_notice_id` = " . $this->conn->real_escape_string($noticeId);
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
while ($ref_row = $res2->fetch_assoc()) {
$iavm->add_Reference(new iavm_reference($ref_row['id'], $ref_row['title'], $ref_row['url']));
}
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
$sql = "SELECT `id`, `details` FROM `sagacity`.`iavm_tech_overview` " . "WHERE `iavm_notice_id` = " . $this->conn->real_escape_string($noticeId);
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
$to_row = $res2->fetch_assoc();
$to = new iavm_tech_overview($to_row['id'], $to_row['details']);
$iavm->set_Tech_Overview($to);
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
$sql = "SELECT `id`, `type`, `title`, `url` FROM sagacity.iavm_patches " . "WHERE `iavm_notice_id` = " . $this->conn->real_escape_string($noticeId);
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
while ($patch_row = $res2->fetch_assoc()) {
$iavm->add_Patch(new iavm_patch($patch_row['id'], $patch_row['type'], $patch_row['title'], $patch_row['url']));
}
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
$sql = "SELECT `header`, `body` FROM `sagacity`.`iavm_mitigations` " . "WHERE `iavm_notice_id` = " . $this->conn->real_escape_string($noticeId);
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
$mit_row = $res2->fetch_assoc();
$iavm->set_Mitigation(new iavm_mitigation($mit_row['header'], $mit_row['body']));
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
$sql = "SELECT `bid` FROM `sagacity`.`iavm_bids` " . "WHERE `iavm_notice_id` = " . $this->conn->real_escape_string($noticeId);
if ($res2 = $this->conn->query($sql)) {
if ($res2->num_rows) {
while ($bid_row = $res2->fetch_assoc()) {
$iavm->add_Bid(new iavm_bid($bid_row['bid']));
}
}
}
return $iavm;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return null;
}
/**
* Get IAVM from external data (reference or patch)
*
* @param string $ext
* The external data to search for
*
* @return iavm|NULL Returns an iavm object if any are found, otherwise NULL
*/
public function get_IAVM_From_External($ext)
{
$sql = "SELECT `iavm_notice_id` FROM `sagacity`.`iavm_references` " . "WHERE `title` LIKE '%" . $this->conn->real_escape_string($ext) . "%' OR " . "`url` LIKE '%" . $this->conn->real_escape_string($ext) . "%' " . "GROUP BY `iavm_notice_id` " . "ORDER BY `iavm_notice_id` DESC";
if ($res = $this->conn->query($sql)) {
if ($res->num_rows) {
$row = $res->fetch_assoc();
$iavm = $this->get_IAVM($row['iavm_notice_id']);
return $iavm;
}
} else {
Sagacity_Error::sql_handler($sql);
}
$sql = "SELECT `iavm_notice_id` FROM `sagacity`.`iavm_patches` " . "WHERE `title` LIKE '%" . $this->conn->real_escape_string($ext) . "%' OR " . "`url` LIKE '%" . $this->conn->real_escape_string($ext) . "%' " . "GROUP BY `iavm_notice_id` " . "ORDER BY `iavm_notice_id` DESC";
if ($res = $this->conn->query($sql)) {
if ($res->num_rows) {
$row = $res->fetch_assoc();
$iavm = $this->get_IAVM($row['iavm_notice_id']);
return $iavm;
}
} else {
Sagacity_Error::sql_handler($sql);
}
return null;
}
/**
* Method to save IAVM BIDs
*
* @param iavm $iavm
*/
public function save_Iavm_Bids($iavm)
{
$params = [];
if (is_array($iavm->get_Bids()) && count($iavm->get_Bids())) {
foreach ($iavm->get_Bids() as $bid) {
$params[] = [
$iavm->get_Notice_ID(),
$bid
];
}
}
if (count($params)) {
$this->help->extended_replace('iavm_bids', [
'iavm_notice_id',
'bid'
], $params);
$this->help->execute();
}
}
/**
* Method to save IAVM mitigations
*
* @param iavm $iavm
*/
public function save_Iavm_Mitigation($iavm)
{
if ($iavm->get_Mitigation()) {
$this->help->replace("iavm_mitiagations", [
'iavm_notice_id' => $iavm->get_Notice_ID(),
'header' => $iavm->get_Mitigation()
->get_Header(),
'body' => $iavm->get_Mitigation()
->get_Text()
]);
$this->help->execute();
}
}
/**
* Method to save IAVM patches
*
* @param iavm $iavm
*/
public function save_Iavm_Patches($iavm)
{
$params = [];
if (is_array($iavm->get_Patches()) && count($iavm->get_Patches())) {
foreach ($iavm->get_Patches() as $patch) {
$params[] = [
$iavm->get_Notice_ID(),
$patch->get_Type(),
$patch->get_Title(),
$patch->get_URL()
];
}
}
if (count($params)) {
$this->help->extended_replace("iavm_patches", [
'iavm_notice_id',
'type',
'title',
'url'
], $params);
$this->help->execute();
}
}
/**
* Method to save IAVM references
*
* @param iavm $iavm
*/
public function save_Iavm_References($iavm)
{
$params = [];
if (is_array($iavm->get_References()) && count($iavm->get_References())) {
foreach ($iavm->get_References() as $ref) {
$params[] = [
$iavm->get_Notice_ID(),
$ref->get_Title(),
$ref->get_URL()
];
}
}
if (count($params)) {
$this->help->extended_replace("iavm_references", [
'iavm_notice_id',
'title',
'url'
], $params);
$this->help->execute();
}
}
/**
* Method to save IAVM tech overview data
*
* @param iavm $iavm
*/
public function save_Iavm_Tech_Overview($iavm)
{
if ($iavm->get_Tech_Overview()) {
$this->help->replace("iavm_tech_overview", [
'iavm_notice_id' => $iavm->get_Notice_ID(),
'details' => $iavm->get_Tech_Overview()
->get_Details()
]);
$this->help->execute();
}
}
/**
* Method to save IAVM-to-CVE references
*
* @param iavm $iavm
*/
public function save_Iavm_Cves($iavm)
{
$params = [];
if (is_array($iavm->get_CVE()) && count($iavm->get_CVE())) {
foreach ($iavm->get_CVE() as $cve) {
$params[] = [
$iavm->get_Notice_ID(),
$cve
];
}
}
if (count($params)) {
$this->help->extended_replace("iavm_to_cve", [
'noticeId',
'cve_id'
], $params);
$this->help->execute();
}
}
/**
* Function to save IAVMs
*
* @param iavm $iavm_in
* The IAVM to save
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_IAVM($iavm_in)
{
// check to see if the IAVM already exists
$db_iavm = $this->get_IAVM($iavm_in->get_Notice_ID());
if (is_null($db_iavm)) {
$this->help->insert('iavm_notices', [
'noticeId' => $iavm_in->get_Notice_ID(),
'pdi_id' => $iavm_in->get_PDI_ID(),
'xmlUrl' => $iavm_in->get_XML_URL(),
'htmlUrl' => $iavm_in->get_HTML_URL(),
'iavmNoticeNumber' => $iavm_in->get_Notice_Number(),
'title' => $iavm_in->get_Title(),
'type' => $iavm_in->get_Type(),
'state' => $iavm_in->get_State(),
'lastUpdate' => $iavm_in->get_Last_Updated_Date(),
'releaseDate' => $iavm_in->get_Release_Date_Date(),
'supersedes' => $iavm_in->get_Supersedes(),
'executiveSummary' => $iavm_in->get_Executive_Summary(),
'fixAction' => $iavm_in->get_Fix_Action(),
'note' => $iavm_in->get_Notes(),
'vulnAppsSysAndCntrmsrs' => $iavm_in->get_Vuln_Apps(),
'stigFindingSeverity' => $iavm_in->get_Stig_Severity(),
'knownExploits' => $iavm_in->get_Known_Exploits()
]);
} else {
$this->help->update("iavm_notices", [
'type' => $iavm_in->get_Type(),
'state' => $iavm_in->get_State(),
'lastUpdated' => $iavm_in->get_Last_Updated_Date(),
'supersedes' => $iavm_in->get_Supersedes(),
'executiveSummary' => $iavm_in->get_Executive_Summary(),
'fixAction' => $iavm_in->get_Fix_Action(),
'knownExploits' => $iavm_in->get_Known_Exploits(),
'note' => $iavm_in->get_Notes(),
'vulnAppsSysAndCntrmsrs' => $iavm_in->get_Vuln_Apps()
], [
[
'field' => 'noticeId',
'op' => '=',
'value' => $iavm_in->get_Notice_ID()
]
]);
}
if ($this->help->execute()) {
$this->save_Iavm_Bids($iavm_in);
$this->save_Iavm_Mitigation($iavm_in);
$this->save_Iavm_Patches($iavm_in);
$this->save_Iavm_References($iavm_in);
$this->save_Iavm_Tech_Overview($iavm_in);
$this->save_Iavm_Cves($iavm_in);
}
}
// }}}
// {{{ INTERFACES CLASS FUNCTIONS
/**
* Get all interfaces for a target
*
* @param integer $tgtID
* Target ID to get interface information for
*
* @return array:interfaces|NULL Returns array of interfaces (with ports), or NULL if none found
*/
public function get_Interfaces($tgtID)
{
$ret = [];
if (! $tgtID) {
return [];
}
$this->help->select("sagacity.interfaces", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgtID
],
[
'field' => 'ipv4',
'op' => '!=',
'value' => '',
'sql_op' => 'AND'
],
[
'field' => 'ipv4',
'op' => IS_NOT,
'value' => null,
'sql_op' => 'AND'
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$int = new interfaces($row['id'], $row['tgt_id'], $row['name'], $row['ipv4'], $row['ipv6'], $row['hostname'], $row['fqdn'], $row['description']);
$int->set_MAC($row['mac']);
$this->help->select("sagacity.get_ports", null, [
[
'field' => 'int_id',
'op' => '=',
'value' => $row['id']
]
]);
$rows2 = $this->help->execute();
if (is_array($rows2) && count($rows2) && isset($rows2['id'])) {
$rows2 = [
0 => $rows2
];
}
if (is_array($rows2) && count($rows2) && isset($rows2[0])) {
foreach ($rows2 as $p) {
if ($p['proto'] == 'tcp') {
$port = new tcp_ports($p['id'], $p['port'], $p['name'], $p['banner'], $p['notes']);
$int->add_TCP_Ports($port);
} else {
$port = new udp_ports($p['id'], $p['port'], $p['name'], $p['banner'], $p['notes']);
$int->add_UDP_Ports($port);
}
}
}
if ($row['ipv6']) {
$ret[$row['ipv6']] = $int;
} else {
$ret[$row['ipv4']] = $int;
}
}
}
return $ret;
}
/**
* Function to get an interface object using the IP address
*
* @param integer $tgt_id
* @param string $ip
*
* @return NULL|interfaces
*/
public function get_Interface_By_IP($tgt_id, $ip)
{
$this->help->select("sagacity.interfaces", null, array(
array(
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
),
array(
'field' => 'ipv4',
'op' => '=',
'value' => $ip,
'sql_op' => 'AND',
'open-paren' => true
),
array(
'field' => 'ipv6',
'op' => '=',
'value' => $ip,
'sql_op' => 'OR',
'close-paren' => true
)
));
$row = $this->help->execute();
if (is_null($row)) {
return null;
}
$int = new interfaces($row['id'], $row['tgt_id'], $row['name'], $row['ipv4'], $row['ipv6'], $row['hostname'], $row['fqdn'], $row['description']);
$this->help->select("sagacity.ports_proto_services pps", array(
'pps.id',
'pps.port',
'pps.proto',
"IF(ppsl.name != pps.IANA_Name, ppsl.name, pps.IANA_Name) AS 'name'",
"IF(ppsl.banner != pps.banner, ppsl.banner, pps.banner) AS 'banner'",
"IF(ppsl.notes != pps.notes, ppsl.notes, pps.notes) AS 'notes'"
), array(
array(
'field' => 'ppsl.int_id',
'op' => '=',
'value' => $row['id']
),
array(
'field' => 'pps.id',
'op' => IN,
'value' => "(SELECT pps_id FROM sagacity.pps_list WHERE int_id={$row['id']})",
'sql_op' => 'AND'
)
), array(
'table_joins' => array(
"LEFT JOIN sagacity.pps_list ppsl ON ppsl.pps_id=pps.id"
)
));
$rows2 = $this->help->execute();
if (is_array($rows2) && count($rows2) && isset($rows2['id'])) {
$rows2 = array(
0 => $rows2
);
}
if (is_array($rows2) && count($rows2) && isset($rows2[0])) {
foreach ($rows2 as $port) {
$class = "{$port['proto']}_ports";
$method = "add_" . strtoupper($port['proto']) . "_Ports";
$port = new $class($port['id'], $port['port'], $port['name'], $port['banner'], $port['notes']);
$int->$method($port);
}
}
return $int;
}
/**
* Return the last ID of the last interface in the database
*
* @return integer Returns the ID of the last interface that was inserted
*/
public function get_Last_Interface_ID()
{
$this->help->select("sagacity.interfaces", array(
'id'
), [], array(
'order' => 'id DESC',
'limit' => 1
));
$row = $this->help->execute();
if (isset($row['id']) && $row['id'])
return $row['id'];
else
return 0;
}
/**
* Save an interface
*
* @param array|interfaces $req
* Associative array of data to insert into database
* @param string $action
* [optional]
* String representing the action to be taken ('insert','update', defaulted to 'insert')
* @param integer $tgt_id
* [optional]
* Integer that the interface info is going to be save to (defaulted to 0)
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Interface($req, $action = 'insert')
{
if ($action == 'insert') {
if (is_array($req)) {
$first = array_shift($req);
if (! is_a($first, 'interfaces')) {
return false;
}
$req[$first->get_IPv4()] = $first;
foreach ($req as $int) {
$this->help->insert("sagacity.interfaces", array(
'tgt_id' => $int->get_TGT_ID(),
'ipv4' => $int->get_IPv4(),
'ipv6' => $int->get_IPv6(),
'hostname' => $int->get_Hostname(),
'fqdn' => $int->get_FQDN(),
'description' => $int->get_Description(),
'mac' => $int->get_MAC()
), true);
if (! ($int_id = $this->help->execute())) {
$this->help->debug(E_ERROR);
return false;
}
$int->set_ID($int_id);
$ports = [];
if (is_array($int->get_TCP_Ports()) && count($int->get_TCP_Ports())) {
foreach ($int->get_TCP_Ports() as $tcp) {
$ports[] = array(
$int->get_ID(),
$tcp->get_ID(),
$tcp->get_Banner(),
$tcp->get_Notes()
);
}
}
if (is_array($int->get_UDP_Ports()) && count($int->get_UDP_Ports())) {
foreach ($int->get_UDP_Ports() as $udp) {
$ports[] = array(
$int->get_ID(),
$udp->get_ID(),
$udp->get_Banner(),
$udp->get_Notes()
);
}
}
if (count($ports)) {
$this->help->extended_insert("pps_list", array(
'int_id',
'pps_id',
'banner',
'notes'
), $ports, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
}
}
} elseif (is_a($req, 'interfaces')) {
$this->help->insert("interfaces", array(
'tgt_id' => $req->get_TGT_ID(),
'ipv4' => $req->get_IPv4(),
'ipv6' => $req->get_IPv6(),
'hostname' => $req->get_Hostname(),
'fqdn' => $req->get_FQDN(),
'description' => $req->get_Description(),
'mac' => $req->get_MAC()
), true);
if (! ($int_id = $this->help->execute())) {
$this->help->debug(E_ERROR);
return false;
}
$req->set_ID($int_id);
$ports = [];
if (is_array($req->get_TCP_Ports()) && count($req->get_TCP_Ports())) {
foreach ($req->get_TCP_Ports() as $tcp) {
$ports[] = array(
$int->get_ID(),
$tcp->get_ID(),
$tcp->get_Banner(),
$tcp->get_Notes()
);
}
}
if (is_array($req->get_UDP_Ports()) && count($req->get_UDP_Ports())) {
foreach ($req->get_UDP_Ports() as $udp) {
$ports[] = array(
$int->get_ID(),
$udp->get_ID(),
$udp->get_Banner(),
$udp->get_Notes()
);
}
}
if (count($ports)) {
$this->help->extended_insert("sagacity.pps_list", array(
'int_id',
'pps_id',
'banner',
'notes'
), $ports, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
}
} else {
$this->help->insert("interfaces", array(
'tgt_id' => $req['tgt_id'],
'ipv4' => $req['ipv4'],
'hostname' => (isset($req['hostname']) ? $req['hostname'] : $req['ipv4']),
'fadn' => (isset($req['fqdn']) ? $req['fqdn'] : $req['fqdn'])
), true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
}
} else {
if (isset($req['ip']) && $req['ip'] != null) {
foreach ($req['ip'] as $int_id => $val) {
if (isset($req['new'][$int_id])) {
$this->help->insert("sagacity.interfaces", [
'tgt_id' => $req['tgt'],
'ipv4' => $req['ip'][$int_id],
'hostname' => $req['hostname'][$int_id],
'fqdn' => $req['fqdn'][$int_id],
'name' => $req['name'][$int_id],
'description' => $req['description'][$int_id]
], true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
} elseif ($val != 'DELETE') {
$this->help->update("sagacity.interfaces", [
'name' => $req['name'][$int_id],
'ipv4' => $val,
'hostname' => $req['hostname'][$int_id],
'fqdn' => $req['fqdn'][$int_id],
'description' => $req['description'][$int_id]
], [
[
'field' => 'id',
'op' => '=',
'value' => $int_id
]
]);
if ($this->help->execute()) {
$tcp_ports = isset($req['tcp_port'][$int_id]) ? $req['tcp_port'][$int_id] : [];
$udp_ports = isset($req['udp_port'][$int_id]) ? $req['udp_port'][$int_id] : [];
$ports = [];
if (is_array($tcp_ports) && count($tcp_ports) > 0) {
foreach ($tcp_ports as $port_id => $val2) {
$ports[] = [
$int_id,
$port_id,
$req['iana_name'][$int_id][$port_id],
$req['banner'][$int_id][$port_id],
$req['notes'][$int_id][$port_id]
];
}
}
if (is_array($udp_ports) && count($udp_ports) > 0) {
foreach ($udp_ports as $int_id => $val2) {
$ports[] = [
$int_id,
$port_id,
$req['iana_name'][$int_id][$port_id],
$req['banner'][$int_id][$port_id],
$req['notes'][$int_id][$port_id]
];
}
}
if (count($ports)) {
$this->help->extended_insert("pps_list", [
'int_id',
'pps_id',
'name',
'banner',
'notes'
], $ports, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
}
} else {
$this->help->debug(E_ERROR);
return false;
}
} else {
$this->help->delete("sagacity.pps_list", null, array(
array(
'field' => 'int_id',
'op' => '=',
'value' => $int_id
)
));
$this->help->execute();
$this->help->delete("sagacity.interfaces", null, array(
array(
'field' => 'id',
'op' => '=',
'value' => $int_id
)
));
$this->help->execute();
}
}
}
}
return true;
}
/**
* Function to delete an target interface from the database
*
* @param int $id
* The ID of the interface to be deleted
*
* @return boolean Returns TRUE if interface successfully deleted, otherwise FALSE
*/
public function delete_Interface($id)
{
// delete all associated ports
$this->help->delete("sagacity.pps_list", null, [
[
'field' => 'int_id',
'op' => '=',
'value' => $id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
// delete the interface itself
$this->help->delete("sagacity.interfaces", null, [
[
'field' => 'id',
'op' => '=',
'value' => $id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
return true;
}
// {{{ Ports
/**
* Save the port to the database
*
* @param interfaces $int
* Interface to tie the ports to
* @param array:tcp_ports|array:udp_ports $ports
* Array of tcp and udp ports that are to be saved
* @param string $action
* [optional]
* Whether or not the ports are to be updated or inserted (defaulted 'insert')
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Ports($int, $ports, $action = 'insert')
{
$ret = true;
$ins_sql = 'REPLACE INTO `sagacity`.`pps_list` (`int_id`,`pps_id`,`name`,`banner`,`notes`) VALUES ';
if ($action == 'insert') {
foreach ($ports as $port) {
$ins_sql .= "(" . $int->get_ID() . ", " . "(SELECT `id` FROM `sagacity`.`ports_proto_services` WHERE `port` = '" . $port->get_Port() . "'" . " AND `proto` = '" . (is_a($port, 'tcp_ports') ? 'tcp' : 'udp') . "' " . " AND `notes` NOT LIKE '%historic%' LIMIT 1), " . "'" . $this->conn->real_escape_string($port->get_IANA_Name()) . "', " . "'" . $this->conn->real_escape_string($port->get_Banner()) . "', " . "'" . $this->conn->real_escape_string($port->get_Notes()) . "'), ";
}
$ins_sql = substr($ins_sql, 0, - 1);
if (strlen($ins_sql) > 84) {
if (! $this->conn->real_query($ins_sql)) {
Sagacity_Error::sql_handler($ins_sql);
error_log($this->conn->error);
$ret = false;
}
}
} else {}
return $ret;
}
// }}}
// {{{ TCP_PORTS CLASS FUNCTIONS
/**
* Get TCP port data
*
* @param integer $port_number
* [optional]
* Port number to retrieve from database
*
* @return array:tcp_ports|NULL Returns array of tcp ports, or null if none found
*/
public function get_TCP_Ports($port_number = null)
{
$ret = [];
$where = [
[
'field' => 'proto',
'op' => '=',
'value' => 'tcp'
]
];
if (! is_null($port_number)) {
$where[] = [
'field' => 'port',
'op' => '=',
'value' => $port_number,
'sql_op' => 'AND'
];
}
$this->help->select("ports_proto_services", [
'id',
'port',
'iana_Name',
'banner',
'notes'
], $where);
$rows = $this->help->execute();
if (isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = new tcp_ports($row['id'], $row['port'], $row['iana_Name'], $row['banner'], $row['notes']);
}
return $ret;
}
return null;
}
// }}}
// {{{ UDP_PORTS CLASS FUNCTIONS
/**
* Get UDP port data
*
* @param integer $port_number
* Port number to retrieve from database
*
* @return array:udp_ports|NULL Returns array of udp ports, or null if none found
*/
public function get_UDP_Ports($port_number = null)
{
$ret = [];
$where = [
[
'field' => 'proto',
'op' => '=',
'value' => 'udp'
]
];
if (! is_null($port_number)) {
$where[] = [
'field' => 'port',
'op' => '=',
'value' => $port_number,
'sql_op' => 'AND'
];
}
$this->help->select("ports_proto_services", [
'id',
'port',
'iana_Name',
'banner',
'notes'
], $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = new udp_ports($row['id'], $row['port'], $row['iana_Name'], $row['banner'], $row['notes']);
}
return $ret;
}
return null;
}
// }}}
// {{{ NESSUS CLASS FUNCTIONS
/**
* Function to retrieve a nessus object
*
* @param string $nessus_id
* Nessus ID of the object you want
*
* @return nessus|NULL Returns nessus object and associated references, or null if none found
*/
public function get_Nessus($nessus_id)
{
$this->help->select("nessus_plugins np", null, [
[
'field' => 'np.plugin_id',
'op' => '=',
'value' => $nessus_id
]
], [
'table_joins' => [
"LEFT JOIN sagacity.nessus n ON n.nessus_id = np.plugin_id"
]
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['plugin_id'])) {
$nessus = new nessus($row['pdi_id'], $row['plugin_id']);
$nessus->set_Name($row['name']);
$nessus->set_Copyright($row['copyright']);
$nessus->set_Version($row['version']);
$nessus->set_FileDate($row['file_date']);
$nessus->set_FileName($row['file_name']);
$this->help->select("sagacity.nessus_meta", null, [
[
'field' => 'plugin_id',
'op' => '=',
'value' => $row['plugin_id']
]
]);
if ($rows = $this->help->execute()) {
if (is_array($rows) && count($rows)) {
foreach ($rows as $row) {
$nessus->add_Reference($row['type'], $row['val']);
}
}
}
return $nessus;
}
return null;
}
/**
* Update Nessus data
*
* @param array:nessus|nessus $nessus
* Nessus object to update
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Nessus($nessus)
{
$nessus_arr = [];
$meta_arr = [];
$plugins_arr = [];
$update_arr = [];
$nessus_fields = array(
'pdi_id',
'nessus_id'
);
$meta_fields = array(
'plugin_id',
'type',
'val'
);
$plugins_fields = array(
'plugin_id',
'name',
'copyright',
'version',
'file_name',
'file_date'
);
$this->help->create_table("tmp_nessus", true, array(
array(
'field' => 'plugin_id',
'datatype' => 'int(11)',
'options' => 'primary key'
),
array(
'field' => 'name',
'datatype' => 'varchar(255)'
),
array(
'field' => 'copyright',
'datatype' => 'varchar(255)'
),
array(
'field' => 'version',
'datatype' => 'varchar(45)'
),
array(
'field' => 'file_name',
'datatype' => 'varchar(100)'
),
array(
'field' => 'file_date',
'datatype' => 'int(11)'
)
));
$this->help->execute();
if (is_a($nessus, 'nessus')) {
$nessus = array(
0 => $nessus
);
}
if (is_array($nessus)) {
$refs = [];
foreach ($nessus as $plug) {
$db_nessus = $this->get_Nessus($plug->get_Nessus_ID());
if (is_null($db_nessus)) {
if (! $plug->get_PDI_ID()) {
$pdi = new pdi(null, $plug->get_Category(), $plug->get_FileDate_Date());
$pdi->set_Short_Title($plug->get_Name());
$pdi->set_Group_Title($plug->get_Name());
$pdi->set_Description($plug->get_Description());
$pdi->set_ID($this->save_PDI($pdi));
$plug->set_PDI_ID($pdi->get_ID());
$stig = new stig($plug->get_PDI_ID(), $plug->get_Nessus_ID(), $plug->get_Name());
$this->add_Stig($stig);
}
$plugins_arr[] = [
$plug->get_Nessus_ID(),
$plug->get_Name(),
$plug->get_Copyright(),
$plug->get_Version(),
$plug->get_FileName(),
$plug->get_FileDate()
];
$refs = $plug->get_Reference();
} else {
$update_arr[] = [
$plug->get_Nessus_ID(),
$plug->get_Name(),
$plug->get_Copyright(),
$plug->get_Version(),
$plug->get_FileName(),
$plug->get_FileDate()
];
$refs = $plug->compare_References($db_nessus);
}
$nessus_arr[] = [
$plug->get_PDI_ID(),
$plug->get_Nessus_ID()
];
if (is_array($refs) && count($refs)) {
foreach ($refs as $type => $ref) {
foreach ($ref as $val) {
$meta_arr[] = array(
$plug->get_Nessus_ID(),
$type,
$val
);
}
}
}
}
if (is_array($plugins_arr) && count($plugins_arr)) {
$this->help->extended_insert("nessus_plugins", $plugins_fields, $plugins_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
}
if (is_array($update_arr) && count($update_arr)) {
$this->help->extended_insert("tmp_nessus", $plugins_fields, $update_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
$this->help->extended_update("nessus_plugins", "tmp_nessus", "plugin_id", $plugins_fields);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
}
if (is_array($nessus_arr) && count($nessus_arr)) {
$this->help->extended_insert("nessus", $nessus_fields, $nessus_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
}
if (is_array($meta_arr) && count($meta_arr)) {
$this->help->extended_insert("nessus_meta", $meta_fields, $meta_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
}
} else {
return false;
}
return true;
}
// }}}
// {{{ OVAL CLASS FUNCTIONS
/**
* Getter function for oval
*
* @param string $oval_id
* Oval ID to retrieve from database
*
* @return oval|NULL Returns oval object, or null if none found
*/
public function get_Oval($oval_id)
{
$oval = null;
$sql = "SELECT " . "`pdi_id`, `oval_id`, `title`, `desc`, `platform`, `ext_def`, `ext_def_op` " . "FROM sagacity.oval " . "WHERE `oval_id` = '" . $this->conn->real_escape_string($oval_id) . "'";
if ($res = $this->conn->query($sql)) {
$row = $res->fetch_assoc();
$oval = new oval($row['pdi_id'], $row['oval_id'], $row['title'], $row['desc'], $row['platform'], $row['ext_def'], $row['ext_def_op']);
$sql = "SELECT" . "`oval_id`, `source`, `url`, `ref_id` " . "FROM sagacity.oval_ref " . "WHERE `oval_id` = '" . $this->conn->real_escape_string($row['oval_id']) . "'";
if ($res2 = $this->conn->query($sql)) {
while ($row2 = $res2->fetch_assoc()) {
$ref = new oval_ref($row2['oval_id'], $row2['source'], $row2['url'], $row2['ref_id']);
$oval->add_Reference($ref);
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return $oval;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return null;
}
/**
* Function to create a OVAL xml file to import into SCC
*
* @param string $os
* Operating system version to query
*
* @return string Returns string representing XML
*/
public function get_OS_Oval($os)
{
$xmlns = "xmlns = 'http://oval.mitre.org/XMLSchema/oval-definitions-5#windows'";
// ------------------------------ Start <registry_test> -----------------------------
// create temporary db table to combine all OVAL checks marked 'M' and not 'M'
$tmp_sql = "CREATE TEMPORARY TABLE `tmp_oval` SELECT " . "pdi.`id`, o.`oval_id`, s.`stig_id`, vms.`vms_id`, pdi.`check_contents`, pdi.`short_title` " . "FROM `pdi_catalog` AS pdi " . "LEFT JOIN `oval` AS o ON pdi.`id` = o.`pdi_id` " . "LEFT JOIN `stigs` AS s ON pdi.`id` = s.`pdi_id` " . "LEFT JOIN `golddisk` AS vms ON pdi.`id` = vms.`pdi_id` " . "LEFT JOIN `pdi_checklist_lookup` AS lookup ON pdi.`id` = lookup.`pdi_id` " . "LEFT JOIN `checklist` AS c ON lookup.`checklist_id` = c.`id` " . "LEFT JOIN `software` AS sft ON sft.`id` = c.`sw_id` " . "WHERE " . "o.`oval_id` = 'M' AND " . "pdi.`check_contents` LIKE '%Registry Hive%' AND " . "sft.`man` = 'MS' AND " . "sft.`name` = 'Windows' AND " . "sft.`ver` = '$os' " . "GROUP BY `stig_id`";
$this->conn->real_query($tmp_sql);
// delete rows in temporary table from other checklist that cannot designated as manual
$del_sql = "DELETE FROM tmp_oval " . "WHERE `id` IN (" . "SELECT pdi.`id` " . "FROM `pdi_catalog` AS pdi " . "LEFT JOIN `oval` AS o ON pdi.`id` = o.`pdi_id` " . "LEFT JOIN `stigs` AS s ON pdi.`id` = s.`pdi_id` " . "LEFT JOIN `golddisk` AS vms ON pdi.`id` = vms.`pdi_id` " . "LEFT JOIN `pdi_checklist_lookup` AS lookup ON pdi.`id` = lookup.`pdi_id` " . "LEFT JOIN `checklist` AS c ON lookup.`checklist_id` = c.`id` " . "LEFT JOIN `software` AS sft ON sft.`id` = c.`sw_id` " . "WHERE " . "o.`oval_id` != 'M' AND " . "pdi.`check_contents` REGEXP 'Registry Hive' AND " . "sft.`man` = 'MS' AND " . "sft.`name` = 'Windows' AND " . "sft.`ver` = '$os' " . "GROUP BY pdi.`id`)";
$this->conn->real_query($del_sql);
$sql = "SELECT " . "`id`, `oval_id`, `stig_id`, `vms_id`, `check_contents`, `short_title` " . "FROM `tmp_oval`";
if ($sth = $this->conn->prepare($sql)) {
if ($sth->execute()) {
$pdi_id = 0;
$oval_id = '';
$stig_id = '';
$vms_id = '';
$check_contents = '';
$short_title = '';
$x = 0;
$sth->bind_result($pdi_id, $oval_id, $stig_id, $vms_id, $check_contents, $short_title);
// oval_file xml validation check
$root = '<?xml version="1.0" encoding="UTF-8"?><oval_definitions xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oval="http://oval.mitre.org/XMLSchema/oval-common-5" xmlns:oval-def="http://oval.mitre.org/XMLSchema/oval-definitions-5" xmlns:windows-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#windows" xmlns:independent-def="http://oval.mitre.org/XMLSchema/oval-definitions-5#independent" xsi:schemaLocation="http://oval.mitre.org/XMLSchema/oval-definitions-5#windows http://oval.mitre.org/language/download/schema/version5.3/ovaldefinition/complete/windows-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5#independent http://oval.mitre.org/language/download/schema/version5.3/ovaldefinition/complete/independent-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-definitions-5 http://oval.mitre.org/language/download/schema/version5.3/ovaldefinition/complete/oval-definitions-schema.xsd http://oval.mitre.org/XMLSchema/oval-common-5 http://oval.mitre.org/language/download/schema/version5.3/ovaldefinition/complete/oval-common-schema.xsd">';
// declaring string variables and setting values to empty
$def = '';
$tst = '';
$obj = '';
$ste = '';
// generator node in xml format
$date = new DateTime(); // insert date and time when file completed
$gen = "<generator><oval:product_name>DISA FSO</oval:product_name><oval:schema_version>5.3</oval:schema_version><oval:timestamp>" . $date->format(DATE_W3C) . "</oval:timestamp></generator>";
while ($sth->fetch()) {
$x ++;
$match = [];
preg_match('/Registry Hive: +(\S*)/', $check_contents, $match);
$hive = $match[1];
preg_match('/(Subkey|Path|Registry Path): +(\\\)?(.*)/', $check_contents, $match);
$path = is_array($match) && count($match) > 3 ? $match[3] : "STIG ID: $stig_id" . PHP_EOL;
preg_match('/Value Name: +(\S*)/', $check_contents, $match);
$name = is_array($match) && count($match) > 1 ? $match[1] : '';
if (is_array($match) && count($match) == 2) {
$c_operator = 'AND';
$c_count = 1;
}
preg_match('/Type: +(\S*)/', $check_contents, $match);
$type = is_array($match) && count($match) > 0 ? $match[1] : "PDI ID: $pdi_id" . PHP_EOL;
preg_match('/Value: +(\S*)/', $check_contents, $match);
$value = is_array($match) && count($match) > 0 ? $match[1] : "PDI ID: $pdi_id" . PHP_EOL;
if (strpos($type, "PDI ID: " . $pdi_id) !== false) {
// print "$pdi_id, $vms_id this VMS item cannot be automated".PHP_EOL.PHP_EOL;
continue;
}
// variables set for various xml nodes
$def_id = 'oval:smc.gpea.windows:def:' . $pdi_id;
$tst_id = 'oval:smc.gpea.windows:tst:' . $pdi_id . "00";
$ste_id = 'oval:smc.gpea.windows:ste:' . $pdi_id . "00";
$obj_id = 'oval:smc.gpea.windows:obj:' . $pdi_id . "00";
$var_id = 'oval:smc.gpea.windows:var:' . $pdi_id . "00";
$def_class = 'compliance';
$m_family = 'windows';
$aft_platform = 'Microsoft Windows ' . $os;
$tst_chk_existence = ($c_count == 1 ? "all_exist" : '');
// definitions node in xml format
$def .= "<definition id='$def_id' version='1' class='$def_class'>" . "<metadata>" . "<title>$short_title</title>" . "<affected family='$m_family'>" . "<platform>$aft_platform</platform>" . "</affected>" . "<reference source='' ref_url='' ref_id='' />" . "<description>$short_title</description>" . "</metadata>" . "<criteria operator='$c_operator'>";
if ($c_count == 1) {
$def .= "<criterion test_ref='oval:smc.gpea.windows:tst:" . $pdi_id . "00' comment='" . $hive . "\\" . $path . "!" . $name . " value equals variable' />" . PHP_EOL;
}
$def .= "</criteria></definition>";
$tst .= "<registry_test $xmlns id='$tst_id' version='1' check_existence='$tst_chk_existence' check='all' comment='comment'>" . "<object object_ref='$obj_id' />" . "<state state_ref='$ste_id' />" . "</registry_test>";
if (substr($path, - 1) != "\\") {
$path .= "\\";
}
$obj .= "<registry_object $xmlns id='$obj_id' version='1' comment='comment'>" . "<hive datatype='string'>" . strtoupper($hive) . "</hive>" . "<key datatype='string'>$path</key>" . "<name datatype='string'>$name</name>" . "</registry_object>";
$ste .= "<registry_state $xmlns id='$ste_id' version='1' comment='comment'>" . "<type>" . strtolower($type) . "</type>" . "<value datatype='" . (strpos(strtolower($type), 'sz') ? 'string' : 'int') . "' operation='equals'>$value</value>" . "</registry_state>";
}
$sth->close();
}
}
// ------------------------------ End <registry_test> -----------------------------
// ------------------------------ Start <auditeventpolicysubcategories_test> -----------------------------
$tmp_sql = "CREATE TEMPORARY TABLE `tmp_oval` SELECT " . "pdi.`id`,o.`oval_id`,s.`stig_id`,vms.`vms_id`,pdi.`check_contents`,pdi.`short_title` " . "FROM `sagacity`.`pdi_catalog` AS pdi " . "LEFT JOIN `sagacity`.`oval` AS o ON pdi.`id`=o.`pdi_id` " . "LEFT JOIN `sagacity`.`stigs` AS s ON pdi.`id`=s.`pdi_id` " . "LEFT JOIN `sagacity`.`golddisk` AS vms ON pdi.`id`=vms.`pdi_id` " . "LEFT JOIN `sagacity`.`pdi_checklist_lookup` AS lookup ON pdi.`id`=lookup.`pdi_id` " . "LEFT JOIN `sagacity`.`checklist` AS c ON lookup.`checklist_id`=c.`id` " . "LEFT JOIN `sagacity`.`software` AS sft ON sft.`id`=c.`sw_id` " . "WHERE " . "o.`oval_id`='M' AND " . "pdi.`check_contents` LIKE '%AuditPol%' AND " . "sft.`man`='MS' AND " . "sft.`name`='Windows' AND " . "sft.`ver`='$os' " . "GROUP BY `stig_id`";
$this->conn->real_query($tmp_sql);
$del_sql = "DELETE FROM tmp_oval " . "WHERE `id` IN (" . "SELECT pdi.`id` " . "FROM `sagacity`.`pdi_catalog` AS pdi " . "LEFT JOIN `sagacity`.`oval` AS o ON pdi.`id`=o.`pdi_id` " . "LEFT JOIN `sagacity`.`stigs` AS s ON pdi.`id`=s.`pdi_id` " . "LEFT JOIN `sagacity`.`golddisk` AS vms ON pdi.`id`=vms.`pdi_id` " . "LEFT JOIN `sagacity`.`pdi_checklist_lookup` AS lookup ON pdi.`id`=lookup.`pdi_id` " . "LEFT JOIN `sagacity`.`checklist` AS c ON lookup.`checklist_id`=c.`id` " . "LEFT JOIN `sagacity`.`software` AS sft ON sft.`id`=c.`sw_id` " . "WHERE " . "o.`oval_id`!='M' AND " . "pdi.`check_contents` REGEXP 'AuditPol' AND " . "sft.`man`='MS' AND " . "sft.`name`='Windows' AND " . "sft.`ver`='$os' " . "GROUP BY pdi.`id`)";
$this->conn->real_query($del_sql);
$sql = "SELECT " . "`id`,`oval_id`,`stig_id`,`vms_id`,`check_contents`,`short_title` " . "FROM `tmp_oval`";
if ($sth = $this->conn->prepare($sql)) {
if ($sth->execute()) {
$pdi_id = 0;
$oval_id = '';
$stig_id = '';
$vms_id = '';
$check_contents = '';
$short_title = '';
$x = 0;
$sth->bind_result($pdi_id, $oval_id, $stig_id, $vms_id, $check_contents, $short_title);
$sth->store_result();
if ($sth->num_rows > 0) {
$obj .= "<auditeventpolicysubcategories_object $xmlns id='oval:smc.gpea.windows:obj:1' version='1' comment='Audit Event Policy Subcategories' />";
}
while ($sth->fetch()) {
$tst_id = "oval:smc.gpea.windows:tst:" . $pdi_id . "00";
$ste_id = "oval:smc.gpea.windows:ste:" . $pdi_id . "00";
$arrow_idx = strpos($check_contents, '->') + 3;
$dash_idx = strpos($check_contents, ' - ');
$subcat = substr($check_contents, $arrow_idx, $dash_idx - $arrow_idx);
$tag = str_replace(' ', '_', strtolower($subcat));
$audit = substr($check_contents, $dash_idx + 3);
$ste .= "<auditeventpolicysubcategories_state $xmlns id='$ste_id' version='1' comment='comment'>" . "<$tag datatype='string'>" . ($audit == 'Failure' ? 'AUDIT_FAILURE' : 'AUDIT_SUCCESS') . "</$tag>" . "</auditeventpolicysubcategories_state>";
$tst .= "<auditeventpolicysubcategories_test $xmlns id='$tst_id' version='1' check_existence='at_least_one_exists' check='all' comment='comment'>" . "<object object_ref='oval:smc.gpea.windows:obj:1' />" . "<state state_ref='$ste_id' />" . "</auditeventpolicysubcategories_test>";
}
} else {
error_log($sth->error);
}
} else {
error_log($this->conn->error);
}
// ------------------------------ End <auditeventpolicysubcategories_test> -----------------------------
// ------------------------------ Start <passwordpolicy_test> -----------------------------
// ------------------------------ End <passwordpolicy_test> -----------------------------
$xml_string = $root . "$gen<definitions>$def</definitions><tests>$tst</tests><objects>$obj</objects><states>$ste</states></oval_definitions>";
return $xml_string;
}
/**
* Function to get oval constant data from database
*
* @param string $oval_id
* Oval ID to get constant data for
*
* @return array Returns array of constant ID and value
*/
public function get_Oval_Const($oval_id)
{
$sql = "SELECT `const_id`,`value` FROM `sagacity`.`ov_convert` WHERE `var_id`=" . $oval_id;
if ($res = $this->conn->query($sql)) {
$vals = [];
while ($row = $res->fetch_assoc()) {
$vals[] = $row['value'];
}
return array(
'const_id' => $row['const_id'],
'values' => $vals
);
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
return null;
}
}
/**
* Function to add an Oval
*
* @param oval $oval
* Oval to add to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function add_Oval($oval)
{
$this->help->insert("sagacity.oval", array(
'pdi_id' => $oval->get_PDI_ID(),
'oval_id' => $oval->get_Oval_ID(),
'title' => $oval->get_Title(),
'desc' => $oval->get_Description(),
'platform' => $oval->get_Platform(),
'ext_def' => $oval->get_External_Definition(),
'ext_def_op' => $oval->get_External_Definition_Operator()
), true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to save oval data
*
* @param oval $oval_in
* Oval to update database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Oval($oval_in)
{
$this->help->replace("sagacity.oval", array(
'pdi_id' => $oval_in->get_PDI_ID(),
'oval_id' => $oval_in->get_Oval_ID(),
'title' => $oval_in->get_Title(),
'desc' => $oval_in->get_Description(),
'platform' => $oval_in->get_Platform(),
'ext_def' => $oval_in->get_External_Definition(),
'ext_def_op' => $oval_in->get_External_Definition_Operator()
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ PDI_CATALOG CLASS FUNCTIONS
/**
* Function to retrieve a PDI from the database
*
* @param integer $pdi_id
* PDI ID to get from database
* @param integer $chk_id
* Checklist ID to filter on
*
* @return pdi|NULL Returns PDI object, or null if none found
*/
public function get_PDI($pdi_id, $chk_id = null)
{
$pdi = null;
$this->help->select("sagacity.pdi_catalog p", null, [
[
'field' => 'p.id',
'op' => '=',
'value' => $pdi_id
]
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['id'])) {
$pdi = new pdi($row['id'], $row['cat'], $row['update']);
$pdi->set_Short_Title($row['short_title']);
$pdi->set_Check_Contents($row['check_contents']);
if (! is_null($chk_id)) {
$this->help->select("sagacity.pdi_checklist_lookup", null, [
[
'field' => 'pdi_id',
'op' => '=',
'value' => $pdi_id
],
[
'field' => 'checklist_id',
'op' => '=',
'value' => $chk_id,
'sql_op' => 'AND'
]
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['pdi_id'])) {
$pdi->set_Short_Title($row['short_title']);
$pdi->set_Group_Title($row['group_title']);
$pdi->set_Check_Contents($row['check_contents']);
$pdi->set_Fix_Text($row['fix_text']);
}
}
}
return $pdi;
}
/**
* Function to get pdi catalog item from database
*
* @param integer $pdi_id
* Get PDI Catalog entry from database using this ID
*
* @return array|NULL Returns associative array with record, or null if none found
*/
public function get_PDI_Catalog($pdi_id)
{
$this->help->select("sagacity.pdi", null, array(
array(
'field' => 'pdi_id',
'op' => '=',
'value' => $pdi_id
)
));
return $this->help->execute();
}
/**
* Function to attempt to match text
*
* @param pdi $pdi
* PDI to match in database
* @param nessus $nessus
* Nessus to match in database
* @param cve $cve
* CVE to match in database
* @param iavm $iavm
* IAVM to match in database
*
* @return array|NULL Returns array of possible matches, or null if none found
*/
public function get_Matching_PDIs($pdi, $nessus, $cve, $iavm)
{
/*
* $string = '';
*
* if (!is_null($nessus)) {
* $string = $nessus->get_Name() . ' ' . $nessus->get_Description() . ' ' . $nessus->get_Summary();
* }
* elseif (!is_null($cve)) {
* $string = $cve->get_Description();
* }
* elseif (!is_null($iavm)) {
* $string = $iavm->get_Title() . ' ' . $iavm->get_Executive_Summary();
* }
*
* foreach ($this->DISALLOWED as $word) {
* $string = preg_replace("/\s" . $word . "\s/i", " ", $string);
* }
*
* $sql = "SELECT " .
* "MATCH(pdi.`short_title`,pdi.`description`,pdi.`check_content`) " .
* "AGAINST('" . $this->conn->real_escape_string($string) . "' IN NATURAL LANGUAGE MODE) AS 'score'," .
* "pdi.`id`,pdi.`short_title`,pdi.`description`,pdi.`check_content` " .
* "FROM `sagacity`.`pdi_catalog` pdi " .
* "GROUP BY pdi.`id`,`score` " .
* "HAVING `score` > 10 " .
* "ORDER BY `score` DESC " .
* "LIMIT 0, 5";
*
* $ret = [];
*
* if ($res = $this->conn->query($sql)) {
* while ($row = $res->fetch_assoc()) {
* $ret[] = array(
* 'score' => number_format($row['score'], 3),
* 'pdi_id' => $row['id'],
* 'title' => $row['short_title'],
* 'check_content' => $row['check_content'],
* 'desc' => $row['description']
* );
* }
*
* return $ret;
* }
* else {
* Sagacity_Error::sql_handler($sql);
* error_log($this->conn->error);
* }
*/
return null;
}
/**
* Function to try and find a PDI
*
* @param array $data_in
* An array of a type and value to search for. This will primarily be intended for types that don't have a readily available link to a PDI (nessus, retina, CVE, IAVM, etc).
*
* @return integer Returns the PDI id of the matching entry, or 0 if none found
*/
public function find_PDI($data_in)
{
if ($data_in['type'] == 'nessus') {
$nessus = $this->get_Nessus($data_in['value']);
if (is_null($nessus)) {
return 0;
}
if ($nessus->get_PDI_ID()) {
return $nessus->get_PDI_ID();
} else {
$cves = $nessus->get_Reference_By_Type('cve');
foreach ($cves as $cve_num) {
$cve = $this->get_CVE($cve_num);
if ($cve->get_PDI_ID()) {
return $cve->get_PDI_ID();
}
$sql = "SELECT `noticeId` FROM `sagacity`.`iavm_to_cve` WHERE `cve_id`='" . $this->conn->real_escape_string($cve_num) . "'";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$iavm = $this->get_IAVM($row['noticeId']);
if (! is_null($iavm)) {
return $iavm->get_PDI_ID();
}
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
}
$bids = $nessus->get_Reference_By_Type('bid');
foreach ($bids as $bid_num) {
$sql = "SELECT iavm.`pdi_id` " . "FROM `sagacity`.`nessus_refs` nr " . "JOIN `sagacity`.`iavm_bids` ib ON ib.`bid`=nr.`val` " . "JOIN `sagacity`.`iavm_notices` iavm ON iavm.`noticeId`=ib.`iavm_notice_id` " . "WHERE " . "nr.`type`='bid' AND " . "nr.`val`=" . $this->conn->real_escape_string($bid_num) . " AND " . "nr.`plugin_id`=" . $this->conn->real_escape_string($nessus->get_Nessus_ID());
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
return $row['pdi_id'];
}
}
}
}
}
return 0;
}
/**
* Function to save an existing PDI
*
* @param pdi $pdi_in
* The PDI to save or update
* @param checklist $checklist
* [optional]
* The checklist to link new PDIs to (if null links to Orphan checklist)
*
* @return boolean|int Returns ID of PDI or FALSE if failed to save.
*/
public function save_PDI($pdi_in, $checklist = null)
{
$pdi_id = null;
if ($pdi_in->get_ID()) {
$this->help->update('sagacity.pdi_catalog', [
'cat' => $pdi_in->get_Category_Level(),
'update' => $pdi_in->get_Last_Update(),
'short_title' => $pdi_in->get_Short_Title(),
'check_contents' => $pdi_in->get_Check_Contents()
], [
[
'field' => 'id',
'op' => '=',
'value' => $pdi_in->get_ID()
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$pdi_id = $pdi_in->get_ID();
} else {
$this->help->insert("sagacity.pdi_catalog", [
"cat" => $pdi_in->get_Category_Level(),
'update' => $pdi_in->get_Last_Update(),
'short_title' => $pdi_in->get_Short_Title(),
'check_contents' => $pdi_in->get_Check_Contents()
]);
if (! ($pdi_id = $this->help->execute())) {
$this->help->debug(E_ERROR);
return false;
}
$pdi_in->set_ID($pdi_id);
}
if (is_null($checklist)) {
$checklist = $this->get_Checklist('Orphan');
}
if (is_array($checklist) && isset($checklist[0]) && is_a($checklist[0], 'checklist')) {
$this->help->insert('sagacity.pdi_checklist_lookup', [
'pdi_id' => $pdi_id,
'checklist_id' => $checklist[0]->get_ID(),
'check_contents' => $pdi_in->get_Check_Contents(),
'group_title' => $pdi_in->get_Group_Title(),
'short_title' => $pdi_in->get_Short_Title(),
'fix_text' => $pdi_in->get_Fix_Text()
], true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
} elseif (is_a($checklist, 'checklist')) {
$this->help->insert("sagacity.pdi_checklist_lookup", array(
'pdi_id' => $pdi_id,
'checklist_id' => $checklist->get_ID(),
'check_contents' => $pdi_in->get_Check_Contents(),
'group_title' => $pdi_in->get_Group_Title(),
'short_title' => $pdi_in->get_Short_Title(),
'fix_text' => $pdi_in->get_Fix_Text()
), true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
} else {
Sagacity_Error::err_handler("Cannon link PDI ID $pdi_id with a checklist", E_WARNING);
}
return $pdi_id;
}
/**
* Function to save the check contents to a specific PDI and checklist
*
* @param pdi $pdi_in
* The PDI (containing the check contents)
* @param checklist $checklist_in
* The checklist
* @param string $check_contents_in
* [optional]
* The check contents to save (will use check contents in $pdi_in if this is null)
* @param string $fix_text_in
* [optional]
* The fix text to save
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Check_Contents($pdi_in, $checklist_in, $check_contents_in = null, $fix_text_in = null)
{
$this->help->replace("sagacity.pdi_checklist_lookup", array(
'pdi_id' => $pdi_in->get_ID(),
'checklist_id' => $checklist_in->get_ID(),
'group_title' => $pdi_in->get_Group_Title(),
'short_title' => $pdi_in->get_Short_Title(),
'check_contents' => (! is_null($check_contents_in) ? $check_contents_in : $pdi_in->get_Check_Contents()),
'fix_text' => (! is_null($fix_text_in) ? $fix_text_in : $pdi_in->get_Fix_Text())
));
if (! $this->help->execute()) {
return false;
}
return true;
}
// }}}
// {{{ PROC_IA_CONTROLS CLASS FUNCTIONS
/**
* Function to get all procedural IA controls for specified system
*
* @param ste $ste_in
* ST&E to query the database for
* @param string $control_id
* [optional]
* Control ID to query (default null)
*
* @return array:proc_ia_controls Return array of proc_ia_controls and associated sub controls, or empty array if none found
*/
public function get_Proc_IA_Controls($ste_in, $control_id = null)
{
$ret = [];
$sys = $this->get_System($ste_in->get_System()
->get_ID())[0];
switch ($sys->get_Classification()) {
case 'Public':
$class = 'pub';
break;
case 'Sensitive':
$class = 'sen';
break;
case 'Classified':
$class = 'cl';
break;
default:
$class = '';
}
$sql = "SELECT " . "pia.`control_id`,pia.`name`,pia.`subject_area`,pia.`description`," . "pia.`threat_vul_cm`,pia.`gen_imp_guide`,pia.`guide_resource`,pia.`impact` " . "FROM `sagacity`.`proc_ia_controls` pia " . "LEFT JOIN `sagacity`.`proc_level_type` plt ON plt.`proc_control`=pia.`control_id` " . "WHERE plt.`type`='diacap' AND " . "plt.`level`=" . $sys->get_MAC() . " AND " . "plt.`class`='$class'";
if (! is_null($control_id)) {
$sql .= " AND pia.`control_id`='" . $this->conn->real_escape_string($control_id) . "'";
}
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$ia = new proc_ia_controls($row['control_id'], $row['name'], $row['subject_area'], $row['description'], $row['threat_vul_cm'], $row['gen_imp_guide'], $row['guide_resource'], $row['impact']);
$sql2 = "SELECT `ste_id`,`control_id`,`vul_desc`,`mitigations`,`references`,`risk_analysis`,`notes`,`done` " . "FROM `sagacity`.`control_findings` " . "WHERE `ste_id`=" . $ste_in->get_ID() . " AND " . "`control_id`='" . $row['control_id'] . "'";
if ($res2 = $this->conn->query($sql2)) {
if ($res2->num_rows > 0) {
$row2 = $res2->fetch_assoc();
$ia->finding->control_id = $row2['control_id'];
$ia->finding->ste_id = $row2['ste_id'];
$ia->finding->vul_desc = $row2['vul_desc'];
$ia->finding->mitigations = $row2['mitigations'];
$ia->finding->reference = $row2['references'];
$ia->finding->notes = $row2['notes'];
$ia->finding->risk_analysis = $row2['risk_analysis'];
$ia->finding->done = $row2['done'];
}
}
$sql2 = "SELECT " . "`sub_control_id`,`name`,`objective`," . "`prep`,`script`,`exp_result` " . "FROM `sagacity`.`proc_ia_sub_controls` " . "WHERE `parent_control_id`='" . $row['control_id'] . "'";
if ($res2 = $this->conn->query($sql2)) {
while ($row2 = $res2->fetch_assoc()) {
$ia_sub = new proc_sub_ia_controls($row2['sub_control_id'], $row2['name'], $row2['objective'], $row2['prep'], $row2['script'], $row2['exp_result']);
$sql3 = "SELECT " . "`ste_id`,`proc_id`,`status`,`test_results`," . "`mitigations`,`milestones`,`ref`,`notes` " . "FROM `sagacity`.`proc_findings` " . "WHERE `ste_id`=" . $ste_in->get_ID() . " AND " . "`proc_id`='" . $row2['sub_control_id'] . "'";
if ($res3 = $this->conn->query($sql3)) {
if ($res3->num_rows > 0) {
$row3 = $res3->fetch_assoc();
$ia_sub->finding->control_id = $row3['proc_id'];
$ia_sub->finding->ste_id = $row3['ste_id'];
$ia_sub->finding->test_result = $row3['test_results'];
$ia_sub->finding->mitigation = $row3['mitigations'];
$ia_sub->finding->milestone = $row3['milestones'];
$ia_sub->finding->reference = $row3['ref'];
$ia_sub->finding->notes = $row3['notes'];
$ia_sub->finding->status = $row3['status'];
} else {
$ia_sub->finding->status = 'Not Reviewed';
}
}
$ia->add_Sub($ia_sub);
}
}
$ret[] = $ia;
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return $ret;
}
// }}}
// {{{ INTERVIEW QUESTION CLASS FUNCTIONS
/**
* Function to return the categories
*
* @return array:string
*/
public function get_Question_Categories()
{
$ret = [];
$this->help->select("interview_questions", [
'cat'
], [], [
'group' => 'cat'
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = $row['cat'];
}
}
return $ret;
}
/**
* Function to get the questions
*
* @param integer $cat_in
* @param string $type_in
*/
public function get_Questions($cat_in, $type_in = null)
{
$ret = [];
$sql = "SELECT " . "iq.`id`,iq.`key`,iq.`cat`,iq.`question`," . "(SELECT ci.`answer` " . "FROM `category_interview` ci " . "WHERE ci.`ques_id`=iq.`id` AND ci.`cat_id`=" . $this->conn->real_escape_string($cat_in) . ") AS 'answer' " . "FROM `interview_questions` iq " . "WHERE iq.`cat`='" . $this->conn->real_escape_string($type_in) . "'";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$ques = new question();
$ques->id = $row['id'];
$ques->cat = $row['cat'];
$ques->key = $row['key'];
$ques->question = $row['question'];
$ques->answer = $row['answer'];
$ret[] = $ques;
}
} else {
print $sql . "<br />";
print $this->conn->error;
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return $ret;
}
/**
* Function to return the interview questions with their answers
*
* @param int $cat_id_in
*
* @return array:question
*/
public function get_Interview_Answers($cat_id_in)
{
$ret = [];
$this->help->select("interview_questions iq", [
'iq.id',
'iq.key',
'iq.question',
'ci.answer'
], [
[
'field' => 'ci.cat_id',
'op' => '=',
'value' => $cat_id_in
]
], [
'table_joins' => "LEFT JOIN category_interview ci ON iq.id = ci.ques_id"
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ques = new question();
$ques->id = $row['id'];
$ques->key = $row['key'];
$ques->question = $row['question'];
$ques->answer = ($row['answer'] ? true : false);
$ret[] = $ques;
}
}
return $ret;
}
/**
* Function to reset the interview answers
*
* @param string $type_in
* @param int $cat_in
*/
public function set_Questions($type_in, $cat_in)
{
$this->help->delete("category_interview", null, [
[
'field' => 'cat_id',
'op' => '=',
'value' => $cat_in
]
]);
$this->help->execute();
$this->help->sql = "INSERT IGNORE INTO `category_interview` (`cat_id`,`ques_id`)" . " SELECT " . $this->conn->real_escape_string($cat_in) . ",`id`" . " FROM `interview_questions`" . " WHERE `cat`='" . $this->conn->real_escape_string($type_in) . "'";
$this->help->query_type = db_helper::INSERT;
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to set the answer for an interview question
*
* @param int $cat_id_in
* @param question $question
* @return boolean
*/
public function set_QA($cat_id_in, $question)
{
$this->help->update("category_interview", [
'answer' => ($question->answer)
], [
[
'field' => 'ques_id',
'op' => '=',
'value' => $question->id
],
[
'field' => 'cat_id',
'op' => '=',
'value' => $cat_id_in,
'sql_op' => 'AND'
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ RETINA CLASS FUNCTIONS
/**
* Update retina data
*
* @param retina $retina_In
* Retina object to save to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Retina($retina_In)
{
$sql = "REPLACE INTO `sagacity`.`retina` (`pdi_id`,`retina_id`) VALUES (" . $this->conn->real_escape_string($retina_In->get_PDI_ID()) . "," . $this->conn->real_escape_string($retina_In->get_Retina_ID()) . ")";
if (! $this->conn->real_query($sql)) {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
return false;
}
return true;
}
// }}}
// {{{ RMF_CONTROL CLASS FUNCTIONS
/**
* Function to get all the RMF controls that apply to a certain baseline impact<br />
* Used for tailoring later
*
* @param string $baseline
*
* @return array:rmf_control
*/
public function get_RMF_Control_By_Baseline($baseline)
{
$ret = [];
if (! in_array($baseline, array(
"low",
"moderate",
"high"
))) {
return [];
}
$sql = "SELECT " . "f.`abbr`,f.`name` AS 'family_name' " . "c.`control_id`,c.`name` AS 'control_name',c.`pri`,c.`statement`,c.`guidance` " . "cb.`impact_level` " . "FROM `rmf`.`controls` c " . "JOIN `rmf`.`control_baseline` cb ON cb.`control_id`=c.`control_id` " . "JOIN `rmf`.`family` f ON f.`abbr`=c.`family_id` " . "WHERE cb.`impact_level`='" . $this->conn->real_escape_string($baseline) . "'";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$family = new rmf_family();
$family->set_Abbr($row['abbr']);
$family->set_Name($row['family_name']);
$rmf = new rmf_control();
$rmf->family = $family;
$rmf->set_Control_ID($row['control_id']);
$rmf->set_Name($row['control_name']);
$rmf->set_Priority($row['pri']);
$rmf->set_Statement($row['statement']);
$rmf->set_Guidance($row['guidance']);
$rmf->set_Baseline($baseline, true);
$this->get_RMF_Related_Controls($rmf);
$this->get_RMF_Enhanced_Controls($rmf, $baseline);
$ret[] = $rmf;
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return $ret;
}
/**
* Function to get all the related controls
*
* @param rmf_control $rmf
*/
public function get_RMF_Related_Controls(rmf_control &$rmf)
{
$sql = "SELECT rc.`related_control_id` " . "FROM `rmf`.`related_controls rc " . "WHERE rc.`control_id`='" . $this->conn->real_escape_string($rmf->get_Control_ID()) . "'";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$rmf->add_Related_Control($row['related_control_id']);
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
}
/**
* Function to get all the enhanced controls
*
* @param rmf_control $rmf
* @param string $baseline
*/
public function get_RMF_Enhanced_Controls(rmf_control &$rmf, $baseline = null)
{
$sql = "SELECT " . "ce.`enh_id`,ce.`name`,ce.`desc`,ce.`guidance` " . "FROM `rmf`.`control_enh` ce " . "JOIN `rmf`.`enhancement_baseline eb ON eb.`control_id`=ce.`control_id` AND " . "eb.`enh_id`=ce.`enh_id` " . "WHERE ce.`control_id`='" . $this->conn->real_escape_string($rmf->get_Control_ID()) . "' AND " . "eb.`impact`='" . (is_null($baseline) ? $rmf->get_Worst_Baseline() : $baseline) . "'";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$enh = new rmf_control_enhancements();
$enh->set_Enhanced_ID($row['enh_id']);
$enh->set_Guidance($row['guidance']);
$enh->set_Name($row['name']);
$enh->set_Statement($row['desc']);
$rmf->add_Enhanced_Control($enh);
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
}
// }}}
// {{{ SCAN CLASS FUNCTIONS
/**
* Get ScanData for Results page
*
* @param integer $intSTE
* ST&E ID to grab scans for
* @param integer|string $Scan_ID
* [optional]
* Scan ID or file name to grab (defaulted null)
*
* @return array:scan|NULL Returns array of scans associated with the ST&E, or null if none found
*/
public function get_ScanData($intSTE, $Scan_ID = null, $status_in = null, $type_in = null)
{
$ret = [];
$where = [
[
'field' => 's.ste_id',
'value' => $intSTE
]
];
if (! is_null($Scan_ID)) {
if (is_numeric($Scan_ID)) {
$where[] = [
'field' => 's.id',
'value' => $Scan_ID,
'sql_op' => 'AND'
];
} else {
$where[] = [
'field' => 's.file_name',
'value' => $Scan_ID,
'sql_op' => 'AND'
];
}
}
if (! is_null($status_in)) {
$where[] = [
'field' => 's.status',
'value' => $status_in,
'sql_op' => 'AND'
];
}
if (! is_null($type_in)) {
$where[] = [
'field' => 'src.name',
'value' => $type_in,
'sql_op' => 'AND'
];
}
$this->help->select("scans s", [
's.*'
], $where, [
'table_joins' => [
"JOIN sources src ON src.id=s.src_id"
],
'order' => 's.file_name'
]);
$scan_rows = $this->help->execute();
if (isset($scan_rows['id'])) {
$scan_rows = [
0 => $scan_rows
];
}
if (is_array($scan_rows) && count($scan_rows)) {
foreach ($scan_rows as $row) {
$src = $this->get_Sources($row['src_id']);
if (is_array($src) && count($src) && isset($src[0]) && is_a($src[0], 'source')) {
$src = $src[0];
} else {
continue;
}
$ste = $this->get_STE($intSTE);
if (is_array($ste) && count($ste) && isset($ste[0]) && is_a($ste[0], 'ste')) {
$ste = $ste[0];
} else {
continue;
}
$scan = new scan($row['id'], $src, $ste, $row['itr'], $row['file_name'], $row['file_date']);
$scan->set_Notes($row['notes']);
$scan->set_PID($row['pid']);
$scan->set_Start_Time($row['start_time']);
$scan->set_Last_Update($row['last_update']);
$scan->set_Status($row['status']);
$scan->set_Percentage_Complete($row['perc_comp']);
$scan->set_Last_Host($row['last_host']);
$scan->set_Total_Host_Count($row['host_count']);
$this->help->select("host_list hl", [
'hl.tgt_id',
't.name',
'hl.finding_count',
'hl.scanner_error',
'hl.notes'
], [
[
'field' => 'hl.scan_id',
'value' => $row['id']
]
], [
'table_joins' => [
"LEFT JOIN target t ON t.id=hl.tgt_id"
]
]);
$hl_rows = $this->help->execute();
if (is_array($hl_rows) && count($hl_rows) && isset($hl_rows['tgt_id'])) {
$hl_rows = [
0 => $hl_rows
];
}
if (is_array($hl_rows) && count($hl_rows) && isset($hl_rows[0])) {
foreach ($hl_rows as $row) {
$tgt = new target($row['name']);
$tgt->set_ID($row['tgt_id']);
$tgt->set_STE_ID($intSTE);
$tgt->interfaces = $this->get_Interfaces($tgt->get_ID());
if ((bool) $row['scanner_error']) {
$scan->setScanError((bool) $row['scanner_error']);
}
$hl = new host_list();
$hl->setTargetId($tgt->get_ID());
$hl->setTargetName($tgt->get_Name());
$hl->setTargetIp($tgt->getIP());
$hl->setFindingCount($row['finding_count']);
$hl->setScanError((bool) $row['scanner_error']);
$hl->setScanNotes($row['notes']);
$scan->add_Target_to_Host_List($hl);
}
}
$ret[] = $scan;
}
}
return $ret;
}
/**
* Save scan data
*
* @param scan $new_Scan
* New scan to save to database
*
* @return integer Returns ID of new scan, or 0 if fail
*/
public function save_Scan($new_Scan)
{
if (! is_a($new_Scan, "scan")) {
return;
}
if (! is_a($new_Scan->get_Source(), 'source')) {
throw (new Exception("Wrong source type " . print_r($new_Scan->get_Source(), true)));
}
if ($new_Scan->get_ID()) {
$this->help->update("scans", [
'src_id' => $new_Scan->get_Source()->get_ID(),
'itr' => $new_Scan->get_Itr(),
'file_date' => $new_Scan->get_File_DateTime(),
'pid' => $new_Scan->get_PID(),
'start_time' => $new_Scan->get_Start_Time(),
'last_update' => $new_Scan->get_Last_Update(),
'status' => $new_Scan->get_Status(),
'perc_comp' => $new_Scan->get_Percentage_Complete(),
'last_host' => $new_Scan->get_Last_Host(),
'host_count' => $new_Scan->get_Total_Host_Count(),
'notes' => $new_Scan->get_Notes()
], [
[
'field' => 'id',
'value' => $new_Scan->get_ID()
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
} else {
$this->help->insert("scans", [
'src_id' => $new_Scan->get_Source()->get_ID(),
'ste_id' => $new_Scan->get_STE()->get_ID(),
'itr' => $new_Scan->get_Itr(),
'file_name' => $new_Scan->get_File_Name(),
'file_date' => $new_Scan->get_File_DateTime(),
'pid' => $new_Scan->get_PID(),
'start_time' => $new_Scan->get_Start_Time(),
'last_update' => $new_Scan->get_Last_Update(),
'status' => $new_Scan->get_Status(),
'perc_comp' => $new_Scan->get_Percentage_Complete(),
'last_host' => $new_Scan->get_Last_Host(),
'host_count' => $new_Scan->get_Total_Host_Count(),
'notes' => $new_Scan->get_Notes()
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return 0;
}
$new_Scan->set_ID($this->conn->insert_id);
}
$this->update_Scan_Host_List($new_Scan, $new_Scan->get_Host_List());
return $new_Scan->get_ID();
}
/**
* Delete a scan (associated finding data and optionally targets)
*
* @param integer $ste_id
* ST&amp;E ID where the scan exists
* @param integer $scan_id
* Scan to delete from database
* @param boolean $del_tgts
* Boolean to decide if we are deleting targets as well
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function delete_Scan($ste_id, $scan_id, $del_tgts = false)
{
$scan = $this->get_ScanData($ste_id, $scan_id);
if (is_array($scan) && count($scan) && isset($scan[0]) && is_a($scan[0], 'scan')) {
$scan = $scan[0];
} elseif (! is_a($scan, 'scan')) {
Sagacity_Error::err_handler("Failed to find Scan ($scan_id)", E_ERROR);
return false;
}
$this->help->delete("finding_controls fc", [
'fc.*'
], [
[
'field' => 'f.scan_id',
'op' => '=',
'value' => $scan_id
]
], [
"JOIN findings f ON f.tgt_id = fc.tgt_id AND f.pdi_id = fc.pdi_id"
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("findings", null, [
[
'field' => 'scan_id',
'op' => '=',
'value' => $scan_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("host_list", null, [
[
'field' => 'scan_id',
'op' => '=',
'value' => $scan_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("scans", null, [
[
'field' => 'id',
'op' => '=',
'value' => $scan_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
if ($del_tgts) {
/**
*
* @var host_list $host
*/
foreach ($scan->get_Host_List() as $host) {
$this->delete_Target($host->getTargetId());
}
}
return true;
}
/**
* Updates the host_list field for a particular scan
*
* @param scan $scan
* Scan to update
* @param array $host_list
* [optional]
* Formatted host list to update (default null)
*
* @return boolean Returns TRUE if successful, otherwise FALSEs
*/
public function update_Scan_Host_List($scan, $host_list = null)
{
$this->help->delete("host_list", null, [
[
'field' => 'scan_id',
'value' => $scan->get_ID()
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$params = [];
if (is_null($host_list)) {
foreach ($scan->get_Host_List() as $host) {
$params[] = [
$scan->get_ID(),
$host->getTargetId(),
$host->getFindingCount(),
$host->getScanError(),
$host->getScanNotes()
];
$this->update_Target_Counts($host->getTargetId());
}
} else {
foreach ($host_list as $host) {
if (! is_a($host, 'host_list')) {
break;
}
$params[] = [
$scan->get_ID(),
$host->getTargetId(),
$host->getFindingCount(),
$host->getScanError(),
$host->getScanNotes()
];
$this->update_Target_Counts($host->getTargetId());
}
}
if (count($params)) {
$this->help->extended_insert("host_list", [
'scan_id',
'tgt_id',
'finding_count',
'scanner_error',
'notes'
], $params);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
}
return true;
}
/**
* Get the scan source data
*
* @param integer|string $srcID
* Source ID or name to grab from database
*
* @return source|NULL Returns source, or null if none found
*/
public function get_Sources($srcID = null)
{
$where = [];
$ret = null;
if (! is_null($srcID)) {
if (is_numeric($srcID)) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $srcID
];
} else {
$where[] = [
'field' => 'name',
'op' => '=',
'value' => $srcID,
'case_insensitive' => true
];
}
}
$this->help->select("sagacity.sources", null, $where, [
'order' => 'name'
]);
$src_rows = $this->help->execute();
if (is_array($src_rows) && isset($src_rows['id'])) {
$src_rows = [
0 => $src_rows
];
}
if (is_array($src_rows) && count($src_rows) && isset($src_rows[0])) {
foreach ($src_rows as $row) {
$src = new source($row['id'], $row['name']);
$src->set_Icon($row['icon']);
$ret[] = $src;
}
}
return $ret;
}
/**
* Function to get the expected sources for a category
*
* @param ste_cat $cat
*
* @return array:source|array
*/
public function get_Expected_Category_Sources($cat)
{
if (is_array($cat) && count($cat)) {
$cat = $cat[0];
}
if (! is_a($cat, "ste_cat")) {
return [];
}
$ret = [];
$this->help->select("sagacity.sources s", [
's.id',
's.name',
's.icon'
], [
[
'field' => 'cat.id',
'op' => '=',
'value' => $cat->get_ID()
]
], [
'table_joins' => [
"JOIN sagacity.ste_cat_sources src ON s.id=src.src_id",
"JOIN sagacity.ste_cat cat ON cat.id=src.cat_id"
]
]);
$src_arr = $this->help->execute();
if (is_array($src_arr) && count($src_arr) && isset($src_arr['id'])) {
$src_arr = [
0 => $src_arr
];
}
if (is_array($src_arr) && count($src_arr) && isset($src_arr[0])) {
foreach ($src_arr as $row) {
$src = new source($row['id'], $row['name']);
$icon = null;
$missing_fname = str_replace(" ", "-", substr($row['icon'], 0, - 4)) . "-missing.png";
if ($row['icon'] && file_exists(DOC_ROOT . "/img/scan_types/{$missing_fname}")) {
$icon = $missing_fname;
} else {
$icon = $row['icon'];
}
$src->set_Icon($icon);
$ret[$src->get_ID()]['src'] = $src;
}
}
return $ret;
}
/**
* Find the sources that have contained this target
*
* @param target $tgt
* @param array $exp_scan_srcs
*
* @return array:sources
*/
public function get_Target_Scan_Sources($tgt, &$exp_scan_srcs = null)
{
$ret = (!is_null($exp_scan_srcs) ? $exp_scan_srcs : []);
$this->help->select("sources src", [
"src.id",
"src.name",
"src.icon",
"hl.scanner_error",
"hl.notes",
"hl.finding_count",
"s.file_name"
], [
[
'field' => 'hl.tgt_id',
'value' => $tgt->get_ID()
]
], [
'table_joins' => [
"LEFT JOIN scans s ON s.src_id=src.id",
"LEFT JOIN host_list hl ON hl.scan_id=s.id"
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach($rows as $row) {
$src = new source($row['id'], $row['name']);
$src->set_Icon($row['icon']);
$ret[$src->get_ID()]['src'] = $src;
$ret[$src->get_ID()]['count'] = $row['finding_count'];
$ret[$src->get_ID()]['notes'] = $row['notes'];
$ret[$src->get_ID()]['scan_error'] = $row['scanner_error'];
$ret[$src->get_ID()]['file_name'] = $row['file_name'];
}
}
return $ret;
}
// }}}
// {{{ SCRIPT FUNCTIONS
/**
* Function to get a catalog script
*
* @param string $file_name_in
* [optional]
* Look for a specific catalog/STIG file that is processing
*
* @return array:catalog_script|NULL
*/
public function get_Catalog_Script($file_name_in = null)
{
$ret = [];
$where = [];
if (! is_null($file_name_in)) {
$where[] = [
'field' => 'file_name',
'op' => '=',
'value' => $file_name_in
];
}
$this->help->select("sagacity.catalog_scripts cs", ['c.id', 'cs.*'], $where, [
'table_joins' => [
"LEFT JOIN sagacity.checklist c ON c.file_name = cs.file_name"
],
'order' => "FIELD(`status`, 'ERROR','RUNNING','IN QUEUE','COMPLETE'),`file_name`"
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['file_name'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows)) {
foreach ($rows as $row) {
$script = new catalog_script();
$script->{'id'} = $row['id'];
$script->file_name = $row['file_name'];
$script->pid = $row['pid'];
$script->start_time = new DateTime($row['start_time']);
$script->last_update = new DateTime($row['last_update']);
$script->status = $row['status'];
$script->perc_comp = $row['perc_comp'];
$script->stig_count = $row['stig_count'];
$ret[] = $script;
}
}
return $ret;
}
/**
* Function to get script count
*
* @param string $status
* [optional]
* Return only the count for a script that is in a certain status (defaulted null)
*
* @return integer Returns the number of script that are in the database or count in a specific status
*/
public function get_Catalog_Script_Count($status = null)
{
$where = [];
if (! is_null($status)) {
$where[] = [
'field' => 'status',
'op' => '=',
'value' => $status
];
if ($status == 'RUNNING') {
$where[] = [
'field' => 'perc_comp',
'op' => '<',
'value' => 100,
'sql_op' => 'AND',
'open-paren' => true
];
$where[] = [
'field' => 'perc_comp',
'op' => IS,
'value' => null,
'sql_op' => 'OR',
'close-paren' => true
];
}
}
$this->help->select_count("sagacity.catalog_scripts", $where);
if ($count = $this->help->execute()) {
return $count;
}
return 0;
}
/**
* Function to add new catalog parsing script
*
* @param string $file_name_in
* The catalog/STIG file that is processing
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function add_Catalog_Script($file_name_in)
{
$this->help->insert("sagacity.catalog_scripts", [
'file_name' => $file_name_in
], true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to update catalog script execution
*
* @param string $file
* Script to update
* @param array $field
* Array with the name and value of the column to update
* 'name' => 'pid',
* 'value' => 1234
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function update_Catalog_Script($file, $field)
{
$where = array(
array(
'field' => 'file_name',
'op' => '=',
'value' => $file
)
);
if ($field['name'] == 'perc_comp' && $field['value'] == 100 && isset($field['complete'])) {
$this->help->update("sagacity.catalog_scripts", array(
$field['name'] => $field['value'],
'status' => 'COMPLETE',
'last_update' => 'NOW()'
), $where);
} elseif ($field['name'] == 'pid') {
$this->help->update("sagacity.catalog_scripts", array(
$field['name'] => $field['value'],
'status' => 'RUNNING',
'start_time' => 'NOW()',
'last_update' => 'NOW()'
), $where);
} else {
$this->help->update('sagacity.catalog_scripts', array(
$field['name'] => $field['value'],
'last_update' => 'NOW()'
), $where);
}
if (! $this->help->execute()) {
return false;
}
return true;
}
/**
* Function to get the number of scripts that are currently running
*
* @param integer $ste
* ST&E to evaluate
*
* @return integer Returns the count of scripts that are running
*/
public function get_Running_Script_Count($ste)
{
$this->help->select_count("scans", [
[
'field' => 'status',
'op' => '=',
'value' => 'RUNNING'
],
[
'field' => 'ste_id',
'op' => '=',
'value' => $ste,
'sql_op' => 'AND'
]
]);
return $this->help->execute();
}
/**
* Getter function to retrieve the status of a result script
*
* @param int $ste_id
* @param string $file
*
* @return string
*/
public function get_Running_Script_Status($ste_id, $file)
{
$this->help->select("sagacity.scans", [
'status',
'perc_comp'
], [
[
'field' => 'ste_id',
'op' => '=',
'value' => $ste_id
],
[
'field' => 'file_name',
'op' => '=',
'value' => $file,
'sql_op' => 'AND'
]
]);
return $this->help->execute();
}
/**
* Add a new script to the database
*
* @param string $file
* Result file name
* @param integer $ste_id
* The STE ID that the script is being added to
* @param string $type
* The result type
*
* @return boolean Return TRUE if successful, otherwise FALSE
*/
public function add_Running_Script($file, $ste_id, $type, $location)
{
$existing_scan = $this->get_ScanData($ste_id, $file);
if (is_array($existing_scan) && count($existing_scan) && isset($existing_scan[0]) && is_a($existing_scan[0], 'scan')) {
$scan = $existing_scan[0];
$this->help->update("scans", [
'start_time' => 'NOW()',
'last_update' => 'NOW()',
'perc_comp' => 0.0
], [
[
'field' => 'id',
'op' => '=',
'value' => $scan->get_ID()
]
]);
} else {
$type = str_replace("_", " ", $type);
$src = $this->get_Sources($type);
if (is_array($src) && count($src) && is_a($src[0], 'source')) {
$src = $src[0];
} else {
return false;
}
$fd = date("Y-m-d", filemtime(TMP . "/" . $file));
$this->help->insert("sagacity.scans", [
'ste_id' => $ste_id,
'src_id' => $src->get_ID(),
'file_name' => $file,
'file_date' => $fd,
'start_time' => 'NOW()',
'last_update' => 'NOW()',
'status' => 'IN QUEUE',
'perc_comp' => 0.0,
'location' => $location
], true);
}
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to update a running script entry to add the process ID
*
* @param string $file
* The result file to update
* @param array $field
* Associative array (name,value) to know what field to update
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function update_Running_Scan($file, $field)
{
$where = [
[
'field' => 'file_name',
'op' => '=',
'value' => $file
]
];
if ($field['name'] == 'perc_comp' && $field['value'] == 100 && isset($field['complete'])) {
$this->help->update("sagacity.scans", [
$field['name'] => $field['value'],
'status' => 'COMPLETE',
'last_update' => 'NOW()'
], $where);
} elseif ($field['name'] == 'pid') {
$this->help->update("sagacity.scans", [
$field['name'] => $field['value'],
'status' => 'RUNNING',
'start_time' => 'NOW()',
'last_update' => 'NOW()',
'host_count' => 0
], $where);
} elseif ($field['name'] == 'last_host') {
$this->help->update("sagacity.scans s", [
"s.{$field['name']}" => $field['value'],
's.last_update' => 'NOW()',
's.hosts_comp' => "s.`hosts_comp`+1"
], $where);
} else {
$this->help->update("sagacity.scans", [
$field['name'] => $field['value'],
'last_update' => 'NOW()'
], $where);
}
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
return true;
}
// }}}
// {{{ SITE CLASS FUNCTIONS
/**
* Get site data
*
* @param integer $siteID
* [optional]
* Site ID to get from database
*
* @return array:site Returns array of sites, or empty array if none found
*/
public function get_Site($siteID = null)
{
$where = [];
$sites = [];
if (! is_null($siteID)) {
if (is_numeric($siteID)) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $siteID
];
} else {
$where = [
'field' => 'name',
'op' => '=',
'value' => $siteID
];
}
}
$this->help->select("sites", null, $where, [
'order' => 'name'
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$sites[] = new site($row['id'], $row['name'], $row['address'], $row['city'], $row['state'], $row['zip'], $row['country'], $row['poc_name'], $row['poc_email'], $row['poc_phone']);
}
}
return $sites;
}
/**
* Get a site for an ST&E
*
* @param integer $intSTE
* ID of the STE to isolate
*
* @return site|NULL Returns array of sites associated with a specific ST&E, or null if none found
*/
public function get_Site_By_STE_ID($intSTE)
{
$this->help->select("sites s", [
's.*'
], [
[
'field' => 'ste.id',
'op' => '=',
'value' => $intSTE
]
], [
'table_joins' => [
"LEFT JOIN ste ON ste.site_id = s.id"
]
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['id'])) {
$site = new site($row['id'], $row['name'], $row['address'], $row['city'], $row['state'], $row['zip'], $row['country'], $row['poc_name'], $row['poc_email'], $row['poc_phone']);
return $site;
}
return null;
}
/**
* Update or insert a site
*
* @param site $site_In
* Site to save to the database
*
* @return boolean|NULL Returns TRUE if successful, otherwise FALSE
*/
public function save_Site(site $site_In)
{
if ($site_In->get_Id()) {
$this->help->update("sites", [
'name' => $site_In->get_Name(),
'address' => $site_In->get_Address(),
'city' => $site_In->get_City(),
'state' => $site_In->get_State(),
'zip' => $site_In->get_Zip(),
'country' => $site_In->get_Country(),
'poc_name' => $site_In->get_POC_Name(),
'poc_email' => $site_In->get_POC_Email(),
'poc_phone' => $site_In->get_POC_Phone()
], [
[
'field' => 'id',
'op' => '=',
'value' => $site_In->get_Id()
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return $site_In->get_Id();
} else {
$this->help->insert("sites", [
'name' => $site_In->get_Name(),
'address' => $site_In->get_Address(),
'city' => $site_In->get_City(),
'state' => $site_In->get_State(),
'zip' => $site_In->get_Zip(),
'country' => $site_In->get_Country(),
'poc_name' => $site_In->get_POC_Name(),
'poc_email' => $site_In->get_POC_Email(),
'poc_phone' => $site_In->get_POC_Phone()
], true);
if (! ($site_id = $this->help->execute())) {
$this->help->debug(E_ERROR);
return false;
}
return $site_id;
}
return true;
}
// }}}
// {{{ SOFTWARE CLASS FUNCTIONS
/**
* Get software data
*
* @param integer|string|software $software_In
* Specific ID, array of software objects, or associative array to use (default null)
* @param boolean $exact_match
* [optional]
* Perform an exact match on a CPE (default false)
*
* @return array:software Returns array of matching software, or empty array if none found
*/
public function get_Software($software_In, $exact_match = false)
{
$ret = [];
$cpe = null;
$sw = null;
$query = false;
if (is_array($software_In)) {
if (isset($software_In[0]) && is_a($software_In[0], 'software')) {
$cpe = $software_In[0]->get_CPE();
} elseif (isset($software_In[0]) && isset($software_In[0]['man'])) {
$software_In = $software_In[0];
$type = (isset($software_In['type']) && $software_In['type'] ? "o" : "a");
$ver = (isset($software_In['ver']) && $software_In['ver'] ? $software_In['ver'] : "-");
$cpe = strtolower(str_replace(array(
" ",
"(",
")"
), array(
"_",
"%28",
"%29"
), "cpe:/{$type}:{$software_In['man']}:{$software_In['name']}:{$ver}"));
}
if ($cpe) {
$this->help->select("sagacity.software", null, array(
array(
'field' => 'cpe',
'op' => LIKE,
'value' => "'%$cpe%'"
)
));
$query = true;
}
} elseif (is_numeric($software_In)) {
$this->help->select("sagacity.software", null, array(
array(
'field' => 'id',
'op' => '=',
'value' => $software_In
)
));
$query = true;
} elseif (is_string($software_In)) {
$op = $exact_match ? '=' : LIKE;
$field = 'cpe';
if (strpos($software_In, "cpe:2.3") !== false) {
$field = 'cpe23';
}
$exclude_r2 = null;
if (preg_match("/windows_server_20[\d]+/", $software_In)) {
if (! preg_match("/r2/", $software_In)) {
$exclude_r2 = array(
'field' => $field,
'op' => NOT_LIKE,
'value' => "'%r2%'",
'sql_op' => 'AND'
);
}
}
$this->help->select("software", null, [
[
'field' => $field,
'op' => $op,
'value' => ($op == LIKE ? "'$software_In%'" : $software_In)
],
$exclude_r2
], [
'order' => 'cpe'
]);
$query = true;
} elseif (is_a($software_In, 'software')) {
$os = ($software_In->is_OS() ? "/o" : "/a");
$man = str_replace(" ", "_", strtolower($software_In->get_Man()));
$name = str_replace(" ", "_", strtolower($software_In->get_Name()));
$ver = str_replace(" ", "_", strtolower($software_In->get_Version()));
$value = "'cpe:{$os}:{$man}:{$name}:{$ver}:%'";
$field = 'cpe';
if (! is_null($software_In->get_CPE23())) {
$os = substr($os, 1);
$value = "'cpe:2.3:{$os}:{$man}:{$name}:{$ver}:%'";
$field = 'cpe23';
}
$this->help->select("software", null, [
[
'field' => $field,
'op' => LIKE,
'value' => $value
]
], [
'order' => 'cpe'
]);
$query = true;
}
if ($query) {
$rows = $this->help->execute();
if (isset($rows['cpe'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$sw = new software($row['cpe'], $row['cpe23']);
$sw->set_ID($row['id']);
$sw->set_SW_String($row['sw_string']);
$sw->set_Shortened_SW_String($row['short_sw_string']);
$ret[] = $sw;
}
}
}
return $ret;
}
/**
* Function to retrieve a software item by using the software string
*
* @param string $sw_string
*
* @return software|NULL
*/
public function get_Software_By_String($sw_string)
{
$ret = null;
$this->help->select("software", null, [
[
'field' => 'sw_string',
'op' => '=',
'value' => $sw_string
]
], [
'limit' => 1
]);
$row = $this->help->execute();
if(is_array($row) && count($row) && isset($row['id'])) {
$ret = new software($row['cpe'], $row['cpe23']);
$ret->set_ID($row['id']);
$ret->set_SW_String($row['sw_string']);
$ret->set_Shortened_SW_String($row['short_sw_string']);
}
return $ret;
}
/**
* Function to retrieve a software item by using the CPE or CPE v2.3
*
* @param string $cpe_in
* CPE to search for
*
* @return software|NULL Returns software object if found, otherwise null
*/
public function get_Software_By_CPE($cpe_in)
{
$field = "cpe";
if (strpos($cpe_in, "cpe:2.3") !== false) {
$field = "cpe23";
}
$this->help->select("software", null, [
[
'field' => $field,
'op' => '=',
'value' => $cpe_in
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$sw = new software($row['cpe'], $row['cpe23']);
$sw->set_ID($row['id']);
$sw->set_SW_String($row['sw_string']);
return $sw;
}
}
return null;
}
/**
* Get the IDs of the CPEs passed in
*
* @param array:string $cpes
*/
public function get_Software_Ids(array $cpes = [])
{
$ret = [];
$this->help->select("software", [
'id'
], [
[
'field' => 'cpe',
'op' => IN,
'value' => $cpes
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = $row['id'];
}
}
return $ret;
}
// @TODO - Finish
/**
* Get a list of all software items
*
* @param boolean $isOS
* Boolean to isolate the operating systems
* @param integer $os_ID
* ID of a specific software, used to select an element in the drop-down
*
* @return string Returns a string with the drop-down option tags
*/
public function get_Software_List($isOS, $os_ID = null)
{
$ret = '<option value=0> -- Please Select An Option -- </option>';
$sql = "SELECT `id`,`cpe`,`cpe23`,`sw_string` " . "FROM `sagacity`.`software`";
if (! is_null($os_ID)) {
$sql .= " WHERE `id`=" . $os_ID;
} elseif ($isOS) {
$sql .= " WHERE `cpe23` LIKE '%:o:%'";
} elseif (! $isOS) {
$sql .= " WHERE `cpe23` LIKE '%:a:%'";
}
// set up query to split cpe string then group by man and name
// $sql .= " GROUP BY ";
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$sw = new software($row['cpe'], $row['cpe23']);
$ret .= "<option value='" . $row['id'] . "'";
if ($os_ID == $row['id']) {
$ret .= " selected ";
}
$ret .= ">" . $sw->man . " " . $sw->name . " " . $sw->ver . (! empty($sw->sp) ? "(" . $sw->sp . ")" : "") . "</option>";
}
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return $ret;
}
/**
* Get array of software that a target has installed
*
* @param integer $tgt_id
* Target ID to query for
*
* @return array:software|NULL Returns array of software that are assigned to associated target, or null if none found
*/
public function get_Target_Software($tgt_id)
{
$this->help->select("software s", [
's.*'
], [
[
'field' => 'ts.tgt_id',
'op' => '=',
'value' => $tgt_id
]
], [
'table_joins' => [
"LEFT JOIN sagacity.target_software ts ON ts.sft_id=s.id"
]
]);
$sw_arr = $this->help->execute();
$sft = [];
if (is_array($sw_arr) && count($sw_arr)) {
if (isset($sw_arr['cpe'])) {
$sw_arr = [
0 => $sw_arr
];
}
foreach ($sw_arr as $row) {
$sw = new software($row['cpe'], $row['cpe23']);
$sw->set_ID($row['id']);
$sw->set_SW_String($row['sw_string']);
$sw->set_Shortened_SW_String($row['short_sw_string']);
$sft[] = $sw;
}
}
return $sft;
}
/**
* Update existing software or add new
*
* @param software $sw_in
* The software to save
*
* @return integer Returns the ID of the software that was just inserted or updated if successful, otherwise it returns 0
*/
public function save_Software($sw_in)
{
if (! is_null($sw_in->get_ID())) {
$this->help->update("sagacity.software", array(
'cpe' => $sw_in->get_CPE(),
'cpe23' => $sw_in->get_CPE23(),
'sw_string' => $sw_in->get_SW_String(),
'short_sw_string' => $sw_in->get_Shortened_SW_String()
), array(
array(
'field' => 'id',
'op' => '=',
'value' => $sw_in->get_ID()
)
));
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return 0;
}
return $sw_in->get_ID();
} else {
$this->help->insert("sagacity.software", array(
'cpe' => $sw_in->get_CPE(),
'cpe23' => $sw_in->get_CPE23(),
'sw_string' => $sw_in->get_SW_String(),
'short_sw_string' => $sw_in->get_Shortened_SW_String()
), true);
if (! ($sw_id = $this->help->execute())) {
$this->help->debug(E_WARNING);
return 0;
}
return $sw_id;
}
return 0;
}
/**
* Function to retrieve an array of all the software detection regex's
*
* @param string $type
*
* @return array
*/
public function get_Regex_Array($type)
{
$ret = [];
$where = [];
if ($type != 'os') {
$where[] = [
'field' => 'type',
'op' => '=',
'value' => $type
];
$where[] = [
'field' => 'type',
'op' => '=',
'value' => 'multiple',
'sql_op' => 'OR'
];
} else {
$where[] = [
'field' => 'type',
'op' => LIKE,
'value' => "'%os'"
];
}
$this->help->select("sagacity.sw_man_match", null, $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$tmp = [
'id' => $row['id'],
'man' => $row['man'],
'rgx' => $row['rgx'],
'name' => []
];
$this->help->select("sagacity.sw_name_match", null, [
[
'field' => 'man_id',
'op' => '=',
'value' => $row['id']
]
]);
$name_rows = $this->help->execute();
if (is_array($name_rows) && count($name_rows) && isset($name_rows['id'])) {
$name_rows = [
0 => $name_rows
];
}
if (is_array($name_rows) && count($name_rows) && isset($name_rows[0])) {
foreach ($name_rows as $row2) {
$tmp['name'][$row2['id']] = array(
'name' => $row2['name'],
'man_override' => $row2['man_override'],
'rgx' => $row2['rgx'],
'name_match' => $row2['name_match'],
'ver_match' => $row2['ver_match'],
'ver' => $row2['ver'],
'update_match' => $row2['update_match'],
'is_os' => ($row2['is_os'] ? true : false),
'multiple' => ($row2['multiple'] ? true : false)
);
}
}
$ret[] = $tmp;
}
}
return $ret;
}
// }}}
// {{{ STE CLASS FUNCTIONS
/**
* Get ST&E data
*
* @param integer $steID
* ST&E ID to isolate
*
* @return array:ste|NULL Returns array of ste objects, or null if none found
*/
public function get_STE($steID = null)
{
$where = [];
$ret = null;
if ($steID != null) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $steID
];
} else {
$where[] = [
'field' => 'primary',
'op' => '=',
'value' => 0
];
}
$this->help->select("ste", null, $where, [
'order' => 'eval_start DESC'
]);
$ste_rows = $this->help->execute();
if (isset($ste_rows['id'])) {
$ste_rows = [
0 => $ste_rows
];
}
if (is_array($ste_rows) && count($ste_rows) && isset($ste_rows[0])) {
foreach ($ste_rows as $row) {
$sys = $this->get_System($row['system_id']);
if (is_array($sys) && count($sys) && isset($sys[0]) && is_a($sys[0], 'system')) {
$sys = $sys[0];
} else {
Sagacity_Error::err_handler("Unable to find system for ST&E ID {$row['id']}", E_ERROR);
}
$site = $this->get_Site($row['site_id']);
if (is_array($site) && count($site) && isset($site[0]) && is_a($site[0], 'site')) {
$site = $site[0];
} else {
Sagacity_Error:
err_handler("Unable to find site for ST&E ID {$row['id']}", E_ERROR);
}
$ste = new ste($row['id'], $sys, $site, $row['eval_start'], $row['eval_end'], $row['multiple'], $row['primary']);
$ste->set_Assumptions($row['assumptions']);
$ste->set_Conclusions($row['conclusion']);
$ste->set_Constraints($row['constraints']);
$ste->set_Deviations($row['deviations']);
$ste->set_Recommendations($row['recommendations']);
$ste->set_Residual_Risk($row['residual_risk']);
$ste->set_Scope($row['scope']);
$ste->set_Status($row['risk_status']);
$ste->set_AO($row['ao']);
$this->help->select("people p", [
'st.pos',
'p.*'
], [
[
'field' => 'st.ste_id',
'op' => '=',
'value' => $ste->get_ID()
]
], [
'table_joins' => [
"JOIN ste_team st ON st.people_id=p.id"
]
]);
$people_rows = $this->help->execute();
if (is_array($people_rows) && isset($people_rows['id'])) {
$people_rows = [
0 => $people_rows
];
}
if (is_array($people_rows) && count($people_rows) && isset($people_rows[0])) {
foreach ($people_rows as $row2) {
$people = new people();
$people->id = $row2['id'];
$people->org = $row2['org'];
$people->name = $row2['name'];
$people->phone = $row2['phone'];
$people->position = $row2['pos'];
$ste->add_STE_Team_Member($people);
}
}
$ret[] = $ste;
}
}
return $ret;
}
/**
* Get the subsystems for a particular site
*
* @param ste $ste_in
* ST&E to get subsystems for
*
* @return array:ste Returns the subsystem ST&E, or empty array if none found
*/
public function get_Subsystems($ste_in)
{
$this->help->select("sagacity.ste", null, array(
array(
'field' => 'primary',
'op' => '=',
'value' => $ste_in->get_ID()
)
));
$ret = [];
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = array(
0 => $rows
);
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = new ste($row['id'], $row['system_id'], $row['site_id'], $row['eval_start'], $row['eval_end'], $row['multiple'], $row['primary']);
}
}
return $ret;
}
/**
* This function returns ST&E list and creates options for a select box
* Will organize into optgroup tags if subsystems are found
*
* @param boolean $select_first
* [optional]
* Force the selection of the first element in the drop-down
*
* @return string|NULL Returns a string of option tag elements, or null if none found
*/
public function get_STE_List($select_first = false)
{
$ret = '<option value="0"' . ($select_first ? ' selected' : '') . '> -- Please Select An Option -- </option>';
$stes = $this->get_STE();
if (is_array($stes) && count($stes) && isset($stes['id'])) {
$stes = [
0 => $stes
];
}
if (is_array($stes) && count($stes) && isset($stes[0])) {
foreach ($stes as $ste) {
$start_str = $ste->get_Eval_Start_Date()->format('d M Y');
$subs = $this->get_Subsystems($ste);
if (is_array($subs) && count($subs) > 0) {
$ret .= "<optgroup label='{$ste->get_System()->get_Name()} {$ste->get_Site()->get_Name()}'>" . "<option value='{$ste->get_ID()}'";
if ((isset($_REQUEST['ste']) && $_REQUEST['ste'] == $ste->get_ID()) || (isset($_COOKIE['ste']) && $_COOKIE['ste'] == $ste->get_ID())) {
if (! $select_first) {
$ret .= " selected ";
}
}
$ret .= ">{$ste->get_System()->get_Name()}, {$ste->get_Site()->get_Name()}, {$start_str} ({$ste->get_ID()})</option>";
foreach ($subs as $sub) {
$ret .= "<option value='{$sub->get_ID()}'";
if ((isset($_REQUEST['ste']) && $_REQUEST['ste'] == $sub->get_ID()) || (isset($_COOKIE['ste']) && $_COOKIE['ste'] == $sub->get_ID())) {
if (! $select_first) {
$ret .= " selected ";
}
}
$ret .= ">{$sub->get_System()->get_Name()}, {$sub->get_Site()->get_Name()}, {$start_str} ({$sub->get_ID()})</option>";
}
$ret .= "</optgroup>";
} else {
$ret .= "<option value='{$ste->get_ID()}'";
if ((isset($_REQUEST['ste']) && $_REQUEST['ste'] == $ste->get_ID()) || (isset($_COOKIE['ste']) && $_COOKIE['ste'] == $ste->get_ID())) {
if (! $select_first) {
$ret .= " selected ";
}
}
$ret .= ">{$ste->get_System()->get_Name()}, {$ste->get_Site()->get_Name()}, {$start_str} ({$ste->get_ID()})</option>";
}
}
}
// return the string of <option> tags
return $ret;
}
/**
* This function returns an array of ste_cat objects that are within the specified ST&amp;E
*
* @param integer $intSTE
* ST&E id to grab the categories for
* @param mixed $intCatID
* [optional]
* ID or name of the specific Category you want to retrieve
*
* @return array:ste_cat Returns array of ste_cat objects that match criteria
*/
public function get_STE_Cat_List($intSTE, $intCatID = null)
{
$ret = [];
$where = [
[
'field' => 'ste_id',
'op' => '=',
'value' => $intSTE
]
];
if (! is_null($intCatID) && is_numeric($intCatID)) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $intCatID,
'sql_op' => 'AND'
];
} elseif (! is_null($intCatID) && is_string($intCatID)) {
$where[] = [
'field' => 'name',
'op' => '=',
'value' => $intCatID,
'sql_op' => 'AND'
];
}
$this->help->select("ste_cat", null, $where, [
'order' => 'name'
]);
$cats = $this->help->execute();
if (is_array($cats) && count($cats) && isset($cats['id'])) {
$cats = [
0 => $cats
];
}
if (is_array($cats) && count($cats) && isset($cats[0])) {
foreach ($cats as $cat) {
$tmp = new ste_cat($cat['id'], $cat['ste_id'], $cat['name'], $cat['analysts']);
$this->help->select("ste_cat_sources", null, [
[
'field' => 'cat_id',
'op' => '=',
'value' => $cat['id']
]
]);
$srcs = $this->help->execute();
if (is_array($srcs) && count($srcs) && isset($srcs['cat_id'])) {
$srcs = [
0 => $srcs
];
}
if (is_array($srcs) && count($srcs) && isset($srcs[0])) {
foreach ($srcs as $src) {
$tmp->add_Source($this->get_Sources($src['src_id']));
}
}
$this->get_Cat_Count($tmp);
$ret[] = $tmp;
}
}
return $ret;
}
/**
* Method to retrieve the category count data from the database for a specific category
*
* @param ste_cat $cat
*/
public function get_Cat_Count(ste_cat &$cat)
{
$where = [];
if ($cat->get_ID()) {
$where[] = [
'field' => 'cat_id',
'op' => '=',
'value' => $cat->get_ID()
];
} else {
$where[] = [
'field' => 'ste_id',
'op' => '=',
'value' => $cat->get_STE_ID()
];
}
$this->help->select("target", [
'cat_1',
'cat_2',
'cat_3',
'closed',
'not_applicable',
'not_reviewed'
], $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['cat_1'])) {
$cat->open += $rows['cat_1'] + $rows['cat_2'] + $rows['cat_3'];
$cat->na += $rows['not_applicable'];
$cat->nf += $rows['closed'];
$cat->nr += $rows['not_reviewed'];
} elseif (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $r) {
$cat->open += $r['cat_1'] + $r['cat_2'] + $r['cat_3'];
$cat->na += $r['not_applicable'];
$cat->nf += $r['closed'];
$cat->nr += $r['not_reviewed'];
}
}
$cat->total = $cat->open + $cat->na + $cat->nf + $cat->nr;
$this->help->select_count("target", $where);
$cat->tgt_count = $this->help->execute();
}
/**
* Function to get categories assigned to a ST&E
*
* @param integer $intSTE
* The ST&E ID to grab categories for
* @param string $strName
* The specific name the look for
*
* @return array:ste_cat |NULL
* Returns array of ste_cat for all categories in that ST&E, or null if none found
*/
public function get_STE_Category_List($intSTE, $strName = null)
{
$sql = "SELECT `id`,`ste_id`,`name`,`analysts` " . "FROM `sagacity`.`ste_cat` " . "WHERE `ste_id`=$intSTE ";
if (! is_null($strName)) {
$sql .= "AND `name`='" . $this->conn->real_escape_string($strName) . "' ";
}
$sql .= "ORDER BY `name`";
$ret = [];
if ($res = $this->conn->query($sql)) {
while ($row = $res->fetch_assoc()) {
$ret[] = new ste_cat($row['id'], $row['ste_id'], $row['name'], $row['analysts']);
}
return $ret;
} else {
Sagacity_Error::sql_handler($sql);
error_log($this->conn->error);
}
return [];
}
/**
* Update or insert an ST&E
*
* @param ste $ste_In
* ST&E to save to the database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_STE($ste_In)
{
if (! isset($_REQUEST['action'])) {
$this->help->insert("ste", [
'system_id' => $ste_In->get_System()
->get_ID(),
'site_id' => $ste_In->get_Site()
->get_ID(),
'eval_start' => $ste_In->get_Eval_Start_Date(),
'eval_end' => $ste_In->get_Eval_End_Date(),
'scope' => $ste_In->get_Scope(),
'assumptions' => $ste_In->get_Assumptions(),
'constraints' => $ste_In->get_Constratints(),
'deviations' => $ste_In->get_Deviations(),
'recommendations' => $ste_In->get_Recommendations(),
'residual_risk' => $ste_In->get_Residual_Risk(),
'conclusion' => $ste_In->get_Conclusions(),
'ao' => $ste_In->get_AO()
]);
$id = $this->help->execute();
if (! $id) {
return false;
}
return $id;
} elseif ($_REQUEST['action'] == 'save-ste' && ! $ste_In->get_ID()) {
$multiple = isset($_REQUEST['add_subsystems']) ? '1' : '0';
$this->help->insert("ste", [
'system_id' => $ste_In->get_System()
->get_ID(),
'site_id' => $ste_In->get_Site()
->get_ID(),
'eval_start' => $ste_In->get_Eval_Start_Date(),
'eval_end' => $ste_In->get_Eval_End_Date(),
'scope' => $ste_In->get_Scope(),
'assumptions' => $ste_In->get_Assumptions(),
'constraints' => $ste_In->get_Constraints(),
'deviations' => $ste_In->get_Deviations(),
'recommendations' => $ste_In->get_Recommendations(),
'residual_risk' => $ste_In->get_Residual_Risk(),
'conclusion' => $ste_In->get_Conclusions(),
'ao' => $ste_In->get_AO(),
'multiple' => $multiple,
'primary' => 0
]);
$ste_id = $this->help->execute();
if ($ste_id) {
$this->conn->real_query("INSERT INTO `ste_cat` (`ste_id`,`name`) " . "SELECT $ste_id,`name` FROM `category`");
} else {
return false;
}
if (isset($_REQUEST['add_subsystems'])) {
$ins = [];
foreach ($_REQUEST['subsystems'] as $subs) {
$ins[] = [
$subs,
$ste_In->get_Site()->get_ID(),
$ste_In->get_Eval_Start_Date(),
$ste_In->get_Eval_End_Date(),
0,
$ste_id
];
$this->help->extended_insert("ste", [
'system_id',
'site_id',
'eval_start',
'eval_end',
'multiple',
'primary'
], $ins);
$sec_ste_id = $this->help->execute();
if ($sec_ste_id) {
$this->conn->real_query("INSERT INTO `ste_cat` (`ste_id`,`name`) SELECT $sec_ste_id, `name` FROM `category`");
} else {
return false;
}
}
}
return true;
} elseif ($_REQUEST['action'] == 'save-ste' && $ste_In->get_ID()) {
$this->help->update("ste", [
'system_id' => $ste_In->get_System()
->get_ID(),
'site_id' => $ste_In->get_Site()
->get_ID(),
'eval_start' => $ste_In->get_Eval_Start_Date(),
'eval_end' => $ste_In->get_Eval_End_Date(),
'multiple' => (isset($_REQUEST['add_subsystems']) ? '1' : '0'),
'scope' => $ste_In->get_Scope(),
'assumptions' => $ste_In->get_Assumptions(),
'constraints' => $ste_In->get_Constraints(),
'deviations' => $ste_In->get_Deviations(),
'recommendations' => $ste_In->get_Recommendations(),
'residual_risk' => $ste_In->get_Residual_Risk(),
'conclusion' => $ste_In->get_Conclusions(),
'ao' => $ste_In->get_AO(),
'primary' => 0
], [
[
'field' => 'id',
'op' => '=',
'value' => $ste_In->get_ID()
]
]);
if (! $this->help->execute()) {
return false;
}
$this->help->update("ste", [
'primary' => '0'
], [
[
'field' => 'primary',
'op' => '=',
'value' => $ste_In->get_ID()
]
]);
$this->help->execute();
if (isset($_REQUEST['add_subsystems'])) {
foreach ($_REQUEST['subsystems'] as $subs) {
$ids = explode('_', $subs);
$this->help->update("ste", [
'system_id' => $ids[0],
'site_id' => $ste_In->get_Site()
->get_ID(),
'eval_start' => $ste_In->get_Eval_Start_Date(),
'eval_end' => $ste_In->get_Eval_End_Date(),
'multiple' => '0',
'primary' => $ste_In->get_ID()
], [
[
'field' => 'id',
'op' => '=',
'value' => $ids[1]
]
]);
if (! $this->help->execute()) {
return false;
}
}
}
return true;
}
return false;
}
// }}}
// {{{ STIG CLASS FUNCTIONS
/**
* Get STIG data
*
* @param string $str_Stig_ID
* [optional]
* STIG ID to get from database (default null)
* @param boolean $like
* [optional]
* To perform a like comparison (default false)
*
* @return array:stig |NULL
* Returns array of stigs, or null if none found
*/
public function get_Stig($str_Stig_ID = null, $like = false)
{
$where = [];
$ret = [];
if (! is_null($str_Stig_ID)) {
if (preg_match("/^\d\.\d{1,2}$/", $str_Stig_ID)) {
if (strlen($str_Stig_ID) == 3) {
$str_Stig_ID .= "00";
} elseif (strlen($str_Stig_ID) == 4) {
$str_Stig_ID .= "0";
}
}
if ($like) {
$where[] = [
'field' => 'stig_id',
'op' => LIKE,
'value' => "'%$str_Stig_ID%'"
];
} else {
$where[] = [
'field' => 'stig_id',
'op' => '=',
'value' => $str_Stig_ID
];
}
}
$this->help->select("stigs", null, $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['stig_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$stig = new stig($row['pdi_id'], $row['stig_id'], $row['description']);
$stig->set_Function($row['tweak_data']);
$ret[] = $stig;
}
}
return $ret;
}
/**
* Function for retrieving the STIG by the PDI
*
* @param integer $pdi_id
* PDI ID of the stig we want to isolate
*
* @return stig|NULL Returns stigs, or null if none found
*/
public function get_STIG_By_PDI($pdi_id)
{
$stig = null;
$this->help->select("stigs", null, [
[
'field' => 'pdi_id',
'op' => '=',
'value' => $pdi_id
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['stig_id'])) {
$stig = new stig($rows['pdi_id'], $rows['stig_id'], $rows['description']);
}
return $stig;
}
/**
* Function to return possible stig functions
*
* @param stig $stig
* @param target $tgt
*
* @return string
*/
public function get_STIG_Function($stig, $tgt)
{
$ret = null;
$chk_arr = [];
foreach ($tgt->checklists as $chk) {
$chk_arr[] = $chk->get_ID();
}
$this->help->select("pdi_checklist_lookup", [
'tweak_data'
], [
[
'field' => 'pdi_id',
'op' => '=',
'value' => $stig->get_PDI_ID()
],
[
'field' => 'checklist_id',
'op' => IN,
'op' => $chk_arr,
'sql_op' => 'AND'
],
[
'field' => 'tweak_data',
'op' => IS_NOT,
'value' => null,
'sql_op' => 'AND'
]
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['tweak_data'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret = $row['tweak_data'];
}
} else {
$this->help->debug(E_WARNING);
}
return $ret;
}
/**
* Function to add STIG to database
*
* @param stig $new_Stig
* STIG to add to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function add_Stig($new_Stig)
{
$this->help->insert("sagacity.stigs", array(
'pdi_id' => $new_Stig->get_PDI_ID(),
'stig_id' => $new_Stig->get_ID(),
'description' => $new_Stig->get_Description()
), true);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ SV_RULE CLASS FUNCTIONS
/**
* Function to get SV Rule data
*
* @param integer $pdi_id
* [optional]
* PDI ID (defaulted null)
* @param string $sv_rule_id
* [optional]
* SV Rule to get from database (defaulted null)
*
* @return array:sv_rule|NULL Returns SV Rule, or null if none found
*/
public function get_SV_Rule($pdi_id = null, $sv_rule_id = null)
{
$ret = [];
$where = [];
if (! is_null($pdi_id) && ! is_null($sv_rule_id)) {
$where = [
[
'field' => 'pdi_id',
'op' => '=',
'value' => $pdi_id
],
[
'field' => 'sv_rule',
'op' => '=',
'value' => $sv_rule_id,
'sql_op' => 'AND'
]
];
} elseif (! is_null($sv_rule_id)) {
$where[] = [
'field' => 'sv_rule',
'op' => '=',
'value' => $sv_rule_id
];
} elseif (! is_null($pdi_id)) {
$where[] = [
'field' => 'pdi_id',
'op' => '=',
'value' => $pdi_id
];
}
$this->help->select("sv_rule", null, $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['pdi_id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = new sv_rule($row['pdi_id'], $row['sv_rule']);
}
}
return $ret;
}
/**
* Update an SV Rule
*
* @param sv_rule $sv_Rule
* Array of SV Rules to save to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_SV_Rule($sv_Rule = null)
{
$params = [];
if (is_array($sv_Rule) && count($sv_Rule) && isset($sv_Rule[0]) && is_a($sv_Rule[0], 'sv_rule')) {
foreach ($sv_Rule as $rule) {
$params[] = [
$rule->get_PDI_ID(),
$rule->get_SV_Rule()
];
}
} elseif (is_a($sv_Rule, 'sv_rule')) {
$params[] = [
$sv_Rule->get_PDI_ID(),
$sv_Rule->get_SV_Rule()
];
} else {
return false;
}
$this->help->extended_replace("sagacity.sv_rule", array(
'pdi_id',
'sv_rule'
), $params);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
return true;
}
// }}}
// {{{ SYSTEM CLASS FUNCTIONS
/**
* Get system data
*
* @param integer $systemID
* [optional]
* System ID to get from database (default null)
*
* @return array:system|NULL Returns array of systems, or null if none found
*/
public function get_System($systemID = null)
{
$ret = [];
$where = [];
if (! is_null($systemID)) {
if (is_numeric($systemID)) {
$where[] = [
'field' => 'id',
'op' => '=',
'value' => $systemID
];
} else {
$where[] = [
'field' => 'name',
'op' => '=',
'value' => $systemID
];
}
}
$this->help->select("sagacity.system", null, $where, [
'order' => 'name'
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$sys = new system($row['id'], $row['name'], $row['mac'], $row['classification']);
$sys->set_Description($row['description']);
$sys->set_Mitigations($row['mitigations']);
$sys->set_Executive_Summary($row['executive_summary']);
$sys->set_Abbreviation($row['abbr']);
switch ($row['acred_type']) {
case 'diacap':
$sys->set_Accreditation_Type(accrediation_types::DIACAP);
break;
case 'rmf':
$sys->set_Accreditation_Type(accrediation_types::RMF);
break;
case 'pci':
$sys->set_Accreditation_Type(accrediation_types::PCI);
break;
case 'nispom':
$sys->set_Accreditation_Type(accrediation_types::NISPOM);
break;
case 'sox':
$sys->set_Accreditation_Type(accrediation_types::SOX);
break;
case 'hipaa':
$sys->set_Accreditation_Type(accrediation_types::HIPAA);
break;
case 'cobit':
$sys->set_Accreditation_Type(accrediation_types::COBIT);
}
$ret[] = $sys;
}
}
return $ret;
}
/**
* Get the system for a ST&E
*
* @param integer $intSTE
* ST&E ID to query for
*
* @return system|NULL Returns array of systems, or null if none found
*/
public function get_System_By_STE_ID($intSTE)
{
$ret = null;
$this->help->select("system s", [
's.*'
], [
[
'field' => 'ste.id',
'op' => '=',
'value' => $intSTE
]
], [
'table_joins' => [
"LEFT JOIN ste ON ste.system_id = s.id"
]
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['id'])) {
$ret = new system($row['id'], $row['name'], $row['mac'], $row['classification']);
$ret->set_Description($row['description']);
$ret->set_Mitigations($row['mitigations']);
$ret->set_Executive_Summary($row['executive_summary']);
$ret->set_Abbreviation($row['abbr']);
switch ($row['acred_type']) {
case 'diacap':
$ret->set_Accreditation_Type(accrediation_types::DIACAP);
break;
case 'rmf':
$ret->set_Accreditation_Type(accrediation_types::RMF);
break;
case 'pci':
$ret->set_Accreditation_Type(accrediation_types::PCI);
break;
case 'nispom':
$ret->set_Accreditation_Type(accrediation_types::NISPOM);
break;
case 'sox':
$ret->set_Accreditation_Type(accrediation_types::SOX);
break;
case 'hipaa':
$ret->set_Accreditation_Type(accrediation_types::HIPAA);
break;
case 'cobit':
$ret->set_Accreditation_Type(accrediation_types::COBIT);
}
}
return $ret;
}
/**
* Update or insert a system
*
* @param system $system_In
* System to save to database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_System($system_In)
{
$acred_type = '';
switch ($system_In->get_Accreditation_Type()) {
case accrediation_types::DIACAP:
$acred_type = 'diacap';
break;
case accrediation_types::RMF:
$acred_type = 'rmf';
break;
case accrediation_types::PCI:
$acred_type = 'pci';
break;
case accrediation_types::NISPOM:
$acred_type = 'nispom';
break;
case accrediation_types::SOX:
$acred_type = 'sox';
break;
case accrediation_types::HIPAA:
$acred_type = 'hipaa';
break;
case accrediation_types::COBIT:
$acred_type = 'cobit';
}
if ($system_In->get_ID()) {
$this->help->update("sagacity.system", array(
'name' => $system_In->get_Name(),
'mac' => $system_In->get_MAC(),
'classification' => $system_In->get_Classification(),
'description' => $system_In->get_Description(),
'mitigations' => $system_In->get_Mitigations(),
'abbr' => $system_In->get_Abbreviation(),
'executive_summary' => $system_In->get_Executive_Summary(),
'acred_type' => $acred_type
), array(
array(
'field' => 'id',
'op' => '=',
'value' => $system_In->get_ID()
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return $system_In->get_ID();
} else {
$this->help->insert("sagacity.system", array(
'name' => $system_In->get_Name(),
'mac' => $system_In->get_MAC(),
'classification' => $system_In->get_Classification(),
'description' => $system_In->get_Description(),
'mitigations' => $system_In->get_Mitigations(),
'abbr' => $system_In->get_Abbreviation(),
'executive_summary' => $system_In->get_Executive_Summary(),
'acred_type' => $acred_type
), true);
if (! ($sys_id = $this->help->execute())) {
$this->help->debug(E_ERROR);
return false;
}
return $sys_id;
}
}
// }}}
// {{{ TARGET CLASS FUNCTIONS
/**
* Check for the presents of a target
*
* @param integer $ste_id
* STE ID
* @param string $tgt_in
* Target to look for (can be IPv4, IPv6, ID, name, hostname, or FQDN)
*
* @return integer Returns the ID of the target if found, otherwise 0
*/
public function check_Target($ste_id, $tgt_in)
{
if (preg_match("/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/", $tgt_in)) {
$this->help->select("target t", [
't.id'
], [
[
'field' => 'i.ipv4',
'op' => '=',
'value' => $tgt_in,
'open-paren' => true
],
[
'field' => 't.name',
'op' => '=',
'value' => $tgt_in,
'close-paren' => true,
'sql_op' => 'OR'
],
[
'field' => 't.ste_id',
'op' => '=',
'value' => $ste_id,
'sql_op' => 'AND'
]
], [
'table_joins' => [
"LEFT JOIN interfaces i ON i.tgt_id=t.id"
],
'group' => 't.id',
'limit' => 1
]);
} elseif (preg_match("/\:/", $tgt_in)) {
$this->help->select("target t", [
't.id'
], [
[
'field' => 'i.ipv6',
'op' => '=',
'value' => $tgt_in
],
[
'field' => 't.ste_id',
'op' => '=',
'value' => $ste_id,
'sql_op' => 'AND'
]
], [
'table_joins' => [
"LEFT JOIN interfaces i ON i.tgt_id=t.id"
]
]);
} else {
$this->help->select("target t", [
't.id'
], [
[
'field' => 't.ste_id',
'op' => '=',
'value' => $ste_id
],
[
'field' => 't.id',
'op' => '=',
'value' => $tgt_in,
'sql_op' => 'AND',
'open-paren' => true
],
[
'field' => 't.name',
'op' => '=',
'value' => $tgt_in,
'sql_op' => 'OR',
'case_insensitive' => true
],
[
'field' => 'i.hostname',
'op' => '=',
'value' => $tgt_in,
'sql_op' => 'OR',
'case_insensitive' => true
],
[
'field' => 'i.fqdn',
'op' => '=',
'value' => $tgt_in,
'sql_op' => 'OR',
'case_insensitive' => true,
'close-paren' => true
]
], [
'table_joins' => [
"LEFT JOIN interfaces i ON i.tgt_id=t.id"
],
'group' => 't.id'
]);
}
$tgt = $this->help->execute();
if (is_array($tgt) && count($tgt) && isset($tgt['id'])) {
return $tgt['id'];
} elseif (is_array($tgt) && count($tgt) > 1) {
Sagacity_Error::err_handler("Return more than one target " . print_r($tgt, true), E_WARNING);
}
/*
* if () {
* print_r($tgt);
* if (isset($tgt['id'])) {
* return $tgt['id'];
* }
* }
*/
return 0;
}
/**
* Get a target by name or ID or all targets in an ST&E
*
* @param integer $int_STE_ID
* ST&E ID to get targets from
* @param integer|string $TGT
* [optional]
* Target to specific grab (can be integer ID or name)
*
* @return array:target Returns array of targets
*/
public function get_Target_Details($int_STE_ID, $TGT = null)
{
$ret = [];
$where = [
[
'field' => 't.ste_id',
'op' => '=',
'value' => $int_STE_ID
]
];
if (! is_null($TGT)) {
if (is_numeric($TGT)) {
$where[] = [
'field' => 't.id',
'op' => '=',
'value' => $TGT,
'sql_op' => 'AND'
];
} else {
$where[] = [
'field' => 't.name',
'op' => '=',
'value' => $TGT,
'sql_op' => 'AND',
'case_insensitive' => true
];
}
}
$this->help->select("target t", null, $where);
$tgts = $this->help->execute();
if (isset($tgts['name'])) {
$tgts = [
0 => $tgts
];
}
if (is_array($tgts) && count($tgts)) {
foreach ($tgts as $row) {
$tgt = new target($row['name']);
$tgt->set_ID($row['id']);
$tgt->set_STE_ID($row['ste_id']);
$tgt->set_Cat_ID($row['cat_id']);
$tgt->set_OS_ID($row['os_id']);
$tgt->set_Auto_Status_ID($row['auto_status_id']);
$tgt->set_Man_Status_ID($row['man_status_id']);
$tgt->set_Data_Status_ID($row['data_status_id']);
$tgt->set_FP_Cat1_Status_ID($row['fp_cat1_status_id']);
$tgt->set_Location($row['location']);
$tgt->set_Source($row['source']);
$tgt->set_Notes($row['notes']);
$tgt->set_Missing_Patches($row['missing_patches']);
$tgt->set_OS_String($row['os_string']);
$tgt->set_PP_Flag($row['pp_flag']);
$tgt->set_PP_Suspended($row['pp_off']);
$tgt->setCat1Count($row['cat_1']);
$tgt->setCat2Count($row['cat_2']);
$tgt->setCat3Count($row['cat_3']);
$tgt->setNotAFindingCount($row['closed']);
$tgt->setNotApplicableCount($row['not_applicable']);
$tgt->setNotReviewedCount($row['not_reviewed']);
$tgt->classification = $row['class'];
$tgt->software = $this->get_Target_Software($row['id']);
$tgt->checklists = $this->get_Target_Checklists($row['id']);
$interfaces = $this->get_Interfaces($row['id']);
if (is_array($interfaces)) {
foreach ($interfaces as $int) {
if ($int->get_IPv4()) {
$tgt->interfaces["{$int->get_IPv4()}"] = $int;
} else {
$tgt->interfaces["{$int->get_IPv6()}"] = $int;
}
}
}
$this->get_Target_MetaData($tgt);
$ret[] = $tgt;
}
}
return $ret;
}
/**
* Get the target's meta data
*
* @param target $tgt
* Target to retrieve the meta data for
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function get_Target_MetaData(&$tgt)
{
if (! is_a($tgt, 'target')) {
return;
}
$this->help->select("target t", [
't.id',
'nm.*',
'sm.*',
'um.*'
], [
[
'field' => 't.id',
'op' => '=',
'value' => $tgt->get_ID()
]
], [
'table_joins' => [
"LEFT JOIN target_net_meta nm ON nm.tgt_id=t.id",
"LEFT JOIN target_sys_meta sm ON sm.tgt_id=t.id",
"LEFT JOIN target_user_meta um ON um.tgt_id=t.id"
],
'group' => 't.id'
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row[0])) {
$row = $row[0];
}
if (is_array($row) && count($row) && isset($row['netstat_connections'])) {
$tgt->set_Netstat_Connections($row['netstat_connections']);
$tgt->set_Shares($row['shares']);
$tgt->set_Routes($row['routes']);
$tgt->set_Firewall_Config($row['firewall_config']);
$tgt->set_Autorun($row['autorun']);
$tgt->set_Mounted($row['mounted']);
$tgt->set_Process_List($row['process_list']);
$tgt->set_Services($row['services']);
$tgt->set_Last_Boot($row['last_boot']);
$tgt->set_Remote_Registry($row['remote_registry']);
$tgt->set_Copyright($row['copyrighted']);
$tgt->set_VM(($row['is_vm'] ? true : false));
$tgt->set_System($row['system']);
$tgt->set_BIOS($row['bios']);
$tgt->set_WMI_PID($row['wmi_listening_pid']);
$tgt->set_Login($row['login']);
$tgt->set_User_List($row['user_list']);
$tgt->set_Last_Login($row['last_login']);
$tgt->set_Disabled_Accts($row['disabled_accts']);
$tgt->set_Stag_Pwds($row['stag_pwds']);
$tgt->set_Never_Logged_In($row['never_logged_in']);
$tgt->set_Pwds_Never_Expire($row['pwd_never_expires']);
return true;
}
return false;
}
/**
* Get all targets in a particular group/category
*
* @param integer $intCat
* Category to get targets from
*
* @return array:target|NULL Returns array of targets for selected category
*/
public function get_Target_By_Category($intCat)
{
$ret = [];
$cat = $this->get_Category($intCat);
if (is_array($cat) && count($cat)) {
$cat = $cat[0];
}
$this->help->select("target", [
'id'
], [
[
'field' => 'cat_id',
'op' => (is_null($intCat) ? IS : '='),
'value' => $intCat
]
], [
'order' => 'LENGTH(name),name'
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = $this->get_Target_Details($cat->get_STE_ID(), $row['id'])[0];
}
}
return $ret;
}
/**
* Function to return all targets that are not assigned to a category
*
* @param int $intSTE
*
* @return array:target |NULL
*/
public function get_Unassigned_Targets($intSTE)
{
$this->help->select("target", [
'id'
], [
[
'field' => 'cat_id',
'op' => IS,
'value' => null
],
[
'field' => 'ste_id',
'op' => '=',
'value' => $intSTE,
'sql_op' => 'AND'
]
], [
'order' => 'LENGTH(name),name'
]);
$ret = [];
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[] = $this->get_Target_Details($intSTE, $row['id'])[0];
}
}
return $ret;
}
/**
* Get the count of all targets in an ST&E
*
* @param integer $intCat
* Category to look in
*
* @return integer Returns count of targets in specific category
*/
public function get_STE_Cat_TGT_Count($intCat)
{
$this->help->select_count("target", [
[
'field' => 'cat_id',
'op' => '=',
'value' => $intCat
]
]);
$count = $this->help->execute();
if (is_numeric($count)) {
return $count;
}
return 0;
}
/**
* Function to retrieve list of targets from 2 ST&amp;E's
*
* @param ste $left_ste
* The left or primary ST&E to compare
* @param ste $right_ste
* The right or secondary ST&E to compare
*
* @return array:target Returns an array of targets that are similar for both or null if it exists in one, but not the other
*/
public function get_Target_Comparison($left_ste, $right_ste)
{
$left_missing_sql = "SELECT `id` FROM `sagacity`.`target` WHERE `ste_id`=" . $left_ste->get_ID() . " ORDER BY `name`";
$right_missing_sql = "SELECT `id` FROM `sagacity`.`target` WHERE `ste_id`=" . $right_ste->get_ID() . " ORDER BY `name`";
$ret = array(
'left' => [],
'right' => []
);
if ($res = $this->conn->query($left_missing_sql)) {
while ($row = $res->fetch_assoc()) {
$tgt = $this->get_Target_Details($left_ste->get_ID(), $row['id'])[0];
$ret['left'][$tgt->get_Name()] = $tgt;
}
}
if ($res = $this->conn->query($right_missing_sql)) {
while ($row = $res->fetch_assoc()) {
$tgt = $this->get_Target_Details($right_ste->get_ID(), $row['id'])[0];
$ret['right'][$tgt->get_Name()] = $tgt;
if (! isset($ret['left'][$tgt->get_Name()])) {
$ret['left'][$tgt->get_Name()] = null;
}
}
}
return $ret;
}
/**
* Function to perform post processing on nessus scan data
*
* @param integer $id
* [optional]
* Specific target ID to process (default null), if null will look for all targets that need the post-processing run
* @param integer $os_id
* [optional]
* OS ID of the target (only used when passing in a specific target)
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function post_Processing($id = null)
{
if (is_null($id)) {
$this->help->select("target", [
'id',
'os_id'
], [
[
'field' => 'pp_flag',
'op' => '=',
'value' => 1
],
[
'field' => 'pp_off',
'op' => '=',
'value' => 0,
'sql_op' => 'AND'
]
]);
$tgts = $this->help->execute();
if (is_array($tgts) && count($tgts) && isset($tgts['id'])) {
$tgts = [
0 => $tgts
];
}
if (is_array($tgts) && count($tgts) && isset($tgts[0])) {
foreach ($tgts as $tgt) {
$this->help->query_type = db_helper::INSERT;
$this->help->sql = "INSERT IGNORE INTO `target_checklist` (`tgt_id`,`chk_id`) " .
"SELECT {$tgt['id']},c.`id` FROM (" .
"SELECT chk.*,s.`id` AS 'real_sw' " .
"FROM `checklist` chk " .
"JOIN `checklist_software_lookup` csl ON csl.`chk_id`=chk.`id` " .
"JOIN `software` s ON csl.`sw_id`=s.`id` " .
"WHERE " .
"chk.`type` = 'manual' AND " .
"s.`cpe` NOT LIKE '%generic:generic%' AND " .
"s.`cpe` NOT LIKE '%apache:http\_server%' AND " .
"s.`cpe` NOT LIKE '%oracle:jre%' " .
"ORDER BY chk.`ver` DESC,CONVERT(chk.`release`, DECIMAL(4,2)) DESC" .
") c " .
"JOIN `target_software` ts ON ts.`sft_id`=c.`real_sw` " .
"WHERE ts.`tgt_id`={$tgt['id']} " .
"GROUP BY c.`name`,c.`type`";
$this->help->execute();
$os = $this->get_Software($tgt['os_id']);
if (is_array($os) && count($os) && isset($os[0]) && is_a($os[0], 'software')) {
$os = $os[0];
}
// to apply JRE STIGs to correct software
$sw = "Unix";
if (strtolower($os->man) == 'microsoft' && (strtolower($os->name) == 'windows xp' || (strtolower($os->name) == 'windows-nt' && strtolower($os->ver) == 'xp'))) {
$sw = "WinXP";
} elseif (strtolower($os->man) == 'microsoft' && strtolower($os->name) == 'windows 7') {
$sw = "Win7";
}
$this->help->query_type = db_helper::INSERT;
$this->help->sql = "INSERT IGNORE INTO `target_checklist` (`tgt_id`,`chk_id`) " .
"SELECT '{$tgt['id']}',c.`id` FROM (" .
"SELECT chk.*,s.`id` as 'real_sw' " .
"FROM `checklist` chk " .
"JOIN `checklist_software_lookup` csl ON csl.`chk_id`=chk.`id` " .
"JOIN `software` s ON s.`id`=csl.`sw_id` " .
"WHERE s.`cpe` LIKE '%oracle:jre%' AND " .
"chk.`checklist_id` LIKE 'JRE%{$sw}%' " .
"ORDER BY chk.`ver` DESC,CONVERT(chk.`release`, DECIMAL(4,2)) DESC" .
") c " .
"JOIN `target_software` ts ON ts.`sft_id`=c.`real_sw` " .
"WHERE ts.`tgt_id`={$tgt['id']} " .
"GROUP BY c.`name`,c.`type`"
;
$this->help->execute();
// specific checklist assignment for Apache
$sw = "Unix";
if (strtolower($os->man) == 'microsoft') {
$sw = "Windows";
}
$this->help->query_type = db_helper::INSERT;
$this->help->sql = "INSERT IGNORE INTO `target_checklist` (`tgt_id`,`chk_id`) " .
"SELECT '{$tgt['id']}',c.`id` FROM (" .
"SELECT chk.*,s.`id` AS 'real_sw' " .
"FROM `checklist` chk " .
"JOIN `checklist_software_lookup` csl ON csl.`chk_id`=chk.`id` " .
"JOIN `software` s ON s.`id`=csl.`sw_id` " .
"WHERE s.`cpe` LIKE '%apache:http\_server%' AND " .
"chk.`checklist_id` LIKE 'Apache%{$sw}%' " .
"ORDER BY chk.`ver` DESC,CONVERT(chk.`release`,DECIMAL(4,2)) DESC" .
") c " .
"JOIN `target_software` ts ON ts.`sft_id`=c.`real_sw` " .
"WHERE ts.`tgt_id`={$tgt['id']} " .
"GROUP BY c.`name`,c.`type`"
;
$this->help->execute();
$this->help->query_type = db_helper::INSERT;
$this->help->sql = "INSERT IGNORE INTO `target_checklist` (`tgt_id`,`chk_id`) " .
"SELECT '{$tgt['id']}',c.`id` FROM (" .
"SELECT chk.*,csl.`sw_id` AS 'real_sw' FROM `checklist` chk " .
"JOIN `checklist_software_lookup` csl ON csl.`chk_id`=chk.`id` " .
"JOIN `software` s ON csl.`sw_id`=s.`id` " .
"WHERE s.`cpe` NOT LIKE '%generic:generic%' " .
"ORDER BY chk.`ver` DESC,CONVERT(chk.`release`, DECIMAL(4,2)) DESC" .
") c " .
"LEFT JOIN `target` t ON c.`real_sw`=t.`os_id` " .
"WHERE t.`id`={$tgt['id']} " .
"GROUP BY c.`name`,c.`type`";
$this->help->execute();
$this->help->update("target", [
'pp_flag' => 0
], [
[
'field' => 'id',
'op' => '=',
'value' => $tgt['id']
]
]);
$this->help->execute();
$this->help->query_type = db_helper::INSERT;
$this->help->sql = "INSERT IGNORE INTO `findings` (`tgt_id`,`pdi_id`,`findings_status_id`) " .
"SELECT {$tgt['id']},pcl.pdi_id,'1' " .
"FROM target_checklist tc " .
"JOIN pdi_checklist_lookup pcl ON pcl.checklist_id = tc.chk_id " .
"WHERE tc.tgt_id = {$tgt['id']}";
$this->help->execute();
$this->update_Target_Counts($tgt['id']);
}
}
} else {
$this->help->select("checklist c", [
'c.*',
"s.id AS 'sw_id'"
], [
[
'field' => 's.cpe',
'op' => NOT_LIKE,
'value' => "'%generic:generic%'"
],
[
'field' => 's.cpe',
'op' => NOT_LIKE,
'value' => "'%apache:http\_server%'",
'sql_op' => 'AND'
],
[
'field' => 's.cpe',
'op' => NOT_LIKE,
'value' => "'%oracle:jre%'",
'sql_op' => 'AND'
]
], [
'table_joins' => [
"JOIN checklist_software_lookup csl ON csl.chk_id=c.id",
"JOIN software s ON csl.sw_id=s.id"
],
'order' => 'c.ver DESC,CONVERT(c.`release`, DECIMAL(4,2)) DESC'
]);
$this->help->create_table("c", true);
$this->help->execute();
$this->help->select("c", [
"c.id"
], [
[
'field' => 'ts.tgt_id',
'op' => '=',
'value' => $id
],
[
'field' => 't.pp_off',
'op' => '=',
'value' => '0',
'sql_op' => 'AND'
]
], [
'table_joins' => [
"JOIN target_software ts ON ts.sft_id=c.sw_id",
"JOIN target t ON t.id=ts.tgt_id"
],
'group' => 'c.name,c.type'
]);
$tc_arr = [];
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$tc_arr[] = [
$id,
$row['id']
];
}
}
if (count($tc_arr)) {
$this->help->extended_insert("target_checklist", [
'tgt_id',
'chk_id'
], $tc_arr, true);
$this->help->execute();
}
$this->help->drop("sagacity", "c", true);
$this->help->select("checklist c", [
'c.*',
"s.id AS 'sw_id'"
], [
[
'field' => 's.cpe',
'op' => NOT_LIKE,
'value' => "'%generic:generic%'"
]
], [
'table_joins' => [
"JOIN checklist_software_lookup csl ON csl.chk_id=c.id",
"JOIN software s ON csl.sw_id=s.id"
],
'order' => 'c.ver DESC,CONVERT(c.`release`, DECIMAL(4,2)) DESC'
]);
$this->help->create_table("c", true);
$this->help->execute();
$this->help->select("c", [
"c.id"
], [
[
'field' => 't.id',
'op' => '=',
'value' => $id
],
[
'field' => 't.pp_off',
'op' => '=',
'value' => '0',
'sql_op' => 'AND'
]
], [
'table_joins' => [
"LEFT JOIN target t ON c.sw_id=t.os_id"
],
'group' => 'c.name,c.type'
]);
$tc_arr = [];
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['id'])) {
$rows = [
0 => $rows
];
}
if (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$tc_arr[] = [
$id,
$row['id']
];
}
}
if (count($tc_arr)) {
$this->help->extended_insert("target_checklist", [
'tgt_id',
'chk_id'
], $tc_arr, true);
$this->help->execute();
}
$this->help->update("target", [
"pp_flag" => 0
], [
[
'field' => 'id',
'op' => '=',
'value' => $id
]
]);
$this->help->execute();
$this->help->query_type = db_helper::INSERT;
$this->help->sql = "INSERT IGNORE INTO findings (tgt_id,pdi_id,findings_status_id) " .
"SELECT {$id},pcl.pdi_id,1 " .
"FROM target_checklist tc " .
"JOIN pdi_checklist_lookup pcl ON pcl.checklist_id = tc.chk_id " .
"WHERE tc.tgt_id = {$id}";
$this->help->execute();
$this->update_Target_Counts($id);
}
return true;
}
/**
* Function to update the target finding counts
*
* @param int $tgt_id
*/
public function update_Target_Counts($tgt_id)
{
$nf = 0;
$nr = 0;
$na = 0;
$cat_1 = 0;
$cat_2 = 0;
$cat_3 = 0;
$this->help->select('target t', [
"COUNT(DISTINCT(pcl.pdi_id)) AS 'pdi_count'"
], [
[
'field' => 't.id',
'op' => '=',
'value' => $tgt_id
]
], [
'table_joins' => [
"LEFT JOIN target_checklist tc ON tc.tgt_id = t.id",
"LEFT JOIN pdi_checklist_lookup pcl ON pcl.checklist_id = tc.chk_id",
"LEFT JOIN findings f ON f.pdi_id = pcl.pdi_id AND f.tgt_id = t.id"
],
'group' => 't.id'
]);
$row = $this->help->execute();
if (is_array($row) && count($row) && isset($row['pdi_count'])) {
$nr = $row['pdi_count'];
}
$this->help->select("target t", [
"IF(ISNULL(fs.status), 'Not Reviewed', fs.status) AS 'status'",
"f.cat AS 'severity'",
"COUNT(DISTINCT f.pdi_id) AS 'finding_count'"
], [
[
'field' => 't.id',
'op' => '=',
'value' => $tgt_id
]
], [
'table_joins' => [
"JOIN target_checklist tc ON tc.tgt_id = t.id",
"JOIN pdi_checklist_lookup pcl ON tc.chk_id = pcl.checklist_id",
"LEFT JOIN findings f ON f.tgt_id = t.id AND f.pdi_id = pcl.pdi_id",
"LEFT JOIN findings_status fs ON f.findings_status_id = fs.id"
],
'group' => "t.id,`severity`,f.findings_status_id"
]);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows['status'])) {
$nr -= $rows['finding_count'];
switch ($rows['status']) {
case 'Not a Finding':
case 'False Positive':
$nf += $rows['finding_count'];
break;
case 'Not Applicable':
$na += $rows['finding_count'];
break;
case 'Not Reviewed':
$nr += $rows['finding_count'];
break;
case 'Open':
case 'Exception':
${'cat_' . $rows['severity']} += $rows['finding_count'];
}
} elseif (is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$nr -= $row['finding_count'];
switch ($row['status']) {
case 'Not a Finding':
case 'False Positive':
$nf += $row['finding_count'];
break;
case 'Not Applicable':
$na += $row['finding_count'];
break;
case 'Not Reviewed':
case 'No Data':
$nr += $row['finding_count'];
break;
case 'Open':
case 'Exception':
${'cat_' . $row['severity']} += $row['finding_count'];
}
}
}
$this->help->update("target", [
'cat_1' => $cat_1,
'cat_2' => $cat_2,
'cat_3' => $cat_3,
'closed' => $nf,
'not_reviewed' => $nr,
'not_applicable' => $na
], [
[
'field' => 'id',
'op' => '=',
'value' => $tgt_id
]
]);
$this->help->execute();
}
/**
* Update or insert a target
*
* @param target $tgt
* The target to save
* @param boolean $pp
*
* @return integer Returns ID of newly added target, or 0 if fail
*/
public function save_Target($tgt, $pp = null)
{
if (! is_a($tgt, 'target')) {
Sagacity_Error::err_handler("Invalid type in db::save_Target()", E_ERROR);
}
if (! in_array($tgt->classification, [
'U',
'S',
'FOUO'
])) {
$sys = $this->get_System_By_STE_ID($tgt->get_STE_ID());
if (is_a($sys, 'system')) {
switch ($sys->get_Classification()) {
case 'Sensitive':
$tgt->classification = "FOUO";
break;
case 'Classified':
$tgt->classification = "S";
break;
default:
$tgt->classification = "U";
}
} else {
$tgt->classification = "U";
}
}
if (! $tgt->get_ID()) {
$this->help->insert("target", [
'ste_id' => $tgt->get_STE_ID(),
'name' => $tgt->get_Name(),
'cat_id' => $tgt->get_Cat_ID(),
'os_id' => $tgt->get_OS_ID(),
'os_string' => $tgt->get_OS_String(),
'location' => $tgt->get_Location(),
'auto_status_id' => $tgt->get_Auto_Status_ID(),
'man_status_id' => $tgt->get_Man_Status_ID(),
'data_status_id' => $tgt->get_Data_Status_ID(),
'fp_cat1_status_id' => $tgt->get_FP_Cat1_Status_ID(),
'pp_off' => $tgt->is_PP_Suspended(),
'class' => $tgt->classification,
'notes' => $tgt->get_Notes()
]);
if (! $this->help->execute()) {
Sagacity_Error::err_handler("Failed to insert target {$tgt->get_Name()}", E_WARNING);
$this->help->debug(E_ERROR);
}
$this->help->select("target", [
'id'
], [
[
'field' => 'ste_id',
'op' => '=',
'value' => $tgt->get_STE_ID()
],
[
'field' => 'name',
'op' => '=',
'value' => $tgt->get_Name(),
'sql_op' => 'AND'
]
]);
$tmp = $this->help->execute();
if (is_array($tmp) && count($tmp) && isset($tmp['id'])) {
$tgt->set_ID($tmp['id']);
} else {
Sagacity_Error::err_handler("Failure retrieving the target that was just saved {$tgt->get_Name()}", E_ERROR);
}
} else {
$exist_tgt = $this->get_Target_Details($tgt->get_STE_ID(), $tgt->get_ID());
if (is_array($exist_tgt) && count($exist_tgt) && isset($exist_tgt[0]) && is_a($exist_tgt[0], 'target')) {
$exist_tgt = $exist_tgt[0];
if ($exist_tgt->get_OS_ID() <= 2 && $tgt->get_OS_ID() <= 2) {
$os_id = $exist_tgt->get_OS_ID();
$os_string = $exist_tgt->get_OS_String();
} elseif ($exist_tgt->get_OS_ID() > 2 && $tgt->get_OS_ID() <= 2) {
$os_id = $exist_tgt->get_OS_ID();
$os_string = $exist_tgt->get_OS_String();
} elseif ($exist_tgt->get_OS_ID() <= 2 && $tgt->get_OS_ID() > 2) {
$os_id = $tgt->get_OS_ID();
$os_string = $tgt->get_OS_String();
} else {
$os_id = $tgt->get_OS_ID();
$os_string = $tgt->get_OS_String();
}
} else {
$os_id = $tgt->get_OS_ID();
$os_string = $tgt->get_OS_String();
}
$this->help->update("target", [
'name' => $tgt->get_Name(),
'os_id' => $os_id,
'os_string' => $os_string,
'cat_id' => $tgt->get_Cat_ID(),
'location' => $tgt->get_Location(),
'auto_status_id' => $tgt->get_Auto_Status_ID(),
'man_status_id' => $tgt->get_Man_Status_ID(),
'data_status_id' => $tgt->get_Data_Status_ID(),
'fp_cat1_status_id' => $tgt->get_FP_Cat1_Status_ID(),
'missing_patches' => $tgt->get_Missing_Patches(),
'class' => $tgt->classification,
'pp_flag' => $tgt->is_PP_Flag_Set(),
'pp_off' => $tgt->is_PP_Suspended(),
'notes' => $tgt->get_Notes()
], [
[
'field' => 'id',
'op' => '=',
'value' => $tgt->get_ID()
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return 0;
}
}
if (is_array($tgt->interfaces) && count($tgt->interfaces)) {
$this->save_Target_Interfaces($tgt);
}
if (is_array($tgt->software) && count($tgt->software)) {
$this->save_Target_Software($tgt);
} else {
$this->delete_Target_Software($tgt);
}
if (is_array($tgt->checklists) && count($tgt->checklists)) {
$this->save_Target_Checklists($tgt);
} else {
$this->delete_Target_Checklists($tgt);
}
if ($pp === null) {
if (! $tgt->is_PP_Suspended()) {
$this->post_Processing($tgt->get_ID());
} else {
$this->update_Target_Counts($tgt->get_ID());
}
} else {
if ($pp === true) {
$this->post_Processing($tgt->get_ID());
} else {
$this->update_Target_Counts($tgt->get_ID());
}
}
$this->help->replace("target_net_meta", [
'tgt_id' => $tgt->get_ID(),
'netstat_connections' => $tgt->get_Netstat_Connections(),
'shares' => $tgt->get_Shares(),
'routes' => $tgt->get_Routes(),
'firewall_config' => $tgt->get_Firewall_Config()
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
$this->help->replace("target_sys_meta", [
'tgt_id' => $tgt->get_ID(),
'mounted' => $tgt->get_Mounted(),
'process_list' => $tgt->get_Process_List(),
'autorun' => $tgt->get_Autorun(),
'services' => $tgt->get_Services(),
'last_boot' => (is_a($tgt->get_Last_Boot(), 'DateTime') ? $tgt->get_Last_Boot() : null),
'remote_registry' => $tgt->get_Remote_Registry(),
'copyrighted' => $tgt->get_Copyright(),
'is_vm' => $tgt->is_VM(),
'system' => $tgt->get_System(),
'bios' => $tgt->get_BIOS(),
'wmi_listening_pid' => $tgt->get_WMI_PID()
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
$this->help->replace("target_user_meta", [
'tgt_id' => $tgt->get_ID(),
'login' => $tgt->get_Login(),
'user_list' => $tgt->get_User_List(),
'last_login' => $tgt->get_Last_Login(),
'disabled_accts' => $tgt->get_Disabled_Accts(),
'stag_pwds' => $tgt->get_Stag_Pwds(),
'never_logged_in' => $tgt->get_Never_Logged_In(),
'pwd_never_expires' => $tgt->get_Pwds_Never_Expire()
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
return $tgt->get_ID();
}
/**
* Function to save the checklists that are assigned to a target
*
* @param target $tgt
* Target
* @param array:checklist $checklists
* [optional]
* Array of checklists to assign to target (if null, uses $tgt->checklists value)
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Target_Checklists($tgt, $checklists = null)
{
$this->help->delete("target_checklist", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt->get_ID()
]
]);
$this->help->execute();
/** @var checklist $chk */
$chk_arr = [];
if (is_array($checklists) && count($checklists) && isset($checklists[0]) && is_a($checklists[0], 'checklist')) {
foreach ($checklists as $chk) {
$chk_arr[] = [
$tgt->get_ID(),
$chk->get_ID()
];
}
} else {
foreach ($tgt->checklists as $chk) {
$chk_arr[] = [
$tgt->get_ID(),
$chk->get_ID()
];
}
}
if (count($chk_arr)) {
$this->help->extended_insert("target_checklist", [
'tgt_id',
'chk_id'
], $chk_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
return true;
}
/**
* Function to save the software installed on the target
*
* @param target $tgt
* The target that the software is installed on
* @param array:software $software
* [optional]
* Array of software that are installed on the target (if null uses the $tgt->software)
*
* @return boolean
*/
public function save_Target_Software($tgt, $software = null)
{
$this->help->delete("target_software", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt->get_ID()
]
]);
$this->help->execute();
$sw_arr = [];
if (is_array($software) && count($software) && isset($software[0]) && is_a($software[0], 'software')) {
foreach ($software as $sw) {
$sw_arr[] = [
$tgt->get_ID(),
$sw->get_ID(),
$sw->get_Shortened_SW_String()
];
}
} else {
foreach ($tgt->software as $sw) {
$sw_arr[] = [
$tgt->get_ID(),
$sw->get_ID(),
$sw->get_Shortened_SW_String()
];
}
}
if (count($sw_arr)) {
$this->help->extended_insert("target_software", [
'tgt_id',
'sft_id',
'sw_string'
], $sw_arr, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
return true;
}
/**
* Function to save the interfaces of a target
*
* @param target $tgt
*/
public function save_Target_Interfaces($tgt)
{
if (! is_array($tgt->interfaces) || ! count($tgt->interfaces)) {
return;
}
foreach ($tgt->interfaces as $int) {
if ($int->get_ID()) {
$this->help->update("sagacity.interfaces", array(
'name' => $int->get_Name(),
'ipv4' => $int->get_IPv4(),
'ipv6' => $int->get_IPv6(),
'hostname' => $int->get_Hostname(),
'fqdn' => $int->get_FQDN(),
'description' => $int->get_Description()
), array(
array(
'field' => 'id',
'op' => '=',
'value' => $int->get_ID()
)
));
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
} else {
$this->help->insert("interfaces", [
'tgt_id' => $tgt->get_ID(),
'name' => $int->get_Name(),
'ipv4' => $int->get_IPv4(),
'ipv6' => $int->get_IPv6(),
'hostname' => $int->get_Hostname(),
'fqdn' => $int->get_FQDN(),
'description' => $int->get_Description()
]);
if (! ($int_id = $this->help->execute())) {
$this->help->debug(E_WARNING);
continue;
}
$int->set_ID($int_id);
}
$ports = [];
if (is_array($int->get_TCP_Ports()) && count($int->get_TCP_Ports())) {
foreach ($int->get_TCP_Ports() as $port_num => $tcp) {
$port_num = ($tcp->get_ID() ? $tcp->get_ID() : "(SELECT `id` FROM `sagacity`.`ports_proto_services` WHERE `port`=$port_num AND `proto`='tcp' AND `notes` NOT LIKE '%historic%' LIMIT 1)");
$ports[] = [
$int->get_ID(),
$port_num,
$tcp->get_IANA_Name(),
$tcp->get_Banner(),
$tcp->get_Notes()
];
}
}
if (is_array($int->get_UDP_Ports()) && count($int->get_UDP_Ports())) {
foreach ($int->get_UDP_Ports() as $port_num => $udp) {
$port_num = ($udp->get_ID() ? $udp->get_ID() : "(SELECT `id` FROM `sagacity`.`ports_proto_services` WHERE `port`=$port_num AND `proto`='udp' AND `notes` NOT LIKE '%historic%' LIMIT 1)");
$ports[] = array(
$int->get_ID(),
$port_num,
$udp->get_IANA_Name(),
$udp->get_Banner(),
$udp->get_Notes()
);
}
}
if (count($ports)) {
$this->help->extended_insert("sagacity.pps_list", array(
'int_id',
'pps_id',
'name',
'banner',
'notes'
), $ports, true);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
}
}
}
/**
* Function to merge the secondary target into the primary target data
*
* @param int $ste_id
* ID of the ST&E both targets are in
* @param int $pri_tgt_id
* ID of the primary target
* @param int $sec_tgt_id
* ID of the secondary target (will be deleted after complete)
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function merge_Target($ste_id, $pri_tgt_id, $sec_tgt_id)
{
// update the primary target checklists to add any that are in the secondary that are not already in the primary
$this->help->select("sagacity.target_checklist", [
'chk_id'
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $pri_tgt_id
]
]);
$tcs = $this->help->execute();
if (is_array($tcs) && count($tcs) && isset($tcs['chk_id'])) {
$tcs = [
0 => $tcs
];
}
$pri_tcs = [];
if (is_array($tcs) && count($tcs)) {
foreach ($tcs as $tc) {
if (isset($tc['chk_id']) && is_numeric($tc['chk_id'])) {
$pri_tcs[] = $tc['chk_id'];
}
}
}
if (count($pri_tcs)) {
$this->help->update("sagacity.target_checklist", [
'tgt_id' => $pri_tgt_id
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $sec_tgt_id
],
[
'field' => 'chk_id',
'op' => NOT_IN,
'value' => $pri_tcs,
'sql_op' => 'AND'
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
return false;
}
}
// update the primary target software to add any that are in the secondary that are not already in the primary
$this->help->select("sagacity.target_software", [
'sft_id'
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $pri_tgt_id
]
]);
$tss = $this->help->execute();
if (is_array($tss) && count($tss) && isset($tss['sft_id'])) {
$tss = [
0 => $tss
];
}
$pri_tss = [];
if (is_array($tss) && count($tss)) {
foreach ($tss as $ts) {
if (isset($ts['sft_id']) && is_numeric($ts['sft_id'])) {
$pri_tss[] = $ts['sft_id'];
}
}
}
if (count($pri_tss)) {
$this->help->update("sagacity.target_software", [
'tgt_id' => $pri_tgt_id
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $sec_tgt_id
],
[
'field' => 'sft_id',
'op' => NOT_IN,
'value' => $pri_tss,
'sql_op' => 'AND'
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_WARNING);
}
}
// delete any remaining checklists from the secondary
$this->delete_Target_Checklists($sec_tgt_id);
// delete any remaining software from the secondary
$this->delete_Target_Software($sec_tgt_id);
// need to clear this up...
// check on PDI, and status, deconflict different statuses
$pri_tgt = $this->get_Target_Details($ste_id, $pri_tgt_id)[0];
$sec_tgt = $this->get_Target_Details($ste_id, $sec_tgt_id)[0];
$sec_findings = $this->get_Finding($sec_tgt);
foreach ($sec_findings as $key => $find) {
$stig = $this->get_STIG_By_PDI($find->get_PDI_ID());
$pri_find = $this->get_Finding($pri_tgt, $stig);
if (is_array($pri_find) && count($pri_find) && isset($pri_find[0])) {
$pri_find = $pri_find[0];
if ($pri_find->get_Finding_Status() != $find->get_Finding_Status()) {
$pri_find->set_Finding_Status_By_String($pri_find->get_Deconflicted_Status($find->get_Finding_Status_String()));
$pri_find->prepend_Notes("Merge target-different status");
}
if ($find->get_Notes()) {
$pri_find->append_Notes($find->get_Notes(), true);
}
$this->help->update("sagacity.findings", [
'findings_status_id' => $pri_find->get_Finding_Status(),
'notes' => $pri_find->get_Notes()
], [
[
'field' => 'id',
'op' => '=',
'value' => $pri_find->get_ID()
]
]);
} else {
$sec_findings[$key]->set_Tgt_ID($pri_tgt_id);
$pri_find = $sec_findings[$key];
$this->help->update("sagacity.findings", [
'tgt_id' => $pri_find->get_Tgt_ID()
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $sec_tgt_id
],
[
'field' => 'pdi_id',
'op' => '=',
'value' => $pri_find->get_PDI_ID(),
'sql_op' => 'AND'
]
]);
}
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
}
}
$this->delete_Target_Findings($sec_tgt_id);
// update host_list
$this->help->select("sagacity.host_list", [
'scan_id'
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $pri_tgt_id
]
]);
$scan_ids = $this->help->execute();
if (is_array($scan_ids) && count($scan_ids) && isset($scan_ids['scan_id'])) {
$scan_ids = array(
0 => $scan_ids
);
}
if (is_array($scan_ids) && count($scan_ids) && isset($scan_ids[0])) {
foreach ($scan_ids as $scan) {
$this->help->select_count("sagacity.host_list", [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $sec_tgt_id
],
[
'field' => 'scan_id',
'op' => '=',
'value' => $scan['scan_id'],
'sql_op' => 'AND'
]
]);
if ($this->help->execute()) {
$this->help->delete("sagacity.host_list", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $sec_tgt_id
],
[
'field' => 'scan_id',
'op' => '=',
'value' => $scan['scan_id'],
'sql_op' => 'AND'
]
]);
$this->help->execute();
}
}
}
$this->help->update("sagacity.host_list", [
'tgt_id' => $pri_tgt_id
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $sec_tgt_id
]
]);
$this->help->execute();
// reassign the secondary pps to the primary
$sec_int = $this->get_Interfaces($sec_tgt_id);
foreach ($sec_int as $int) {
if ($int->get_IPv6()) {
$pri_int = $this->get_Interface_By_IP($pri_tgt_id, $int->get_IPv6());
} else {
$pri_int = $this->get_Interface_By_IP($pri_tgt_id, $int->get_IPv4());
}
if (is_null($pri_int)) {
// add the interface by updating the tgt_id pointer
$this->help->update('sagacity.interfaces', [
'tgt_id' => $pri_tgt_id
], [
[
'field' => 'id',
'op' => '=',
'value' => $int->get_ID()
]
]);
$this->help->execute();
} else {
// loop through tcp/udp port list to see if we need to update those
foreach ($int->get_TCP_Ports() as $tcp) {
if (! $pri_int->get_TCP_Port_By_Port_Number($tcp->get_Port())) {
$this->help->update("sagacity.pps_list", array(
'int_id' => $pri_int->get_ID()
), array(
array(
'field' => 'int_id',
'op' => '=',
'value' => $int->get_ID()
),
array(
'field' => 'pps_id',
'op' => '=',
'value' => $tcp->get_ID(),
'sql_op' => 'AND'
)
));
$this->help->execute();
}
}
foreach ($int->get_UDP_Ports() as $udp) {
if (! $pri_int->get_UDP_Port_By_Port_Number($udp->get_Port())) {
$this->help->update("sagacity.pps_list", array(
'int_id' => $pri_int->get_ID()
), array(
array(
'field' => 'int_id',
'op' => '=',
'value' => $int->get_ID()
),
array(
'field' => 'pps_id',
'op' => '=',
'value' => $udp->get_ID(),
'sql_op' => 'AND'
)
));
$this->help->execute();
}
}
}
}
$this->delete_Target($sec_tgt_id);
return true;
}
/**
* Function to delete all checklists assigned to a target
*
* @param target|int $tgt
* Target to delete all checklists from
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function delete_Target_Checklists($tgt)
{
if (is_a($tgt, "target")) {
$tgt_id = $tgt->get_ID();
} elseif (is_numeric($tgt)) {
$tgt_id = $tgt;
} else {
error_log("Invalid type");
return false;
}
$this->help->delete("sagacity.target_checklist", null, array(
array(
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to delete all software assigned to a target
*
* @param target|int $tgt
* Target to delete all software from
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function delete_Target_Software($tgt)
{
if (is_a($tgt, 'target')) {
$tgt_id = $tgt->get_ID();
} elseif (is_numeric($tgt)) {
$tgt_id = $tgt;
} else {
error_log("Invalid Type");
return false;
}
$this->help->delete("sagacity.target_software", null, array(
array(
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Delete a target and associated data from the database
*
* @param integer $tgt_id
* Target ID to delete from database
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function delete_Target($tgt_id)
{
$this->delete_Target_Checklists($tgt_id);
$this->delete_Target_Software($tgt_id);
$this->help->delete("finding_controls fc", [
'fc.*'
], [
[
'field' => 'f.tgt_id',
'op' => '=',
'value' => $tgt_id
]
], [
"JOIN sagacity.findings f ON f.tgt_id = fc.tgt_id AND f.pdi_id = fc.pdi_id"
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("findings", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("host_list", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->select("interfaces", [
'id'
], [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
$int_ids = $this->help->execute();
if (is_array($int_ids) && count($int_ids) > 1) {
$tmp = [];
foreach ($int_ids as $id) {
$tmp[] = $id['id'];
}
$int_ids = $tmp;
}
if (is_array($int_ids) && count($int_ids)) {
$this->help->delete("pps_list", null, [
[
'field' => 'int_id',
'op' => IN,
'value' => $int_ids
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
}
$this->help->delete("interfaces", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("target_net_meta", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("target_sys_meta", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("target_user_meta", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("analyst_notes", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("target", null, [
[
'field' => 'id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to delete all findings for a specific target
*
* @param target|int $tgt
* ID of the target to delete the findings for
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function delete_Target_Findings($tgt)
{
if (is_a($tgt, 'target')) {
$tgt_id = $tgt->get_ID();
} elseif (is_numeric($tgt)) {
$tgt_id = $tgt;
} else {
error_log("Invalid type");
return false;
}
$this->help->delete("sagacity.finding_controls fc", [
"fc.*"
], [
[
'field' => 'f.tgt_id',
'op' => '=',
'value' => $tgt_id
]
], [
"JOIN sagacity.findings f ON f.tgt_id = fc.tgt_id AND f.pdi_id = fc.pdi_id"
]);
if (! $this->help->execute()) {
error_log("Error deleting finding controls");
$this->help->debug(E_ERROR);
return false;
}
$this->help->delete("sagacity.findings", null, [
[
'field' => 'tgt_id',
'op' => '=',
'value' => $tgt_id
]
]);
if (! $this->help->execute()) {
error_log("Error deleting findings from findings table");
$this->help->debug(E_ERROR);
return false;
}
return true;
}
// }}}
// {{{ SETTINGS FUNCTIONS
/**
* Function to get a setting from the database
*
* @param string|array:string $key
* [optional]
* If a string is passed, will return the meta_value value for the setting with that meta_key.
* If an array is passed, will return the key/value pair matching for each item matching e.g.
* [
* 'meta_key' => 'meta_value',
* 'meta_key2' => 'meta_value2',
* 'meta_key3' => 'meta_value3',
* ...
* ]
* @param mixed $default
* [optional]
* Returns this value if the key is not found in the database (defaulted to null)
*
* @return mixed
*/
public function get_Settings($key = null, $default = null)
{
$where = [];
if (is_array($key) && count($key)) {
$where[] = [
'field' => 'meta_key',
'op' => IN,
'value' => $key
];
} elseif (is_string($key) && strlen($key)) {
$where[] = [
'field' => 'meta_key',
'op' => '=',
'value' => $key
];
}
$this->help->select("settings", [
'meta_key',
'meta_value'
], $where);
$rows = $this->help->execute();
if (is_array($rows) && count($rows) && isset($rows[0])) {
$ret = [];
foreach ($rows as $row) {
$ret[$row['meta_key']] = $row['meta_value'];
}
return $ret;
} elseif (isset($rows['meta_value'])) {
return $rows['meta_value'];
} else {
return $default;
}
}
/**
* Function to set a setting in the database
*
* @param string $key
* @param mixed $value
*/
public function set_Setting($key, $value)
{
$ret = false;
$this->help->select_count("sagacity.settings", [
[
'field' => 'meta_key',
'op' => '=',
'value' => $key
]
]);
if ($this->help->execute()) {
$this->help->update("sagacity.settings", [
'meta_value' => $value
], [
[
'field' => 'meta_key',
'op' => '=',
'value' => $key
]
]);
$ret = (boolean) $this->help->execute();
} else {
$this->help->insert("sagacity.settings", [
'meta_key' => $key,
'meta_value' => $value
]);
$ret = (boolean) $this->help->execute();
}
return $ret;
}
/**
* Method to update settings values using name => value pairs
*
* @param array $settings
*
* @return boolean
*/
public function set_Setting_Array($settings = [])
{
if (is_array($settings) && count($settings)) {
foreach ($settings as $key => $val) {
$this->help->update('settings', [
'meta_value' => $val
], [
[
'field' => 'meta_key',
'value' => $key
]
]);
if (! $this->help->execute()) {
return false;
}
}
} else {
return false;
}
return true;
}
// }}} END META
// {{{ VARIOUS FUNCTIONS
/**
* This function returns an array of all the task statuses
*
* @return array:string|NULL Array of all task statuses, or null if none found
*/
public function get_Task_Statuses()
{
$ret = [];
$this->help->select("task_status");
$rows = $this->help->execute();
if (! is_null($rows) && is_array($rows) && count($rows) && isset($rows[0])) {
foreach ($rows as $row) {
$ret[$row['id']] = $row['status'];
}
}
return $ret;
}
/**
* This function updates the task status for a target
*
* @param string $strStatus
* Status to update
* @param array:integer $arrTgts
* Array of target ids to update
* @param int $intStatus
* New status id
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function update_Task_Status($strStatus, $arrTgts, $intStatus)
{
$field = null;
switch ($strStatus) {
case 'update_auto':
$field = "auto_status_id";
break;
case 'update_manual':
$field = "man_status_id";
break;
case 'update_data':
$field = "data_status_id";
break;
case 'update_fp_cat1':
$field = "fp_cat1_status_id";
break;
default:
return false;
}
$this->help->update("sagacity.target", array(
$field => $intStatus
), array(
array(
'field' => 'id',
'op' => IN,
'value' => $arrTgts
)
));
if (! $this->help->execute()) {
$this->help->debug(E_ERROR);
return false;
}
return true;
}
/**
* Function to retrieve all helps
*
* @return array:string|NULL Returns array of strings with section and title data, or null if none found
*/
public function get_All_Helps()
{
$this->help->select("sagacity.help", array(
'section',
'title'
), [], array(
'order' => 'section'
));
return $this->help->execute();
}
/**
* Function to retrieve a help topic or function
*
* @param string $type
* Grab a specific help
*
* @return array:string|NULL Return array with help information, or null if none found
*/
public function get_Help($type)
{
$where = [];
if (preg_match('/(\d|[A-D]\.?)+$/', $type)) {
$where[] = [
'field' => 'section',
'op' => '=',
'value' => $type
];
} else {
if ($type != 'all') {
$where[] = [
'field' => 'topic',
'op' => '=',
'value' => $type
];
}
}
$this->help->select("help", [
'section',
'topic',
'title',
'content'
], $where);
return $this->help->execute();
}
/**
* Function to save the help as entered in the help_editor.php
*
* @param string $topic
* Topic of the help
* @param string $section
* Section of the help
* @param string $title
* Title
* @param string $content
* The HTML content
*
* @return boolean Returns TRUE if successful, otherwise FALSE
*/
public function save_Help($topic, $section, $title, $content)
{
$this->help->replace("help", array(
'section' => $section,
'topic' => $topic,
'title' => $title,
'content' => $content
));
return $this->help->execute();
}
/**
* Function for deleting help data
*
* @param string $section
*
* @return int
*/
public function delete_Help($section)
{
$this->help->delete("help", null, array(
array(
'field' => 'section',
'op' => '=',
'value' => $section
)
));
return $this->help->execute();
}
/**
* Function to check to see if the handler for the database is connected and reconnect if there is an error
*/
public function is_Connected()
{
if (! mysqli_ping($this->conn)) {
// here is the major trick, you have to close the connection (even though its not currently working) for it to recreate properly.
$this->conn->close();
$this->conn = new mysqli(DB_SERVER, 'web', self::decrypt_pwd(), 'sagacity');
}
}
// }}}
}