From b1a835e952ee777a43edefbeff0840f9f6b57b67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 02:58:30 +0000 Subject: [PATCH 01/14] Bump tmp from 0.2.4 to 0.2.7 Bumps [tmp](https://github.com/raszi/node-tmp) from 0.2.4 to 0.2.7. - [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md) - [Commits](https://github.com/raszi/node-tmp/compare/v0.2.4...v0.2.7) --- updated-dependencies: - dependency-name: tmp dependency-version: 0.2.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42e50a928..dcfc5978f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1925,9 +1925,9 @@ "license": "MIT" }, "node_modules/tmp": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.4.tgz", - "integrity": "sha512-UdiSoX6ypifLmrfQ/XfiawN6hkjSBpCjhKxxZcWlUUmoXLaCKQU0bx4HF/tdDK2uzRuchf1txGvrWBzYREssoQ==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { From 5a44ef4b4e1b10284e7b4c9b3bc59b39a7c544fc Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sat, 6 Jun 2026 10:25:45 +0100 Subject: [PATCH 02/14] Per-install encryption key and session checks Replace hardcoded encryption key in installer with a %encryption_key% placeholder and generate a unique per-install key during installation (uses random_bytes, falls back to openssl_random_pseudo_bytes or sha1(uniqid)). Inject the generated key into config.php. Also tighten User_Model::validate_session(): return early if no user_id, clear session if user_hash is missing, re-read the user from the database to verify user_type before authenticating, and refresh or clear the session accordingly. These changes improve security by using a unique encryption key per install and by validating session state against persisted user data. --- application/models/User_model.php | 40 ++++++++++++++++++++----------- install/config/config.php | 2 +- install/includes/core_class.php | 14 +++++++++++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/application/models/User_model.php b/application/models/User_model.php index 1912cf832..a9f372a4e 100644 --- a/application/models/User_model.php +++ b/application/models/User_model.php @@ -540,23 +540,35 @@ function update_session($id) { // If the user's session is corrupted in any way, it will clear the session function validate_session() { - if($this->session->userdata('user_id')) - { - $user_id = $this->session->userdata('user_id'); - $user_type = $this->session->userdata('user_type'); - $user_hash = $this->session->userdata('user_hash'); + if (!$this->session->userdata('user_id')) { + return 0; + } - if($this->_auth($user_id."-".$user_type, $user_hash)) { - // Freshen the session - $this->update_session($user_id); - return 1; - } else { - $this->clear_session(); - return 0; - } - } else { + $user_id = $this->session->userdata('user_id'); + $user_hash = $this->session->userdata('user_hash'); + + if (empty($user_hash)) { + $this->clear_session(); return 0; } + + // Re-read the user from the database and validate against persisted state. + $u = $this->get_by_id($user_id); + if ($u->num_rows() !== 1) { + $this->clear_session(); + return 0; + } + + $db_user_type = $u->row()->user_type; + + if ($this->_auth($user_id."-".$db_user_type, $user_hash)) { + // Freshen the session + $this->update_session($user_id); + return 1; + } + + $this->clear_session(); + return 0; } // FUNCTION: bool authenticate($username, $password) diff --git a/install/config/config.php b/install/config/config.php index f50cb1dfa..9944f68cb 100644 --- a/install/config/config.php +++ b/install/config/config.php @@ -459,7 +459,7 @@ | https://codeigniter.com/user_guide/libraries/encryption.html | */ -$config['encryption_key'] = 'flossie1234555541'; +$config['encryption_key'] = '%encryption_key%'; /* |-------------------------------------------------------------------------- diff --git a/install/includes/core_class.php b/install/includes/core_class.php index 09cc9ee15..c7b91cc88 100644 --- a/install/includes/core_class.php +++ b/install/includes/core_class.php @@ -100,6 +100,7 @@ function write_configfile($data) { $new = str_replace("%baselocator%",$data['locator'],$database_file); $new = str_replace("%websiteurl%",$data['websiteurl'],$new); $new = str_replace("%directory%",$data['directory'],$new); + $new = str_replace("%encryption_key%",$this->generate_encryption_key(),$new); // Write the new config.php file $handle = fopen($output_path,'w+'); @@ -122,5 +123,18 @@ function write_configfile($data) { } } + // Generate a per-install encryption key for the application config. + private function generate_encryption_key() { + try { + return bin2hex(random_bytes(32)); + } catch (Exception $e) { + if (function_exists('openssl_random_pseudo_bytes')) { + return bin2hex(openssl_random_pseudo_bytes(32)); + } + + return sha1(uniqid('', true) . mt_rand()); + } + } + } ?> \ No newline at end of file From a1bc0f415e5103acdac825645903fc6682c1fbe1 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Sat, 6 Jun 2026 10:29:41 +0100 Subject: [PATCH 03/14] Use per-install temp dir for session files Replace hardcoded '/tmp' sess_save_path with a dynamically generated temp directory based on sys_get_temp_dir() and a hash of the installation path (realpath(FCPATH)). Create the directory with 0700 permissions if it does not exist. Changes applied to application/config/config.sample.php and install/config/config.php to avoid shared /tmp collisions and improve session isolation/security. --- application/config/config.sample.php | 6 +++++- install/config/config.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/application/config/config.sample.php b/application/config/config.sample.php index 115b87b46..3dc42eaa5 100644 --- a/application/config/config.sample.php +++ b/application/config/config.sample.php @@ -513,7 +513,11 @@ $config['sess_driver'] = 'files'; $config['sess_cookie_name'] = 'ci_cloudlog'; $config['sess_expiration'] = 0; -$config['sess_save_path'] = '/tmp'; +$cloudlog_session_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'cloudlog_' . md5((string) (realpath(FCPATH) ?: FCPATH)) . '_sessions'; +if (!is_dir($cloudlog_session_path)) { + @mkdir($cloudlog_session_path, 0700, true); +} +$config['sess_save_path'] = $cloudlog_session_path; $config['sess_match_ip'] = FALSE; $config['sess_time_to_update'] = 300; $config['sess_regenerate_destroy'] = FALSE; diff --git a/install/config/config.php b/install/config/config.php index 9944f68cb..758c66dcf 100644 --- a/install/config/config.php +++ b/install/config/config.php @@ -515,7 +515,11 @@ $config['sess_driver'] = 'files'; $config['sess_cookie_name'] = 'ci_cloudlog'; $config['sess_expiration'] = 0; -$config['sess_save_path'] = '/tmp'; +$cloudlog_session_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'cloudlog_' . md5((string) (realpath(FCPATH) ?: FCPATH)) . '_sessions'; +if (!is_dir($cloudlog_session_path)) { + @mkdir($cloudlog_session_path, 0700, true); +} +$config['sess_save_path'] = $cloudlog_session_path; $config['sess_match_ip'] = FALSE; $config['sess_time_to_update'] = 300; $config['sess_regenerate_destroy'] = FALSE; From 2476c65beb37cd207b523b34e3fa04bb7dbf87b1 Mon Sep 17 00:00:00 2001 From: Peter Goodhall Date: Tue, 9 Jun 2026 11:22:01 +0100 Subject: [PATCH 04/14] Treat empty numeric inputs as NULL In Logbook_model, only sanitize 'a_index' and 'age' when the input is not an empty string; if filter_var returns an empty string or false, set the variable to NULL. This prevents empty/invalid sanitized values from being treated as valid numeric inputs. --- application/models/Logbook_model.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/application/models/Logbook_model.php b/application/models/Logbook_model.php index e9d894d76..e4f18122b 100755 --- a/application/models/Logbook_model.php +++ b/application/models/Logbook_model.php @@ -4313,14 +4313,20 @@ function import($record, $station_id = "0", $skipDuplicate = false, $markClublog $rx_pwr = NULL; } - if (isset($record['a_index'])) { + if (isset($record['a_index']) && $record['a_index'] !== '') { $input_a_index = filter_var($record['a_index'], FILTER_SANITIZE_NUMBER_INT); + if ($input_a_index === '' || $input_a_index === false) { + $input_a_index = NULL; + } } else { $input_a_index = NULL; } - if (isset($record['age'])) { + if (isset($record['age']) && $record['age'] !== '') { $input_age = filter_var($record['age'], FILTER_SANITIZE_NUMBER_INT); + if ($input_age === '' || $input_age === false) { + $input_age = NULL; + } } else { $input_age = NULL; } From 0ef8d2a67380d0c3a700dee2cd75f987ad694d12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 10:53:02 +0000 Subject: [PATCH 05/14] Bump form-data from 4.0.4 to 4.0.6 Bumps [form-data](https://github.com/form-data/form-data) from 4.0.4 to 4.0.6. - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v4.0.4...v4.0.6) --- updated-dependencies: - dependency-name: form-data dependency-version: 4.0.6 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42e50a928..26e26c56c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -878,17 +878,17 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -1065,9 +1065,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "dev": true, "license": "MIT", "dependencies": { From a0f953dfa8669bf3800b87ea518dbbc22c9cb68c Mon Sep 17 00:00:00 2001 From: Peter Goodhall <84308+magicbug@users.noreply.github.com> Date: Tue, 23 Jun 2026 11:35:51 +0100 Subject: [PATCH 06/14] Add CAT cache and satellite lookup helper Introduce window.cloudlogLastCatData and window.cloudlogLastCatRadioId to cache the last CAT response and radio ID, set on successful CAT poll and cleared on UI reset. Add isSatelliteLookupContext() in qso.js to centralize logic for detecting satellite lookup context using UI fields or the cached CAT data, and replace scattered sat_name checks with this helper. This makes satellite lookups more reliable when CAT populates fields and avoids duplicated logic across handlers. --- application/views/interface_assets/footer.php | 7 +++++ assets/js/sections/qso.js | 31 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index a9b8c4c68..1417ced15 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -2364,6 +2364,9 @@ function getUTCDateStamp(el) { let lastSuccessfulCatUpdateAt = null; let lockSatelliteFieldsToUserInput = false; + window.cloudlogLastCatData = null; + window.cloudlogLastCatRadioId = null; + const CAT_POLL_BASE_INTERVAL_MS = 3000; const CAT_POLL_MAX_INTERVAL_MS = 15000; const CAT_POLL_WARNING_THRESHOLD = 3; @@ -2418,6 +2421,8 @@ function getUTCDateStamp(el) { clearLoginError(); handleCATPollSuccess(); + window.cloudlogLastCatData = data || null; + window.cloudlogLastCatRadioId = requestedRadioID; updateUIWithCATData(data); }, error: () => { @@ -2616,6 +2621,8 @@ function getUTCDateStamp(el) { // Reset UI when no radio is selected const resetUI = () => { lockSatelliteFieldsToUserInput = false; + window.cloudlogLastCatData = null; + window.cloudlogLastCatRadioId = null; $("#sat_name, #sat_mode, #frequency, #frequency_rx, #band_rx").val(""); $("#selectPropagation").val($("#selectPropagation option:first").val()); // Clear CAT value cache so re-selecting a radio with identical values still repopulates fields. diff --git a/assets/js/sections/qso.js b/assets/js/sections/qso.js index e828253ec..b2af653a8 100644 --- a/assets/js/sections/qso.js +++ b/assets/js/sections/qso.js @@ -798,7 +798,7 @@ $(document).on('change', 'input', function(){ }); function changebadge(entityname) { - if($("#sat_name" ).val() != "") { + if (isSatelliteLookupContext()) { $.getJSON(base_url + 'index.php/logbook/jsonlookupdxcc/' + convert_case(entityname) + '/SAT/0/0', function(result) { @@ -896,6 +896,31 @@ function clearSatelliteFields() { } } +function isSatelliteLookupContext() { + var propagationMode = normalizeFieldValue($('#selectPropagation').val()).toUpperCase(); + var propagationModeCat = normalizeFieldValue($('#selectPropagation').data('catValue')).toUpperCase(); + var satName = normalizeFieldValue($('#sat_name').val()); + var satMode = normalizeFieldValue($('#sat_mode').val()); + var satNameCat = normalizeFieldValue($('#sat_name').data('catValue')); + var satModeCat = normalizeFieldValue($('#sat_mode').data('catValue')); + + if (propagationMode === 'SAT' || propagationModeCat === 'SAT' || satName !== '' || satMode !== '' || satNameCat !== '' || satModeCat !== '') { + return true; + } + + var selectedRadioID = normalizeFieldValue($('select.radios').first().val()); + var lastCatRadioID = normalizeFieldValue(window.cloudlogLastCatRadioId); + var lastCatData = window.cloudlogLastCatData; + if (selectedRadioID === '' || selectedRadioID === '0' || selectedRadioID !== lastCatRadioID || !lastCatData) { + return false; + } + + var catPropMode = normalizeFieldValue(lastCatData.prop_mode).toUpperCase(); + var catSatName = normalizeFieldValue(lastCatData.satname); + var catSatMode = normalizeFieldValue(lastCatData.satmode); + return catPropMode === 'SAT' || catSatName !== '' || catSatMode !== ''; +} + function clearCatTrackedFieldState() { $('#frequency, #frequency_rx, #sat_name, #sat_mode, #transmit_power, #selectPropagation, #mode').removeData('catValue'); } @@ -1047,7 +1072,7 @@ $("#callsign").focusout(function() { /* Find and populate DXCC */ $('.callsign-suggest').hide(); - if($("#sat_name").val() != ""){ + if (isSatelliteLookupContext()) { var sat_type = "SAT"; var json_band = "0"; var json_mode = "0"; @@ -1516,7 +1541,7 @@ $("#locator").keyup(function(){ if(qra_lookup.length >= 4) { // Check Log if satname is provided - if($("#sat_name" ).val() != "") { + if (isSatelliteLookupContext()) { //logbook/jsonlookupgrid/io77/SAT/0/0 From 43715eed98c529c813b8145863920f88e4bbba08 Mon Sep 17 00:00:00 2001 From: Peter Goodhall <84308+magicbug@users.noreply.github.com> Date: Tue, 23 Jun 2026 12:01:45 +0100 Subject: [PATCH 07/14] Use language lines for Prev/Next pagination Replace direct lang('prev')/lang('next') calls with localized labels using general_word_previous and general_word_next language lines (falling back to 'Previous'/'Next'). Adds $prev_label and $next_label and updates the pagination links/spans to use them, improving localization support for the previous-contacts pagination. --- .../views/qso/components/previous_contacts.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/application/views/qso/components/previous_contacts.php b/application/views/qso/components/previous_contacts.php index 22b1d8f6f..2bde1e3fc 100644 --- a/application/views/qso/components/previous_contacts.php +++ b/application/views/qso/components/previous_contacts.php @@ -50,16 +50,20 @@ 1): ?> +lang->line('general_word_previous', false) ?: 'Previous'; + $next_label = $this->lang->line('general_word_next', false) ?: 'Next'; +?>