Skip to content

Commit 0ce9343

Browse files
committed
fix(plugins): harden fragile version regexes and slicing (#1070)
Three plugins parsed version strings in ways that either had a real bug or only worked by accident: - axenita-stats: the slice `axenita_ver[8 : 8 + axenita_ver.find('-') - 1]` was designed for the fixed `release-14.0.8-...` format and happened to extract "14.0.8" when the patch was a single digit. For any longer patch (e.g. `14.0.12`) the slice silently truncated the trailing digit, and for 2-digit majors it would slide out of bounds. Replace with a simple `split('-')[1]` and document why the dots are stripped (perfdata wants a numeric value). - keycloak-version: the regex `r'n (.*)'` happened to match the "n " in the word "Version" of `/opt/keycloak/version.txt` (contents: `Keycloak - Version 25.0.4`) and capture the version tail. Replace with an explicit `r'[Vv]ersion\s+(\d+(?:\.\d+)*)'`. Also fall through to the API fallback when the file is present but does not match, instead of aborting with "Keycloak not found". - openvpn-version: the regex `r'N (\d+\.\d+\.\d+)'` matched the "N" at the end of "OpenVPN" plus the following space. Anchor the match on "OpenVPN" explicitly.
1 parent 7ef6a8e commit 0ce9343

File tree

4 files changed

+30
-11
lines changed

4 files changed

+30
-11
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
* wildfly-memory-usage: same `state += get_worst(...)` accumulation bug in both the heap and non-heap branches. Replaced with the new variadic `lib.base.get_worst(state, used_state, committed_state)` so the heap/non-heap used/committed states can be combined in a single call ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
2828
* wildfly-non-xa-datasource-stats, wildfly-xa-datasource-stats: the "max used" threshold check passed `active_pct` to `lib.base.get_state(...)` instead of `max_used_pct`, so the plugin alerted on the wrong metric. The output text already showed `max_used_pct` correctly ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
2929
* xml: actually use the captured `result = r[0].text` in the string-search branch. Before, `result` was assigned but unused and the branch re-evaluated `r[0].text` three more times ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
30+
* axenita-stats: fix the version-number extraction slice `[8 : 8 + find('-') - 1]` that only worked for exactly 6-character versions like `14.0.8` and silently truncated any longer patch number. Use `split('-')[1]` instead, which handles `14.0.12`, `1.2.3`, and 2-digit majors correctly ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
31+
* keycloak-version: replace the fragile regex `r'n (.*)'` (which relied on the "n " in the word "Version" of `/opt/keycloak/version.txt`) with an explicit `r'[Vv]ersion\s+(\d+(?:\.\d+)*)'` pattern. Also fall through to the API fallback when the file is present but the regex does not match, instead of aborting with "Keycloak not found" ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
32+
* openvpn-version: replace the fragile regex `r'N (\d+\.\d+\.\d+)'` (which relied on the "N" at the end of "OpenVPN") with an explicit `r'OpenVPN\s+(\d+\.\d+\.\d+)'` pattern anchored on the command name ([#1070](https://github.com/Linuxfabrik/monitoring-plugins/issues/1070))
3033

3134
### Security
3235

check-plugins/axenita-stats/axenita-stats

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import lib.url
2121
from lib.globals import STATE_OK, STATE_UNKNOWN, STATE_WARN
2222

2323
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
24-
__version__ = '2026040801'
24+
__version__ = '2026041201'
2525

2626
DESCRIPTION = """Monitors the health and performance of an Axenita/Achilles installation by querying
2727
four API endpoints: ReadModel state, active user sessions, build information, and
@@ -268,7 +268,16 @@ def main():
268268
lib.base.oao(msg, state, perfdata, always_ok=args.ALWAYS_OK)
269269
axenita_ver = achilles_buildinfo['data']['version']
270270
msg += f'{axenita_ver} (timestamp {achilles_buildinfo["data"]["timestamp"]}), '
271-
axenita_ver = axenita_ver[8 : 8 + axenita_ver.find('-') - 1].replace('.', '')
271+
# the version field looks like "release-14.0.8-20210312134147-cf77b213090";
272+
# take the second hyphen-separated segment (the dotted version) and collapse
273+
# the dots to get a numeric perfdata value. the previous slice
274+
# `[8 : 8 + find('-') - 1]` happened to work only for exactly-6-char
275+
# versions like "14.0.8" and silently truncated multi-digit patches.
276+
version_parts = axenita_ver.split('-')
277+
if len(version_parts) >= 2:
278+
axenita_ver = version_parts[1].replace('.', '')
279+
else:
280+
axenita_ver = ''
272281
perfdata += lib.base.get_perfdata(
273282
'axenita-version',
274283
axenita_ver,

check-plugins/keycloak-version/keycloak-version

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import lib.version
2222
from lib.globals import STATE_UNKNOWN
2323

2424
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
25-
__version__ = '2026040801'
25+
__version__ = '2026041201'
2626

2727
DESCRIPTION = """Checks the installed Keycloak version against the endoflife.date API and alerts if the
2828
version is end-of-life or if newer major, minor, or patch releases are available. By
@@ -169,8 +169,14 @@ def get_installed_version(args):
169169
"""Fetch the Keycloak version. First, try to access the file system,
170170
then try to fetch the API.
171171
"""
172-
success, version = lib.disk.grep_file(args.PATH + '/version.txt', r'n (.*)')
173-
if success:
172+
# /opt/keycloak/version.txt contains a line like "Keycloak - Version 25.0.4".
173+
# The old regex `r'n (.*)'` happened to match the "n " in "Version" and
174+
# capture the rest of the line, but would silently break on any format
175+
# change. Match the version number explicitly instead.
176+
success, version = lib.disk.grep_file(
177+
args.PATH + '/version.txt', r'[Vv]ersion\s+(\d+(?:\.\d+)*)'
178+
)
179+
if success and version:
174180
return version
175181

176182
# Discover the OIDC endpoints for the realm (no authentication needed),

check-plugins/openvpn-version/openvpn-version

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import lib.version
2121
from lib.globals import STATE_UNKNOWN
2222

2323
__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
24-
__version__ = '2025100601'
24+
__version__ = '2026041201'
2525

2626
DESCRIPTION = """Checks the installed OpenVPN version against the endoflife.date API and alerts if the
2727
version is end-of-life or if newer major, minor, or patch releases are available. By
@@ -132,12 +132,13 @@ def get_installed_version(path):
132132
# OpenVPN 2.4.12 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Nov 10 2023
133133
# OpenVPN 2.5.11 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jul 18 2024
134134
# OpenVPN 2.6.13 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] [DCO]
135-
version_regex = r'N (\d+\.\d+\.\d+)'
136-
try:
137-
stdout = re.search(version_regex, stdout)
138-
return stdout.group(1).strip()
139-
except Exception:
135+
# The old regex `r'N (\d+\.\d+\.\d+)'` happened to match the "N" in
136+
# "OpenVPN" and capture the version, but would silently break on any
137+
# format change. Anchor on "OpenVPN" explicitly instead.
138+
match = re.search(r'OpenVPN\s+(\d+\.\d+\.\d+)', stdout)
139+
if not match:
140140
return ''
141+
return match.group(1).strip()
141142

142143

143144
def main():

0 commit comments

Comments
 (0)