Copilot commented on code in PR #3740: URL: https://github.com/apache/celeborn/pull/3740#discussion_r3548642749
########## master/src/main/scala/org/apache/celeborn/service/deploy/master/ApplicationMetricsSource.scala: ########## @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.celeborn.service.deploy.master + +import java.util.{Map => JMap} +import java.util.concurrent.TimeUnit + +import scala.collection.JavaConverters._ + +import org.apache.celeborn.common.CelebornConf +import org.apache.celeborn.common.internal.Logging +import org.apache.celeborn.common.metrics.{ClientMetric, MetricType} +import org.apache.celeborn.common.metrics.source.{AbstractSource, Role} +import org.apache.celeborn.common.util.{JavaUtils, Utils} + +class ApplicationMetricsSource(conf: CelebornConf) + extends AbstractSource(conf, Role.MASTER) with Logging { + override val sourceName = "application" + + private val masterClientMetricsEnabled = conf.masterClientMetricsEnabled + private val removedAppRetentionMs = conf.masterClientMetricsRemovedAppRetentionMs + + // Tracking applications that have been terminated + private val removedAppIds = + JavaUtils.newConcurrentHashMap[String, java.lang.Long]() + + if (masterClientMetricsEnabled) { + startRemovedAppCleaner() + } + + private def startRemovedAppCleaner(): Unit = { + val cleanTask: Runnable = new Runnable { + override def run(): Unit = Utils.tryLogNonFatalError { + val cutoff = System.currentTimeMillis() - removedAppRetentionMs + removedAppIds.entrySet().asScala.foreach { entry => + if (entry.getValue < cutoff) { + removedAppIds.remove(entry.getKey, entry.getValue) + } + } + } + } + metricsCleaner.scheduleWithFixedDelay( + cleanTask, + removedAppRetentionMs, + removedAppRetentionMs, + TimeUnit.MILLISECONDS) + } + + def updateApplicationMetrics( + appId: String, + metricLabels: Map[String, String], + metrics: JMap[String, ClientMetric]): Unit = { + if (!masterClientMetricsEnabled || metricLabels.isEmpty || removedAppIds.containsKey(appId)) { + return + } Review Comment: metricLabels from heartbeats are used directly when registering Prometheus metrics. Since label rendering does not escape values, a client can send invalid/unsafe label keys or values (e.g. containing quotes/newlines) and corrupt the master’s Prometheus endpoint for all scrapes. Validate/sanitize metricLabels on the master side before registering/updating metrics and drop (or reject) invalid label sets. ########## common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala: ########## @@ -6011,6 +6045,20 @@ object CelebornConf extends Logging { "Allowed pattern is: `<label1_key>=<label1_value>[,<label2_key>=<label2_value>]*`") .createWithDefault(Seq.empty) + val CLIENT_METRICS_APP_LABELS: ConfigEntry[Seq[String]] = + buildConf("celeborn.client.metrics.appLabels") + .categories("client", "metrics") + .doc("Custom metric labels sent from the client in each application heartbeat and applied " + + "to client metrics exposed on the master's Prometheus endpoint. " + + "Labels' pattern is: `<label1_key>=<label1_value>[,<label2_key>=<label2_value>]*`; e.g. `env=prod,version=1`") + .version("0.7.0") + .stringConf + .toSequence + .checkValue( + labels => labels.map(_ => Try(Utils.parseKeyValuePair(_))).forall(_.isSuccess), + "Allowed pattern is: `<label1_key>=<label1_value>[,<label2_key>=<label2_value>]*`") Review Comment: CLIENT_METRICS_APP_LABELS only validates the presence of a single '=' via Utils.parseKeyValuePair, but these labels are later rendered into Prometheus output as k="v" without any escaping. This means label keys that aren’t valid Prometheus identifiers or values containing quotes/backslashes/newlines can break the master’s /metrics output (and a malicious client can exploit this via heartbeat). Tighten validation here to enforce Prometheus-safe label keys/values. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
