From 8a58495172555dc48afed0f63369efef48469cf0 Mon Sep 17 00:00:00 2001 From: Ivan Bodrov Date: Tue, 21 Oct 2025 10:08:03 -0400 Subject: [PATCH 1/2] runtime-v2: add option to mark strings as sensitive using "mask" function --- .../concord/runtime/v2/runner/MainTest.java | 11 ++++ .../v2/runner/functions/TestFunction.java | 20 +++++++ .../v2/runner/maskFunction/concord.yml | 6 ++ .../v2/runner/el/functions/MaskFunction.java | 59 +++++++++++++++++++ .../runner/guice/ExpressionSupportModule.java | 3 +- 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml create mode 100644 runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java diff --git a/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java b/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java index f553ca61b0..cef9f6cf4e 100644 --- a/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java +++ b/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java @@ -1760,6 +1760,17 @@ public void prefixedFunctionsInExpressions() throws Exception { assertLog(runtime.allLogs(), ".*Hello, world!.*"); } + @Test + public void maskFunction() throws Exception { + deploy("maskFunction"); + + save(ProcessConfiguration.builder().build()); + + run(); + + assertLog(runtime.allLogs(), ".*" + Pattern.quote("The '******' is masked now") + ".*"); + } + private void deploy(String name) throws URISyntaxException, IOException { runtime.deploy(name); } diff --git a/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/functions/TestFunction.java b/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/functions/TestFunction.java index 794c8c54a4..133b100179 100644 --- a/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/functions/TestFunction.java +++ b/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/functions/TestFunction.java @@ -1,5 +1,25 @@ package com.walmartlabs.concord.runtime.v2.runner.functions; +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2025 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 com.walmartlabs.concord.runtime.v2.sdk.ELFunction; import javax.inject.Named; diff --git a/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml b/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml new file mode 100644 index 0000000000..4ac2aadc7b --- /dev/null +++ b/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml @@ -0,0 +1,6 @@ +flows: + default: + - set: + regularValue: "regularValue" + secretValue: "${mask(regularValue)}" + - log: "The 'regularValue' is masked now" diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java new file mode 100644 index 0000000000..9c599b5253 --- /dev/null +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java @@ -0,0 +1,59 @@ +package com.walmartlabs.concord.runtime.v2.runner.el.functions; + +/*- + * ***** + * Concord + * ----- + * Copyright (C) 2017 - 2025 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 com.walmartlabs.concord.runtime.v2.sdk.ELFunction; +import com.walmartlabs.concord.runtime.v2.sdk.SensitiveDataHolder; + +import javax.inject.Inject; + +import static java.util.Objects.requireNonNull; + +public class MaskFunction { + + private static SensitiveDataHolder sensitiveData; + + @Inject + public MaskFunction(SensitiveDataHolder sensitiveData) { + MaskFunction.sensitiveData = requireNonNull(sensitiveData); + } + + @ELFunction + public static String mask(Object v) { + if (MaskFunction.sensitiveData == null) { + throw new IllegalStateException("MaskFunction must be initialized first"); + } + + if (v == null) { + return null; + } + + if (v instanceof String s) { + if (s.isBlank()) { + return s; + } + sensitiveData.add(s); + return s; + } + + throw new IllegalArgumentException("Only string values can be masked. Got a " + v.getClass()); + } +} diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java index 2371b2f15a..b8fd60958f 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java @@ -27,9 +27,9 @@ import com.google.inject.spi.TypeEncounter; import com.google.inject.spi.TypeListener; import com.walmartlabs.concord.runtime.v2.runner.el.DefaultExpressionEvaluator; -import com.walmartlabs.concord.runtime.v2.sdk.ELFunction; import com.walmartlabs.concord.runtime.v2.runner.el.FunctionHolder; import com.walmartlabs.concord.runtime.v2.runner.el.functions.*; +import com.walmartlabs.concord.runtime.v2.sdk.ELFunction; import com.walmartlabs.concord.runtime.v2.sdk.ExpressionEvaluator; import java.lang.reflect.Modifier; @@ -84,6 +84,7 @@ public void hear(TypeLiteral type, TypeEncounter encounter) { binder.bind(HasVariableFunction.class).asEagerSingleton(); binder.bind(IsDebugFunction.class).asEagerSingleton(); binder.bind(IsDryRunFunction.class).asEagerSingleton(); + binder.bind(MaskFunction.class).asEagerSingleton(); binder.bind(OrDefaultFunction.class).asEagerSingleton(); binder.bind(ThrowFunction.class).asEagerSingleton(); binder.bind(UuidFunction.class).asEagerSingleton(); From 979258f7167d1f6f7f9ddc5435d75826ba12228a Mon Sep 17 00:00:00 2001 From: Ivan Bodrov Date: Wed, 22 Oct 2025 11:42:44 -0400 Subject: [PATCH 2/2] review up --- .../concord/runtime/v2/runner/MainTest.java | 4 ++-- .../{maskFunction => sensitiveFunction}/concord.yml | 2 +- ...askFunction.java => MarkAsSensitiveFunction.java} | 12 ++++++------ .../v2/runner/guice/ExpressionSupportModule.java | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) rename runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/{maskFunction => sensitiveFunction}/concord.yml (69%) rename runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/{MaskFunction.java => MarkAsSensitiveFunction.java} (80%) diff --git a/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java b/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java index cef9f6cf4e..2f473b6233 100644 --- a/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java +++ b/runtime/v2/runner-test/src/test/java/com/walmartlabs/concord/runtime/v2/runner/MainTest.java @@ -1761,8 +1761,8 @@ public void prefixedFunctionsInExpressions() throws Exception { } @Test - public void maskFunction() throws Exception { - deploy("maskFunction"); + public void sensitiveFunction() throws Exception { + deploy("sensitiveFunction"); save(ProcessConfiguration.builder().build()); diff --git a/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml b/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/sensitiveFunction/concord.yml similarity index 69% rename from runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml rename to runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/sensitiveFunction/concord.yml index 4ac2aadc7b..adfb344deb 100644 --- a/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/maskFunction/concord.yml +++ b/runtime/v2/runner-test/src/test/resources/com/walmartlabs/concord/runtime/v2/runner/sensitiveFunction/concord.yml @@ -2,5 +2,5 @@ flows: default: - set: regularValue: "regularValue" - secretValue: "${mask(regularValue)}" + secretValue: "${sensitive(regularValue)}" - log: "The 'regularValue' is masked now" diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MarkAsSensitiveFunction.java similarity index 80% rename from runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java rename to runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MarkAsSensitiveFunction.java index 9c599b5253..265b02a172 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MaskFunction.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/el/functions/MarkAsSensitiveFunction.java @@ -27,18 +27,18 @@ import static java.util.Objects.requireNonNull; -public class MaskFunction { +public class MarkAsSensitiveFunction { private static SensitiveDataHolder sensitiveData; @Inject - public MaskFunction(SensitiveDataHolder sensitiveData) { - MaskFunction.sensitiveData = requireNonNull(sensitiveData); + public MarkAsSensitiveFunction(SensitiveDataHolder sensitiveData) { + MarkAsSensitiveFunction.sensitiveData = requireNonNull(sensitiveData); } @ELFunction - public static String mask(Object v) { - if (MaskFunction.sensitiveData == null) { + public static String sensitive(Object v) { + if (MarkAsSensitiveFunction.sensitiveData == null) { throw new IllegalStateException("MaskFunction must be initialized first"); } @@ -54,6 +54,6 @@ public static String mask(Object v) { return s; } - throw new IllegalArgumentException("Only string values can be masked. Got a " + v.getClass()); + throw new IllegalArgumentException("Only string values can be masked. Got a " + v.getClass() + " instead"); } } diff --git a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java index b8fd60958f..35139a4e22 100644 --- a/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java +++ b/runtime/v2/runner/src/main/java/com/walmartlabs/concord/runtime/v2/runner/guice/ExpressionSupportModule.java @@ -84,7 +84,7 @@ public void hear(TypeLiteral type, TypeEncounter encounter) { binder.bind(HasVariableFunction.class).asEagerSingleton(); binder.bind(IsDebugFunction.class).asEagerSingleton(); binder.bind(IsDryRunFunction.class).asEagerSingleton(); - binder.bind(MaskFunction.class).asEagerSingleton(); + binder.bind(MarkAsSensitiveFunction.class).asEagerSingleton(); binder.bind(OrDefaultFunction.class).asEagerSingleton(); binder.bind(ThrowFunction.class).asEagerSingleton(); binder.bind(UuidFunction.class).asEagerSingleton();