From 1c33307a5992a78f35f1c82be75e068cae363e3a Mon Sep 17 00:00:00 2001 From: moktamd Date: Tue, 31 Mar 2026 11:20:40 +0000 Subject: [PATCH] fix(metrics): validate label names in Metric, sanitize in AppsInfo Add validation in the Metric constructor that rejects invalid OpenMetrics label names with InvalidArgumentException. Sanitize app IDs at the source in AppsInfo by replacing hyphens with underscores before creating the Metric. Fixes nextcloud/server#59247 Signed-off-by: moktamd --- lib/private/OpenMetrics/Exporters/AppsInfo.php | 10 +++++----- lib/public/OpenMetrics/Metric.php | 9 +++++++++ tests/Core/Controller/OpenMetricsControllerTest.php | 11 +++++++++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/private/OpenMetrics/Exporters/AppsInfo.php b/lib/private/OpenMetrics/Exporters/AppsInfo.php index 33597795e7d51..0910007c10a58 100644 --- a/lib/private/OpenMetrics/Exporters/AppsInfo.php +++ b/lib/private/OpenMetrics/Exporters/AppsInfo.php @@ -47,10 +47,10 @@ public function help(): string { #[Override] public function metrics(): Generator { - yield new Metric( - 1, - $this->appManager->getAppInstalledVersions(true), - time() - ); + $apps = []; + foreach ($this->appManager->getAppInstalledVersions(true) as $appId => $version) { + $apps[str_replace('-', '_', $appId)] = $version; + } + yield new Metric(1, $apps, time()); } } diff --git a/lib/public/OpenMetrics/Metric.php b/lib/public/OpenMetrics/Metric.php index 47e925b01b75f..b5deb324db41f 100644 --- a/lib/public/OpenMetrics/Metric.php +++ b/lib/public/OpenMetrics/Metric.php @@ -19,9 +19,18 @@ public function __construct( public array $labels = [], public int|float|null $timestamp = null, ) { + $this->validateLabels(); } public function label(string $name): ?string { return $this->labels[$name] ?? null; } + + private function validateLabels(): void { + foreach ($this->labels as $label => $_value) { + if (preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', (string)$label) !== 1) { + throw new \InvalidArgumentException('Invalid OpenMetrics label name: "' . $label . '"'); + } + } + } } diff --git a/tests/Core/Controller/OpenMetricsControllerTest.php b/tests/Core/Controller/OpenMetricsControllerTest.php index fdc81ad5d2c0e..7832afeb40092 100644 --- a/tests/Core/Controller/OpenMetricsControllerTest.php +++ b/tests/Core/Controller/OpenMetricsControllerTest.php @@ -87,6 +87,17 @@ public function testGetMetrics(): void { $this->assertStringMatchesFormat($expected, $fullOutput); } + public function testMetricRejectsInvalidLabelNames(): void { + $this->expectException(\InvalidArgumentException::class); + new Metric(1, ['hide-photos' => '1.0.0']); + } + + public function testMetricAcceptsValidLabelNames(): void { + $metric = new Metric(1, ['hide_photos' => '1.0.0', 'normal_app' => '2.0.0']); + $this->assertEquals('1.0.0', $metric->label('hide_photos')); + $this->assertEquals('2.0.0', $metric->label('normal_app')); + } + public function testGetMetricsFromForbiddenIp(): void { $this->config->expects($this->once()) ->method('getSystemValue')