From 3d8fce4edb95333ca37a7aaa49980a153620e0da Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov Date: Mon, 6 Dec 2021 13:51:48 +0300 Subject: [PATCH] [Native][tests] Log environment variables and JVM properties --- .../konan/blackboxtest/support/LoggedData.kt | 23 ++++- .../support/util/SafeEnvironment.kt | 96 +++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/SafeEnvironment.kt diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/LoggedData.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/LoggedData.kt index 5fb62039f6d..a7cdbab02af 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/LoggedData.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/LoggedData.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.konan.blackboxtest.support import org.jetbrains.kotlin.cli.common.ExitCode import org.jetbrains.kotlin.konan.blackboxtest.support.runner.RunResult +import org.jetbrains.kotlin.konan.blackboxtest.support.util.SafeEnvVars +import org.jetbrains.kotlin.konan.blackboxtest.support.util.SafeProperties import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.io.File import kotlin.time.Duration @@ -29,9 +31,26 @@ internal abstract class LoggedData { appendLine(this@LoggedData) } + class JVMEnvironment : LoggedData() { + private val properties = SafeProperties() + + override fun computeText() = buildString { + appendLine("ENVIRONMENT VARIABLES:") + SafeEnvVars.forEach { (name, safeValue) -> + append("- ").append(name).append(" = ").appendLine(safeValue) + } + appendLine() + appendLine("JVM PROPERTIES:") + properties.forEach { (name, safeValue) -> + append("- ").append(name).append(" = ").appendLine(safeValue) + } + } + } + class CompilerParameters( private val compilerArgs: Array, - private val sourceModules: Collection + private val sourceModules: Collection, + private val environment: JVMEnvironment = JVMEnvironment() // Capture environment. ) : LoggedData() { private val testDataFiles: List get() = buildList { @@ -44,6 +63,8 @@ internal abstract class LoggedData { override fun computeText() = buildString { appendArguments("COMPILER ARGUMENTS:", listOf("\$\$kotlinc-native\$\$") + compilerArgs) + appendLine() + appendLine(environment) val testDataFiles = testDataFiles if (testDataFiles.isNotEmpty()) { diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/SafeEnvironment.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/SafeEnvironment.kt new file mode 100644 index 00000000000..c5ed94a5aad --- /dev/null +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/blackboxtest/support/util/SafeEnvironment.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.konan.blackboxtest.support.util + +import com.intellij.openapi.util.text.StringUtil.containsLineBreak +import com.intellij.openapi.util.text.StringUtil.escapeLineBreak +import java.util.* + +internal data class NameAndSafeValue(val name: String, val safeValue: String) + +internal object SafeEnvVars : Iterable { + private val environment: List by lazy { + buildList { + System.getenv().forEach { (name, value) -> + val safeValue = if (isSafeEnvVar(name)) doEscape(value) else HIDDEN_VALUE + this += NameAndSafeValue(name, safeValue) + } + sortBy { it.name } + } + } + + override fun iterator() = environment.iterator() + + private fun isSafeEnvVar(name: String): Boolean { + if (name in SAFE_ENV_VARS) return true + if (isUnsafeVariableName(name)) return false + + return KONAN_WORD in name + || SAFE_ENV_VAR_PREFIXES.any { prefix -> name.startsWith(prefix, ignoreCase = true) } + || SAFE_ENV_VAR_SUFFIXES.any { suffix -> name.endsWith(suffix, ignoreCase = true) } + } + + private const val KONAN_WORD = "KONAN" + + private val SAFE_ENV_VARS = setOf("PATH", "USER", "LANG", "PWD", "TEMP", "TMP") + private val SAFE_ENV_VAR_PREFIXES = listOf("JAVA_", "JDK_") + private val SAFE_ENV_VAR_SUFFIXES = listOf("DIR", "DIRS", "HOME", "ROOT", "PATH", "FILE") +} + +internal class SafeProperties : Iterable { + // Properties are mutable. So, need to capture the current values. + private val properties: Map = TreeMap().apply { + System.getProperties().forEach { (name, value) -> + this[name.toString()] = value.toString() + } + } + + override fun iterator() = properties.map { (name, value) -> + val safeValue = if (isSafeProperty(name)) doEscape(value) else HIDDEN_VALUE + NameAndSafeValue(name, safeValue) + }.iterator() + + companion object { + private fun isSafeProperty(name: String): Boolean { + if (name in SUPPRESSED_PROPERTIES || isUnsafeVariableName(name)) return false + + return SAFE_PROPERTY_PREFIXES.any { prefix -> name.startsWith(prefix) } + || SAFE_PROPERTY_SUFFIXES.any { suffix -> name.endsWith(suffix) } + } + + private val SUPPRESSED_PROPERTIES = setOf("java.class.path") // Too long. Makes logs poorly readable. + private val SAFE_PROPERTY_PREFIXES = listOf( + "ast.", + "file.", + "ide.", + "idea.", + "java.", + "jdk.", + "kotlin.", + "org.gradle.", + "org.jetbrains.", + "os.", + "psi.", + "user." + ) + private val SAFE_PROPERTY_SUFFIXES = listOf(".separator") + } +} + +private const val HIDDEN_VALUE = "" + +private val UNSAFE_VARIABLE_PARTS = listOf("key", "token", "secret", "private", "pass", "password", "passphrase") + +private fun isUnsafeVariableName(name: String) = name + .splitToSequence('.', '_') + .any { namePart -> + UNSAFE_VARIABLE_PARTS.any { unsafeVariableNamePart -> + namePart.startsWith(unsafeVariableNamePart, ignoreCase = true) + || namePart.endsWith(unsafeVariableNamePart, ignoreCase = true) + } + } + +private fun doEscape(value: String) = if (containsLineBreak(value)) escapeLineBreak(value) else value