Security Updates for Drupal with Composer
Use when:
- Running a security audit on a Drupal project
- Fixing packages flagged by
composer audit - Applying a specific security advisory
- Verifying no known vulnerabilities remain
Before You Start — Create a Branch
This step is mandatory. Do not run any composer commands until a new branch is created and confirmed. Never update packages directly onmainormaster.
Check the current branch first:
git branch --show-currentIf the user is on main, master, or any protected branch, stop and ask: "What would you like to name the new branch for these security fixes?"
Suggest a default if they are unsure (e.g., security/drupal-updates-YYYY-MM-DD).
git checkout -b <branch-name>Confirm the new branch is active before proceeding:
git branch --show-currentOnly continue to the next step once the output confirms a non-protected branch.
Audit for Vulnerabilities
composer auditOutput lists packages with known advisories, CVE IDs, and links to the advisory.
JSON output (for scripting)
composer audit --format=jsonAudit without dev dependencies
composer audit --no-devFix a Specific Vulnerable Package
composer update drupal/package --with-all-dependenciesUse --with-all-dependencies to allow transitive dependency version changes required by the update.
Example — fix a known advisory in drupal/core
composer update drupal/core-recommended drupal/core-composer-scaffold --with-all-dependenciesFix All Packages with Advisories
Update only packages flagged by the audit, staying within the version constraints in composer.json:
composer update --with-all-dependencies $(composer audit --format=json 2>/dev/null \
| python3 -c "import sys,json; data=json.load(sys.stdin); print(' '.join(set(a['packageName'] for a in data.get('advisories', {}).values() if isinstance(a, dict)) or [v[0]['packageName'] for v in data.get('advisories', {}).values()]))" 2>/dev/null)Or update them manually after reviewing the audit output:
# List vulnerable packages from audit output, then update each
composer update drupal/package1 drupal/package2 --with-all-dependenciesVerify No Vulnerabilities Remain
composer auditExpected output after all fixes:
No security vulnerability advisories found.After the audit is clean, always ask the user these questions in order: 1. "Do you want to commit these changes?" - If yes:git add composer.json composer.lock git commit -m "Apply Drupal security updates"- If no → remind the user thatcomposer.jsonandcomposer.lockare uncommitted before proceeding. 2. "Do you want to deploy these changes to an Acquia environment?" - If yes → follow the Drupal Update and Deploy playbook to push code, switch the environment, and optionally trigger a pipeline build. - If no → done.
Troubleshooting
"Your requirements could not be resolved"
The version required to fix the advisory conflicts with another constraint. Options:
# Check what requires the package
composer why drupal/package
# Check what prevents the update
composer why-not drupal/package 2.x
# Relax the constraint in composer.json if safe, then retry
composer update drupal/package --with-all-dependenciesAdvisory persists after update
Composer's local advisory database may be stale. Refresh it:
composer audit --update-cache
composer auditPackage cannot be updated without breaking other packages
Pin the conflicting package temporarily and file a follow-up:
# Check the full dependency tree
composer depends drupal/conflicting-packageResolve the constraint in composer.json before retrying.
Best Practices
- Run
composer auditbefore every deploy — catch new advisories early. - Use
--with-all-dependencies— security fixes often require transitive updates. - Review
composer.lockdiff — confirm only expected packages changed. - Check the advisory link — understand what the vulnerability is before updating.