mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
4d7c35076b
- system.php: fix null dereference on fetchOne (TypeError on null['value']) - system.php: validate update_channel to ['stable','beta'] to prevent shell injection - system.php: escapeshellarg remoteBranch in git log/show calls (was RCE vector) - system.php: fix backup path — rsync contents, not directory, so restore is symmetric - system.php: syntax check only changed files (git diff) not all 300+ panel files - system.php: copy VERSION to $webRoot/VERSION not $webRoot/../VERSION (wrong path) - system.php: fix 3× ON DUPLICATE KEY UPDATE → SQLite ON CONFLICT syntax - deploy-runner.sh: hoist DB_PATH/CHANNEL above while loop - deploy-runner.sh: sanitize NEW_VERSION and commit hashes before SQL interpolation - deploy-runner.sh: parse queued branch (4th field) from webhook queue entry - webhook.php: remove dead $branch config variable - webhook.php: include pushed branch in queue entry to eliminate TOCTOU race Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
58 lines
2.1 KiB
PHP
58 lines
2.1 KiB
PHP
<?php
|
|
/**
|
|
* NovaCPX Auto-Deploy Webhook Handler
|
|
* Place at: https://<panel-ip>:2083/deploy/webhook.php
|
|
* GitHub webhook: push to main branch → this endpoint
|
|
* Secret set in /etc/novacpx/config.ini [deploy] webhook_secret
|
|
*/
|
|
|
|
$configFile = '/etc/novacpx/config.ini';
|
|
$cfg = is_file($configFile) ? parse_ini_file($configFile, true) : [];
|
|
$secret = $cfg['deploy']['webhook_secret'] ?? '';
|
|
$repoPath = $cfg['deploy']['repo_path'] ?? '/opt/novacpx-src';
|
|
$webRoot = $cfg['deploy']['web_root'] ?? '/srv/novacpx/public';
|
|
$logFile = '/var/log/novacpx/deploy.log';
|
|
|
|
header('Content-Type: application/json');
|
|
|
|
function log_deploy(string $msg): void {
|
|
global $logFile;
|
|
file_put_contents($logFile, date('[Y-m-d H:i:s] ') . $msg . "\n", FILE_APPEND | LOCK_EX);
|
|
}
|
|
|
|
// Verify HMAC signature
|
|
$rawBody = file_get_contents('php://input');
|
|
$hubSig = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';
|
|
if ($secret) {
|
|
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
|
|
if (!hash_equals($expected, $hubSig)) {
|
|
http_response_code(403);
|
|
log_deploy('BLOCKED: invalid signature');
|
|
echo json_encode(['error' => 'Invalid signature']);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
$payload = json_decode($rawBody, true);
|
|
$pushedBranch = basename($payload['ref'] ?? '');
|
|
|
|
// Accept pushes to main (stable) or beta — both can trigger deploys
|
|
$allowedBranches = ['main', 'beta'];
|
|
if (!in_array($pushedBranch, $allowedBranches)) {
|
|
echo json_encode(['status' => 'skipped', 'reason' => "Not a deployable branch ($pushedBranch)"]);
|
|
exit;
|
|
}
|
|
|
|
$commit = $payload['after'] ?? 'unknown';
|
|
$pusher = $payload['pusher']['name'] ?? 'unknown';
|
|
$message = $payload['head_commit']['message'] ?? '';
|
|
|
|
log_deploy("Deploy triggered by $pusher | branch $pushedBranch | commit $commit | $message");
|
|
|
|
// Queue the deploy — include branch so runner uses the exact pushed branch
|
|
$queueFile = '/tmp/novacpx-deploy-queue.txt';
|
|
file_put_contents($queueFile, "$repoPath|$webRoot|$commit|$pushedBranch\n", FILE_APPEND | LOCK_EX);
|
|
|
|
http_response_code(200);
|
|
echo json_encode(['status' => 'queued', 'commit' => $commit]);
|