Skip to content

Commit c07d689

Browse files
authored
Control Panel Update
1 parent 79f1d59 commit c07d689

5 files changed

Lines changed: 129 additions & 8 deletions

File tree

.github/workflows/stale-issue.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: 'Close stale issues and PRs'
2+
on:
3+
schedule:
4+
- cron: '30 1 * * *'
5+
6+
jobs:
7+
stale:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/stale@v10
11+
with:
12+
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
13+
stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
14+
close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
15+
close-pr-message: 'This PR was closed because it has been stalled for 10 days with no activity.'
16+
days-before-issue-stale: 30
17+
days-before-pr-stale: 45
18+
days-before-issue-close: 5
19+
days-before-pr-close: 10

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ All notable changes to EngineScript will be documented in this file.
44

55
Changes are organized by date, with the most recent changes listed first.
66

7+
## 2025-11-06
8+
9+
### 🔒 SECURITY IMPROVEMENTS
10+
11+
- **CSRF Token Protection**: Added cryptographic CSRF token generation and validation for API endpoints
12+
13+
### ⚡ PERFORMANCE IMPROVEMENTS
14+
15+
- **Removed Loading Delay**: Eliminated hardcoded 1.5s loading screen delay for faster perceived performance
16+
717
## 2025-11-03
818

919
### 🔧 CONFIGURATION IMPROVEMENTS

config/etc/php/php.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,11 @@ opcache.validate_timestamps=1
515515
;opcache.file_cache_fallback=1
516516
;opcache.file_cache_only=0
517517
;opcache.force_restart_timeout=180
518-
; A note on OpCache JIT: For typical web applications with I/O-bound workloads (e.g., database queries or API calls), the performance gains from JIT may be minimal.
518+
;
519+
; A note on OpCache JIT WordPress Performance:
520+
; For typical web applications with I/O-bound workloads (e.g., database queries or API calls), the performance gains from JIT may be minimal.
519521
; JIT is more beneficial for CPU-bound workloads (e.g., heavy computations, number crunching).
522+
; It is recommended to benchmark your specific application with and without JIT enabled to determine its impact.
520523
;opcache.jit=tracing
521524
;opcache.jit_buffer_size=SEDOPCACHEJITBUFFER
522525
;opcache.lockfile_path=/tmp

config/var/www/admin/control-panel/api.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,19 @@
4646
}
4747

4848
header('Access-Control-Allow-Methods: GET, OPTIONS'); // codacy:ignore - CORS header required
49-
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With'); // codacy:ignore - CORS header required
49+
header('Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-CSRF-Token'); // codacy:ignore - CORS header required
5050
header('Access-Control-Allow-Credentials: true'); // codacy:ignore - CORS header required
5151
header('Access-Control-Max-Age: 86400'); // codacy:ignore - CORS header required
5252

5353
// Rate limiting (basic implementation) - session functions required for API rate limiting
5454
if (session_status() === PHP_SESSION_NONE) { // codacy:ignore - session_status() required for session management in standalone API
5555
session_start(); // codacy:ignore - session_start() required for rate limiting functionality
5656
}
57+
58+
// Initialize CSRF token if not exists
59+
if (!isset($_SESSION['csrf_token'])) { // codacy:ignore - Direct $_SESSION access required for CSRF protection
60+
$_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // codacy:ignore - random_bytes() required for cryptographic token generation
61+
}
5762
$client_ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'unknown'; // codacy:ignore - Direct $_SERVER access required, wp_unslash() not available
5863
$rate_limit_key = 'api_rate_' . hash('sha256', $client_ip);
5964

