Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Optionally hide rendered environment variables #798

Merged
merged 1 commit into from
Oct 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
@@ -95,7 +95,11 @@ lazy val configLib = Project("config", file("config"))
"CONFIG_FORCE_a__c" -> "3",
"CONFIG_FORCE_a___c" -> "4",
"CONFIG_FORCE_akka_version" -> "foo",
"CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10")
"CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10",
"SECRET_A" -> "A", // ConfigTest.renderShowEnvVariableValues
"SECRET_B" -> "B", // ConfigTest.renderShowEnvVariableValues
"SECRET_C" -> "C" // ConfigTest.renderShowEnvVariableValues
)

OsgiKeys.exportPackage := Seq("com.typesafe.config", "com.typesafe.config.impl")
publish := sys.error("use publishSigned instead of plain publish")
43 changes: 36 additions & 7 deletions config/src/main/java/com/typesafe/config/ConfigRenderOptions.java
Original file line number Diff line number Diff line change
@@ -21,13 +21,15 @@ public final class ConfigRenderOptions {
private final boolean comments;
private final boolean formatted;
private final boolean json;
private final boolean showEnvVariableValues;

private ConfigRenderOptions(boolean originComments, boolean comments, boolean formatted,
boolean json) {
boolean json, boolean showEnvVariableValues) {
this.originComments = originComments;
this.comments = comments;
this.formatted = formatted;
this.json = json;
this.showEnvVariableValues = showEnvVariableValues;
}

/**
@@ -38,7 +40,7 @@ private ConfigRenderOptions(boolean originComments, boolean comments, boolean fo
* @return the default render options
*/
public static ConfigRenderOptions defaults() {
return new ConfigRenderOptions(true, true, true, true);
return new ConfigRenderOptions(true, true, true, true, true);
}

/**
@@ -48,7 +50,7 @@ public static ConfigRenderOptions defaults() {
* @return the concise render options
*/
public static ConfigRenderOptions concise() {
return new ConfigRenderOptions(false, false, false, true);
return new ConfigRenderOptions(false, false, false, true, true);
}

/**
@@ -64,7 +66,7 @@ public ConfigRenderOptions setComments(boolean value) {
if (value == comments)
return this;
else
return new ConfigRenderOptions(originComments, value, formatted, json);
return new ConfigRenderOptions(originComments, value, formatted, json, showEnvVariableValues);
}

/**
@@ -97,7 +99,7 @@ public ConfigRenderOptions setOriginComments(boolean value) {
if (value == originComments)
return this;
else
return new ConfigRenderOptions(value, comments, formatted, json);
return new ConfigRenderOptions(value, comments, formatted, json, showEnvVariableValues);
}

/**
@@ -122,7 +124,7 @@ public ConfigRenderOptions setFormatted(boolean value) {
if (value == formatted)
return this;
else
return new ConfigRenderOptions(originComments, comments, value, json);
return new ConfigRenderOptions(originComments, comments, value, json, showEnvVariableValues);
}

/**
@@ -150,7 +152,32 @@ public ConfigRenderOptions setJson(boolean value) {
if (value == json)
return this;
else
return new ConfigRenderOptions(originComments, comments, formatted, value);
return new ConfigRenderOptions(originComments, comments, formatted, value, showEnvVariableValues);
}

/**
* Returns options with showEnvVariableValues toggled. This controls if values set from
* environment variables are included in the rendered string.
*
* @param value
* true to include environment variable values in the render
* @return options with requested setting for environment variables
*/
public ConfigRenderOptions setShowEnvVariableValues(boolean value) {
if (value == showEnvVariableValues)
return this;
else
return new ConfigRenderOptions(originComments, comments, formatted, json, value);
}

/**
* Returns whether the options enable rendering of environment variable values. This method is mostly used
* by the config lib internally, not by applications.
*
* @return true if environment variable values should be rendered
*/
public boolean getShowEnvVariableValues() {
return showEnvVariableValues;
}

/**
@@ -174,6 +201,8 @@ public String toString() {
sb.append("formatted,");
if (json)
sb.append("json,");
if (showEnvVariableValues)
sb.append("showEnvVariableValues,");
if (sb.charAt(sb.length() - 1) == ',')
sb.setLength(sb.length() - 1);
sb.append(")");
Original file line number Diff line number Diff line change
@@ -357,8 +357,20 @@ protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey
}

protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
Object u = unwrapped();
sb.append(u.toString());
if (hideEnvVariableValue(options)) {
sb.append("<env variable>");
} else {
Object u = unwrapped();
sb.append(u.toString());
}
}

protected boolean hideEnvVariableValue(ConfigRenderOptions options) {
return !options.getShowEnvVariableValues() && origin.originType() == OriginType.ENV_VARIABLE;
}

protected void appendHiddenEnvVariableValue(StringBuilder sb) {
sb.append("\"<env variable>\"");
}

@Override
Original file line number Diff line number Diff line change
@@ -342,7 +342,7 @@ public static void reloadSystemPropertiesConfig() {
}

private static AbstractConfigObject loadEnvVariables() {
return PropertiesParser.fromStringMap(newSimpleOrigin("env variables"), System.getenv());
return PropertiesParser.fromStringMap(newEnvVariable("env variables"), System.getenv());
}

private static class EnvVariablesHolder {
@@ -544,4 +544,8 @@ public static ConfigOrigin newFileOrigin(String filename) {
public static ConfigOrigin newURLOrigin(URL url) {
return SimpleConfigOrigin.newURL(url);
}

public static ConfigOrigin newEnvVariable(String description) {
return SimpleConfigOrigin.newEnvVariable(description);
}
}
16 changes: 10 additions & 6 deletions config/src/main/java/com/typesafe/config/impl/ConfigString.java
Original file line number Diff line number Diff line change
@@ -80,11 +80,15 @@ String transformToString() {

@Override
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
String rendered;
if (options.getJson())
rendered = ConfigImplUtil.renderJsonString(value);
else
rendered = ConfigImplUtil.renderStringUnquotedIfPossible(value);
sb.append(rendered);
if (hideEnvVariableValue(options)) {
appendHiddenEnvVariableValue(sb);
} else {
String rendered;
if (options.getJson())
rendered = ConfigImplUtil.renderJsonString(value);
else
rendered = ConfigImplUtil.renderStringUnquotedIfPossible(value);
sb.append(rendered);
}
}
}
Original file line number Diff line number Diff line change
@@ -5,5 +5,6 @@ enum OriginType {
GENERIC,
FILE,
URL,
RESOURCE
RESOURCE,
ENV_VARIABLE
}
Original file line number Diff line number Diff line change
@@ -77,6 +77,10 @@ static SimpleConfigOrigin newResource(String resource) {
return newResource(resource, null);
}

static SimpleConfigOrigin newEnvVariable(String description) {
return new SimpleConfigOrigin(description, -1, -1, OriginType.ENV_VARIABLE, null, null, null);
}

@Override
public SimpleConfigOrigin withLineNumber(int lineNumber) {
if (lineNumber == this.lineNumber && lineNumber == this.endLineNumber) {
@@ -139,6 +143,10 @@ public String description() {
}
}

OriginType originType() {
return originType;
}

@Override
public boolean equals(Object other) {
if (other instanceof SimpleConfigOrigin) {
@@ -484,7 +492,11 @@ static SimpleConfigOrigin fromFields(Map<SerializedField, Object> m) throws IOEx
Number originTypeOrdinal = (Number) m.get(SerializedField.ORIGIN_TYPE);
if (originTypeOrdinal == null)
throw new IOException("Missing ORIGIN_TYPE field");
OriginType originType = OriginType.values()[originTypeOrdinal.byteValue()];
OriginType originType;
if (originTypeOrdinal.byteValue() < OriginType.values().length)
originType = OriginType.values()[originTypeOrdinal.byteValue()];
else
originType = OriginType.GENERIC; // ENV_VARIABLE was added in a later version
String urlOrNull = (String) m.get(SerializedField.ORIGIN_URL);
String resourceOrNull = (String) m.get(SerializedField.ORIGIN_RESOURCE);
@SuppressWarnings("unchecked")
4 changes: 4 additions & 0 deletions config/src/test/resources/env-variables.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
secret = a
secret = ${?SECRET_A}
secrets = ["b", "c"]
secrets = [${?SECRET_B}, ${?SECRET_C}]
4 changes: 4 additions & 0 deletions config/src/test/scala/Rendering.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package foo;

import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigRenderOptions

@@ -6,11 +8,13 @@ object RenderExample extends App {
val originComments = args.contains("--origin-comments")
val comments = args.contains("--comments")
val hocon = args.contains("--hocon")
val hideEnvVariableValues = args.contains("--hide-env-variable-values")
val options = ConfigRenderOptions.defaults()
.setFormatted(formatted)
.setOriginComments(originComments)
.setComments(comments)
.setJson(!hocon)
.setShowEnvVariableValues(!hideEnvVariableValues)

def render(what: String) {
val conf = ConfigFactory.defaultOverrides()
29 changes: 29 additions & 0 deletions config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala
Original file line number Diff line number Diff line change
@@ -1207,6 +1207,35 @@ class ConfigTest extends TestUtils {
}
}

@Test
def renderShowEnvVariableValues(): Unit = {
val config = ConfigFactory.load("env-variables")
assertEquals("A", config.getString("secret"))
assertEquals("B", config.getStringList("secrets").get(0))
assertEquals("C", config.getStringList("secrets").get(1))
val hideRenderOpt = ConfigRenderOptions.defaults().setShowEnvVariableValues(false)
val rendered1 = config.root().render(hideRenderOpt)
assertTrue(rendered1.contains(""""secret" : "<env variable>""""))
assertTrue(rendered1.contains(
"""| "secrets" : [
| # env variables
| "<env variable>",
| # env variables
| "<env variable>"
| ]""".stripMargin))

val showRenderOpt = ConfigRenderOptions.defaults()
val rendered2 = config.root().render(showRenderOpt)
assertTrue(rendered2.contains(""""secret" : "A""""))
assertTrue(rendered2.contains(
"""| "secrets" : [
| # env variables
| "B",
| # env variables
| "C"
| ]""".stripMargin))
}

@Test
def serializeRoundTrip() {
for (i <- 1 to 10) {