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 <moktamd@users.noreply.github.com>
This commit is contained in:
moktamd
2026-03-31 11:20:40 +00:00
parent f8cc0adefb
commit 1c33307a59
3 changed files with 25 additions and 5 deletions
@@ -47,10 +47,10 @@ class AppsInfo implements IMetricFamily {
#[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());
}
}
+9
View File
@@ -19,9 +19,18 @@ final readonly class Metric {
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 . '"');
}
}
}
}
@@ -87,6 +87,17 @@ class OpenMetricsControllerTest extends TestCase {
$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')