mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
Fix 10 code review findings: security, correctness, and SQLite compat
- 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>
This commit is contained in:
+17
-11
@@ -15,15 +15,21 @@ log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG"; }
|
||||
exec 9>"$LOCK"
|
||||
flock -n 9 || { log "Deploy already running, skipping"; exit 0; }
|
||||
|
||||
while IFS='|' read -r REPO_PATH WEB_ROOT COMMIT; do
|
||||
[[ -z "$REPO_PATH" ]] && continue
|
||||
log "--- Deploying commit $COMMIT ---"
|
||||
# Read DB path and channel once before processing the queue
|
||||
DB_PATH=$(python3 -c "import configparser; c=configparser.ConfigParser(); c.read('/etc/novacpx/config.ini'); print(c.get('database','path',fallback='/var/lib/novacpx/panel.db'))" 2>/dev/null || echo "/var/lib/novacpx/panel.db")
|
||||
CHANNEL=$(sqlite3 "$DB_PATH" "SELECT value FROM settings WHERE key='update_channel'" 2>/dev/null || echo "stable")
|
||||
[[ "$CHANNEL" != "beta" ]] && CHANNEL="stable"
|
||||
|
||||
# Read update channel from DB to know which branch to pull
|
||||
DB_PATH=$(python3 -c "import configparser; c=configparser.ConfigParser(); c.read('/etc/novacpx/config.ini'); print(c.get('database','path',fallback='/var/lib/novacpx/panel.db'))" 2>/dev/null || echo "/var/lib/novacpx/panel.db")
|
||||
CHANNEL=$(sqlite3 "$DB_PATH" "SELECT value FROM settings WHERE key='update_channel'" 2>/dev/null || echo "stable")
|
||||
while IFS='|' read -r REPO_PATH WEB_ROOT COMMIT QUEUED_BRANCH; do
|
||||
[[ -z "$REPO_PATH" ]] && continue
|
||||
|
||||
# Use branch recorded in queue entry (from webhook); fall back to DB channel
|
||||
TARGET_BRANCH="main"
|
||||
[[ "$CHANNEL" == "beta" ]] && TARGET_BRANCH="beta"
|
||||
if [[ "$QUEUED_BRANCH" == "beta" || ("$QUEUED_BRANCH" != "main" && "$CHANNEL" == "beta") ]]; then
|
||||
TARGET_BRANCH="beta"
|
||||
fi
|
||||
|
||||
log "--- Deploying commit $COMMIT (branch: $TARGET_BRANCH) ---"
|
||||
|
||||
# Validate PHP syntax before applying
|
||||
cd "$REPO_PATH" || continue
|
||||
@@ -47,9 +53,9 @@ while IFS='|' read -r REPO_PATH WEB_ROOT COMMIT; do
|
||||
fi
|
||||
|
||||
# Pull from channel branch
|
||||
BEFORE=$(git rev-parse HEAD)
|
||||
BEFORE=$(git rev-parse HEAD | tr -cd 'a-f0-9A-F')
|
||||
git pull origin "${TARGET_BRANCH}" >> "$LOG" 2>&1
|
||||
AFTER=$(git rev-parse HEAD)
|
||||
AFTER=$(git rev-parse HEAD | tr -cd 'a-f0-9A-F')
|
||||
|
||||
if [[ "$BEFORE" == "$AFTER" ]]; then
|
||||
log "Nothing new to deploy (already at $AFTER)"
|
||||
@@ -88,8 +94,8 @@ while IFS='|' read -r REPO_PATH WEB_ROOT COMMIT; do
|
||||
done
|
||||
fi
|
||||
|
||||
# Record new version in DB
|
||||
NEW_VERSION=$(cat "$REPO_PATH/VERSION" 2>/dev/null | tr -d '[:space:]' || true)
|
||||
# Record new version in DB — sanitize values before interpolating into SQL
|
||||
NEW_VERSION=$(cat "$REPO_PATH/VERSION" 2>/dev/null | tr -d '[:space:]' | tr -cd 'a-zA-Z0-9.-' || true)
|
||||
if [[ -n "$NEW_VERSION" && -f "$DB_PATH" ]]; then
|
||||
sqlite3 "$DB_PATH" "INSERT INTO novacpx_version (version, installed_at, notes, git_commit) VALUES ('$NEW_VERSION', datetime('now'), 'Auto-deployed via webhook ($CHANNEL channel)', '$AFTER')" 2>/dev/null || true
|
||||
sqlite3 "$DB_PATH" "INSERT INTO settings (key, value, updated_at) VALUES ('panel_version', '$NEW_VERSION', datetime('now')) ON CONFLICT(key) DO UPDATE SET value=excluded.value, updated_at=excluded.updated_at" 2>/dev/null || true
|
||||
|
||||
+2
-3
@@ -11,7 +11,6 @@ $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';
|
||||
$branch = $cfg['deploy']['branch'] ?? 'main';
|
||||
$logFile = '/var/log/novacpx/deploy.log';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
@@ -50,9 +49,9 @@ $message = $payload['head_commit']['message'] ?? '';
|
||||
|
||||
log_deploy("Deploy triggered by $pusher | branch $pushedBranch | commit $commit | $message");
|
||||
|
||||
// Queue the deploy — include branch so runner knows what to pull
|
||||
// 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\n", FILE_APPEND | LOCK_EX);
|
||||
file_put_contents($queueFile, "$repoPath|$webRoot|$commit|$pushedBranch\n", FILE_APPEND | LOCK_EX);
|
||||
|
||||
http_response_code(200);
|
||||
echo json_encode(['status' => 'queued', 'commit' => $commit]);
|
||||
|
||||
Reference in New Issue
Block a user