diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/JavaSrc2Cpg.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/JavaSrc2Cpg.scala index 373806a112a9..1f0822bc2db9 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/JavaSrc2Cpg.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/JavaSrc2Cpg.scala @@ -50,4 +50,22 @@ object JavaSrc2Cpg { new JavaTypeHintCallLinker(cpg) ) } + + def showEnv(): Unit = { + val value = + JavaSrcEnvVar.values.foreach { envVar => + val currentValue = Option(System.getenv(envVar.name)).getOrElse("") + println(s"${envVar.name}:") + println(s"- Description : ${envVar.description}") + println(s"- Current value: $currentValue") + } + } + + enum JavaSrcEnvVar(val name: String, val description: String) { + case JdkPath + extends JavaSrcEnvVar( + "JAVASRC_JDK_PATH", + "Path to the JDK home used for retrieving type information about builtin Java types." + ) + } } diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/Main.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/Main.scala index 9ed16f78fbe7..63c8418e1430 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/Main.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/Main.scala @@ -14,7 +14,8 @@ final case class Config( delombokMode: Option[String] = None, enableTypeRecovery: Boolean = false, disableDummyTypes: Boolean = false, - jdkPath: Option[String] = None + jdkPath: Option[String] = None, + showEnv: Boolean = false ) extends X2CpgConfig[Config] { def withInferenceJarPaths(paths: Set[String]): Config = { copy(inferenceJarPaths = paths).withInheritedFields(this) @@ -47,6 +48,10 @@ final case class Config( def withJdkPath(path: String): Config = { copy(jdkPath = Some(path)).withInheritedFields(this) } + + def withShowEnv(value: Boolean): Config = { + copy(showEnv = value).withInheritedFields(this) + } } private object Frontend { @@ -84,14 +89,25 @@ private object Frontend { .text("disable generation of dummy types during type recovery"), opt[String]("jdk-path") .action((path, c) => c.withJdkPath(path)) - .text("JDK used for resolving builtin Java types. If not set, current classpath will be used") + .text("JDK used for resolving builtin Java types. If not set, current classpath will be used"), + opt[Unit]("show-env") + .action((_, c) => c.withShowEnv(true)) + // This should really be a print-and-exit but, with the current scopt setup, input paths + // are still required, so for now `javasrc2cpg --show-env ` is less confusing + // than `javasrc2cpg --show-env ` + .text("print information about environment variables used by javasrc2cpg prior to analysis") ) } } object Main extends X2CpgMain(cmdLineParser, new JavaSrc2Cpg()) { + def run(config: Config, javasrc2Cpg: JavaSrc2Cpg): Unit = { - javasrc2Cpg.run(config) + if (config.showEnv) { + JavaSrc2Cpg.showEnv() + } else { + javasrc2Cpg.run(config) + } } def getCmdLineParser = cmdLineParser diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreationPass.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreationPass.scala index 9380dff7f283..ba5bce57ec03 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreationPass.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreationPass.scala @@ -29,6 +29,7 @@ import com.github.javaparser.symbolsolver.resolution.typesolvers.ClassLoaderType import java.net.URLClassLoader import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver import io.joern.javasrc2cpg.typesolvers.CachingReflectionTypeSolver +import io.joern.javasrc2cpg.JavaSrc2Cpg.JavaSrcEnvVar case class SourceDirectoryInfo(typeSolverSourceDirs: List[String], sourceFiles: List[SourceFileInfo]) case class SplitDirectories(analysisSourceDir: String, typesSourceDir: String) @@ -90,10 +91,24 @@ class AstCreationPass(config: Config, cpg: Cpg, preCreatedAsts: Option[SplitJpAs val combinedTypeSolver = new SimpleCombinedTypeSolver() - val jdkPath = config.jdkPath.getOrElse { - val javaHome = System.getProperty("java.home") - logger.debug("No explicit jdkPath set in config, so using system java.home at $javaHome") - javaHome + val jdkPathFromEnvVar = Option(System.getenv(JavaSrcEnvVar.JdkPath.name)) + val jdkPath = (config.jdkPath, jdkPathFromEnvVar) match { + case (None, None) => + val javaHome = System.getProperty("java.home") + logger.info( + s"No explicit jdk-path set in config, so using system java.home for JDK type information: $javaHome" + ) + javaHome + + case (None, Some(jdkPath)) => + logger.info( + s"Using JDK path from environment variable ${JavaSrcEnvVar.JdkPath.name} for JDK type information: $jdkPath" + ) + jdkPath + + case (Some(jdkPath), _) => + logger.info(s"Using JDK path set with jdk-path option for JDK type information: $jdkPath") + jdkPath } combinedTypeSolver.add(JdkJarTypeSolver.fromJdkPath(jdkPath)) diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/typesolvers/noncaching/JdkJarTypeSolver.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/typesolvers/noncaching/JdkJarTypeSolver.scala index 5be3e0601e38..9b64772b083d 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/typesolvers/noncaching/JdkJarTypeSolver.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/typesolvers/noncaching/JdkJarTypeSolver.scala @@ -162,6 +162,9 @@ object JdkJarTypeSolver { def fromJdkPath(jdkPath: String): JdkJarTypeSolver = { val jarPaths = SourceFiles.determine(jdkPath, Set(JarExtension, JmodExtension)) + if (jarPaths.isEmpty) { + throw new IllegalArgumentException(s"No .jar or .jmod files found at JDK path ${jdkPath}") + } new JdkJarTypeSolver(jdkPath).withJars(jarPaths) }