Files
timebank-cc-public/references/DEPLOYMENT_HTMLPURIFIER_FIX.md
Ronald Huynen 2547717edb Initial commit
2026-03-23 21:37:59 +01:00

6.2 KiB

HTMLPurifier Cache Directory Fix

Problem

When deploying to a server, you may encounter this error:

Directory /var/www/timebank_cc_dev/vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer not writable.

This occurs because HTMLPurifier tries to write cache files to its vendor directory, which:

  1. Should NOT be writable for security reasons
  2. May not have correct permissions on production servers
  3. Can be overwritten during composer install

Solution

We've reconfigured HTMLPurifier to use Laravel's cache directory instead.

Changes Made

  1. Updated app/Helpers/StringHelper.php (Lines 55-63)

    • HTMLPurifier now uses storage/framework/cache/htmlpurifier
    • Auto-creates directory if it doesn't exist
    • Works across all environments without manual configuration
  2. Created deployment-htmlpurifier-fix.sh

    • Standalone script to set up HTMLPurifier cache directory
    • Auto-detects web server user or accepts it as argument
    • Gracefully handles permission errors
    • Always exits successfully (doesn't break deployment)
  3. Integrated into deploy.sh (Lines 306-316)

    • Automatically runs during deployment
    • Uses same web user/group as rest of application
    • Runs for both local and server environments

How It Works

Automatic (via deploy.sh)

When you run ./deploy.sh, it will automatically:

  1. Create storage/framework/cache/htmlpurifier directory
  2. Set ownership to web server user (www-data, apache, nginx, etc.)
  3. Set permissions to 755
  4. Verify the directory is writable

Manual (if needed)

If you need to run the setup separately:

# Let script auto-detect web server user
./deployment-htmlpurifier-fix.sh

# Or specify web server user
./deployment-htmlpurifier-fix.sh www-data

# Or specify both user and group
./deployment-htmlpurifier-fix.sh www-data www-data

Code-Level (automatic)

The StringHelper::sanitizeHtml() method automatically creates the cache directory if it doesn't exist (lines 58-61):

$cacheDir = storage_path('framework/cache/htmlpurifier');
if (!is_dir($cacheDir)) {
    mkdir($cacheDir, 0755, true);
}

Deployment Instructions

First Time Setup (New Servers)

Just run your normal deployment:

./deploy.sh

The HTMLPurifier cache directory will be set up automatically.

Existing Deployments (Already Running)

If you're updating an existing server that already has the old code deployed:

  1. Pull and deploy the latest code:

    ./deploy.sh
    
  2. The script will automatically:

    • Update code to use new cache location
    • Create and configure the cache directory
    • Set correct permissions
  3. Verify (optional):

    ls -la storage/framework/cache/htmlpurifier
    

    You should see the directory with correct ownership and permissions.

If You Still Get Errors

If you encounter permission errors after deployment:

  1. Manually run the fix script:

    cd /var/www/timebank_cc_dev
    ./deployment-htmlpurifier-fix.sh www-data www-data
    
  2. Check storage directory permissions:

    sudo chown -R www-data:www-data storage/
    sudo chmod -R 775 storage/
    
  3. Check SELinux (if applicable):

    sudo chcon -R -t httpd_sys_rw_content_t storage/
    
  4. Clear all caches:

    php artisan cache:clear
    php artisan view:clear
    php artisan config:clear
    

Benefits

  1. Security: Vendor directory remains read-only
  2. Reliability: Cache survives composer install updates
  3. Laravel Standard: Uses Laravel's cache directory structure
  4. Cross-Environment: Works on local, dev, and production without changes
  5. Auto-Recovery: Code creates directory if missing
  6. Deployment Safe: Script always continues even if errors occur

Technical Details

Cache Location

  • Old (problematic): vendor/ezyang/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer
  • New (fixed): storage/framework/cache/htmlpurifier

Permissions

  • Directory: 0755 (owner: rwx, group: r-x, others: r-x)
  • Owner: Web server user (www-data, apache, nginx)
  • Group: Web server group (same as owner)

Files Created

When HTMLPurifier runs, it creates definition cache files:

  • HTML/4.01_XHTML1.0_Transitional.ser
  • HTML/4.01_XHTML1.0_Transitional-Attr.AllowedFrameTargets.ser
  • And others based on configuration

These are automatically managed by HTMLPurifier and Laravel's cache clearing commands.

Troubleshooting

Error: "Directory not writable"

Cause: Web server user can't write to cache directory

Fix:

sudo chown -R www-data:www-data storage/framework/cache/htmlpurifier
sudo chmod -R 755 storage/framework/cache/htmlpurifier

Error: "Failed to create directory"

Cause: Parent directory (storage/framework/cache/) doesn't have write permissions

Fix:

sudo chown -R www-data:www-data storage/framework/cache/
sudo chmod -R 775 storage/framework/cache/

Error: SELinux blocking writes

Cause: SELinux security context preventing writes

Fix:

sudo chcon -R -t httpd_sys_rw_content_t storage/framework/cache/

Testing

After deployment, verify HTMLPurifier works:

  1. View a post with HTML content:

    • Visit any page that displays post content
    • No errors should appear
  2. Check cache directory:

    ls -la storage/framework/cache/htmlpurifier/
    

    You should see definition cache files created by HTMLPurifier

  3. Run tests:

    php artisan test --filter PostContentXssProtectionTest
    

    All 16 tests should pass

  • app/Helpers/StringHelper.php - Sanitization method with cache configuration
  • deployment-htmlpurifier-fix.sh - Standalone setup script
  • deploy.sh - Main deployment script (includes HTMLPurifier setup)
  • SECURITY_AUDIT_XSS.md - Complete XSS vulnerability audit report

Support

If you encounter issues not covered here:

  1. Check Laravel logs: storage/logs/laravel.log
  2. Check web server error logs: /var/log/apache2/error.log or /var/log/nginx/error.log
  3. Verify web server user: ps aux | grep -E 'apache|nginx|httpd' | grep -v grep
  4. Check storage permissions: ls -la storage/