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:
EZSCALE
2026-02-07 15:48:49 -06:00
parent e73e85c5a9
commit 6c7cdc6421
11 changed files with 91 additions and 115 deletions

View File

@@ -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",

View File

@@ -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>