fix: XSS escaping, null guards, JS bug fixes, and documentation updates
- Escape $serverObject and $systemUrl in AdminHTML.php heredocs to prevent XSS - Add null guard in Database::getSystemUrl() to prevent fatal error - Guard primaryNetwork access in module.js to prevent null dereference - Reset badge/traffic-bar CSS classes on refresh to prevent accumulation - Add VNC popup-blocked check with user-facing message - Add BS3 input-group-btn dual class for theme compatibility - Escape billing template variables with |escape:'htmlall' - Add cache-busting to admin CSS/JS includes - Switch cache-busting format from version to date-based (20260207) - Create .releaserc.json for automated CHANGELOG.md management - Add changelog/git plugins to semantic-release workflow - Remove manual [Unreleased] section from CHANGELOG.md - Update README: install/upgrade with rsync, accuracy fixes, add keygen.js - Update CLAUDE.md: add keygen.js, document removed features - Fix SECURITY.md grammar and version operator Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -139,7 +139,7 @@ add_hook('ClientAreaFooterOutput', 1, function ($vars) {
|
||||
$systemUrl = Database::getSystemUrl();
|
||||
|
||||
return "
|
||||
<script src=\"" . htmlspecialchars($systemUrl, ENT_QUOTES, 'UTF-8') . "modules/servers/VirtFusionDirect/templates/js/keygen.js?v=0.0.20\"></script>
|
||||
<script src=\"" . htmlspecialchars($systemUrl, ENT_QUOTES, 'UTF-8') . "modules/servers/VirtFusionDirect/templates/js/keygen.js?v=20260207\"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var osTemplates = " . json_encode($dropdownOptions, JSON_THROW_ON_ERROR | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) . ";
|
||||
|
||||
@@ -7,6 +7,7 @@ class AdminHTML
|
||||
|
||||
public static function options($systemUrl, $serviceId)
|
||||
{
|
||||
$systemUrl = htmlspecialchars($systemUrl, ENT_QUOTES, 'UTF-8');
|
||||
return <<<EOT
|
||||
<button onclick="impersonateServerOwner('${serviceId}', '${systemUrl}')" type="button" class="btn btn-primary">Impersonate Server Owner</button>
|
||||
<span class="text-info"> A valid VirtFusion admin session in the same browser is required for this functionality to work.</span>
|
||||
@@ -15,6 +16,7 @@ EOT;
|
||||
|
||||
public static function serverObject($serverObject)
|
||||
{
|
||||
$serverObject = htmlspecialchars($serverObject, ENT_QUOTES, 'UTF-8');
|
||||
return <<<EOT
|
||||
<textarea class="form-control" name="modulefields[1]" rows="10" style="width: 100%" disabled>${serverObject}</textarea>
|
||||
EOT;
|
||||
@@ -31,9 +33,10 @@ EOT;
|
||||
|
||||
public static function serverInfo($systemUrl, $serviceId)
|
||||
{
|
||||
$systemUrl = htmlspecialchars($systemUrl, ENT_QUOTES, 'UTF-8');
|
||||
return <<<EOT
|
||||
<link href="${systemUrl}modules/servers/VirtFusionDirect/templates/css/module.css" rel="stylesheet">
|
||||
<script src="${systemUrl}modules/servers/VirtFusionDirect/templates/js/module.js"></script>
|
||||
<link href="${systemUrl}modules/servers/VirtFusionDirect/templates/css/module.css?v=20260207" rel="stylesheet">
|
||||
<script src="${systemUrl}modules/servers/VirtFusionDirect/templates/js/module.js?v=20260207"></script>
|
||||
<div id="vf-loader" class="vf-loader">
|
||||
<div id="vf-loading"></div>
|
||||
</div>
|
||||
|
||||
@@ -54,6 +54,7 @@ class Database
|
||||
public static function getSystemUrl()
|
||||
{
|
||||
$url = DB::table('tblconfiguration')->where('setting', '=', 'SystemURL')->first();
|
||||
if (!$url) return '';
|
||||
return $url->value;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,15 @@ function vfServerData(serviceId, systemUrl) {
|
||||
$("#vf-data-server-traffic-used").text(response.data.trafficUsed || "-");
|
||||
$("#vf-data-server-storage").text(response.data.storage);
|
||||
$("#vf-data-server-cpu").text(response.data.cpu);
|
||||
$("#vf-data-server-ipv4").text(response.data.primaryNetwork.ipv4);
|
||||
$("#vf-data-server-ipv6").text(response.data.primaryNetwork.ipv6);
|
||||
var pn = response.data.primaryNetwork || {};
|
||||
$("#vf-data-server-ipv4").text(pn.ipv4 || "-");
|
||||
$("#vf-data-server-ipv6").text(pn.ipv6 || "-");
|
||||
|
||||
// Update status badge
|
||||
var statusBadge = $("#vf-status-badge");
|
||||
var status = (response.data.status || "unknown").toLowerCase();
|
||||
statusBadge.text(status.charAt(0).toUpperCase() + status.slice(1));
|
||||
statusBadge.removeClass("vf-badge-active vf-badge-suspended vf-badge-awaiting");
|
||||
if (status === "active" || status === "running") {
|
||||
statusBadge.addClass("vf-badge-active");
|
||||
} else if (status === "suspended") {
|
||||
@@ -56,7 +58,7 @@ function vfServerData(serviceId, systemUrl) {
|
||||
if (trafficTotal > 0) {
|
||||
$("#vf-res-traffic").text(trafficUsed + " / " + trafficTotal + " GB");
|
||||
var pct = Math.min(100, Math.round((trafficUsed / trafficTotal) * 100));
|
||||
$("#vf-res-traffic-bar").css("width", pct + "%");
|
||||
$("#vf-res-traffic-bar").css("width", pct + "%").removeClass("bg-danger bg-warning");
|
||||
if (pct > 90) {
|
||||
$("#vf-res-traffic-bar").addClass("bg-danger");
|
||||
} else if (pct > 70) {
|
||||
@@ -140,8 +142,9 @@ function vfServerDataAdmin(serviceId, systemUrl) {
|
||||
$("#vf-data-server-traffic").text(response.data.traffic);
|
||||
$("#vf-data-server-storage").text(response.data.storage);
|
||||
$("#vf-data-server-cpu").text(response.data.cpu);
|
||||
$("#vf-data-server-ipv4").text(response.data.primaryNetwork.ipv4);
|
||||
$("#vf-data-server-ipv6").text(response.data.primaryNetwork.ipv6);
|
||||
var pnAdmin = response.data.primaryNetwork || {};
|
||||
$("#vf-data-server-ipv4").text(pnAdmin.ipv4 || "-");
|
||||
$("#vf-data-server-ipv6").text(pnAdmin.ipv6 || "-");
|
||||
$("#vf-server-info").show();
|
||||
} else {
|
||||
$("#vf-server-info-error").show();
|
||||
@@ -383,6 +386,14 @@ function vfOpenVnc(serviceId, systemUrl) {
|
||||
|
||||
// Open window immediately in click context to avoid popup blockers
|
||||
var vncWindow = window.open("", "_blank");
|
||||
if (!vncWindow) {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Popup blocked. Please allow popups for this site and try again.");
|
||||
alertDiv.show();
|
||||
spinner.hide();
|
||||
btn.prop("disabled", false);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<link href="{$systemURL}modules/servers/VirtFusionDirect/templates/css/module.css?v=0.0.20" rel="stylesheet">
|
||||
<script src="{$systemURL}modules/servers/VirtFusionDirect/templates/js/module.js?v=0.0.20"></script>
|
||||
<link href="{$systemURL}modules/servers/VirtFusionDirect/templates/css/module.css?v=20260207" rel="stylesheet">
|
||||
<script src="{$systemURL}modules/servers/VirtFusionDirect/templates/js/module.js?v=20260207"></script>
|
||||
|
||||
{if $serviceStatus eq 'Active'}
|
||||
|
||||
@@ -274,7 +274,7 @@
|
||||
<h5 class="vf-bold">Add Credit</h5>
|
||||
<div class="input-group mb-2">
|
||||
<input type="number" id="vf-ss-credit-amount" class="form-control" placeholder="Amount" min="1" step="1">
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-append input-group-btn">
|
||||
<button id="vf-ss-add-credit-btn" onclick="vfAddCredit('{$serviceid}','{$systemURL}')" type="button" class="btn btn-primary">
|
||||
<span id="vf-ss-add-credit-spinner" class="spinner-border spinner-border-sm" style="display:none;"></span>
|
||||
Add Credit
|
||||
@@ -328,29 +328,29 @@
|
||||
<div class="col-lg-6">
|
||||
<div class="row p-2">
|
||||
<div class="col-xs-6 col-6 text-right vf-bold">Product:</div>
|
||||
<div class="col-xs-6 col-6">{$groupname} - {$product}</div>
|
||||
<div class="col-xs-6 col-6">{$groupname|escape:'htmlall'} - {$product|escape:'htmlall'}</div>
|
||||
</div>
|
||||
<div class="row p-2">
|
||||
<div class="col-xs-6 col-6 text-right vf-bold">{$LANG.recurringamount}:</div>
|
||||
<div class="col-xs-6 col-6">{$recurringamount}</div>
|
||||
<div class="col-xs-6 col-6">{$recurringamount|escape:'htmlall'}</div>
|
||||
</div>
|
||||
<div class="row p-2">
|
||||
<div class="col-xs-6 col-6 text-right vf-bold">{$LANG.orderbillingcycle}:</div>
|
||||
<div class="col-xs-6 col-6">{$billingcycle}</div>
|
||||
<div class="col-xs-6 col-6">{$billingcycle|escape:'htmlall'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<div class="row p-2">
|
||||
<div class="col-xs-6 col-6 text-right vf-bold">{$LANG.clientareahostingregdate}:</div>
|
||||
<div class="col-xs-6 col-6">{$regdate}</div>
|
||||
<div class="col-xs-6 col-6">{$regdate|escape:'htmlall'}</div>
|
||||
</div>
|
||||
<div class="row p-2">
|
||||
<div class="col-xs-6 col-6 text-right vf-bold">{$LANG.clientareahostingnextduedate}:</div>
|
||||
<div class="col-xs-6 col-6">{$nextduedate}</div>
|
||||
<div class="col-xs-6 col-6">{$nextduedate|escape:'htmlall'}</div>
|
||||
</div>
|
||||
<div class="row p-2">
|
||||
<div class="col-xs-6 col-6 text-right vf-bold">{$LANG.orderpaymentmethod}:</div>
|
||||
<div class="col-xs-6 col-6">{$paymentmethod}</div>
|
||||
<div class="col-xs-6 col-6">{$paymentmethod|escape:'htmlall'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user