From 7a632e7a77ba60e14249abd221510c8518a9f91328192cdb73cbf97a65e6338d Mon Sep 17 00:00:00 2001 From: reclusejay Date: Wed, 22 Oct 2025 14:21:09 +0100 Subject: [PATCH] Added Shell Page --- src/css/shell.css | 109 ++++++++++++++++++++++++++++++++++++ src/includes/shell_exec.php | 46 +++++++++++++++ src/js/shell.js | 90 +++++++++++++++++++++++++++++ src/pages/shell.php | 26 +++++++++ 4 files changed, 271 insertions(+) create mode 100644 src/css/shell.css create mode 100644 src/includes/shell_exec.php create mode 100644 src/js/shell.js diff --git a/src/css/shell.css b/src/css/shell.css new file mode 100644 index 0000000..55161f1 --- /dev/null +++ b/src/css/shell.css @@ -0,0 +1,109 @@ +/* css/shell.css — Themed terminal styling (dark/light aware) */ + +.shell-page { + max-width: 900px; + margin: 2rem auto; + padding: 1.5rem; + background: var(--card); + border-radius: 12px; + box-shadow: 0 4px 12px var(--shadow); + color: var(--text); + font-family: "JetBrains Mono", monospace; + transition: background 0.3s, color 0.3s; +} + +.shell-page h2 { + text-align: center; + margin-bottom: 1rem; + font-weight: 500; + color: var(--text); +} + +.shell-output { + background: #171717; + color: #01b501; + padding: 1rem; + height: 600px; + overflow-y: auto; + border-radius: 10px; + border: 1px solid #333; + box-shadow: inset 0 0 6px rgba(0,0,0,0.4); + font-size: 0.9rem; + margin-bottom: 1rem; + white-space: pre-wrap; + word-break: break-word; +} + +body.light-mode .shell-output { + background: #f9f9f9; + color: #004400; + border: 1px solid #ddd; + box-shadow: inset 0 0 4px rgba(0,0,0,0.1); +} + +.shell-form { + display: flex; + align-items: center; + gap: 0.6rem; + background: var(--bg); + padding: 0.5rem; + border-radius: 10px; + box-shadow: inset 0 0 4px rgba(0,0,0,0.2); +} + +.shell-form .prompt { + font-weight: bold; + color: var(--accent); +} + +.shell-input { + flex: 1; + padding: 0.6rem; + border-radius: 8px; + border: 1px solid rgba(255,255,255,0.08); + background: var(--card); + color: var(--text); + font-family: inherit; + font-size: 0.95rem; + outline: none; + transition: border-color 0.2s; +} + +body.light-mode .shell-input { + border: 1px solid rgba(0,0,0,0.1); + background: #fff; +} + +.shell-input:focus { + border-color: var(--accent); +} + +.btn-run { + background: var(--accent); + color: #fff; + border: none; + padding: 0.6rem 1.2rem; + border-radius: 8px; + font-weight: 500; + cursor: pointer; + transition: background 0.2s, transform 0.1s; +} + +.btn-run:hover { + background: var(--accentHover); +} + +.btn-run:active { + transform: scale(0.96); +} + +pre.line { + margin: 0; + padding: 0.2rem 0; + font-family: inherit; +} + +pre.line.cmd { color: var(--accent); } +pre.line.err { color: #f33; } +pre.line.out { color: #01b501; } +body.light-mode pre.line.out { color: #007700; } diff --git a/src/includes/shell_exec.php b/src/includes/shell_exec.php new file mode 100644 index 0000000..b967b59 --- /dev/null +++ b/src/includes/shell_exec.php @@ -0,0 +1,46 @@ + 'Not authorized']); + exit; +} + +$cmd = trim($_POST['command'] ?? ''); +if ($cmd === '') { + echo json_encode(['output' => '']); + exit; +} + +// Restrict dangerous commands for safety +$blacklist = ['rm', 'shutdown', 'reboot', 'passwd', 'dd', ':(){']; +foreach ($blacklist as $bad) { + if (stripos($cmd, $bad) !== false) { + echo json_encode(['output' => "⚠️ Command '$bad' not allowed"]); + exit; + } +} + +// Execute safely (captures both stdout and stderr) +$descriptor = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'] +]; +$process = proc_open($cmd, $descriptor, $pipes, '/home'); +if (is_resource($process)) { + $output = stream_get_contents($pipes[1]); + $error = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $status = proc_close($process); + echo json_encode([ + 'output' => trim($output . "\n" . $error), + 'status' => $status + ]); +} else { + echo json_encode(['output' => 'Failed to execute command']); +} diff --git a/src/js/shell.js b/src/js/shell.js new file mode 100644 index 0000000..5e3ac8f --- /dev/null +++ b/src/js/shell.js @@ -0,0 +1,90 @@ +// js/shell.js — SPA-safe interactive shell with history + Ctrl-L clear +(() => { + if (window.shellInitialized) return; + window.shellInitialized = true; + + document.addEventListener('page-loaded', (e) => { + if (e.detail.page !== 'shell') return; + + console.log('[shell.js] Initializing shell...'); + + const form = document.getElementById('shellForm'); + const input = document.getElementById('shellInput'); + const output = document.getElementById('shellOutput'); + const runBtn = document.getElementById('runBtn'); + if (!form || !input || !output || !runBtn) return; + + // ----- History ----- + const history = []; + let histIndex = -1; + + function appendLine(text, type = 'out') { + const line = document.createElement('pre'); + line.className = `line ${type}`; + line.textContent = text; + output.appendChild(line); + output.scrollTop = output.scrollHeight; + } + + form.addEventListener('submit', async (e) => { + e.preventDefault(); + e.stopPropagation(); + + const cmd = input.value.trim(); + if (!cmd) return; + + history.push(cmd); + histIndex = history.length; + appendLine(`$ ${cmd}`, 'cmd'); + input.value = ''; + runBtn.disabled = true; + + try { + const res = await fetch('includes/shell_exec.php', { + method: 'POST', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: new URLSearchParams({ command: cmd }) + }); + const data = await res.json(); + if (data.error) appendLine(data.error, 'err'); + if (data.output) appendLine(data.output, 'out'); + } catch (err) { + appendLine(`Error: ${err.message}`, 'err'); + } finally { + runBtn.disabled = false; + } + }); + + // ----- Keyboard handlers ----- + input.addEventListener('keydown', (e) => { + if (e.key === 'ArrowUp') { + if (histIndex > 0) { + histIndex--; + input.value = history[histIndex]; + setTimeout(() => input.setSelectionRange(input.value.length, input.value.length), 0); + } + e.preventDefault(); + } else if (e.key === 'ArrowDown') { + if (histIndex < history.length - 1) { + histIndex++; + input.value = history[histIndex]; + } else { + histIndex = history.length; + input.value = ''; + } + e.preventDefault(); + } else if (e.key.toLowerCase() === 'l' && e.ctrlKey) { + // Ctrl+L clear screen + output.innerHTML = ''; + e.preventDefault(); + } + }); + + console.log('[shell.js] Shell initialized.'); + console.log('💡 Usage'); + console.log('Shortcut Action'); + console.log('↑ / ↓ Browse previous commands'); + console.log('Ctrl + L Clear the screen'); + console.log('Enter Execute command'); + }); +})(); diff --git a/src/pages/shell.php b/src/pages/shell.php index 473a0f4..17d1e07 100644 --- a/src/pages/shell.php +++ b/src/pages/shell.php @@ -0,0 +1,26 @@ +

Access denied. Admin login required.

"; + exit; +} +?> +
+

Interactive Shell

+ +
+ +
+ $ + + +
+ + + +
+ + +