@@ -293,6 +298,10 @@ function logSecurityEvent($event, $details = '') { // codacy:ignore - Direct $_S
293298

294299
// Route handling
295300
switch ($path) {
301+
case '/csrf-token':
302+
handleCsrfToken();
303+
break;
304+
296305
case '/system/stats':
297306
handleSystemStats();
298307
break;
@@ -361,6 +370,25 @@ function logSecurityEvent($event, $details = '') { // codacy:ignore - Direct $_S
361370
break;
362371
}
363372

373+
function handleCsrfToken() {
374+
try {
375+
// Return the current CSRF token
376+
if (isset($_SESSION['csrf_token'])) { // codacy:ignore - Direct $_SESSION access required for CSRF token response
377+
echo json_encode([
378+
'csrf_token' => $_SESSION['csrf_token'], // codacy:ignore - Direct $_SESSION access required for CSRF token response
379+
'token_name' => '_csrf_token'
380+
]); // codacy:ignore - echo required for JSON API response
381+
} else {
382+
http_response_code(500);
383+
echo json_encode(['error' => 'Unable to generate CSRF token']); // codacy:ignore - echo required for JSON API response
384+
}
385+
} catch (Exception $e) {
386+
http_response_code(500);
387+
logSecurityEvent('CSRF token error', $e->getMessage());
388+
echo json_encode(['error' => 'Unable to generate CSRF token']); // codacy:ignore - echo required for JSON API response
389+
}
390+
}
391+
364392
function handleSystemStats() {
365393
try {
366394
$stats = [

config/var/www/admin/control-panel/dashboard.js

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class EngineScriptDashboard {
99
this.refreshInterval = 30000; // 30 seconds
1010
this.charts = {};
1111
this.refreshTimer = null;
12+
this.csrfToken = null; // CSRF token for future state-changing requests
1213

1314
// Security configurations
1415
this.maxRefreshInterval = 300000; // 5 minutes max
@@ -24,10 +25,28 @@ class EngineScriptDashboard {
2425
this.setupEventListeners();
2526
this.setupNavigation();
2627
this.startClock();
28+
this.loadCsrfToken(); // Load CSRF token before other API calls
2729
this.loadInitialData();
2830
this.hideLoadingScreen();
2931
}
3032

33+
async loadCsrfToken() {
34+
try {
35+
const response = await fetch('/api/csrf-token', {
36+
method: 'GET',
37+
credentials: 'include'
38+
});
39+
if (response.ok) {
40+
const data = await response.json();
41+
this.csrfToken = data.csrf_token;
42+
} else {
43+
console.warn('Failed to load CSRF token');
44+
}
45+
} catch (error) {
46+
console.error('Error loading CSRF token:', error);
47+
}
48+
}
49+
3150
setupEventListeners() {
3251
// Mobile menu toggle
3352
const mobileMenuToggle = document.getElementById("mobile-menu-toggle");
@@ -173,16 +192,17 @@ class EngineScriptDashboard {
173192
}
174193

175194
hideLoadingScreen() {
176-
setTimeout(() => {
177-
const loadingScreen = document.getElementById("loading-screen");
178-
const dashboard = document.getElementById("dashboard");
195+
// Hide loading screen immediately, no artificial delay
196+
const loadingScreen = document.getElementById("loading-screen");
197+
const dashboard = document.getElementById("dashboard");
179198

199+
if (loadingScreen && dashboard) {
180200
loadingScreen.style.opacity = "0";
181201
setTimeout(() => {
182202
loadingScreen.style.display = "none";
183203
dashboard.style.display = "flex";
184-
}, 500);
185-
}, 1500);
204+
}, 500); // Fade out animation only
205+
}
186206
}
187207

188208
toggleMobileMenu() {
@@ -767,7 +787,16 @@ class EngineScriptDashboard {
767787
return fallback;
768788
}
769789

770-
const response = await fetch(endpoint);
790+
const headers = {};
791+
if (this.csrfToken) {
792+
headers['X-CSRF-Token'] = this.csrfToken;
793+
}
794+
795+
const response = await fetch(endpoint, {
796+
method: 'GET',
797+
headers: headers,
798+
credentials: 'include'
799+
});
771800

772801
if (!response.ok) {
773802
throw new Error(`API ${endpoint} returned ${response.status}: ${response.statusText}`);
@@ -797,6 +826,38 @@ class EngineScriptDashboard {
797826
}
798827
}
799828

829+
async postApiData(endpoint, data = {}) {
830+
try {
831+
// Check if fetch is available
832+
if (typeof fetch === "undefined" || this.isOperaMini()) {
833+
return { error: 'Fetch not supported' };
834+
}
835+
836+
const headers = {
837+
'Content-Type': 'application/json'
838+
};
839+
if (this.csrfToken) {
840+
headers['X-CSRF-Token'] = this.csrfToken;
841+
}
842+
843+
const response = await fetch(endpoint, {
844+
method: 'POST',
845+
headers: headers,
846+
credentials: 'include',
847+
body: JSON.stringify(data)
848+
});
849+
850+
if (!response.ok) {
851+
throw new Error(`API ${endpoint} returned ${response.status}: ${response.statusText}`);
852+
}
853+
854+
return await response.json();
855+
} catch (error) {
856+
console.error(`Error posting to ${endpoint}:`, error);
857+
return { error: error.message };
858+
}
859+
}
860+
800861
async getServiceStatus(service) {
801862
try {
802863
// Check if fetch is available and supported (Opera Mini compatibility)

0 commit comments

Comments
 (0)