Publish Upgrade System Analysis
Executive Summary
The publish_upgrade.php file is the server-side component that creates and publishes upgrade packages for distribution to other Joinery installations. This comprehensive analysis identifies the current workflow, architectural issues, security concerns, and required improvements.
Current Workflow
1. System Status Check (Lines 14-17)
- Checks if
upgrade_server_activesetting is enabled - Exits if disabled with a plain text message
2. Permission Check (Line 20)
- Verifies user has admin permission level 8 or higher
3. Upgrade Publishing Process (Lines 22-99)
When POST request containsversion_major and version_minor:Step 1: Input Processing (Lines 24-30)
- Reads
$_REQUEST['version_major']and$_REQUEST['version_minor'] - Constructs filename:
current_upgrade{major}-{minor}.upg.zip - Sets output path to
{baseDir}/{site_template}/static_files/
Step 2: Permission Validation (Lines 34-65)
- Checks directory permissions and ownership
- Verifies www-data ownership
- Validates user read/write permissions
Step 3: File Collection (Lines 73-80)
- Recursively collects all files from
public_htmldirectory - Excludes
.gitand.gitignorefolders - Creates zip archive with excluded filenames removed from paths
Step 4: Database Recording (Lines 88-94)
- Creates new Upgrade record
- Stores major/minor versions, filename, and release notes
- Saves to database
4. Display Form (Lines 101+)
When no POST request or for initial display:- Lists recent upgrades (10 most recent)
- Displays form to create new upgrade
- Auto-suggests version numbers based on latest in database
- Allows entry of release notes
Current Architecture
Entry Point
- File:
/var/www/html/joinerytest/public_html/utils/publish_upgrade.php - URL:
/utils/publish_upgrade - Access: Requires permission level 8 (admin)
- Link: Available in admin settings when
upgrade_server_activeis enabled
Data Model
- Class:
Upgrade(extendsSystemBase) - Table:
upg_upgrades - Primary Key:
upg_upgrade_id(auto-increment) - Fields:
upg_major_version (int4) - Major version number
- upg_minor_version (int4) - Minor version number
- upg_name (varchar 64) - Filename of upgrade package
- upg_release_notes (text) - Release notes text
- upg_create_time (timestamp) - Auto-generated timestamp- Multi class:
MultiUpgrade- For querying multiple upgrades
Related Settings (Database stg_settings table)
upgrade_server_active- Boolean (0/1), enables/disables server functionalityupgrade_source- URL string, used by client to find server
Related Files
- Upgrade receiving:
/utils/upgrade.php- Client-side upgrade receiver - Settings UI:
/adm/admin_settings.php(lines 1025-1031) - Data model:
/data/upgrades_class.php
Critical Issues Found
Issue 1: No Input Validation/Sanitization
Location: Lines 24-25, 92 Severity: CRITICAL Problem:$version_major = $_REQUEST['version_major'];
$version_minor = $_REQUEST['version_minor'];
// ... directly used without type checking
$upgrade->set('upg_release_notes', $_REQUEST['release_notes']);
// ... no HTML escaping or sanitization
Impact:
- Could accept invalid version formats
- SQL injection risk if directly used in queries
- XSS risk when release notes displayed
- Could store malicious content in database
Issue 2: File Path Manipulation Risk
Location: Lines 24-30 Severity: CRITICAL Problem:$filename = 'current_upgrade'.$version_major.'-'.$version_minor.'.upg.zip';
$file_output_location = $full_site_dir.'/static_files/'.$filename;
Issues:
- No path traversal prevention
- Version numbers not validated for special characters
- Could potentially create files outside intended directory
- Vulnerable to directory traversal:
../../../etc/
Issue 3: Missing Error Handling & Rollback
Location: Lines 69-70, 77-80 Severity: CRITICAL Problem:$file = fopen($file_output_location, 'w') or die("can't open file");
fclose($file);
// No error checking on create_zip result
create_zip(...);
// ... and then database record created regardless
Issues:
- Uses
die()instead of proper exception handling - No rollback if zip creation fails
- Zip may be partially created but file collection fails
- Database record created even if zip fails
- Orphaned files and database inconsistency
Issue 4: Deprecated Direct File Includes
Location: Lines 2-6 Severity: CRITICAL Problem:require_once( __DIR__ . '/../includes/Globalvars.php');
require_once( __DIR__ . '/../includes/SessionControl.php');
require_once( __DIR__ . '/../includes/AdminPage.php');
require_once( __DIR__ . '/../includes/LibraryFunctions.php');
require_once( __DIR__ . '/../data/upgrades_class.php');
Issues:
- Uses direct file paths instead of PathHelper
- Violates codebase patterns (see CLAUDE.md critical rule)
- Inconsistent with other utility files like
upgrade.php - Will break if directory structure changes
- Prevents theme/plugin file overrides
PathHelper::getIncludePath() for all includesIssue 5: No Version Number Validation
Location: Lines 27, 79 Severity: CRITICAL Problem:- No validation that version numbers are integers
- Could produce invalid filenames with special characters
- Race condition: version could change between validation and save
- No check for duplicate version numbers
Issue 6: FormWriter Not Initialized Per Codebase Pattern
Location: Line 130 Severity: HIGH Problem:$formwriter = new FormWriter('form1');
Issues:
- FormWriter instantiated directly instead of via
$page->getFormWriter() - Doesn't use theme-aware approach (see CLAUDE.md patterns)
- Different from all admin pages in codebase
- Won't support theme overrides
$page->getFormWriter('form1') patternIssue 7: Wrong Menu ID
Location: Line 108 Severity: MEDIUM Problem:'menu-id'=> 'orders-list', // This is for orders, not upgrades!
Issues:
- Wrong menu ID causes incorrect menu highlighting
- Should reference upgrade or publish-upgrade menu ID
- Confuses admin navigation
Issue 8: Wrong Field Names in MultiUpgrade Queries
Location: Lines 136, 147 Severity: HIGH Problem:$major = new MultiUpgrade(array(), array('major_version' => 'DESC'));
$minor = new MultiUpgrade(array(), array('minor_version' => 'DESC'));
Issues:
- Field names are wrong (should be prefixed with 'upg_')
- Database columns are 'upg_major_version' and 'upg_minor_version'
- Sorting likely fails silently, returning unordered results
- Auto-suggested version numbers may be wrong
MultiUpgrade::getMultiResults() for correct option keysHigh Priority Issues
Issue 9: No Transaction Safety
Location: Lines 69-94 Problem:- File created, then database record added
- If database write fails, zip file remains orphaned
- If zip fails after record created, database has stale record
Issue 10: Unrestricted File Access
Location: Lines 29-30 Problem:- Upgrade files stored in public web directory
- Anyone with URL can download full codebase
- No authentication required for downloads
- No rate limiting
Issue 11: Direct Output Instead of Logging
Location: Lines 77, 98 Problem:- Outputs directly to stdout during zip creation
- No option to suppress or log output
- Progress information clutters page
- Difficult to debug issues
Issue 12: Hard-coded Paths
Location: Lines 75, 79 Problem:- Uses direct path concatenation
- No use of PathHelper for path resolution
- Inconsistent with architecture
Medium Priority Issues
Issue 13: Unused Permission Variables
Location: Lines 35-53 Problem:- Complex permission bit calculations never used
- Code is hard to maintain
- Could be simplified
Issue 14: No Zip Integrity Verification
Location: Line 82-85 Problem:- Only checks file existence, not validity
- Corrupted zip would pass validation
- No checksums or integrity verification
Issue 15: No Audit Logging
Problem:- No record of who published what upgrade and when
- Cannot trace upgrade history
- Security and debugging issues
Database Integration Points
Download Flow
upgrade.php (client instance)
-> GET /utils/upgrade?serve-upgrade=1
-> Returns JSON: {system_version, upgrade_name, upgrade_location, release_date, release_notes}
-> Downloads ZIP from upgrade_locationData Consistency Issues
- No cascading deletes (upgrade file orphaned if record deleted)
- Version numbers not unique (duplicates allowed)
- No soft-delete support (upgrades deleted are gone forever)
- No status tracking (draft, published, archived)
Security Concerns Summary
1. Unauthenticated File Access
- Upgrade packages in web-accessible directory
- Anyone can download full codebase
2. Path Traversal Vulnerability
- Version numbers not validated
- Could escape
static_filesdirectory
3. Input Validation Missing
- Version numbers could contain special characters
- Release notes not sanitized for XSS
4. No Audit Trail
- Cannot track who published what
- No version history or rollback capability
5. Race Conditions
- Two admins could publish same version simultaneously
- File could be overwritten during zip creation
Implementation Roadmap
Phase 1: Critical Security Fixes (2-3 hours)
- Fix includes to use PathHelper
- Add input validation for version numbers (integer only)
- Sanitize release notes (htmlentities)
- Fix FormWriter initialization
- Add proper error handling with rollback
Phase 2: Data Safety (2-3 hours)
- Implement transaction safety
- Add duplicate version detection
- Add file integrity checks
- Implement logging
Phase 3: Enhancement (3-4 hours)
- Move file location to non-web-accessible
- Improve version management UI
- Add audit logging
- Improve API
Files to Modify
/var/www/html/joinerytest/public_html/utils/publish_upgrade.php
/var/www/html/joinerytest/public_html/data/upgrades_class.php
/var/www/html/joinerytest/public_html/migrations/migrations.php
/var/www/html/joinerytest/public_html/adm/admin_settings.php
Recommendations
Immediate Actions (Must Do)
- Fix PathHelper includes in publish_upgrade.php
- Add integer validation for version numbers
- Add HTML sanitization for release notes
- Fix FormWriter initialization
- Correct menu ID
Short Term (Should Do)
- Implement transaction safety
- Add duplicate version detection
- Validate MultiUpgrade field names
- Add error logging
Medium Term (Nice to Have)
- Move files to secure location
- Add version status tracking
- Implement audit logging
- Add pre-publish validation
Related Documentation
- System Overview: See CLAUDE.md for architecture
- Upgrade System Comparison: See specs/upgrade_system.md for full feature comparison with deploy.sh
- Admin Pages Guide: See docs/admin_pages.md for admin page patterns
- Plugin Developer Guide: See docs/plugin_developer_guide.md for architecture patterns