fix: address PR review — error logging, ACL check, fetch error handling
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Version Bump (push) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Joomla: Extension CI / Release Readiness Check (pull_request) Has been cancelled
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Joomla: Extension CI / Lint & Validate (pull_request) Has been cancelled
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been cancelled
Universal: Build & Release / Promote to RC (pull_request) Has been cancelled

- Log failures in protectBackupDir() and protectWebAccessibleDir() instead
  of silently suppressing with @ (security-critical .htaccess writes)
- Add error_log() to empty catch blocks in boot() and syncMenuIcons()
- Add core.manage ACL check to checkDir() AJAX endpoint
- Surface opendir() failures in browseDir() with warning message
- Add HTTP status check (r.ok) to JS fetch calls before parsing JSON
- Log temp SQL file deletion failures in SteppedBackupEngine
This commit is contained in:
Jonathan Miller
2026-06-07 09:11:39 -05:00
parent 3edf635a4c
commit e72a007041
7 changed files with 41 additions and 14 deletions
@@ -109,6 +109,7 @@ class AjaxController extends BaseController
// that could contain a backup folder (e.g., /home/user/backups)
$dirs = [];
$handle = @opendir($path);
$warning = null;
if ($handle) {
while (($entry = readdir($handle)) !== false) {
@@ -127,18 +128,26 @@ class AjaxController extends BaseController
}
closedir($handle);
} else {
$warning = 'Cannot read directory contents (check permissions)';
}
usort($dirs, fn($a, $b) => strcasecmp($a['name'], $b['name']));
$parent = dirname($path);
$this->sendJson([
$response = [
'error' => false,
'current' => $path,
'parent' => ($parent !== $path) ? $parent : null,
'dirs' => $dirs,
]);
];
if ($warning !== null) {
$response['warning'] = $warning;
}
$this->sendJson($response);
}
/**
@@ -205,6 +214,12 @@ class AjaxController extends BaseController
return;
}
if (!$this->app->getIdentity()->authorise('core.manage', 'com_mokojoombackup')) {
$this->sendJson(['error' => true, 'message' => 'Access denied']);
return;
}
$rawPath = trim($this->input->getString('path', ''));
if ($rawPath === '') {
@@ -530,13 +530,17 @@ class BackupEngine
$htaccess = $dir . '/.htaccess';
if (!is_file($htaccess)) {
@file_put_contents($htaccess, "Order deny,allow\nDeny from all\n");
if (@file_put_contents($htaccess, "Order deny,allow\nDeny from all\n") === false) {
error_log('MokoJoomBackup: Could not create .htaccess in backup directory: ' . $dir);
}
}
$index = $dir . '/index.html';
if (!is_file($index)) {
@file_put_contents($index, '<!DOCTYPE html><title></title>');
if (@file_put_contents($index, '<!DOCTYPE html><title></title>') === false) {
error_log('MokoJoomBackup: Could not create index.html in backup directory: ' . $dir);
}
}
}
@@ -317,8 +317,8 @@ class SteppedBackupEngine
$zip->close();
// Clean up temp SQL file
if (is_file($sqlFile)) {
@unlink($sqlFile);
if (is_file($sqlFile) && !@unlink($sqlFile)) {
error_log('MokoJoomBackup: Could not delete temp SQL file: ' . $sqlFile);
}
$totalSize = file_exists($session->archivePath) ? filesize($session->archivePath) : 0;
@@ -572,13 +572,17 @@ class SteppedBackupEngine
$htaccess = $dir . '/.htaccess';
if (!is_file($htaccess)) {
@file_put_contents($htaccess, "Order deny,allow\nDeny from all\n");
if (@file_put_contents($htaccess, "Order deny,allow\nDeny from all\n") === false) {
error_log('MokoJoomBackup: Could not create .htaccess in backup directory: ' . $dir);
}
}
$index = $dir . '/index.html';
if (!is_file($index)) {
@file_put_contents($index, '<!DOCTYPE html><title></title>');
if (@file_put_contents($index, '<!DOCTYPE html><title></title>') === false) {
error_log('MokoJoomBackup: Could not create index.html in backup directory: ' . $dir);
}
}
}
@@ -39,7 +39,7 @@ class MokoJoomBackupComponent extends MVCComponent
. ' #menu a[href*="com_mokojoombackup"][href*="view=profiles"] .sidebar-item-title::before { content: "\f013"; }'
);
} catch (\Throwable $e) {
// Non-critical
error_log('MokoJoomBackup: boot() CSS injection failed: ' . $e->getMessage());
}
}
}
@@ -183,7 +183,7 @@ class FolderPickerField extends FormField
body: form,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(function(r) { return r.json(); })
.then(function(r) { if (!r.ok) throw new Error('Server error (HTTP ' + r.status + ')'); return r.json(); })
.then(function(data) {
if (data.error) {
setStatus('text-danger', 'icon-unpublish', data.message || 'Error', null);
@@ -241,7 +241,7 @@ class FolderPickerField extends FormField
body: form,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(function(r) { return r.json(); })
.then(function(r) { if (!r.ok) throw new Error('Server error (HTTP ' + r.status + ')'); return r.json(); })
.then(function(data) {
if (data.error) {
tree.textContent = data.message || 'Error loading directory';
@@ -65,13 +65,17 @@ class ProfileTable extends Table
$htaccess = $resolved . '/.htaccess';
if (!is_file($htaccess)) {
@file_put_contents($htaccess, "Order deny,allow\nDeny from all\n");
if (@file_put_contents($htaccess, "Order deny,allow\nDeny from all\n") === false) {
error_log('MokoJoomBackup: Could not create .htaccess in: ' . $resolved);
}
}
$index = $resolved . '/index.html';
if (!is_file($index)) {
@file_put_contents($index, '<!DOCTYPE html><title></title>');
if (@file_put_contents($index, '<!DOCTYPE html><title></title>') === false) {
error_log('MokoJoomBackup: Could not create index.html in: ' . $resolved);
}
}
}
}
+1 -1
View File
@@ -243,7 +243,7 @@ class Pkg_MokoJoomBackupInstallerScript
$db->setQuery($query);
$db->execute();
} catch (\Throwable $e) {
// Non-critical
error_log('MokoJoomBackup: syncMenuIcons() failed: ' . $e->getMessage());
}
}