diff --git a/plugins/tasks/crypto/src/main/java/com/walmartlabs/concord/plugins/crypto/CryptoTaskV2.java b/plugins/tasks/crypto/src/main/java/com/walmartlabs/concord/plugins/crypto/CryptoTaskV2.java index 991723be67..2b1aeba208 100644 --- a/plugins/tasks/crypto/src/main/java/com/walmartlabs/concord/plugins/crypto/CryptoTaskV2.java +++ b/plugins/tasks/crypto/src/main/java/com/walmartlabs/concord/plugins/crypto/CryptoTaskV2.java @@ -61,11 +61,11 @@ public CryptoTaskV2(Context context) { this.processOrg = projectInfo != null ? projectInfo.orgName() : null; } - public String exportAsString(String orgName, String name, String password) throws Exception { + public String exportAsString(String orgName, String name, @SensitiveData String password) throws Exception { return secretService.exportAsString(orgName, name, password); } - public Map exportKeyAsFile(String orgName, String name, String password) throws Exception { + public Map exportKeyAsFile(String orgName, String name, @SensitiveData String password) throws Exception { KeyPair keyPair = secretService.exportKeyAsFile(orgName, name, password); Path baseDir = workDir; @@ -76,7 +76,7 @@ public Map exportKeyAsFile(String orgName, String name, String p return m; } - public Map exportCredentials(String orgName, String name, String password) throws Exception { + public Map exportCredentials(String orgName, String name, @SensitiveData String password) throws Exception { UsernamePassword credentials = secretService.exportCredentials(orgName, name, password); Map m = new HashMap<>(); @@ -85,7 +85,7 @@ public Map exportCredentials(String orgName, String name, String return m; } - public String exportAsFile(String orgName, String name, String password) throws Exception { + public String exportAsFile(String orgName, String name, @SensitiveData String password) throws Exception { Path path = secretService.exportAsFile(orgName, name, password); return workDir.relativize(path).toString(); } diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/resolvers/TaskMethodResolver.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/resolvers/TaskMethodResolver.java index 819694ba75..604b220940 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/resolvers/TaskMethodResolver.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/resolvers/TaskMethodResolver.java @@ -24,7 +24,6 @@ import com.walmartlabs.concord.runtime.v2.model.Expression; import com.walmartlabs.concord.runtime.v2.model.Step; import com.walmartlabs.concord.runtime.v2.runner.el.MethodNotFoundException; -import com.walmartlabs.concord.runtime.v2.runner.tasks.ImmutableMethod; import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallInterceptor; import com.walmartlabs.concord.runtime.v2.sdk.Context; import com.walmartlabs.concord.runtime.v2.sdk.Task; @@ -67,7 +66,7 @@ public Object invoke(ELContext elContext, Object base, Object method, Class[] TaskCallInterceptor interceptor = context.execution().runtime().getService(TaskCallInterceptor.class); try { - return interceptor.invoke(callContext, getMethod(method, params), + return interceptor.invoke(callContext, Method.of(base, (String)method, Arrays.asList(params)), () -> super.invoke(elContext, base, method, paramTypes, params)); } catch (javax.el.MethodNotFoundException e) { throw new MethodNotFoundException(base, method, paramTypes); @@ -83,13 +82,6 @@ public Object invoke(ELContext elContext, Object base, Object method, Class[] } } - private static Method getMethod(Object method, Object[] params) { - return ImmutableMethod.builder() - .name((String)method) - .arguments(Arrays.asList(params)) - .build(); - } - private static String getName(Object task) { Named n = ReflectionUtils.findAnnotation(task.getClass(), Named.class); if (n != null) { diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/remote/TaskCallEventRecordingListener.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/remote/TaskCallEventRecordingListener.java index 76fb8fb72e..d78c507df8 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/remote/TaskCallEventRecordingListener.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/remote/TaskCallEventRecordingListener.java @@ -33,14 +33,12 @@ import com.walmartlabs.concord.runtime.v2.model.Step; import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallEvent; import com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallListener; -import com.walmartlabs.concord.runtime.v2.sdk.Context; -import com.walmartlabs.concord.runtime.v2.sdk.ProcessConfiguration; -import com.walmartlabs.concord.runtime.v2.sdk.TaskResult; -import com.walmartlabs.concord.runtime.v2.sdk.Variables; +import com.walmartlabs.concord.runtime.v2.sdk.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; +import java.lang.annotation.Annotation; import java.time.Instant; import java.time.ZoneOffset; import java.util.*; @@ -49,6 +47,8 @@ public class TaskCallEventRecordingListener implements TaskCallListener { private static final Logger log = LoggerFactory.getLogger(TaskCallEventRecordingListener.class); + private static final String MASK = "***"; + private final ProcessEventsApi eventsApi; private final InstanceId processInstanceId; private final EventConfiguration eventConfiguration; @@ -68,7 +68,7 @@ public void onEvent(TaskCallEvent event) { List inVars = event.input(); if (inVars != null && eventConfiguration.recordTaskInVars()) { - Map vars = maskVars(convertInput(inVars), eventConfiguration.inVarsBlacklist()); + Map vars = maskVars(convertInput(hideSensitiveData(inVars, event.inputAnnotations())), eventConfiguration.inVarsBlacklist()); if (eventConfiguration.truncateInVars()) { vars = ObjectTruncater.truncateMap(vars, eventConfiguration.truncateMaxStringLength(), eventConfiguration.truncateMaxArrayLength(), eventConfiguration.truncateMaxDepth()); } @@ -154,7 +154,7 @@ static Map maskVars(Map vars, Collection String[] path = b.split("\\."); if (ConfigurationUtils.has(result, path)) { Map m = ensureModifiable(result, path.length - 1, path); - m.put(path[path.length - 1], "***"); + m.put(path[path.length - 1], MASK); } } return result; @@ -206,4 +206,20 @@ private static Map convertInput(List input) { return result; } + + private static List hideSensitiveData(List input, List> annotations) { + if (annotations.isEmpty()) { + return input; + } + + List result = new ArrayList<>(input); + for (int i = 0; i < result.size(); i++) { + List a = annotations.get(i); + boolean hasSensitiveData = a.stream().anyMatch(v -> v.annotationType() == SensitiveData.class); + if (hasSensitiveData) { + result.set(i, MASK); + } + } + return result; + } } diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallEvent.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallEvent.java index aecf4e64ce..c029a32503 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallEvent.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallEvent.java @@ -27,6 +27,7 @@ import javax.annotation.Nullable; import java.io.Serializable; +import java.lang.annotation.Annotation; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -51,6 +52,12 @@ default List input() { return Collections.emptyList(); } + @AllowNulls + @Value.Default + default List> inputAnnotations() { + return Collections.emptyList(); + } + UUID correlationId(); @Nullable diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallInterceptor.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallInterceptor.java index 73284d301b..f98283034b 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallInterceptor.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/tasks/TaskCallInterceptor.java @@ -20,6 +20,7 @@ * ===== */ +import com.sun.el.util.ReflectionUtil; import com.walmartlabs.concord.common.AllowNulls; import com.walmartlabs.concord.runtime.v2.model.ProcessDefinition; import com.walmartlabs.concord.runtime.v2.model.Step; @@ -28,11 +29,10 @@ import javax.inject.Inject; import java.io.Serializable; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.lang.annotation.Annotation; +import java.util.*; import java.util.concurrent.Callable; +import java.util.stream.Collectors; /** * Intercepts task calls and notifies {@link TaskCallListener}s. @@ -95,6 +95,7 @@ private static ImmutableTaskCallEvent.Builder eventBuilder(Phase phase, Method m .correlationId(ctx.correlationId()) .currentStep(ctx.currentStep()) .input(method.arguments()) + .inputAnnotations(method.annotations()) .methodName(method.name()) .processDefinition(ctx.processDefinition()) .taskName(ctx.taskName()); @@ -112,12 +113,27 @@ default List arguments() { return Collections.emptyList(); } - static Method of(String name, Object... arguments) { + @AllowNulls + @Value.Default + default List> annotations() { + return Collections.emptyList(); + } + + static Method of(Object base, String methodName, List params) { + List> annotations = Collections.emptyList(); + java.lang.reflect.Method m = ReflectionUtil.findMethod(base.getClass(), methodName, null, params.toArray()); + if (m != null) { + annotations = Arrays.stream(m.getParameterAnnotations()) + .map(Arrays::asList) + .collect(Collectors.toList()); + } return ImmutableMethod.builder() - .name(name) - .addArguments(arguments) + .name(methodName) + .arguments(params) + .annotations(annotations) .build(); } + } @Value.Immutable diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskCallCommand.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskCallCommand.java index bb3276449d..f283f54a98 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskCallCommand.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskCallCommand.java @@ -31,6 +31,7 @@ import com.walmartlabs.concord.svm.State; import com.walmartlabs.concord.svm.ThreadId; +import java.util.Collections; import java.util.Objects; import static com.walmartlabs.concord.runtime.v2.runner.tasks.TaskCallInterceptor.CallContext; @@ -79,7 +80,7 @@ protected void execute(Runtime runtime, State state, ThreadId threadId) { TaskResult result; try { - result = interceptor.invoke(callContext, Method.of("execute", input), + result = interceptor.invoke(callContext, Method.of(t, "execute", Collections.singletonList(input)), () -> t.execute(input)); } catch (RuntimeException e) { throw e; diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskResumeCommand.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskResumeCommand.java index 18768de60f..bc615d7ce3 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskResumeCommand.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/vm/TaskResumeCommand.java @@ -29,6 +29,7 @@ import com.walmartlabs.concord.svm.State; import com.walmartlabs.concord.svm.ThreadId; +import java.util.Collections; import java.util.UUID; public class TaskResumeCommand extends StepCommand { @@ -76,7 +77,7 @@ protected void execute(Runtime runtime, State state, ThreadId threadId) { TaskResult result; try { - result = interceptor.invoke(callContext, TaskCallInterceptor.Method.of("resume", event), + result = interceptor.invoke(callContext, TaskCallInterceptor.Method.of(rt, "resume", Collections.singletonList(event)), () -> rt.resume(event)); } catch (RuntimeException e) { throw e; diff --git a/runtime/v2/sdk/src/main/java/com/walmartlabs/concord/runtime/v2/sdk/SensitiveData.java b/runtime/v2/sdk/src/main/java/com/walmartlabs/concord/runtime/v2/sdk/SensitiveData.java new file mode 100644 index 0000000000..93bba7d81c --- /dev/null +++ b/runtime/v2/sdk/src/main/java/com/walmartlabs/concord/runtime/v2/sdk/SensitiveData.java @@ -0,0 +1,47 @@ +package com.walmartlabs.concord.runtime.v2.sdk; + +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2020 Walmart Inc. + * ----- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ===== + */ + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.PARAMETER; + +/** + * This annotation can be used to prevent task arguments values from + * being recorded in process events. + *

+ * Currently, it is applicable only for task methods called directly + * via expressions. For example: + *

{@code
+ * flows:
+ *   default:
+ *     - "${crypto.exportAsString('myOrg', 'mySecret', 'mySecretPassword')}"
+ * }
+ * Assuming the task method has the third argument annotated with + * {@link SensitiveData}, running this flow will result with + * the third argument's value being masked in process events. + */ +@Target({PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface SensitiveData { +}