Ubuntu · Packages & updates

Leave no security update pending

When it fails: Security updates are available but not installed. Packages with published security fixes are still installed in their vulnerable version.

CB-UBU-PKG-008mediumactiveautomatedprofile core

As released in v0.1.2 (beta). Current state.

Description

apt-get -s dist-upgrade simulates an upgrade from the local package lists and shows, for each package, the version and origin of the update. Updates coming from a security pocket (<codename>-security, or <codename>-infra-security and <codename>-apps-security from Ubuntu Pro ESM) fix vulnerabilities. The result is only as fresh as the package lists, refreshed by apt-get update or apt-daily.timer.

The check fails on pending security updates and on package lists older than {{ max_lists_age }} days (newest Release file or /var/lib/apt/periodic/update-success-stamp), since stale lists hide pending updates. Other pending updates (-updates, backports) are only listed in the evidence. The check does not run apt-get update: refresh the lists first for an up-to-date answer.

Phased updates: Ubuntu releases some -updates (SRU) to a growing share of systems, and apt (22.04 and later) holds them back on the others. Security pockets are never phased, so a deferred update is never a pending security fix; deferred packages are counted in the evidence only. ESM updates are only visible on systems attached to Ubuntu Pro: on a 20.04 system that is not attached, the check passes while pro security-status lists ESM fixes it cannot install. CB-UBU-PKG-009 catches that case.

Rationale

An available security update means the vulnerability is public and a fix exists, which is when exploitation is most likely. Checking for pending security updates catches systems where automatic updates are disabled, broken, or blocked by a held package or a dependency change.

Impact

None for the check, which changes nothing. Installing updates restarts the services concerned.

Parameters

ParameterCoreServerServer hardenedDesktopDesktop hardenedBounds
max_lists_ageinteger, days. Maximum age of the newest package list before the check fails.777771 to 90

The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.

Audit

  1. Run apt-get update.
  2. Run apt-get -s dist-upgrade | grep '^Inst' | grep -i security. It must print nothing.

Automated: every check must pass. Scanners report each check by its id.

pending

Updates pending in the local package lists, and the age of these lists.

script: |
  s=$(LC_ALL=C apt-get -s -o Debug::NoLocking=1 dist-upgrade 2>/dev/null) || { echo "apt-get simulation failed"; exit 3; }
  all=$(printf '%s\n' "$s" | grep -c '^Inst ')
  sec=$(printf '%s\n' "$s" | grep '^Inst ' | grep -E '\(.*-security' | awk '{ print $2 }')
  nsec=$(printf '%s' "$sec" | grep -c .)
  # apt dates the Release files with the archive's Last-Modified: also use the stamp that
  # Ubuntu touches after each successful apt-get update.
  newest=$(find /var/lib/apt/lists /var/lib/apt/periodic -maxdepth 1 \( -name '*Release' -o -name update-success-stamp \) -printf '%T@\n' 2>/dev/null | sort -n | tail -1)
  age=999
  [ -n "$newest" ] && age=$(( ($(date +%s) - ${newest%.*}) / 86400 ))
  ph=$(printf '%s\n' "$s" | sed -n '/deferred due to phasing/,/^[^ ]/p' | grep '^ ' | wc -w)
  echo "$all pending, $nsec security ($(printf '%s' "$sec" | head -20 | tr '\n' ' ')), $ph deferred by phasing, lists $age days old"
  [ "$nsec" -eq 0 ] && [ "$age" -le "$CB_MAX_LISTS_AGE" ]

Remediation

Run apt-get update then apt-get dist-upgrade (or unattended-upgrade for security updates only, which also installs ESM updates on attached systems), during a maintenance window if services must not restart. Reboot when the kernel, libc or systemd were updated (/var/run/reboot-required exists, see CB-UBU-PKG-013). If a package is held (apt-mark showhold), find out why before releasing it.

References

Mappings

FrameworkIDRelationNote
ANSSI-BP-028 - Recommandations de configuration d'un système GNU/Linux2.0R61partialR61 asks for a regular update process; this control checks its result for security updates.
NIST SP 800-53 Rev. 55.1.1SI-2related

How this control relates to the other framework's: equivalent, stricter, looser, partial or related.

Threats mitigated

  • related CB-UBU-PKG-007 Install security updates automatically
  • related CB-UBU-PKG-013 Reboot after updates that require it, or use Livepatch

People and lifecycle

Authors
Verified2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Passes on up-to-date VM; fails after downgrading libexpat1 below its security version and with lists older than max_lists_age; passes after upgrade and after apt-get update (update-success-stamp).
Verified2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Passes on up-to-date VM; fails after downgrading libexpat1 below its security version and with lists older than max_lists_age; passes after upgrade and after apt-get update (update-success-stamp).
Verified2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Passes on up-to-date VM; fails after downgrading libexpat1 below its security version and with lists older than max_lists_age; passes after upgrade and after apt-get update (update-success-stamp).
Verified2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Passes on up-to-date VM; fails after downgrading libexpat1 below its security version and with lists older than max_lists_age; passes after upgrade and after apt-get update (update-success-stamp).
Introduced inv0.1.0
Created, updated2026-10-19, 2026-10-19
Translationsfr