Start from the places that already declare versions: repository files, CI definitions and container images. Then add a lightweight check that records installed versions on laptops and agents. Aim to answer one question quickly: which machines or jobs run a version behind the supported one?
Declared versus installed
There are two kinds of truth. Declared versions are what your repositories say: a go line, a packageManager field, a Gradle wrapper file, a Dockerfile base image. Installed versions are what a laptop or agent actually runs. The gap between them is where patching slips.
Declared versions are easy: they live in version control. Installed versions need a collection step.
What to list
| Item | Where the version shows |
|---|---|
| Editor | The About screen, or a command line version flag |
| Extensions and plugins | The editor's installed list |
| Runtimes | Each runtime's version command |
| Package managers | The same, plus the pinned field in the repository |
| Container tooling | Docker Desktop version, base image digests |
| CI runners | Runner version and OS image |
A staged approach
- Week one: repositories. Grep for version files and Dockerfiles. Make a table of runtime majors and compare them to vendor support tables.
- Week two: CI. List the images and runner types each pipeline uses. Note which are self-hosted.
- Week three: laptops. Ask each team to run a short script that prints tool versions and posts them to a shared sheet or inventory.
- Week four: act. Sort by how far behind each version is. Fix the oldest and the most exposed first.
node -v; python3 --version; go version; kubectl version --client
Keep the script boring and read-only. People will run it if it asks for nothing sensitive.
Prioritize by exposure
Not every old tool is equal. An old formatter on one laptop matters less than an end-of-life runtime in a production base image, or an unpatched runner that builds public pull requests. Rank by what the tool can reach.
Check the lifecycle tables for the runtimes you found. For example, the Node.js releases page lists v20 as end-of-life, and the Python developer guide lists 3.10 as end-of-life as of 1 October 2026. Those are quick wins when they show up.
Keep it current
An inventory taken once is stale in a month. Re-run collection on a schedule, and add a check to CI that fails when a pinned runtime falls out of support. Pair the inventory with the one-page patch policy.
Where Safeguard helps
Safeguard's asset discovery and SBOM generation record what your projects contain, and its dependency scanning flags known issues. Try a free scan at gold.safeguard.sh/scan.
Try it yourself
Click through it at your own pace, or open it full screen.
Look at how components and versions are listed per project, then think about what the same table would look like for your tooling.
Quick check
What is the difference between declared and installed versions?
Declared versions are in your repository files. Installed versions are what machines actually run. Patching gaps live between them.
What should I patch first?
Start with end-of-life runtimes and anything that holds credentials or builds untrusted code, such as an unpatched CI runner.
Sources
Last reviewed: 19 October 2026.