diff --git a/agent/src/main/java/com/appland/appmap/Agent.java b/agent/src/main/java/com/appland/appmap/Agent.java index 3de6be8c..1dc9466c 100644 --- a/agent/src/main/java/com/appland/appmap/Agent.java +++ b/agent/src/main/java/com/appland/appmap/Agent.java @@ -42,6 +42,18 @@ public class Agent { public static final TaggedLogger logger = AppMapConfig.getLogger(null); + /** + * Returns only the {@code appmap.*} system properties from {@code props}, so that arbitrary + * properties (e.g. secrets passed via {@code -Dpassword=...}) never end up in the log file. + */ + static java.util.Properties filterAppMapProperties(java.util.Properties props) { + java.util.Properties appmapProperties = new java.util.Properties(); + props.stringPropertyNames().stream() + .filter(name -> name.startsWith("appmap.")) + .forEach(name -> appmapProperties.setProperty(name, props.getProperty(name))); + return appmapProperties; + } + /** * premain is the entry point for the AppMap Java agent. * @@ -72,7 +84,10 @@ public static void premain(String agentArgs, Instrumentation inst) { logger.info("Agent version {}, current time mills: {}", implementationVersion, start); logger.info("config: {}", AppMapConfig.get()); - logger.debug("System properties: {}", System.getProperties()); + // Only log AppMap's own system properties, never the full set: arbitrary + // properties (e.g. -Dpassword=... on the command line) must never end up + // in the log file. + logger.debug("AppMap system properties: {}", () -> filterAppMapProperties(System.getProperties())); if (Agent.class.getClassLoader() == null) { logger.warn("AppMap agent is running on the bootstrap classpath. This is not a recommended configuration and should only be used for troubleshooting. Git integration will be disabled."); diff --git a/agent/src/test/java/com/appland/appmap/AgentTest.java b/agent/src/test/java/com/appland/appmap/AgentTest.java new file mode 100644 index 00000000..a627ba3a --- /dev/null +++ b/agent/src/test/java/com/appland/appmap/AgentTest.java @@ -0,0 +1,41 @@ +package com.appland.appmap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Properties; + +import org.junit.jupiter.api.Test; + +public class AgentTest { + + @Test + void filterAppMapPropertiesKeepsOnlyAppMapKeys() { + Properties props = new Properties(); + props.setProperty("appmap.debug", "true"); + props.setProperty("appmap.output.directory", "/tmp/appmap"); + props.setProperty("password", "hunter2"); + props.setProperty("user.home", "/home/user"); + props.setProperty("javax.net.ssl.trustStorePassword", "secret"); + + Properties filtered = Agent.filterAppMapProperties(props); + + assertEquals(2, filtered.size()); + assertEquals("true", filtered.getProperty("appmap.debug")); + assertEquals("/tmp/appmap", filtered.getProperty("appmap.output.directory")); + assertFalse(filtered.containsKey("password")); + assertFalse(filtered.containsKey("user.home")); + assertFalse(filtered.containsKey("javax.net.ssl.trustStorePassword")); + } + + @Test + void filterAppMapPropertiesReturnsEmptyWhenNoneMatch() { + Properties props = new Properties(); + props.setProperty("password", "hunter2"); + + Properties filtered = Agent.filterAppMapProperties(props); + + assertTrue(filtered.isEmpty()); + } +}