|
|
a9ab27 |
From fa99814967c44e654aae64802947209e2817359f Mon Sep 17 00:00:00 2001
|
|
|
a9ab27 |
From: Severin Gehwolf <sgehwolf@redhat.com>
|
|
|
a9ab27 |
Date: Tue, 6 Dec 2022 17:32:30 +0100
|
|
|
a9ab27 |
Subject: [PATCH] Fix CVE-2022-1471 and add a test
|
|
|
a9ab27 |
|
|
|
a9ab27 |
---
|
|
|
a9ab27 |
.../java/io/prometheus/jmx/JmxCollector.java | 34 +++++++++----
|
|
|
a9ab27 |
.../io/prometheus/jmx/JmxCollectorTest.java | 51 +++++++++++++++++++
|
|
|
a9ab27 |
.../test/java/io/prometheus/jmx/Person.java | 38 ++++++++++++++
|
|
|
a9ab27 |
.../test/resources/testyaml_javabean.config | 6 +++
|
|
|
a9ab27 |
4 files changed, 119 insertions(+), 10 deletions(-)
|
|
|
a9ab27 |
create mode 100644 collector/src/test/java/io/prometheus/jmx/Person.java
|
|
|
a9ab27 |
create mode 100644 collector/src/test/resources/testyaml_javabean.config
|
|
|
a9ab27 |
|
|
|
a9ab27 |
diff --git a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java
|
|
|
a9ab27 |
index a5fc96f..38f13ed 100644
|
|
|
a9ab27 |
--- a/collector/src/main/java/io/prometheus/jmx/JmxCollector.java
|
|
|
a9ab27 |
+++ b/collector/src/main/java/io/prometheus/jmx/JmxCollector.java
|
|
|
a9ab27 |
@@ -1,12 +1,7 @@
|
|
|
a9ab27 |
package io.prometheus.jmx;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
-import io.prometheus.client.Collector;
|
|
|
a9ab27 |
-import io.prometheus.client.Counter;
|
|
|
a9ab27 |
-import org.yaml.snakeyaml.Yaml;
|
|
|
a9ab27 |
-import org.yaml.snakeyaml.error.YAMLException;
|
|
|
a9ab27 |
+import static java.lang.String.format;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
-import javax.management.MalformedObjectNameException;
|
|
|
a9ab27 |
-import javax.management.ObjectName;
|
|
|
a9ab27 |
import java.io.File;
|
|
|
a9ab27 |
import java.io.FileReader;
|
|
|
a9ab27 |
import java.io.IOException;
|
|
|
a9ab27 |
@@ -25,7 +20,19 @@ import java.util.logging.Logger;
|
|
|
a9ab27 |
import java.util.regex.Matcher;
|
|
|
a9ab27 |
import java.util.regex.Pattern;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
-import static java.lang.String.format;
|
|
|
a9ab27 |
+import javax.management.MalformedObjectNameException;
|
|
|
a9ab27 |
+import javax.management.ObjectName;
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.DumperOptions;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.LoaderOptions;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.Yaml;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.constructor.SafeConstructor;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.error.YAMLException;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.representer.Representer;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.resolver.Resolver;
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+import io.prometheus.client.Collector;
|
|
|
a9ab27 |
+import io.prometheus.client.Counter;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
public class JmxCollector extends Collector implements Collector.Describable {
|
|
|
a9ab27 |
static final Counter configReloadSuccess = Counter.build()
|
|
|
a9ab27 |
@@ -76,7 +83,7 @@ public class JmxCollector extends Collector implements Collector.Describable {
|
|
|
a9ab27 |
// will be thrown for bad configs
|
|
|
a9ab27 |
Map<String, Object> yamlConfig = null;
|
|
|
a9ab27 |
try {
|
|
|
a9ab27 |
- yamlConfig = (Map<String, Object>)new Yaml().load(new FileReader(in));
|
|
|
a9ab27 |
+ yamlConfig = (Map<String, Object>)createYamlInstance().load(new FileReader(in));
|
|
|
a9ab27 |
} catch (YAMLException e) {
|
|
|
a9ab27 |
System.err.println("YAML configuration error: " + e.getMessage());
|
|
|
a9ab27 |
throw new IllegalArgumentException(e);
|
|
|
a9ab27 |
@@ -86,7 +93,14 @@ public class JmxCollector extends Collector implements Collector.Describable {
|
|
|
a9ab27 |
}
|
|
|
a9ab27 |
|
|
|
a9ab27 |
public JmxCollector(String yamlConfig) throws MalformedObjectNameException {
|
|
|
a9ab27 |
- config = loadConfig((Map<String, Object>)new Yaml().load(yamlConfig));
|
|
|
a9ab27 |
+ config = loadConfig((Map<String, Object>)createYamlInstance().load(yamlConfig));
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ private static Yaml createYamlInstance() {
|
|
|
a9ab27 |
+ // Equivalent to default Yaml() constructor but using SafeConstructor()
|
|
|
a9ab27 |
+ // over Constructor();
|
|
|
a9ab27 |
+ return new Yaml(new SafeConstructor(), new Representer(), new DumperOptions(), new LoaderOptions(),
|
|
|
a9ab27 |
+ new Resolver());
|
|
|
a9ab27 |
}
|
|
|
a9ab27 |
|
|
|
a9ab27 |
private void reloadConfig() {
|
|
|
a9ab27 |
@@ -94,7 +108,7 @@ public class JmxCollector extends Collector implements Collector.Describable {
|
|
|
a9ab27 |
FileReader fr = new FileReader(configFile);
|
|
|
a9ab27 |
|
|
|
a9ab27 |
try {
|
|
|
a9ab27 |
- Map<String, Object> newYamlConfig = (Map<String, Object>)new Yaml().load(fr);
|
|
|
a9ab27 |
+ Map<String, Object> newYamlConfig = (Map<String, Object>)createYamlInstance().load(fr);
|
|
|
a9ab27 |
config = loadConfig(newYamlConfig);
|
|
|
a9ab27 |
config.lastUpdate = configFile.lastModified();
|
|
|
a9ab27 |
configReloadSuccess.inc();
|
|
|
a9ab27 |
diff --git a/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java b/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java
|
|
|
a9ab27 |
index 3d4ecf0..f499fb3 100644
|
|
|
a9ab27 |
--- a/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java
|
|
|
a9ab27 |
+++ b/collector/src/test/java/io/prometheus/jmx/JmxCollectorTest.java
|
|
|
a9ab27 |
@@ -8,12 +8,15 @@ import static org.junit.Assert.fail;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
import java.io.File;
|
|
|
a9ab27 |
import java.lang.management.ManagementFactory;
|
|
|
a9ab27 |
+import java.nio.file.Files;
|
|
|
a9ab27 |
+import java.util.stream.Collectors;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
import javax.management.MBeanServer;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
import org.junit.Before;
|
|
|
a9ab27 |
import org.junit.BeforeClass;
|
|
|
a9ab27 |
import org.junit.Test;
|
|
|
a9ab27 |
+import org.yaml.snakeyaml.constructor.ConstructorException;
|
|
|
a9ab27 |
import org.yaml.snakeyaml.error.YAMLException;
|
|
|
a9ab27 |
|
|
|
a9ab27 |
import io.prometheus.client.Collector;
|
|
|
a9ab27 |
@@ -272,4 +275,52 @@ public class JmxCollectorTest {
|
|
|
a9ab27 |
assertEquals(prefix + "Number of aliases for non-scalar nodes exceeds the specified max=50", e.getMessage());
|
|
|
a9ab27 |
}
|
|
|
a9ab27 |
}
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ @Test
|
|
|
a9ab27 |
+ public void javaBeanLoadTestFile() throws Exception {
|
|
|
a9ab27 |
+ try {
|
|
|
a9ab27 |
+ File configFile = new File(getClass().getResource("/testyaml_javabean.config").getPath());
|
|
|
a9ab27 |
+ doJavaBeanLoadTest(configFile, false);
|
|
|
a9ab27 |
+ } finally {
|
|
|
a9ab27 |
+ Person.fixtureReset();
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ @Test
|
|
|
a9ab27 |
+ public void javaBeanLoadTestString() throws Exception {
|
|
|
a9ab27 |
+ try {
|
|
|
a9ab27 |
+ File configFile = new File(getClass().getResource("/testyaml_javabean.config").getPath());
|
|
|
a9ab27 |
+ doJavaBeanLoadTest(configFile, true);
|
|
|
a9ab27 |
+ } finally {
|
|
|
a9ab27 |
+ Person.fixtureReset();
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ private void doJavaBeanLoadTest(File configFile, boolean passAsString) throws Exception {
|
|
|
a9ab27 |
+ assertTrue(configFile.exists());
|
|
|
a9ab27 |
+ assertEquals(0, Person.loadCount.get());
|
|
|
a9ab27 |
+ try {
|
|
|
a9ab27 |
+ if (passAsString) {
|
|
|
a9ab27 |
+ String config = Files.readAllLines(configFile.toPath()).stream().collect(Collectors.joining("\n"));
|
|
|
a9ab27 |
+ new JmxCollector(config);
|
|
|
a9ab27 |
+ } else {
|
|
|
a9ab27 |
+ new JmxCollector(configFile);
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+ assertEquals("Expected Person class to not be instantiated", 0, Person.loadCount.get());
|
|
|
a9ab27 |
+ } catch (ConstructorException e) {
|
|
|
a9ab27 |
+ String problem = e.getProblem();
|
|
|
a9ab27 |
+ assertTrue(problem.contains(Person.class.getName()));
|
|
|
a9ab27 |
+ assertTrue(problem.contains("could not determine a constructor"));
|
|
|
a9ab27 |
+ } catch (IllegalArgumentException e) {
|
|
|
a9ab27 |
+ Throwable cause = e.getCause();
|
|
|
a9ab27 |
+ if (cause instanceof ConstructorException) {
|
|
|
a9ab27 |
+ ConstructorException ce = (ConstructorException) cause;
|
|
|
a9ab27 |
+ String problem = ce.getProblem();
|
|
|
a9ab27 |
+ assertTrue(problem.contains(Person.class.getName()));
|
|
|
a9ab27 |
+ assertTrue(problem.contains("could not determine a constructor"));
|
|
|
a9ab27 |
+ } else {
|
|
|
a9ab27 |
+ fail("Expected ConstructorException as cause!");
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
}
|
|
|
a9ab27 |
diff --git a/collector/src/test/java/io/prometheus/jmx/Person.java b/collector/src/test/java/io/prometheus/jmx/Person.java
|
|
|
a9ab27 |
new file mode 100644
|
|
|
a9ab27 |
index 0000000..e3b339a
|
|
|
a9ab27 |
--- /dev/null
|
|
|
a9ab27 |
+++ b/collector/src/test/java/io/prometheus/jmx/Person.java
|
|
|
a9ab27 |
@@ -0,0 +1,38 @@
|
|
|
a9ab27 |
+package io.prometheus.jmx;
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+/**
|
|
|
a9ab27 |
+ * Simple java bean (used by yaml load test)
|
|
|
a9ab27 |
+ *
|
|
|
a9ab27 |
+ */
|
|
|
a9ab27 |
+public class Person {
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ public static final AtomicInteger loadCount = new AtomicInteger(0);
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ private String firstName;
|
|
|
a9ab27 |
+ private String lastName;
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ public String getFirstName() {
|
|
|
a9ab27 |
+ return firstName;
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ public void setFirstName(String firstName) {
|
|
|
a9ab27 |
+ loadCount.incrementAndGet();
|
|
|
a9ab27 |
+ this.firstName = firstName;
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ public String getLastName() {
|
|
|
a9ab27 |
+ return lastName;
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ public void setLastName(String lastName) {
|
|
|
a9ab27 |
+ loadCount.incrementAndGet();
|
|
|
a9ab27 |
+ this.lastName = lastName;
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+ public static void fixtureReset() {
|
|
|
a9ab27 |
+ loadCount.set(0);
|
|
|
a9ab27 |
+ }
|
|
|
a9ab27 |
+
|
|
|
a9ab27 |
+}
|
|
|
a9ab27 |
diff --git a/collector/src/test/resources/testyaml_javabean.config b/collector/src/test/resources/testyaml_javabean.config
|
|
|
a9ab27 |
new file mode 100644
|
|
|
a9ab27 |
index 0000000..6552d83
|
|
|
a9ab27 |
--- /dev/null
|
|
|
a9ab27 |
+++ b/collector/src/test/resources/testyaml_javabean.config
|
|
|
a9ab27 |
@@ -0,0 +1,6 @@
|
|
|
a9ab27 |
+lowercaseOutputName: true
|
|
|
a9ab27 |
+lowercaseOutputLabelNames: true
|
|
|
a9ab27 |
+blacklistObjectNames:
|
|
|
a9ab27 |
+# handled by agent's default exporter
|
|
|
a9ab27 |
+- "java.lang:*"
|
|
|
a9ab27 |
+other: !!io.prometheus.jmx.Person { firstName: Andrey, lastName: Tool }
|
|
|
a9ab27 |
--
|
|
|
a9ab27 |
2.38.1
|
|
|
a9ab27 |
|