Skip to content

Commit 1bc9273

Browse files
ibodrovbrig
authored andcommitted
runtime-v2: use SensitiveDataHolder for task parameter masking
1 parent 901a542 commit 1bc9273

File tree

2 files changed

+85
-26
lines changed

2 files changed

+85
-26
lines changed

runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/remote/TaskCallEventRecordingListener.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.walmartlabs.concord.runtime.v2.model.Location;
2929
import com.walmartlabs.concord.runtime.v2.model.Step;
3030
import com.walmartlabs.concord.runtime.v2.runner.EventReportingService;
31+
import com.walmartlabs.concord.runtime.v2.runner.SensitiveDataHolder;
3132
import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallEvent;
3233
import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallListener;
3334
import com.walmartlabs.concord.runtime.v2.sdk.*;
@@ -64,7 +65,9 @@ public void onEvent(TaskCallEvent event) {
6465

6566
List<Object> inVars = event.input();
6667
if (inVars != null && eventConfiguration.recordTaskInVars()) {
67-
Map<String, Object> vars = maskVars(convertInput(hideSensitiveData(inVars, event.inputAnnotations())), eventConfiguration.inVarsBlacklist());
68+
Map<String, Object> input = convertInput(processSensitiveDataAnnotations(inVars, event.inputAnnotations()));
69+
input = processSensitiveData(input);
70+
Map<String, Object> vars = maskVars(input, eventConfiguration.inVarsBlacklist());
6871
if (eventConfiguration.truncateInVars()) {
6972
vars = ObjectTruncater.truncateMap(vars, eventConfiguration.truncateMaxStringLength(), eventConfiguration.truncateMaxArrayLength(), eventConfiguration.truncateMaxDepth());
7073
}
@@ -75,7 +78,9 @@ public void onEvent(TaskCallEvent event) {
7578

7679
Object outVars = event.result();
7780
if (outVars != null && eventConfiguration.recordTaskOutVars()) {
78-
Map<String, Object> vars = maskVars(asMapOrNull(outVars), eventConfiguration.outVarsBlacklist());
81+
Map<String, Object> output = asMapOrNull(outVars);
82+
output = processSensitiveData(output);
83+
Map<String, Object> vars = maskVars(output, eventConfiguration.outVarsBlacklist());
7984
if (eventConfiguration.truncateOutVars()) {
8085
vars = ObjectTruncater.truncateMap(vars, eventConfiguration.truncateMaxStringLength(), eventConfiguration.truncateMaxArrayLength(), eventConfiguration.truncateMaxDepth());
8186
}
@@ -86,7 +91,9 @@ public void onEvent(TaskCallEvent event) {
8691

8792
Object metaVars = event.meta();
8893
if (metaVars != null && eventConfiguration.recordTaskMeta()) {
89-
Map<String, Object> meta = maskVars(asMapOrNull(metaVars), eventConfiguration.metaBlacklist());
94+
Map<String, Object> rawMeta = asMapOrNull(metaVars);
95+
Map<String, Object> meta = processSensitiveData(rawMeta);
96+
meta = maskVars(meta, eventConfiguration.metaBlacklist());
9097
if (eventConfiguration.truncateMeta()) {
9198
meta = ObjectTruncater.truncateMap(meta, eventConfiguration.truncateMaxStringLength(), eventConfiguration.truncateMaxArrayLength(), eventConfiguration.truncateMaxDepth());
9299
}
@@ -170,6 +177,34 @@ static Map<String, Object> maskVars(Map<String, Object> vars, Collection<String>
170177
return result;
171178
}
172179

180+
@SuppressWarnings({"unchecked", "rawtypes"})
181+
static <T> T processSensitiveData(T v) {
182+
Set<String> sensitiveStrings = SensitiveDataHolder.getInstance().get();
183+
if (sensitiveStrings.isEmpty()) {
184+
return v;
185+
}
186+
187+
if (v instanceof String s) {
188+
for (String sensitiveString : sensitiveStrings) {
189+
s = s.replace(sensitiveString, MASK);
190+
}
191+
return (T) s;
192+
} else if (v instanceof List<?> l) {
193+
List<Object> result = new ArrayList<>(l.size());
194+
for (Object vv : l) {
195+
vv = processSensitiveData(vv);
196+
result.add(vv);
197+
}
198+
return (T) result;
199+
} else if (v instanceof Map m) {
200+
Map<String, Object> result = new HashMap<>(m);
201+
result.replaceAll((k, vv) -> processSensitiveData(vv));
202+
return (T) result;
203+
}
204+
205+
return v;
206+
}
207+
173208
@SuppressWarnings("unchecked")
174209
private static Map<String, Object> ensureModifiable(Map<String, Object> m, int depth, String[] path) {
175210
if (depth == 0) {
@@ -217,7 +252,7 @@ private static Map<String, Object> convertInput(List<Object> input) {
217252
return result;
218253
}
219254

220-
private static List<Object> hideSensitiveData(List<Object> input, List<List<Annotation>> annotations) {
255+
private static List<Object> processSensitiveDataAnnotations(List<Object> input, List<List<Annotation>> annotations) {
221256
if (annotations.isEmpty()) {
222257
return input;
223258
}

runtime/v2/runner/src/test/java/com/walmartlabs/concord/runtime/v2/runner/remote/TaskCallEventRecordingListenerTest.java

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import com.fasterxml.jackson.core.JsonProcessingException;
2424
import com.fasterxml.jackson.databind.ObjectMapper;
25+
import com.walmartlabs.concord.runtime.v2.runner.SensitiveDataHolder;
2526
import org.junit.jupiter.api.Test;
2627

2728
import java.util.Arrays;
@@ -38,33 +39,33 @@ public class TaskCallEventRecordingListenerTest {
3839
@Test
3940
public void testMaskVars() throws Exception {
4041
String in = "{" +
41-
" \"a\":1," +
42-
" \"b\":2," +
43-
" \"c\":{" +
44-
" \"c1\":3," +
45-
" \"c2\":4," +
46-
" \"c3\":{" +
47-
" \"c31\":5," +
48-
" \"c32\":6" +
49-
" }" +
50-
" }" +
51-
"}";
42+
" \"a\":1," +
43+
" \"b\":2," +
44+
" \"c\":{" +
45+
" \"c1\":3," +
46+
" \"c2\":4," +
47+
" \"c3\":{" +
48+
" \"c31\":5," +
49+
" \"c32\":6" +
50+
" }" +
51+
" }" +
52+
"}";
5253

5354
List<String> blackList = Arrays.asList("b", "c.c1", "c.c3.c31");
5455
Map<String, Object> result = TaskCallEventRecordingListener.maskVars(vars(in), blackList);
5556

5657
String expected = "{" +
57-
" \"a\":1," +
58-
" \"b\":\"***\"," +
59-
" \"c\":{" +
60-
" \"c1\":\"***\"," +
61-
" \"c2\":4," +
62-
" \"c3\":{" +
63-
" \"c31\":\"***\"," +
64-
" \"c32\":6" +
65-
" }" +
66-
" }" +
67-
"}";
58+
" \"a\":1," +
59+
" \"b\":\"***\"," +
60+
" \"c\":{" +
61+
" \"c1\":\"***\"," +
62+
" \"c2\":4," +
63+
" \"c3\":{" +
64+
" \"c31\":\"***\"," +
65+
" \"c32\":6" +
66+
" }" +
67+
" }" +
68+
"}";
6869
assertEquals(vars(expected), result);
6970
}
7071

@@ -80,6 +81,29 @@ public void testMaskVarsUnmodifiable() {
8081
assertEquals("{x={y={z=***}}}", result.toString());
8182
}
8283

84+
@Test
85+
public void testSensitiveDataMasking() throws JsonProcessingException {
86+
SensitiveDataHolder holder = SensitiveDataHolder.getInstance();
87+
holder.add("foo");
88+
holder.add("bar");
89+
90+
String in = "{" +
91+
"\"a\": \"foo\"," +
92+
"\"b\": \"bar\"," +
93+
"\"c\": \"baz\"," +
94+
"\"d\": { \"e\": \"foo\" }" +
95+
"}";
96+
97+
Map<String, Object> result = TaskCallEventRecordingListener.processSensitiveData(vars(in));
98+
String expected = "{" +
99+
" \"a\": \"***\"," +
100+
" \"b\": \"***\"," +
101+
" \"c\": \"baz\"," +
102+
" \"d\": { \"e\": \"***\" }" +
103+
"}";
104+
assertEquals(vars(expected), result);
105+
}
106+
83107
@SuppressWarnings("unchecked")
84108
private static Map<String, Object> vars(String in) throws JsonProcessingException {
85109
return om.readValue(in, Map.class);

0 commit comments

Comments
 (0)