Skip to content

GLPI Vuln

Vulnerability and patch exposure for a GLPI 11 estate, derived from the software inventory already in GLPI. No agent, no scanner, no credentials on endpoints, no probing.

Exposure dashboard: severity tiles, affected machines, mean time to patch, coverage fraction and confidence mix

The dashboard leads with three numbers a prettier one would omit: coverage (how many machines could be examined at all), the confidence mix, and — when it is empty — which of the five reasons it is empty for.

Vulnerability scanner This plugin
Needs on the endpoint An agent, credentials, or both Nothing new
What it sees What it can reach on the network today Everything the inventory recorded, including machines that are off
Laptops off the VPN Only if online during a scan window Whenever they last checked in
Evidence of a patch The next scan says so The next inventory no longer shows the vulnerable version
What it can find Anything probeable, including misconfiguration Only what is in the software inventory
Cost to run A scan window, a licence, an appliance Three HTTP feeds on a cron

A vulnerability scanner checks for open ports, weak ciphers, and service misconfigurations. This plugin checks reported package and software versions against published advisories; it complements network scanners rather than replacing them.

Every match carries a tier, shown everywhere the match is shown. The weakest tier never becomes a finding.

Tier Meaning Where it goes
exact The feed asserted this version is affected — OSV matched name, ecosystem and version on its own side, or an NVD CPE names this build Findings
range The installed version was compared against a published affected range, by our comparator Findings, badged
name The product name matched but the version could not be compared Review queue only. Never a finding, dashboard entry, ticket or tool answer

An advisory with the feed’s text and references, and every affected installation with package, installed version, fix version, confidence tier and exposure clock

Approving a name-level match records the mapping so future advisories for that product match automatically by version: for example, verifying that Acrobat Reader DC (64-bit) maps to adobe:acrobat_reader_dc.

Review queue: name-level matches with the software, the claimed CPE product, machines affected, why it is unsure, Approve/Dismiss

  • Linux packages via OSV.dev. Package name, ecosystem and version go out in batches; OSV does the version comparison in each distribution’s own semantics. These are the most trustworthy findings the plugin produces.
  • End-of-life census via endoflife.date. Which machines run something no longer receiving security updates. No CVE, no version comparison, no confidence caveat.
  • Windows and macOS applications via NVD 2.0. CPE matching, which is fuzzy by construction, hence the tiers and the review queue.
  • Findings close themselves. Nothing reports a patch; the next inventory shows the new version and the finding is marked patched with the date, so mean time to patch measures the estate rather than a self-report.
  • Exception register. Per entity or per asset, with a named risk owner, a required reason and an expiry date. A lapsed exception brings the finding back.
  • Optional remediation tickets. Off for every entity by default. One ticket per entity per advisory, under an ITIL category you choose, so remediation is SLA-measured by core.

End-of-life census: product, cycle, when support ended, machines and entities affected, latest release

A cycle flagged as ended with no published date says so rather than inventing one.

Exception register with scope, risk owner, expiry and state

The register is a GLPI itemtype, so filtering, entity scoping, saved searches and CSV export are core’s.

Product names and version strings, and nothing else. No hostname, serial, IP, entity or entity name, user, ticket, or count of anything.

  • OSV.dev — package name, ecosystem name and version, in batches.
  • endoflife.date — a product slug in a URL, such as ubuntu.
  • NVD — no question about your estate at all; it serves modification windows of the public CVE list and matching happens locally.

Every feed is off by default and enabled individually. The settings page states this above the switches.

  • No scanning. Nothing is probed, connected to, port-scanned or fingerprinted. The only outbound traffic is to the three feeds.
  • No exploitation. No proof-of-concept or exploit checks. EPSS and CISA KEV enrichment are not in this cut.
  • No remediation. It never patches, runs a command or touches a package manager. It opens tickets if asked.
  • No re-scoring. Severity is the feed’s. Where only a CVSS 3.x vector was published the base score is computed from it; CVSS 4.0 vectors are left unscored rather than approximated.
  • No configuration or compliance checks. Versions only.
Terminal window
# from the GLPI root
git clone https://github.com/bijstaan/glpi-vuln.git plugins/glpivuln
php bin/console plugin:install -u glpi glpivuln
php bin/console plugin:activate glpivuln

Pages are under Tools → Vulnerability exposure.

Setup → Plugins → GLPI Vuln. Opens on a status card, with each feed’s real last outcome underneath — including partial, which is the ordinary state of an NVD backfill rather than a failure.

Settings page: status card, what leaves the estate, per-feed table with last run, item counts and unmapped count

Setting Default Notes
Enable GLPI Vuln off Master switch. Off, no feed is contacted and nothing is matched
OSV.dev off Linux package advisories
endoflife.date off Support lifecycle census
NVD 2.0 off Windows and macOS applications
NVD API key — Optional. Raises the rate from 5 requests/30s to 50. Encrypted with GLPI’s key
First backfill 30 days How far back the first NVD sync reaches. Full history is ~300,000 CVEs
NVD requests per run 120 The sync is resumable; a small budget is a slow catch-up, not a failure
OSV queries per run 4000 One per distinct package/version/ecosystem, not per machine
Extra EOL products — endoflife.date slugs beyond the operating systems already detected
Entities examined allowlist, empty An empty allowlist permits nothing
Ticket entities empty Empty means no tickets anywhere
Ticket severity threshold Critical Unrated advisories never open a ticket, at any threshold
ITIL category — Pick one your SLA rules already attach to
Tickets for end-of-life off Separate, and opened as requests rather than incidents
Keep patched findings 365 days The evidence behind every MTTP figure. 0 keeps them for ever

Three automatic actions are registered and do nothing until the master switch is on:

Task Every Does
vulnfeeds 12h Refreshes enabled feeds, each inside its own guard and budget
vulnmatch 1h Recomputes exposure from current inventory, closes what has been patched
vulnworkflow 1h Opens and updates remediation tickets for permitted entities
  • plugin_glpivuln_exposure — dashboards, advisory pages, review queue, exception register.
  • plugin_glpivuln_config — settings: which feeds run, the API key, what leaves the estate.

Both are granted at install to profiles holding core config UPDATE. A service-desk lead can be given the first without the second.

vuln_exposure, read-only: answers whether a computer is exposed to anything that matters, or summarises an entity. Scoped by both the conversation’s entity and the session’s. Every response carries the confidence mix and the feed freshness.

  • The package source is inferred, not read. GLPI’s inventory format carries a from field on every software entry (deb, rpm, registry, …) and core discards it. Ecosystem is inferred from the machine’s OS; a machine with no OS recorded contributes no Linux packages, and the dashboard reports how many rather than counting them clean.
  • Not every distribution is covered. OSV has no ecosystem for CentOS, Fedora, Oracle Linux, Amazon Linux, Arch or Linux Mint. Those machines are counted as unmapped on the settings page.
  • The version comparator is pragmatic. dpkg and rpm ordering are faithful; Ubuntu ESM suffixes are not understood and there is no pacman scheme. OSV findings do not depend on it.
  • CPE matching over-matches by construction. NVD’s configuration trees express “this application running on that OS” and GLPI does not record running-on relationships, so the tree is flattened. This is why an NVD match is never better than range.
  • CVSS 4.0 is not scored. Its score needs a 270-entry lookup table plus an interpolation procedure; a partial implementation would produce numbers that look right and are not. A 4.0-only advisory keeps its feed-supplied label or shows as unrated.
  • MTTP has a blind spot. A machine that stops being inventoried looks like one that was patched. Those are excluded and counted separately as “went quiet”.
  • The first MTTP cycle is meaningless. Exposure clocks start when the plugin first sees a finding, not at CVE publication. No figure is shown until ten findings have closed.
Terminal window
docker compose -p glpi exec glpi sh -c 'cd /var/www/glpi/plugins/glpivuln && tests/run.sh'

Five suites, none needing a database: the version comparator, the CPE scorer and CVSS arithmetic, the three feed parsers against captured fixtures, the EOL and ecosystem mapping, and the transport layer against a mock feed server that run.sh starts itself.

tests/matching.php covers the state machine — dismissals surviving a re-sync, a finding closing itself, a regression starting a new clock, an exception suppressing and then lapsing — and needs a live instance with the plugin installed:

Terminal window
docker compose -p glpi exec glpi \
php /var/www/glpi/plugins/glpivuln/tests/matching.php

It writes only rows prefixed ZZTEST-, removes them on the way out, and snapshots and restores the settings it touches through raw glpi_configs rows.

tests/browser/vuln-check.js checks the pages in a real browser and takes the screenshots above.

GPL-3.0-or-later, the same licence as GLPI. The plugin is loaded into GLPI’s process and extends its classes, so it is a derivative work. See LICENSE.