Skip to content

Commit 46ea7f6

Browse files
authored
runtime-v2: add @sensitivedata annotation to hide task method parameters (#263)
1 parent 4fdf30b commit 46ea7f6

File tree

8 files changed

+108
-28
lines changed

8 files changed

+108
-28
lines changed

plugins/tasks/crypto/src/main/java/com/walmartlabs/concord/plugins/crypto/CryptoTaskV2.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ public CryptoTaskV2(Context context) {
6161
this.processOrg = projectInfo != null ? projectInfo.orgName() : null;
6262
}
6363

64-
public String exportAsString(String orgName, String name, String password) throws Exception {
64+
public String exportAsString(String orgName, String name, @SensitiveData String password) throws Exception {
6565
return secretService.exportAsString(orgName, name, password);
6666
}
6767

68-
public Map<String, String> exportKeyAsFile(String orgName, String name, String password) throws Exception {
68+
public Map<String, String> exportKeyAsFile(String orgName, String name, @SensitiveData String password) throws Exception {
6969
KeyPair keyPair = secretService.exportKeyAsFile(orgName, name, password);
7070

7171
Path baseDir = workDir;
@@ -76,7 +76,7 @@ public Map<String, String> exportKeyAsFile(String orgName, String name, String p
7676
return m;
7777
}
7878

79-
public Map<String, String> exportCredentials(String orgName, String name, String password) throws Exception {
79+
public Map<String, String> exportCredentials(String orgName, String name, @SensitiveData String password) throws Exception {
8080
UsernamePassword credentials = secretService.exportCredentials(orgName, name, password);
8181

8282
Map<String, String> m = new HashMap<>();
@@ -85,7 +85,7 @@ public Map<String, String> exportCredentials(String orgName, String name, String
8585
return m;
8686
}
8787

88-
public String exportAsFile(String orgName, String name, String password) throws Exception {
88+
public String exportAsFile(String orgName, String name, @SensitiveData String password) throws Exception {
8989
Path path = secretService.exportAsFile(orgName, name, password);
9090
return workDir.relativize(path).toString();
9191
}

runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/resolvers/TaskMethodResolver.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.walmartlabs.concord.runtime.v2.model.Expression;
2525
import com.walmartlabs.concord.runtime.v2.model.Step;
2626
import com.walmartlabs.concord.runtime.v2.runner.el.MethodNotFoundException;
27-
import com.walmartlabs.concord.runtime.v2.runner.tasks.ImmutableMethod;
2827
import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallInterceptor;
2928
import com.walmartlabs.concord.runtime.v2.sdk.Context;
3029
import com.walmartlabs.concord.runtime.v2.sdk.Task;
@@ -67,7 +66,7 @@ public Object invoke(ELContext elContext, Object base, Object method, Class<?>[]
6766

6867
TaskCallInterceptor interceptor = context.execution().runtime().getService(TaskCallInterceptor.class);
6968
try {
70-
return interceptor.invoke(callContext, getMethod(method, params),
69+
return interceptor.invoke(callContext, Method.of(base, (String)method, Arrays.asList(params)),
7170
() -> super.invoke(elContext, base, method, paramTypes, params));
7271
} catch (javax.el.MethodNotFoundException e) {
7372
throw new MethodNotFoundException(base, method, paramTypes);
@@ -83,13 +82,6 @@ public Object invoke(ELContext elContext, Object base, Object method, Class<?>[]
8382
}
8483
}
8584

86-
private static Method getMethod(Object method, Object[] params) {
87-
return ImmutableMethod.builder()
88-
.name((String)method)
89-
.arguments(Arrays.asList(params))
90-
.build();
91-
}
92-
9385
private static String getName(Object task) {
9486
Named n = ReflectionUtils.findAnnotation(task.getClass(), Named.class);
9587
if (n != null) {

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

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,12 @@
3333
import com.walmartlabs.concord.runtime.v2.model.Step;
3434
import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallEvent;
3535
import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallListener;
36-
import com.walmartlabs.concord.runtime.v2.sdk.Context;
37-
import com.walmartlabs.concord.runtime.v2.sdk.ProcessConfiguration;
38-
import com.walmartlabs.concord.runtime.v2.sdk.TaskResult;
39-
import com.walmartlabs.concord.runtime.v2.sdk.Variables;
36+
import com.walmartlabs.concord.runtime.v2.sdk.*;
4037
import org.slf4j.Logger;
4138
import org.slf4j.LoggerFactory;
4239

4340
import javax.inject.Inject;
41+
import java.lang.annotation.Annotation;
4442
import java.time.Instant;
4543
import java.time.ZoneOffset;
4644
import java.util.*;
@@ -49,6 +47,8 @@ public class TaskCallEventRecordingListener implements TaskCallListener {
4947

5048
private static final Logger log = LoggerFactory.getLogger(TaskCallEventRecordingListener.class);
5149

50+
private static final String MASK = "***";
51+
5252
private final ProcessEventsApi eventsApi;
5353
private final InstanceId processInstanceId;
5454
private final EventConfiguration eventConfiguration;
@@ -68,7 +68,7 @@ public void onEvent(TaskCallEvent event) {
6868

6969
List<Object> inVars = event.input();
7070
if (inVars != null && eventConfiguration.recordTaskInVars()) {
71-
Map<String, Object> vars = maskVars(convertInput(inVars), eventConfiguration.inVarsBlacklist());
71+
Map<String, Object> vars = maskVars(convertInput(hideSensitiveData(inVars, event.inputAnnotations())), eventConfiguration.inVarsBlacklist());
7272
if (eventConfiguration.truncateInVars()) {
7373
vars = ObjectTruncater.truncateMap(vars, eventConfiguration.truncateMaxStringLength(), eventConfiguration.truncateMaxArrayLength(), eventConfiguration.truncateMaxDepth());
7474
}
@@ -154,7 +154,7 @@ static Map<String, Object> maskVars(Map<String, Object> vars, Collection<String>
154154
String[] path = b.split("\\.");
155155
if (ConfigurationUtils.has(result, path)) {
156156
Map<String, Object> m = ensureModifiable(result, path.length - 1, path);
157-
m.put(path[path.length - 1], "***");
157+
m.put(path[path.length - 1], MASK);
158158
}
159159
}
160160
return result;
@@ -206,4 +206,20 @@ private static Map<String, Object> convertInput(List<Object> input) {
206206

207207
return result;
208208
}
209+
210+
private static List<Object> hideSensitiveData(List<Object> input, List<List<Annotation>> annotations) {
211+
if (annotations.isEmpty()) {
212+
return input;
213+
}
214+
215+
List<Object> result = new ArrayList<>(input);
216+
for (int i = 0; i < result.size(); i++) {
217+
List<Annotation> a = annotations.get(i);
218+
boolean hasSensitiveData = a.stream().anyMatch(v -> v.annotationType() == SensitiveData.class);
219+
if (hasSensitiveData) {
220+
result.set(i, MASK);
221+
}
222+
}
223+
return result;
224+
}
209225
}

runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallEvent.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import javax.annotation.Nullable;
2929
import java.io.Serializable;
30+
import java.lang.annotation.Annotation;
3031
import java.util.Collections;
3132
import java.util.List;
3233
import java.util.UUID;
@@ -51,6 +52,12 @@ default List<Object> input() {
5152
return Collections.emptyList();
5253
}
5354

55+
@AllowNulls
56+
@Value.Default
57+
default List<List<Annotation>> inputAnnotations() {
58+
return Collections.emptyList();
59+
}
60+
5461
UUID correlationId();
5562

5663
@Nullable

runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallInterceptor.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* =====
2121
*/
2222

23+
import com.sun.el.util.ReflectionUtil;
2324
import com.walmartlabs.concord.common.AllowNulls;
2425
import com.walmartlabs.concord.runtime.v2.model.ProcessDefinition;
2526
import com.walmartlabs.concord.runtime.v2.model.Step;
@@ -28,11 +29,10 @@
2829

2930
import javax.inject.Inject;
3031
import java.io.Serializable;
31-
import java.util.Collections;
32-
import java.util.List;
33-
import java.util.Set;
34-
import java.util.UUID;
32+
import java.lang.annotation.Annotation;
33+
import java.util.*;
3534
import java.util.concurrent.Callable;
35+
import java.util.stream.Collectors;
3636

3737
/**
3838
* Intercepts task calls and notifies {@link TaskCallListener}s.
@@ -95,6 +95,7 @@ private static ImmutableTaskCallEvent.Builder eventBuilder(Phase phase, Method m
9595
.correlationId(ctx.correlationId())
9696
.currentStep(ctx.currentStep())
9797
.input(method.arguments())
98+
.inputAnnotations(method.annotations())
9899
.methodName(method.name())
99100
.processDefinition(ctx.processDefinition())
100101
.taskName(ctx.taskName());
@@ -112,12 +113,27 @@ default List<Object> arguments() {
112113
return Collections.emptyList();
113114
}
114115

115-
static Method of(String name, Object... arguments) {
116+
@AllowNulls
117+
@Value.Default
118+
default List<List<Annotation>> annotations() {
119+
return Collections.emptyList();
120+
}
121+
122+
static Method of(Object base, String methodName, List<Object> params) {
123+
List<List<Annotation>> annotations = Collections.emptyList();
124+
java.lang.reflect.Method m = ReflectionUtil.findMethod(base.getClass(), methodName, null, params.toArray());
125+
if (m != null) {
126+
annotations = Arrays.stream(m.getParameterAnnotations())
127+
.map(Arrays::asList)
128+
.collect(Collectors.toList());
129+
}
116130
return ImmutableMethod.builder()
117-
.name(name)
118-
.addArguments(arguments)
131+
.name(methodName)
132+
.arguments(params)
133+
.annotations(annotations)
119134
.build();
120135
}
136+
121137
}
122138

123139
@Value.Immutable

runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskCallCommand.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.walmartlabs.concord.svm.State;
3232
import com.walmartlabs.concord.svm.ThreadId;
3333

34+
import java.util.Collections;
3435
import java.util.Objects;
3536

3637
import static com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallInterceptor.CallContext;
@@ -79,7 +80,7 @@ protected void execute(Runtime runtime, State state, ThreadId threadId) {
7980

8081
TaskResult result;
8182
try {
82-
result = interceptor.invoke(callContext, Method.of("execute", input),
83+
result = interceptor.invoke(callContext, Method.of(t, "execute", Collections.singletonList(input)),
8384
() -> t.execute(input));
8485
} catch (RuntimeException e) {
8586
throw e;

runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskResumeCommand.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.walmartlabs.concord.svm.State;
3030
import com.walmartlabs.concord.svm.ThreadId;
3131

32+
import java.util.Collections;
3233
import java.util.UUID;
3334

3435
public class TaskResumeCommand extends StepCommand<TaskCall> {
@@ -76,7 +77,7 @@ protected void execute(Runtime runtime, State state, ThreadId threadId) {
7677

7778
TaskResult result;
7879
try {
79-
result = interceptor.invoke(callContext, TaskCallInterceptor.Method.of("resume", event),
80+
result = interceptor.invoke(callContext, TaskCallInterceptor.Method.of(rt, "resume", Collections.singletonList(event)),
8081
() -> rt.resume(event));
8182
} catch (RuntimeException e) {
8283
throw e;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.walmartlabs.concord.runtime.v2.sdk;
2+
3+
/*-
4+
* *****
5+
* Concord
6+
* -----
7+
* Copyright (C) 2017 - 2020 Walmart Inc.
8+
* -----
9+
* Licensed under the Apache License, Version 2.0 (the "License");
10+
* you may not use this file except in compliance with the License.
11+
* You may obtain a copy of the License at
12+
*
13+
* http://www.apache.org/licenses/LICENSE-2.0
14+
*
15+
* Unless required by applicable law or agreed to in writing, software
16+
* distributed under the License is distributed on an "AS IS" BASIS,
17+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
* See the License for the specific language governing permissions and
19+
* limitations under the License.
20+
* =====
21+
*/
22+
23+
import java.lang.annotation.Retention;
24+
import java.lang.annotation.RetentionPolicy;
25+
import java.lang.annotation.Target;
26+
27+
import static java.lang.annotation.ElementType.PARAMETER;
28+
29+
/**
30+
* This annotation can be used to prevent task arguments values from
31+
* being recorded in process events.
32+
* <p/>
33+
* Currently, it is applicable only for task methods called directly
34+
* via expressions. For example:
35+
* <pre>{@code
36+
* flows:
37+
* default:
38+
* - "${crypto.exportAsString('myOrg', 'mySecret', 'mySecretPassword')}"
39+
* }</pre>
40+
* Assuming the task method has the third argument annotated with
41+
* {@link SensitiveData}, running this flow will result with
42+
* the third argument's value being masked in process events.
43+
*/
44+
@Target({PARAMETER})
45+
@Retention(RetentionPolicy.RUNTIME)
46+
public @interface SensitiveData {
47+
}

0 commit comments

Comments
 (0)