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]

Reply via email to