cloudlog/application/models/Emeinitials_model.php
Peter Goodhall 6b87a10f3d Improve input validation and query safety in Emeinitials_model
Added input type checks and sanitization for band and mode parameters. Refactored SQL query construction to use CodeIgniter's Query Builder for safer, parameterized queries and proper escaping of location values. These changes enhance security and reliability of the get_initials and get_initials_data methods.
2025-09-25 16:45:55 +01:00

165 lines
6.2 KiB
PHP

<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');
class Emeinitials_model extends CI_Model
{
function get_initials($band, $mode) {
// Input validation
if (!is_string($band) || !is_string($mode)) {
log_message('error', 'Invalid input types for band or mode parameters in get_initials');
return null;
}
$CI =& get_instance();
$CI->load->model('logbooks_model');
$logbooks_locations_array = $CI->logbooks_model->list_logbook_relationships($this->session->userdata('active_station_logbook'));
if (!$logbooks_locations_array) {
return null;
}
// Ensure location_list is properly escaped for IN clause
$escaped_locations = array();
foreach ($logbooks_locations_array as $location) {
$escaped_locations[] = $this->db->escape($location);
}
$location_list = implode(', ', $escaped_locations);
$result = $this->get_initials_data($band, $mode, $location_list);
return $result;
}
public function get_initials_data($band, $mode, $location_list) {
// Input validation and sanitization
if (!is_string($band) || !is_string($mode)) {
log_message('error', 'Invalid input types for band or mode parameters in get_initials_data');
return [];
}
// Sanitize inputs - allow only alphanumeric characters, spaces, and common ham radio characters
$band = preg_replace('/[^a-zA-Z0-9\s\-]/', '', trim($band));
$mode = preg_replace('/[^a-zA-Z0-9\s\-]/', '', trim($mode));
// Validate that band and mode are not empty after sanitization (unless they were 'All')
if (($band !== 'All' && empty($band)) || ($mode !== 'All' && empty($mode))) {
log_message('error', 'Invalid band or mode parameter after sanitization in get_initials_data');
return [];
}
// Use CodeIgniter's Query Builder for safe parameterized queries
$this->db->select('COL_CALL as callsign, date(COL_TIME_ON) as date, upper(COL_GRIDSQUARE) as gridsquare, COL_STATE as state');
$this->db->from($this->config->item('table_name') . ' thcv');
$this->db->where('station_id in (' . $location_list . ')', null, false);
$this->db->where('col_prop_mode', 'EME');
if ($band != 'All') {
$this->db->where('col_band', $band);
}
if ($mode != 'All') {
$this->db->group_start();
$this->db->where('col_mode', $mode);
$this->db->or_where('col_submode', $mode);
$this->db->group_end();
}
$this->db->order_by('COL_TIME_ON');
$query = $this->db->get();
// Create a new array after applying rules for initials
$inits_array = [];
foreach ($query->result() as $init) {
$calls_array = $this->find_initials($inits_array, $init);
// Is this a new callsign? If so, add it to the initials array
if (empty($calls_array)) {
$new_init = new stdClass();
$new_init->callsign = $init->callsign;
$new_init->date = $init->date;
$new_init->gridsquare = $init->gridsquare;
$new_init->state = $init->state;
$new_init->count = 1;
array_push($inits_array, $new_init);
}
else {
// The callsign isn't unique, now check the location of the entries with the same callsign
$dup_init = $this->find_duplicate($calls_array, $init);
// Dissimilae locations, we can add this initial to our list, it's new
if (empty($dup_init)) {
$new_init = new stdClass();
$new_init->callsign = $init->callsign;
$new_init->date = $init->date;
$new_init->gridsquare = $init->gridsquare;
$new_init->state = $init->state;
$new_init->count = 1;
array_push($inits_array, $new_init);
} else {
// The location is a duplicate, increment its count by one
$dup_init[0]->count++;
}
}
}
return $inits_array;
}
// Simple test to see if the callsign already appears in the initials array
private function find_initials($array, $initial) {
$found_array = [];
foreach ($array as $data) {
if ($data->callsign === $initial->callsign) {
array_push($found_array, $data);
}
}
return $found_array;
}
// We know that the elements in $array have the same callsign, now check the locator, and state.
private function find_duplicate($array, $initial) {
$found_array = [];
foreach ($array as $data) {
$found = false;
if (empty($data->gridsquare) or empty($initial->gridsquare)) {
// Impossible to do any sort of grid matching, so we mark them as matched
$found = true;
}
else if (strlen($data->gridsquare) == 6 and strlen($initial->gridsquare) == 6) {
// Are the grids an exact match?
if (strncmp($data->gridsquare, $initial->gridsquare, 6) == 0) {
$found = true;
}
}
else {
// Are the grids a partial match?
if (strncmp($data->gridsquare, $initial->gridsquare, 4) == 0) {
$found = true;
}
}
if (!empty($data->state) and !empty($initial->state)) {
// Are the states an exact match?
if ($data->state === $initial->state) {
$found = true;
}
else {
// Even if the locator matches, a different state means that it's an initial
$found = false;
}
}
if ($found) {
array_push($found_array, $data);
return $found_array;
}
}
return $found_array;
}
}