Database audit fix

How to Check and Apply Database Security Patches

Database engines are patched through the host package manager, so a pending mysql, mariadb, postgres, mongo or redis package is an unapplied vendor fix sitting on the box. Refresh the package lists, upgrade those packages, then restart the service.

Hiren KalariyaLast reviewed: Sep 9, 2026Check database-patches
High
severity
No
needs root or sudo
4
results it can return
3 of 7
checks in this audit

Every threshold on this page is transcribed from the database-security-hardening audit script that CtrlOps runs, and a build check fails if the two ever disagree. See all 7 Configuration & Hardening checks, or how the audit runs.

High severityNo sudo needed

What this check reads

The check simulates an upgrade with the first package manager it finds and filters the result for database packages matching mysql|mariadb|postgres|mongo|redis. On apt that is apt-get -s -o Debug::NoLocking=true upgrade, counting the ^Inst lines; any of those lines that also contains security (the archive name, for example jammy-security) is counted a second time as a security update. On dnf and yum it counts matching lines from check-update, on zypper the ^v lines of list-updates, and on apk the output of apk version -l '<' (whose filter omits mongo); none of those four can tell a security update from an ordinary one. Zero matching lines is the pass threshold: one or more security lines on apt is the FAIL, one or more lines without that marker is the WARN, and no supported package manager at all is the SKIP.

When it applies

Runs on any host with a supported package manager (apt-get, dnf, yum, zypper, apk); anything else SKIPs. No root is needed - the apt path uses a simulated upgrade (-s) with locking disabled. It does not require a database to be running, only that database packages are installed and visible to the package manager. Two important scope limits: it reads the cached package lists (a stale apt update gives a stale answer), and only apt distinguishes security updates - on dnf/yum/zypper/apk every pending update is a WARN, never a FAIL. Engines installed from a tarball, from source, or in a container are invisible.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
PASSNo pending DB package updatesThe package manager's cached lists show nothing pending for the database packages it can see. Stale lists give a stale pass, so refresh them first.
FAILapt only: a pending update comes from a `-security` pocketA published security fix for a database package is sitting unapplied on this host. Apply it now rather than at the next maintenance window.
WARNPending updates without the security marker (or any pending on non-apt)Database package updates are pending but nothing marked them as security fixes. On dnf, yum, zypper and apk that marker is never available, so this can still be a security update.
SKIPNo supported package managerNothing was verified: none of apt-get, dnf, yum, zypper or apk was on PATH, so no version comparison happened at all.

Why it matters

Database engines are high-value targets and CVEs (auth bypass, privilege escalation, RCE) are weaponised quickly. OWASP: install security patches promptly. NIST SP 800-40r4 patch management. CIS benchmarks for each engine start with "Ensure the latest version is installed".

Why it fails, and when it is wrong

  • apt uses the cached package lists. If apt update has not run recently the view is stale in both directions. unattended-upgrades and the apt-daily.timer normally refresh daily.
  • Packages installed from vendor repos (MySQL APT repo, PGDG, MongoDB repo) are included as long as the repo is configured. Tarball or Docker installs are invisible.
  • Pinned/held packages (apt-mark hold) do not appear in the simulation.
  • On dnf, check-update exits 100 when updates exist; the script uses 2>/dev/null and counts lines, which works. Use dnf updateinfo list security to see only security fixes.

How to fix it

Refresh the package lists first

The check reads the cached package lists and never refreshes them, so counting pending updates against stale metadata reports zero on a server that is weeks behind.

sudo apt update      # Debian/Ubuntu
sudo dnf makecache   # RHEL family

List what is pending for the engines

Filter the pending updates down to the engines before you upgrade anything, so you know what the window has to cover:

apt-get -s upgrade | grep '^Inst ' | grep -iE 'mysql|mariadb|postgres|mongo|redis'   # Debian/Ubuntu
dnf check-update | grep -iE 'mysql|mariadb|postgres|mongo|redis'                     # RHEL family

Apply the security updates

Upgrade the named database packages in a window where a restart is acceptable:

sudo apt upgrade mysql-server postgresql-16 redis-server           # Debian/Ubuntu
sudo dnf update mysql-server postgresql-server mongodb-org redis   # RHEL family
sudo dnf upgrade --security                                        # RHEL family, security fixes only

Plan minor-version upgrades during a window; PostgreSQL minor upgrades need a restart, major ones need pg_upgrade.

Confirm the engine restarted on the new version

Package upgrades do not always restart the daemon, so the installed package can be current while the process still serving queries is the old binary. Check the running version afterwards, not just the installed one, and restart the service if it is behind.

Verify the fix

# Debian/Ubuntu - refresh first, then confirm nothing database-related is pending:
sudo apt-get update
apt-get -s -o Debug::NoLocking=true upgrade | grep '^Inst ' | grep -iE 'mysql|mariadb|postgres|mongo|redis'
# expected: no output

# RHEL family - and specifically the security-only view the check cannot give you:
dnf -q check-update 'mysql*' 'mariadb*' 'postgresql*' 'mongodb*' 'redis*'
dnf updateinfo list security

# Confirm the running server is actually on the new version (a package upgrade needs a restart):
mysql --version; sudo mysql -N -e 'SELECT VERSION()'
psql --version; sudo -u postgres psql -X -tAc 'SHOW server_version'

Debugging

None of the five managers is on PATH. If the engine was installed from a tarball or source, no package-manager check can see it; track its version manually against the vendor's advisories.

The package lists are probably stale. Run sudo apt-get update (or dnf makecache) and re-run the audit. Also check for held packages, which never appear in the simulation: apt-mark showhold, dnf versionlock list.

Either the upgrade did not include those packages, or the running server is still the old binary. Compare package version with the live server version using the commands above, and restart the service.

Expected. Only the apt branch inspects the archive pocket for the word security. Use dnf updateinfo list security to classify it.

apt-get -s -o Debug::NoLocking=true upgrade | grep '^Inst ' | grep -iE 'mysql|mariadb|postgres|mongo|redis'; lines whose archive contains security are what turn the WARN into a FAIL.

Sources

How the script reads this

Next

Re-run the Configuration & Hardening audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 7 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.

All 7 Configuration & Hardening fixes
Audit your fleet

Find every one of these on every server, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals