17
17
package org .springframework .cloud .dataflow .rest .util ;
18
18
19
19
import java .util .ArrayList ;
20
+ import java .util .Arrays ;
20
21
import java .util .HashMap ;
21
22
import java .util .LinkedHashMap ;
22
23
import java .util .List ;
23
24
import java .util .Map ;
24
25
import java .util .regex .Pattern ;
25
26
27
+ import com .fasterxml .jackson .core .JsonProcessingException ;
28
+ import com .fasterxml .jackson .core .type .TypeReference ;
29
+ import com .fasterxml .jackson .databind .ObjectMapper ;
30
+ import com .fasterxml .jackson .dataformat .yaml .YAMLFactory ;
31
+ import org .slf4j .Logger ;
32
+ import org .slf4j .LoggerFactory ;
33
+
26
34
import org .springframework .batch .core .JobParameter ;
27
35
import org .springframework .batch .core .JobParameters ;
28
36
import org .springframework .cloud .dataflow .core .DefinitionUtils ;
29
37
import org .springframework .cloud .dataflow .core .TaskDefinition ;
30
38
import org .springframework .cloud .dataflow .core .dsl .TaskParser ;
31
39
import org .springframework .cloud .dataflow .core .dsl .graph .Graph ;
40
+ import org .springframework .http .HttpHeaders ;
32
41
import org .springframework .util .CollectionUtils ;
33
42
import org .springframework .util .StringUtils ;
34
43
40
49
* @author Ilayaperumal Gopinathan
41
50
*/
42
51
public class ArgumentSanitizer {
52
+ private final static Logger logger = LoggerFactory .getLogger (ArgumentSanitizer .class );
43
53
44
- private static final String [] REGEX_PARTS = { "*" , "$" , "^" , "+" };
54
+ private static final String [] REGEX_PARTS = {"*" , "$" , "^" , "+" };
45
55
46
56
private static final String REDACTION_STRING = "******" ;
47
57
48
- private static final String [] KEYS_TO_SANITIZE = { "username" , "password" , "secret" , "key" , "token" , ".*credentials.*" ,
49
- "vcap_services" , "url" };
58
+ private static final String [] KEYS_TO_SANITIZE = {"username" , "password" , "secret" , "key" , "token" , ".*credentials.*" ,
59
+ "vcap_services" , "url" };
60
+
61
+ private final static TypeReference <Map <String , Object >> mapTypeReference = new TypeReference <Map <String , Object >>() {};
62
+
63
+ private final ObjectMapper yamlMapper = new ObjectMapper (new YAMLFactory ());
64
+
65
+ private final ObjectMapper jsonMapper = new ObjectMapper ();
50
66
51
67
private Pattern [] keysToSanitize ;
52
68
@@ -80,6 +96,10 @@ private boolean isRegex(String value) {
80
96
* @return the argument with a potentially sanitized value
81
97
*/
82
98
public String sanitize (String argument ) {
99
+ // Oracle handles an empty string as a null.
100
+ if (argument == null ) {
101
+ return "" ;
102
+ }
83
103
int indexOfFirstEqual = argument .indexOf ("=" );
84
104
if (indexOfFirstEqual == -1 ) {
85
105
return argument ;
@@ -95,7 +115,7 @@ public String sanitize(String argument) {
95
115
/**
96
116
* Replaces a potential secure value with "******".
97
117
*
98
- * @param key to check for sensitive words.
118
+ * @param key to check for sensitive words.
99
119
* @param value the argument to cleanse.
100
120
* @return the argument with a potentially sanitized value
101
121
*/
@@ -118,13 +138,12 @@ public String sanitize(String key, String value) {
118
138
* @return the sanitized job parameters
119
139
*/
120
140
public JobParameters sanitizeJobParameters (JobParameters jobParameters ) {
121
- Map <String ,JobParameter > newJobParameters = new HashMap <>();
122
- jobParameters .getParameters ().forEach ( (key , jobParameter ) -> {
141
+ Map <String , JobParameter > newJobParameters = new HashMap <>();
142
+ jobParameters .getParameters ().forEach ((key , jobParameter ) -> {
123
143
String updatedKey = !jobParameter .isIdentifying () ? "-" + key : key ;
124
144
if (jobParameter .getType ().equals (JobParameter .ParameterType .STRING )) {
125
145
newJobParameters .put (updatedKey , new JobParameter (this .sanitize (key , jobParameter .toString ())));
126
- }
127
- else {
146
+ } else {
128
147
newJobParameters .put (updatedKey , jobParameter );
129
148
}
130
149
});
@@ -138,7 +157,7 @@ public JobParameters sanitizeJobParameters(JobParameters jobParameters) {
138
157
* @return Task definition text that has sensitive data redacted.
139
158
*/
140
159
public String sanitizeTaskDsl (TaskDefinition taskDefinition ) {
141
- if (StringUtils .isEmpty (taskDefinition .getDslText ())) {
160
+ if (StringUtils .isEmpty (taskDefinition .getDslText ())) {
142
161
return taskDefinition .getDslText ();
143
162
}
144
163
TaskParser taskParser = new TaskParser (taskDefinition .getTaskName (), taskDefinition .getDslText (), true , true );
@@ -147,7 +166,7 @@ public String sanitizeTaskDsl(TaskDefinition taskDefinition) {
147
166
if (node .properties != null ) {
148
167
node .properties .keySet ().stream ().forEach (key -> {
149
168
node .properties .put (key ,
150
- DefinitionUtils .autoQuotes (sanitize (key , node .properties .get (key ))));
169
+ DefinitionUtils .autoQuotes (sanitize (key , node .properties .get (key ))));
151
170
});
152
171
}
153
172
});
@@ -157,13 +176,14 @@ public String sanitizeTaskDsl(TaskDefinition taskDefinition) {
157
176
/**
158
177
* For all sensitive properties (e.g. key names containing words like password, secret,
159
178
* key, token) replace the value with '*****' string
179
+ *
160
180
* @param properties to be sanitized
161
181
* @return sanitized properties
162
182
*/
163
183
public Map <String , String > sanitizeProperties (Map <String , String > properties ) {
164
184
if (!CollectionUtils .isEmpty (properties )) {
165
185
final Map <String , String > sanitizedProperties = new LinkedHashMap <>(properties .size ());
166
- for (Map .Entry <String , String > property : properties .entrySet ()) {
186
+ for (Map .Entry <String , String > property : properties .entrySet ()) {
167
187
sanitizedProperties .put (property .getKey (), this .sanitize (property .getKey (), property .getValue ()));
168
188
}
169
189
return sanitizedProperties ;
@@ -174,6 +194,7 @@ public Map<String, String> sanitizeProperties(Map<String, String> properties) {
174
194
/**
175
195
* For all sensitive arguments (e.g. key names containing words like password, secret,
176
196
* key, token) replace the value with '*****' string
197
+ *
177
198
* @param arguments to be sanitized
178
199
* @return sanitized arguments
179
200
*/
@@ -187,4 +208,96 @@ public List<String> sanitizeArguments(List<String> arguments) {
187
208
}
188
209
return arguments ;
189
210
}
211
+
212
+ public HttpHeaders sanitizeHeaders (HttpHeaders headers ) {
213
+ HttpHeaders result = new HttpHeaders ();
214
+ for (Map .Entry <String , List <String >> entry : headers .entrySet ()) {
215
+ List <String > values = entry .getValue ();
216
+ for (String value : values ) {
217
+ result .add (entry .getKey (), sanitize (entry .getKey (), value ));
218
+ }
219
+ }
220
+ return result ;
221
+ }
222
+
223
+ /**
224
+ * Will replace sensitive string value in the Map with '*****'
225
+ *
226
+ * @param input to be sanitized
227
+ * @return the sanitized map.
228
+ */
229
+ public Map <String , Object > sanitizeMap (Map <String , Object > input ) {
230
+ Map <String , Object > result = new HashMap <>();
231
+ for (Map .Entry <String , Object > entry : input .entrySet ()) {
232
+ if (entry .getValue () instanceof String ) {
233
+ result .put (entry .getKey (), sanitize (entry .getKey (), (String ) entry .getValue ()));
234
+ } else if (entry .getValue () instanceof Map ) {
235
+ Map <String , Object > map = (Map <String , Object >) entry .getValue ();
236
+ result .put (entry .getKey (), sanitizeMap (map ));
237
+ } else {
238
+ result .put (entry .getKey (), entry .getValue ());
239
+ }
240
+ }
241
+ return result ;
242
+ }
243
+
244
+ /**
245
+ * Will replace the sensitive string fields with '*****'
246
+ *
247
+ * @param input to be sanitized
248
+ * @return The sanitized JSON string
249
+ * @throws JsonProcessingException
250
+ */
251
+ public String sanitizeJsonString (String input ) throws JsonProcessingException {
252
+ if (input == null ) {
253
+ return null ;
254
+ }
255
+ Map <String , Object > data = jsonMapper .readValue (input , mapTypeReference );
256
+ return jsonMapper .writeValueAsString (sanitizeMap (data ));
257
+ }
258
+
259
+ /**
260
+ * Will replace the sensitive string fields with '*****'
261
+ *
262
+ * @param input to be sanitized
263
+ * @return The sanitized YAML string
264
+ * @throws JsonProcessingException
265
+ */
266
+ public String sanitizeYamlString (String input ) throws JsonProcessingException {
267
+ if (input == null ) {
268
+ return null ;
269
+ }
270
+ Map <String , Object > data = yamlMapper .readValue (input , mapTypeReference );
271
+ return yamlMapper .writeValueAsString (sanitizeMap (data ));
272
+ }
273
+
274
+ /**
275
+ * Will determine the type of data and treat as JSON or YAML to sanitize sensitive values.
276
+ *
277
+ * @param input to be sanitized
278
+ * @return the sanitized string
279
+ * @throws JsonProcessingException
280
+ */
281
+ public String sanitizeJsonOrYamlString (String input ) {
282
+ if (input == null ) {
283
+ return null ;
284
+ }
285
+ try { // Try parsing as JSON
286
+ return sanitizeJsonString (input );
287
+ } catch (Throwable x ) {
288
+ logger .trace ("Cannot parse as JSON:" + x );
289
+ }
290
+ try {
291
+ return sanitizeYamlString (input );
292
+ } catch (Throwable x ) {
293
+ logger .trace ("Cannot parse as YAML:" + x );
294
+ }
295
+ if (input .contains ("\n " )) {
296
+ return StringUtils .collectionToDelimitedString (sanitizeArguments (Arrays .asList (StringUtils .split (input , "\n " ))), "\n " );
297
+ }
298
+ if (input .contains ("--" )) {
299
+ return StringUtils .collectionToDelimitedString (sanitizeArguments (Arrays .asList (StringUtils .split (input , "--" ))), "--" );
300
+ }
301
+ return sanitize (input );
302
+ }
190
303
}
0 commit comments