Skip to content

Commit 3a4ebbf

Browse files
authored
fix: Optionally hide rendered environment variables (#798)
* 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
1 parent 5d6a466 commit 3a4ebbf

File tree

10 files changed

+122
-19
lines changed

10 files changed

+122
-19
lines changed

build.sbt

+5-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,11 @@ lazy val configLib = Project("config", file("config"))
9595
"CONFIG_FORCE_a__c" -> "3",
9696
"CONFIG_FORCE_a___c" -> "4",
9797
"CONFIG_FORCE_akka_version" -> "foo",
98-
"CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10")
98+
"CONFIG_FORCE_akka_event__handler__dispatcher_max__pool__size" -> "10",
99+
"SECRET_A" -> "A", // ConfigTest.renderShowEnvVariableValues
100+
"SECRET_B" -> "B", // ConfigTest.renderShowEnvVariableValues
101+
"SECRET_C" -> "C" // ConfigTest.renderShowEnvVariableValues
102+
)
99103

100104
OsgiKeys.exportPackage := Seq("com.typesafe.config", "com.typesafe.config.impl")
101105
publish := sys.error("use publishSigned instead of plain publish")

config/src/main/java/com/typesafe/config/ConfigRenderOptions.java

+36-7
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ public final class ConfigRenderOptions {
2121
private final boolean comments;
2222
private final boolean formatted;
2323
private final boolean json;
24+
private final boolean showEnvVariableValues;
2425

2526
private ConfigRenderOptions(boolean originComments, boolean comments, boolean formatted,
26-
boolean json) {
27+
boolean json, boolean showEnvVariableValues) {
2728
this.originComments = originComments;
2829
this.comments = comments;
2930
this.formatted = formatted;
3031
this.json = json;
32+
this.showEnvVariableValues = showEnvVariableValues;
3133
}
3234

3335
/**
@@ -38,7 +40,7 @@ private ConfigRenderOptions(boolean originComments, boolean comments, boolean fo
3840
* @return the default render options
3941
*/
4042
public static ConfigRenderOptions defaults() {
41-
return new ConfigRenderOptions(true, true, true, true);
43+
return new ConfigRenderOptions(true, true, true, true, true);
4244
}
4345

4446
/**
@@ -48,7 +50,7 @@ public static ConfigRenderOptions defaults() {
4850
* @return the concise render options
4951
*/
5052
public static ConfigRenderOptions concise() {
51-
return new ConfigRenderOptions(false, false, false, true);
53+
return new ConfigRenderOptions(false, false, false, true, true);
5254
}
5355

5456
/**
@@ -64,7 +66,7 @@ public ConfigRenderOptions setComments(boolean value) {
6466
if (value == comments)
6567
return this;
6668
else
67-
return new ConfigRenderOptions(originComments, value, formatted, json);
69+
return new ConfigRenderOptions(originComments, value, formatted, json, showEnvVariableValues);
6870
}
6971

7072
/**
@@ -97,7 +99,7 @@ public ConfigRenderOptions setOriginComments(boolean value) {
9799
if (value == originComments)
98100
return this;
99101
else
100-
return new ConfigRenderOptions(value, comments, formatted, json);
102+
return new ConfigRenderOptions(value, comments, formatted, json, showEnvVariableValues);
101103
}
102104

103105
/**
@@ -122,7 +124,7 @@ public ConfigRenderOptions setFormatted(boolean value) {
122124
if (value == formatted)
123125
return this;
124126
else
125-
return new ConfigRenderOptions(originComments, comments, value, json);
127+
return new ConfigRenderOptions(originComments, comments, value, json, showEnvVariableValues);
126128
}
127129

128130
/**
@@ -150,7 +152,32 @@ public ConfigRenderOptions setJson(boolean value) {
150152
if (value == json)
151153
return this;
152154
else
153-
return new ConfigRenderOptions(originComments, comments, formatted, value);
155+
return new ConfigRenderOptions(originComments, comments, formatted, value, showEnvVariableValues);
156+
}
157+
158+
/**
159+
* Returns options with showEnvVariableValues toggled. This controls if values set from
160+
* environment variables are included in the rendered string.
161+
*
162+
* @param value
163+
* true to include environment variable values in the render
164+
* @return options with requested setting for environment variables
165+
*/
166+
public ConfigRenderOptions setShowEnvVariableValues(boolean value) {
167+
if (value == showEnvVariableValues)
168+
return this;
169+
else
170+
return new ConfigRenderOptions(originComments, comments, formatted, json, value);
171+
}
172+
173+
/**
174+
* Returns whether the options enable rendering of environment variable values. This method is mostly used
175+
* by the config lib internally, not by applications.
176+
*
177+
* @return true if environment variable values should be rendered
178+
*/
179+
public boolean getShowEnvVariableValues() {
180+
return showEnvVariableValues;
154181
}
155182

156183
/**
@@ -174,6 +201,8 @@ public String toString() {
174201
sb.append("formatted,");
175202
if (json)
176203
sb.append("json,");
204+
if (showEnvVariableValues)
205+
sb.append("showEnvVariableValues,");
177206
if (sb.charAt(sb.length() - 1) == ',')
178207
sb.setLength(sb.length() - 1);
179208
sb.append(")");

config/src/main/java/com/typesafe/config/impl/AbstractConfigValue.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,20 @@ protected void render(StringBuilder sb, int indent, boolean atRoot, String atKey
357357
}
358358

359359
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
360-
Object u = unwrapped();
361-
sb.append(u.toString());
360+
if (hideEnvVariableValue(options)) {
361+
sb.append("<env variable>");
362+
} else {
363+
Object u = unwrapped();
364+
sb.append(u.toString());
365+
}
366+
}
367+
368+
protected boolean hideEnvVariableValue(ConfigRenderOptions options) {
369+
return !options.getShowEnvVariableValues() && origin.originType() == OriginType.ENV_VARIABLE;
370+
}
371+
372+
protected void appendHiddenEnvVariableValue(StringBuilder sb) {
373+
sb.append("\"<env variable>\"");
362374
}
363375

364376
@Override

config/src/main/java/com/typesafe/config/impl/ConfigImpl.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ public static void reloadSystemPropertiesConfig() {
342342
}
343343

344344
private static AbstractConfigObject loadEnvVariables() {
345-
return PropertiesParser.fromStringMap(newSimpleOrigin("env variables"), System.getenv());
345+
return PropertiesParser.fromStringMap(newEnvVariable("env variables"), System.getenv());
346346
}
347347

348348
private static class EnvVariablesHolder {
@@ -544,4 +544,8 @@ public static ConfigOrigin newFileOrigin(String filename) {
544544
public static ConfigOrigin newURLOrigin(URL url) {
545545
return SimpleConfigOrigin.newURL(url);
546546
}
547+
548+
public static ConfigOrigin newEnvVariable(String description) {
549+
return SimpleConfigOrigin.newEnvVariable(description);
550+
}
547551
}

config/src/main/java/com/typesafe/config/impl/ConfigString.java

+10-6
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,15 @@ String transformToString() {
8080

8181
@Override
8282
protected void render(StringBuilder sb, int indent, boolean atRoot, ConfigRenderOptions options) {
83-
String rendered;
84-
if (options.getJson())
85-
rendered = ConfigImplUtil.renderJsonString(value);
86-
else
87-
rendered = ConfigImplUtil.renderStringUnquotedIfPossible(value);
88-
sb.append(rendered);
83+
if (hideEnvVariableValue(options)) {
84+
appendHiddenEnvVariableValue(sb);
85+
} else {
86+
String rendered;
87+
if (options.getJson())
88+
rendered = ConfigImplUtil.renderJsonString(value);
89+
else
90+
rendered = ConfigImplUtil.renderStringUnquotedIfPossible(value);
91+
sb.append(rendered);
92+
}
8993
}
9094
}

config/src/main/java/com/typesafe/config/impl/OriginType.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ enum OriginType {
55
GENERIC,
66
FILE,
77
URL,
8-
RESOURCE
8+
RESOURCE,
9+
ENV_VARIABLE
910
}

config/src/main/java/com/typesafe/config/impl/SimpleConfigOrigin.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ static SimpleConfigOrigin newResource(String resource) {
7777
return newResource(resource, null);
7878
}
7979

80+
static SimpleConfigOrigin newEnvVariable(String description) {
81+
return new SimpleConfigOrigin(description, -1, -1, OriginType.ENV_VARIABLE, null, null, null);
82+
}
83+
8084
@Override
8185
public SimpleConfigOrigin withLineNumber(int lineNumber) {
8286
if (lineNumber == this.lineNumber && lineNumber == this.endLineNumber) {
@@ -139,6 +143,10 @@ public String description() {
139143
}
140144
}
141145

146+
OriginType originType() {
147+
return originType;
148+
}
149+
142150
@Override
143151
public boolean equals(Object other) {
144152
if (other instanceof SimpleConfigOrigin) {
@@ -484,7 +492,11 @@ static SimpleConfigOrigin fromFields(Map<SerializedField, Object> m) throws IOEx
484492
Number originTypeOrdinal = (Number) m.get(SerializedField.ORIGIN_TYPE);
485493
if (originTypeOrdinal == null)
486494
throw new IOException("Missing ORIGIN_TYPE field");
487-
OriginType originType = OriginType.values()[originTypeOrdinal.byteValue()];
495+
OriginType originType;
496+
if (originTypeOrdinal.byteValue() < OriginType.values().length)
497+
originType = OriginType.values()[originTypeOrdinal.byteValue()];
498+
else
499+
originType = OriginType.GENERIC; // ENV_VARIABLE was added in a later version
488500
String urlOrNull = (String) m.get(SerializedField.ORIGIN_URL);
489501
String resourceOrNull = (String) m.get(SerializedField.ORIGIN_RESOURCE);
490502
@SuppressWarnings("unchecked")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
secret = a
2+
secret = ${?SECRET_A}
3+
secrets = ["b", "c"]
4+
secrets = [${?SECRET_B}, ${?SECRET_C}]

config/src/test/scala/Rendering.scala

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package foo;
2+
13
import com.typesafe.config.ConfigFactory
24
import com.typesafe.config.ConfigRenderOptions
35

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

1519
def render(what: String) {
1620
val conf = ConfigFactory.defaultOverrides()

config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala

+29
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,35 @@ class ConfigTest extends TestUtils {
12071207
}
12081208
}
12091209

1210+
@Test
1211+
def renderShowEnvVariableValues(): Unit = {
1212+
val config = ConfigFactory.load("env-variables")
1213+
assertEquals("A", config.getString("secret"))
1214+
assertEquals("B", config.getStringList("secrets").get(0))
1215+
assertEquals("C", config.getStringList("secrets").get(1))
1216+
val hideRenderOpt = ConfigRenderOptions.defaults().setShowEnvVariableValues(false)
1217+
val rendered1 = config.root().render(hideRenderOpt)
1218+
assertTrue(rendered1.contains(""""secret" : "<env variable>""""))
1219+
assertTrue(rendered1.contains(
1220+
"""| "secrets" : [
1221+
| # env variables
1222+
| "<env variable>",
1223+
| # env variables
1224+
| "<env variable>"
1225+
| ]""".stripMargin))
1226+
1227+
val showRenderOpt = ConfigRenderOptions.defaults()
1228+
val rendered2 = config.root().render(showRenderOpt)
1229+
assertTrue(rendered2.contains(""""secret" : "A""""))
1230+
assertTrue(rendered2.contains(
1231+
"""| "secrets" : [
1232+
| # env variables
1233+
| "B",
1234+
| # env variables
1235+
| "C"
1236+
| ]""".stripMargin))
1237+
}
1238+
12101239
@Test
12111240
def serializeRoundTrip() {
12121241
for (i <- 1 to 10) {

0 commit comments

Comments
 (0)