Merge pull request #2421 from Squidly271/POC/safeeval

Refactor: Protect GUI from fatal PHP errors
This commit is contained in:
tom mortensen
2025-10-14 21:10:11 -07:00
committed by GitHub
6 changed files with 104 additions and 18 deletions

View File

@@ -1,6 +1,7 @@
Title="Add Container"
Cond="(pgrep('dockerd')!==false)"
Markdown="false"
Eval="true"
---
<?PHP
/* Copyright 2005-2023, Lime Technology

View File

@@ -89,6 +89,7 @@ if (count($pages)) {
@unlink($nchan_pid);
}
}
?>
<!DOCTYPE html>
<html <?= $display['rtl'] ?>lang="<?= strtok($locale, '_') ?: 'en' ?>" class="<?= $themeHelper->getThemeHtmlClass() ?>">
@@ -143,16 +144,23 @@ if (count($pages)) {
<?php require_once "$docroot/webGui/include/DefaultPageLayout/HeadInlineJS.php"; ?>
<?php
foreach ($buttonPages as $button) {
annotate($button['file']);
includePageStylesheets($button);
eval('?>' . parse_text($button['text']));
}
foreach ($buttonPages as $button) {
annotate($button['file']);
includePageStylesheets($button);
$evalContent = '?>' . parse_text($button['text']);
$evalFile = $button['file'];
if ( filter_var($button['Eval']??false, FILTER_VALIDATE_BOOLEAN) ) {
eval($evalContent);
} else {
include "$docroot/webGui/include/DefaultPageLayout/evalContent.php";
}
}
foreach ($pages as $page) {
annotate($page['file']);
includePageStylesheets($page);
}
foreach ($pages as $page) {
annotate($page['file']);
includePageStylesheets($page);
}
?>
<?php include "$docroot/plugins/dynamix.my.servers/include/myservers1.php" ?>
@@ -169,4 +177,4 @@ foreach ($pages as $page) {
<?php include "$docroot/webGui/include/DefaultPageLayout/ToastSetup.php"; ?>
</body>
</html>
</html>

View File

@@ -46,7 +46,13 @@
if (isset($page['text'])) {
$skipIndexIncrement = true;
annotate($page['file']);
eval('?>'.generateContent($page));
$evalContent = '?>'.generateContent($page);
$evalFile = $page['file'];
if ( filter_var($page['Eval']??false, FILTER_VALIDATE_BOOLEAN) ) {
eval($evalContent);
} else {
include "$docroot/webGui/include/DefaultPageLayout/evalContent.php";
}
}
continue;
}
@@ -63,7 +69,15 @@
>
<?= generatePanels($page, $path, $defaultIcon, $docroot) ?>
<? eval('?>'.generateContent($page)); ?>
<?
$evalContent = '?>'.generateContent($page);
$evalFile = $page['file'];
if ( filter_var($page['Eval']??false, FILTER_VALIDATE_BOOLEAN) ) {
eval($evalContent);
} else {
include "$docroot/webGui/include/DefaultPageLayout/evalContent.php";
}
?>
</section>
<?
if ($skipIndexIncrement) {

View File

@@ -19,7 +19,15 @@
<?= generatePanels($page, $path, $defaultIcon, $docroot, true) ?>
<? eval('?>'.generateContent($page)); ?>
<?
$evalContent = '?>'.generateContent($page);
$evalFile = $page['file'];
if ( filter_var($page['Eval']??false, FILTER_VALIDATE_BOOLEAN) ) {
eval($evalContent);
} else {
include "$docroot/webGui/include/DefaultPageLayout/evalContent.php";
}
?>
<? endforeach; ?>
</div>
</div>

View File

@@ -0,0 +1,47 @@
<?php
// This evaluates the contents of PHP code. Has to be "included" because the code is evaluated in the context of the calling page.
// $evalContent is the PHP code to evaluate.
// $evalFile is the file that the code is being evaluated in
// Errors will be logged in the console and the php error log
// The PHP error logged will also include the path of the .page file for easier debugging
$evalSuccess = false;
$evalFile = $evalFile ?? "Unknown";
if ( ! ($evalContent ?? false) ) {
error_log("No evalContent within $evalFile");
return;
}
ob_start();
try {
set_error_handler(function($severity, $message, $file, $line) use ($evalFile) {
// If error was suppressed with @, error_reporting will be 0
if (!(error_reporting() & $severity)) {
return true;
}
// Only convert fatal errors to exceptions
$fatalErrors = E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR;
if ($severity & $fatalErrors) {
error_log("PHP Fatal Error (Severity: $severity) in $evalFile: $message");
throw new ErrorException($message, 0, $severity, $file, $line);
} else {
// Log non-fatal errors with evalFile context
error_log("PHP Error (Severity: $severity) in $evalFile: $message at $file:$line");
}
// Let warnings and notices be handled normally
return false;
});
eval($evalContent);
restore_error_handler();
$evalSuccess = true;
ob_end_flush();
} catch (Throwable $e) {
restore_error_handler();
$severity = ($e instanceof ErrorException) ? $e->getSeverity() : 'N/A';
error_log("Error evaluating content in $evalFile (severity: $severity): ".$e->getMessage()."\nStack trace:\n".$e->getTraceAsString());
ob_clean();
$errorMessage = "Error evaluating content in $evalFile: ".$e->getMessage();
echo "<script>console.error(".json_encode($errorMessage).");</script>";
ob_end_flush();
}
?>

18
emhttp/plugins/dynamix/include/PageBuilder.php Normal file → Executable file
View File

@@ -18,7 +18,11 @@ function get_ini_key($key,$default) {
$x = strpos($key, '[');
$var = $x>0 ? substr($key,1,$x-1) : substr($key,1);
global $$var;
eval("\$var=$key;");
try {
eval("\$var=$key;");
} catch (Throwable $e) {
return $default;
}
return $var ?: $default;
}
@@ -44,10 +48,14 @@ function build_pages($pattern) {
function page_enabled(&$page)
{
global $var,$disks,$devs,$users,$shares,$sec,$sec_nfs,$name,$display,$pool_devices;
$enabled = true;
if (isset($page['Cond'])) eval("\$enabled={$page['Cond']};");
return $enabled;
global $docroot,$var,$disks,$devs,$users,$shares,$sec,$sec_nfs,$name,$display,$pool_devices;
$enabled = $evalSuccess = true;
if (isset($page['Cond'])) {
$evalContent= "\$enabled={$page['Cond']};";
$evalFile = $page['file'];
include "$docroot/webGui/include/DefaultPageLayout/evalContent.php";
}
return ($enabled && $evalSuccess);
}
function find_pages($item) {