Skip to content

Commit

Permalink
fix: Optionally hide rendered environment variables (#798)
Browse files Browse the repository at this point in the history
* new OriginType.ENV_VARIABLE
* deserialization of unknown originType ordinal, but this doesn't really
  help for compatibility if serialized with new version and deserialized
  with old version
  • Loading branch information
patriknw committed Oct 17, 2023
1 parent 5d6a466 commit 3a4ebbf
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 19 deletions.
6 changes: 5 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
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
Expand Up @@ -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;
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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;
}

/**
Expand All @@ -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(")");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Up @@ -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
Expand Up @@ -5,5 +5,6 @@ enum OriginType {
GENERIC,
FILE,
URL,
RESOURCE
RESOURCE,
ENV_VARIABLE
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -139,6 +143,10 @@ public String description() {
}
}

OriginType originType() {
return originType;
}

@Override
public boolean equals(Object other) {
if (other instanceof SimpleConfigOrigin) {
Expand Down Expand Up @@ -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")
Expand Down
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

Expand All @@ -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()
Expand Down
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
Expand Up @@ -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) {
Expand Down

0 comments on commit 3a4ebbf

Please sign in to comment.