From 35add1e6f107131b63ef934e3a70478c68ef757b Mon Sep 17 00:00:00 2001 From: Olivier John Ndjike Nzia Date: Thu, 30 Oct 2025 11:35:52 -0400 Subject: [PATCH 1/4] check for empty env when setting cloud payload tagging --- .../main/java/datadog/trace/api/Config.java | 15 ++++++- .../datadog/trace/api/ConfigTest.groovy | 44 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index d828b06638d..fb05f410798 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -2760,10 +2760,21 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) this.cloudPayloadTaggingServices = configProvider.getSet( TRACE_CLOUD_PAYLOAD_TAGGING_SERVICES, DEFAULT_TRACE_CLOUD_PAYLOAD_TAGGING_SERVICES); - this.cloudRequestPayloadTagging = + + List cloudReqPayloadTaggingConf = configProvider.getList(TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, null); - this.cloudResponsePayloadTagging = + this.cloudRequestPayloadTagging = + cloudReqPayloadTaggingConf != null && cloudReqPayloadTaggingConf.isEmpty() + ? null + : cloudReqPayloadTaggingConf; + + List cloudRespPayloadTaggingConf = configProvider.getList(TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, null); + this.cloudResponsePayloadTagging = + cloudRespPayloadTaggingConf != null && cloudRespPayloadTaggingConf.isEmpty() + ? null + : cloudRespPayloadTaggingConf; + this.cloudPayloadTaggingMaxDepth = configProvider.getInteger(TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH, 10); this.cloudPayloadTaggingMaxTags = diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index 85a3c80974e..44f499b6b60 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -137,6 +137,8 @@ import static datadog.trace.api.config.TracerConfig.TRACE_SAMPLING_OPERATION_RUL import static datadog.trace.api.config.TracerConfig.TRACE_SAMPLING_SERVICE_RULES import static datadog.trace.api.config.TracerConfig.TRACE_X_DATADOG_TAGS_MAX_LENGTH import static datadog.trace.api.config.TracerConfig.WRITER_TYPE +import static datadog.trace.api.config.TracerConfig.TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING +import static datadog.trace.api.config.TracerConfig.TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING import datadog.trace.config.inversion.ConfigHelper class ConfigTest extends DDSpecification { @@ -173,6 +175,8 @@ class ConfigTest extends DDSpecification { private static final DD_LLMOBS_ENABLED_ENV = "DD_LLMOBS_ENABLED" private static final DD_LLMOBS_ML_APP_ENV = "DD_LLMOBS_ML_APP" private static final DD_LLMOBS_AGENTLESS_ENABLED_ENV = "DD_LLMOBS_AGENTLESS_ENABLED" + private static final DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV = "DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING" + private static final DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV = "DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING" def setup() { FixedCapturedEnvironment.useFixedEnv([:]) @@ -271,6 +275,9 @@ class ConfigTest extends DDSpecification { prop.setProperty(TRACE_X_DATADOG_TAGS_MAX_LENGTH, "128") prop.setProperty(JDK_SOCKET_ENABLED, "false") + prop.setProperty(TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, "all") + prop.setProperty(TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, "all") + when: Config config = Config.get(prop) @@ -366,6 +373,8 @@ class ConfigTest extends DDSpecification { config.debuggerExceptionEnabled == true config.jdkSocketEnabled == false + config.cloudRequestPayloadTagging == ["all"] + config.cloudResponsePayloadTagging == ["all"] config.xDatadogTagsMaxLength == 128 } @@ -460,6 +469,9 @@ class ConfigTest extends DDSpecification { System.setProperty(PREFIX + DYNAMIC_INSTRUMENTATION_EXCLUDE_FILES, "exclude file") System.setProperty(PREFIX + TRACE_X_DATADOG_TAGS_MAX_LENGTH, "128") + System.setProperty(PREFIX + TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, "all") + System.setProperty(PREFIX + TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, "all") + when: Config config = new Config() @@ -551,6 +563,9 @@ class ConfigTest extends DDSpecification { config.dynamicInstrumentationInstrumentTheWorld == "method" config.dynamicInstrumentationExcludeFiles == "exclude file" + config.cloudRequestPayloadTagging == ["all"] + config.cloudResponsePayloadTagging == ["all"] + config.xDatadogTagsMaxLength == 128 } @@ -570,6 +585,8 @@ class ConfigTest extends DDSpecification { environmentVariables.set(DD_TRACE_LONG_RUNNING_ENABLED, "true") environmentVariables.set(DD_TRACE_LONG_RUNNING_FLUSH_INTERVAL, "81") environmentVariables.set(DD_TRACE_HEADER_TAGS, "*") + environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, "all") + environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, "all") when: def config = new Config() @@ -591,6 +608,8 @@ class ConfigTest extends DDSpecification { config.getLongRunningTraceFlushInterval() == 81 config.requestHeaderTags == ["*":"http.request.headers."] config.responseHeaderTags == ["*":"http.response.headers."] + config.cloudRequestPayloadTagging == ["all"] + config.cloudResponsePayloadTagging == ["all"] } def "sys props override env vars"() { @@ -600,6 +619,8 @@ class ConfigTest extends DDSpecification { environmentVariables.set(DD_PRIORITIZATION_TYPE_ENV, "EnsureTrace") environmentVariables.set(DD_TRACE_AGENT_PORT_ENV, "777") environmentVariables.set(DD_TRACE_LONG_RUNNING_ENABLED, "false") + environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, "all") + environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, "all") System.setProperty(PREFIX + SERVICE_NAME, "what we actually want") System.setProperty(PREFIX + WRITER_TYPE, "DDAgentWriter") @@ -607,6 +628,8 @@ class ConfigTest extends DDSpecification { System.setProperty(PREFIX + AGENT_HOST, "somewhere") System.setProperty(PREFIX + TRACE_AGENT_PORT, "123") System.setProperty(PREFIX + TRACE_LONG_RUNNING_ENABLED, "true") + System.setProperty(PREFIX + TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, "\$.path1,\$.path2") + System.setProperty(PREFIX + TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, "\$.path3,\$.path4") when: def config = new Config() @@ -619,6 +642,8 @@ class ConfigTest extends DDSpecification { config.agentUrl == "http://somewhere:123" config.longRunningTraceEnabled config.longRunningTraceFlushInterval == 120 + config.cloudRequestPayloadTagging == ["\$.path1", "\$.path2"] + config.cloudResponsePayloadTagging == ["\$.path3", "\$.path4"] } def "default when configured incorrectly"() { @@ -2596,6 +2621,25 @@ class ConfigTest extends DDSpecification { ConfigHelper.get().setConfigInversionStrict(strictness) } + def "set cloud payload tagging to null if invalid or empty value is passed as env"() { + setup: + environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, "") + environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, "") + + when: + def config = new Config() + + then: + config.cloudRequestPayloadTagging == [] + config.@cloudRequestPayloadTagging == null + + config.cloudResponsePayloadTagging == [] + config.@cloudResponsePayloadTagging == null + + !config.isCloudRequestPayloadTaggingEnabled() + !config.isCloudResponsePayloadTaggingEnabled() + } + def "verify rule config #name"() { setup: environmentVariables.set("DD_TRACE_TEST_ENABLED", "true") From 5030a3343644f3f63d81103e692f5a9d4ad602aa Mon Sep 17 00:00:00 2001 From: Olivier John Ndjike Nzia Date: Fri, 7 Nov 2025 15:36:55 -0500 Subject: [PATCH 2/4] parse cloud payload tags during config --- .../tagprocessor/PayloadTagsProcessor.java | 31 ++-- .../json => core/util}/JsonStreamParser.java | 3 +- .../PayloadTagsProcessorTest.groovy | 2 +- .../main/java/datadog/trace/api/Config.java | 44 +++-- .../datadog/trace/util}/json/JsonPath.java | 2 +- .../trace/util}/json/JsonPathParser.java | 26 ++- .../datadog/trace/util}/json/PathCursor.java | 2 +- .../datadog/trace/api/ConfigTest.groovy | 151 ++++++++++-------- .../util}/json/JsonPathParserSpec.groovy | 19 ++- .../trace/util}/json/JsonPathSpec.groovy | 2 +- .../trace/util}/json/PathCursorSpec.groovy | 2 +- 11 files changed, 178 insertions(+), 106 deletions(-) rename dd-trace-core/src/main/java/datadog/trace/{payloadtags/json => core/util}/JsonStreamParser.java (98%) rename {dd-trace-core/src/main/java/datadog/trace/payloadtags => internal-api/src/main/java/datadog/trace/util}/json/JsonPath.java (99%) rename {dd-trace-core/src/main/java/datadog/trace/payloadtags => internal-api/src/main/java/datadog/trace/util}/json/JsonPathParser.java (91%) rename {dd-trace-core/src/main/java/datadog/trace/payloadtags => internal-api/src/main/java/datadog/trace/util}/json/PathCursor.java (97%) rename {dd-trace-core/src/test/groovy/datadog/trace/payloadtags => internal-api/src/test/groovy/datadog/trace/util}/json/JsonPathParserSpec.groovy (90%) rename {dd-trace-core/src/test/groovy/datadog/trace/payloadtags => internal-api/src/test/groovy/datadog/trace/util}/json/JsonPathSpec.groovy (99%) rename {dd-trace-core/src/test/groovy/datadog/trace/payloadtags => internal-api/src/test/groovy/datadog/trace/util}/json/PathCursorSpec.groovy (98%) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java index a52fa938c3e..9f2c9a95b61 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java @@ -1,19 +1,19 @@ package datadog.trace.core.tagprocessor; +import static datadog.trace.util.json.JsonPathParser.parseJsonPaths; + import datadog.trace.api.Config; import datadog.trace.api.ConfigDefaults; import datadog.trace.api.TagMap; import datadog.trace.api.telemetry.LogCollector; import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink; import datadog.trace.core.DDSpanContext; +import datadog.trace.core.util.JsonStreamParser; import datadog.trace.payloadtags.PayloadTagsData; -import datadog.trace.payloadtags.json.JsonPath; -import datadog.trace.payloadtags.json.JsonPathParser; -import datadog.trace.payloadtags.json.JsonStreamParser; -import datadog.trace.payloadtags.json.PathCursor; +import datadog.trace.util.json.JsonPath; +import datadog.trace.util.json.PathCursor; import java.io.InputStream; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,7 +38,7 @@ public static PayloadTagsProcessor create(Config config) { new RedactionRules.Builder() .addRedactionJsonPaths(ConfigDefaults.DEFAULT_CLOUD_COMMON_PAYLOAD_TAGGING) .addRedactionJsonPaths(ConfigDefaults.DEFAULT_CLOUD_REQUEST_PAYLOAD_TAGGING) - .addRedactionJsonPaths(config.getCloudRequestPayloadTagging()) + .addParsedRedactionJsonPaths(config.getCloudRequestPayloadTagging()) .build()); } if (config.isCloudResponsePayloadTaggingEnabled()) { @@ -47,7 +47,7 @@ public static PayloadTagsProcessor create(Config config) { new RedactionRules.Builder() .addRedactionJsonPaths(ConfigDefaults.DEFAULT_CLOUD_COMMON_PAYLOAD_TAGGING) .addRedactionJsonPaths(ConfigDefaults.DEFAULT_CLOUD_RESPONSE_PAYLOAD_TAGGING) - .addRedactionJsonPaths(config.getCloudResponsePayloadTagging()) + .addParsedRedactionJsonPaths(config.getCloudResponsePayloadTagging()) .build()); } if (redactionRulesByTagPrefix.isEmpty()) { @@ -145,20 +145,9 @@ public RedactionRules.Builder addRedactionJsonPaths(List jsonPaths) { return this; } - private static List parseJsonPaths(List rules) { - if (rules.isEmpty() || rules.size() == 1 && rules.get(0).equalsIgnoreCase("all")) { - return Collections.emptyList(); - } - List result = new ArrayList<>(rules.size()); - for (String rule : rules) { - try { - JsonPath jp = JsonPathParser.parse(rule); - result.add(jp); - } catch (Exception ex) { - log.warn("Skipping failed to parse redaction rule '{}'. {}", rule, ex.getMessage()); - } - } - return result; + public RedactionRules.Builder addParsedRedactionJsonPaths(List jsonPaths) { + this.redactionRules.addAll(jsonPaths); + return this; } RedactionRules build() { diff --git a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonStreamParser.java b/dd-trace-core/src/main/java/datadog/trace/core/util/JsonStreamParser.java similarity index 98% rename from dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonStreamParser.java rename to dd-trace-core/src/main/java/datadog/trace/core/util/JsonStreamParser.java index 916e65afb72..b8d1050cfb3 100644 --- a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonStreamParser.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/util/JsonStreamParser.java @@ -1,6 +1,7 @@ -package datadog.trace.payloadtags.json; +package datadog.trace.core.util; import com.squareup.moshi.JsonReader; +import datadog.trace.util.json.PathCursor; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; diff --git a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy index 40ef22a3f4e..b5f97c053ca 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy +++ b/dd-trace-core/src/test/groovy/datadog/trace/core/tagprocessor/PayloadTagsProcessorTest.groovy @@ -3,7 +3,7 @@ package datadog.trace.core.tagprocessor import com.squareup.moshi.JsonWriter import datadog.trace.payloadtags.PayloadTagsData import datadog.trace.payloadtags.PayloadTagsData.PathAndValue -import datadog.trace.payloadtags.json.PathCursor +import datadog.trace.util.json.PathCursor import datadog.trace.test.util.DDSpecification import datadog.trace.api.Config import okio.Buffer diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 992fcdef4ff..19d676f9870 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -660,6 +660,7 @@ import static datadog.trace.util.CollectionUtils.tryMakeImmutableList; import static datadog.trace.util.CollectionUtils.tryMakeImmutableSet; import static datadog.trace.util.ConfigStrings.propertyNameToEnvironmentVariableName; +import static datadog.trace.util.json.JsonPathParser.parseJsonPaths; import datadog.environment.JavaVirtualMachine; import datadog.environment.OperatingSystem; @@ -690,6 +691,7 @@ import datadog.trace.util.PidHelper; import datadog.trace.util.RandomUtils; import datadog.trace.util.Strings; +import datadog.trace.util.json.JsonPath; import datadog.trace.util.throwable.FatalAgentMisconfigurationError; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.BufferedReader; @@ -1260,8 +1262,8 @@ public static String getHostName() { private final String agentlessLogSubmissionProduct; private final Set cloudPayloadTaggingServices; - @Nullable private final List cloudRequestPayloadTagging; - @Nullable private final List cloudResponsePayloadTagging; + @Nullable private final List cloudRequestPayloadTagging; + @Nullable private final List cloudResponsePayloadTagging; private final int cloudPayloadTaggingMaxDepth; private final int cloudPayloadTaggingMaxTags; @@ -2785,17 +2787,35 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) List cloudReqPayloadTaggingConf = configProvider.getList(TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, null); - this.cloudRequestPayloadTagging = - cloudReqPayloadTaggingConf != null && cloudReqPayloadTaggingConf.isEmpty() - ? null - : cloudReqPayloadTaggingConf; + if (null == cloudReqPayloadTaggingConf) { + // if no configuration is provided, disable payload tagging + this.cloudRequestPayloadTagging = null; + } else if (cloudReqPayloadTaggingConf.size() == 1 + && cloudReqPayloadTaggingConf.get(0).equalsIgnoreCase("all")) { + // if "all" is specified enable all JSON paths + this.cloudRequestPayloadTagging = Collections.emptyList(); + } else { + // parse and validate JSON paths. if none are valid, disable payload tagging + List validRequestJsonPaths = parseJsonPaths(cloudReqPayloadTaggingConf); + this.cloudRequestPayloadTagging = + validRequestJsonPaths.isEmpty() ? null : validRequestJsonPaths; + } List cloudRespPayloadTaggingConf = configProvider.getList(TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, null); - this.cloudResponsePayloadTagging = - cloudRespPayloadTaggingConf != null && cloudRespPayloadTaggingConf.isEmpty() - ? null - : cloudRespPayloadTaggingConf; + if (null == cloudRespPayloadTaggingConf) { + // if no configuration is provided, disable payload tagging + this.cloudResponsePayloadTagging = null; + } else if (cloudRespPayloadTaggingConf.size() == 1 + && cloudRespPayloadTaggingConf.get(0).equalsIgnoreCase("all")) { + // if "all" is specified enable all JSON paths + this.cloudResponsePayloadTagging = Collections.emptyList(); + } else { + // parse and validate JSON paths. if none are valid, disable payload tagging + List validResponseJsonPaths = parseJsonPaths(cloudRespPayloadTaggingConf); + this.cloudResponsePayloadTagging = + validResponseJsonPaths.isEmpty() ? null : validResponseJsonPaths; + } this.cloudPayloadTaggingMaxDepth = configProvider.getInteger(TRACE_CLOUD_PAYLOAD_TAGGING_MAX_DEPTH, 10); @@ -5215,7 +5235,7 @@ public boolean isCloudPayloadTaggingEnabled() { return isCloudRequestPayloadTaggingEnabled() || isCloudResponsePayloadTaggingEnabled(); } - public List getCloudRequestPayloadTagging() { + public List getCloudRequestPayloadTagging() { return cloudRequestPayloadTagging == null ? Collections.emptyList() : cloudRequestPayloadTagging; @@ -5225,7 +5245,7 @@ public boolean isCloudRequestPayloadTaggingEnabled() { return cloudRequestPayloadTagging != null; } - public List getCloudResponsePayloadTagging() { + public List getCloudResponsePayloadTagging() { return cloudResponsePayloadTagging == null ? Collections.emptyList() : cloudResponsePayloadTagging; diff --git a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonPath.java b/internal-api/src/main/java/datadog/trace/util/json/JsonPath.java similarity index 99% rename from dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonPath.java rename to internal-api/src/main/java/datadog/trace/util/json/JsonPath.java index a65a1726db7..15774b80fb9 100644 --- a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonPath.java +++ b/internal-api/src/main/java/datadog/trace/util/json/JsonPath.java @@ -1,4 +1,4 @@ -package datadog.trace.payloadtags.json; +package datadog.trace.util.json; import java.util.ArrayList; import java.util.Collection; diff --git a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonPathParser.java b/internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java similarity index 91% rename from dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonPathParser.java rename to internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java index 2d75d00fffc..ddf4f102d3a 100644 --- a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/JsonPathParser.java +++ b/internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java @@ -1,7 +1,13 @@ -package datadog.trace.payloadtags.json; +package datadog.trace.util.json; import static java.lang.Character.isDigit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class JsonPathParser { public static final class ParseError extends Exception { @@ -26,6 +32,24 @@ public ParseError(CharSequence path, int position, String error) { private static final char DOUBLE_QUOTE = '"'; private static final char ESC = '\\'; + private static final Logger log = LoggerFactory.getLogger(JsonPathParser.class); + + public static List parseJsonPaths(List rules) { + if (rules.isEmpty() || rules.size() == 1 && rules.get(0).equalsIgnoreCase("all")) { + return Collections.emptyList(); + } + List result = new ArrayList<>(rules.size()); + for (String rule : rules) { + try { + JsonPath jp = JsonPathParser.parse(rule); + result.add(jp); + } catch (Exception ex) { + log.warn("Skipping failed to parse redaction rule '{}'. {}", rule, ex.getMessage()); + } + } + return result; + } + public static JsonPath parse(String path) throws ParseError { Cursor cur = new Cursor(path); diff --git a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/PathCursor.java b/internal-api/src/main/java/datadog/trace/util/json/PathCursor.java similarity index 97% rename from dd-trace-core/src/main/java/datadog/trace/payloadtags/json/PathCursor.java rename to internal-api/src/main/java/datadog/trace/util/json/PathCursor.java index 1b23b315ccb..5048c724e01 100644 --- a/dd-trace-core/src/main/java/datadog/trace/payloadtags/json/PathCursor.java +++ b/internal-api/src/main/java/datadog/trace/util/json/PathCursor.java @@ -1,4 +1,4 @@ -package datadog.trace.payloadtags.json; +package datadog.trace.util.json; import java.util.Arrays; diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index 44f499b6b60..7a70ea45a52 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -5,6 +5,7 @@ import datadog.trace.bootstrap.config.provider.AgentArgsInjector import datadog.trace.bootstrap.config.provider.ConfigConverter import datadog.trace.bootstrap.config.provider.ConfigProvider import datadog.trace.test.util.DDSpecification +import datadog.trace.util.json.JsonPath import datadog.trace.util.throwable.FatalAgentMisconfigurationError import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_ERROR_STATUSES @@ -373,8 +374,8 @@ class ConfigTest extends DDSpecification { config.debuggerExceptionEnabled == true config.jdkSocketEnabled == false - config.cloudRequestPayloadTagging == ["all"] - config.cloudResponsePayloadTagging == ["all"] + config.cloudRequestPayloadTagging == [] + config.cloudResponsePayloadTagging == [] config.xDatadogTagsMaxLength == 128 } @@ -563,8 +564,8 @@ class ConfigTest extends DDSpecification { config.dynamicInstrumentationInstrumentTheWorld == "method" config.dynamicInstrumentationExcludeFiles == "exclude file" - config.cloudRequestPayloadTagging == ["all"] - config.cloudResponsePayloadTagging == ["all"] + config.cloudRequestPayloadTagging == [] + config.cloudResponsePayloadTagging == [] config.xDatadogTagsMaxLength == 128 } @@ -606,10 +607,10 @@ class ConfigTest extends DDSpecification { config.xDatadogTagsMaxLength == 42 config.isLongRunningTraceEnabled() config.getLongRunningTraceFlushInterval() == 81 - config.requestHeaderTags == ["*":"http.request.headers."] - config.responseHeaderTags == ["*":"http.response.headers."] - config.cloudRequestPayloadTagging == ["all"] - config.cloudResponsePayloadTagging == ["all"] + config.requestHeaderTags == ["*": "http.request.headers."] + config.responseHeaderTags == ["*": "http.response.headers."] + config.cloudRequestPayloadTagging == [] + config.cloudResponsePayloadTagging == [] } def "sys props override env vars"() { @@ -620,7 +621,7 @@ class ConfigTest extends DDSpecification { environmentVariables.set(DD_TRACE_AGENT_PORT_ENV, "777") environmentVariables.set(DD_TRACE_LONG_RUNNING_ENABLED, "false") environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, "all") - environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, "all") + environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, "") System.setProperty(PREFIX + SERVICE_NAME, "what we actually want") System.setProperty(PREFIX + WRITER_TYPE, "DDAgentWriter") @@ -628,8 +629,8 @@ class ConfigTest extends DDSpecification { System.setProperty(PREFIX + AGENT_HOST, "somewhere") System.setProperty(PREFIX + TRACE_AGENT_PORT, "123") System.setProperty(PREFIX + TRACE_LONG_RUNNING_ENABLED, "true") - System.setProperty(PREFIX + TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, "\$.path1,\$.path2") - System.setProperty(PREFIX + TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, "\$.path3,\$.path4") + System.setProperty(PREFIX + TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING, "") + System.setProperty(PREFIX + TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING, "all") when: def config = new Config() @@ -642,8 +643,10 @@ class ConfigTest extends DDSpecification { config.agentUrl == "http://somewhere:123" config.longRunningTraceEnabled config.longRunningTraceFlushInterval == 120 - config.cloudRequestPayloadTagging == ["\$.path1", "\$.path2"] - config.cloudResponsePayloadTagging == ["\$.path3", "\$.path4"] + config.cloudRequestPayloadTagging == [] + config.cloudResponsePayloadTagging == [] + config.@cloudRequestPayloadTagging == null + config.@cloudResponsePayloadTagging == [] } def "default when configured incorrectly"() { @@ -798,7 +801,7 @@ class ConfigTest extends DDSpecification { config.mergedSpanTags == [b: "2", c: "3"] config.mergedJmxTags == [b: "2", d: "4", (RUNTIME_ID_TAG): config.getRuntimeId(), (SERVICE_TAG): config.serviceName] config.requestHeaderTags == [e: "five"] - config.baggageMapping == [f: "six",g: "g"] + config.baggageMapping == [f: "six", g: "g"] config.httpServerErrorStatuses == toBitSet((122..457)) config.httpClientErrorStatuses == toBitSet((111..111)) config.httpClientSplitByDomain == true @@ -1070,8 +1073,8 @@ class ConfigTest extends DDSpecification { value | expected // null means default value // spotless:off "1" | [1] - "3,13,400-403" | [3,13,400,401,402,403] - "2,10,13-15" | [2,10,13,14,15] + "3,13,400-403" | [3, 13, 400, 401, 402, 403] + "2,10,13-15" | [2, 10, 13, 14, 15] "a" | null "" | null "1000" | null @@ -1767,7 +1770,7 @@ class ConfigTest extends DDSpecification { //verify expected behavior when not enabled under feature flag config.logsInjectionEnabled == true - config.globalTags == [env:"test", aKey:"aVal", bKey:"bVal"] + config.globalTags == [env: "test", aKey: "aVal", bKey: "bVal"] } def "verify behavior of DD_TRACE_EXPERIMENTAL_FEATURE_ENABLED when value is 'all'"() { @@ -1842,18 +1845,18 @@ class ConfigTest extends DDSpecification { where: // spotless:off - value | tClass | expected - "true" | Boolean | true - "trUe" | Boolean | true - "false" | Boolean | false - "False" | Boolean | false - "1" | Boolean | true - "0" | Boolean | false - "42.42" | Float | 42.42f - "42.42" | Double | 42.42 - "44" | Integer | 44 - "45" | Long | 45 - "46" | Short | 46 + value | tClass | expected + "true" | Boolean | true + "trUe" | Boolean | true + "false" | Boolean | false + "False" | Boolean | false + "1" | Boolean | true + "0" | Boolean | false + "42.42" | Float | 42.42f + "42.42" | Double | 42.42 + "44" | Integer | 44 + "45" | Long | 45 + "46" | Short | 46 // spotless:on } @@ -2456,12 +2459,12 @@ class ConfigTest extends DDSpecification { where: configuredFlushInterval | flushInterval - "invalid" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL - "-1" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL - "9" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL - "451" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL - "10" | 10 - "450" | 450 + "invalid" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL + "-1" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL + "9" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL + "451" | DEFAULT_TRACE_LONG_RUNNING_INITIAL_FLUSH_INTERVAL + "10" | 10 + "450" | 450 } def "ssi injection enabled"() { @@ -2507,12 +2510,12 @@ class ConfigTest extends DDSpecification { where: configuredFlushInterval | flushInterval - "invalid" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL - "-1" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL - "19" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL - "451" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL - "20" | 20 - "450" | 450 + "invalid" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL + "-1" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL + "19" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL + "451" | DEFAULT_TRACE_LONG_RUNNING_FLUSH_INTERVAL + "20" | 20 + "450" | 450 } def "partial flush and min spans interaction"() { @@ -2555,10 +2558,10 @@ class ConfigTest extends DDSpecification { where: // spotless:off - enablementMode | expectedEnabled | expectedStartDelay | expectedStartForceFirst - "true" | true | 1 | true - "false" | false | 1 | true - "auto" | true | PROFILING_START_DELAY_DEFAULT | PROFILING_START_FORCE_FIRST_DEFAULT + enablementMode | expectedEnabled | expectedStartDelay | expectedStartForceFirst + "true" | true | 1 | true + "false" | false | 1 | true + "auto" | true | PROFILING_START_DELAY_DEFAULT | PROFILING_START_FORCE_FIRST_DEFAULT // spotless:on } @@ -2607,38 +2610,56 @@ class ConfigTest extends DDSpecification { "datad0g.com" | "https://all-http-intake.logs.datad0g.com/api/v2/apmtelemetry" } + static jp() { + return JsonPath.Builder.start() + } + + def "set cloud payload tagging config"() { + setup: + environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, reqEnv) + environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, respEnv) + + when: + def config = new Config() + + then: + if(config.@cloudRequestPayloadTagging == null){ + expectedreqconfig == null + }else{ + config.@cloudRequestPayloadTagging.toString()== expectedreqconfig.toString() + } + + if(config.@cloudResponsePayloadTagging == null){ + expectedrespconfig == null + }else{ + config.@cloudResponsePayloadTagging.toString()== expectedrespconfig.toString() + } + + where: + reqEnv | respEnv | expectedreqconfig | expectedrespconfig + "all" | "all" | [] | [] + "all,invalid" | "all,invalid" | null | null + "" | "" | null | null + "invalid" | "invalid" | null | null + "\$.a" | "\$.b" | [jp().name('a').build()] | [jp().name('b').build()] + "\$.a,invalid" | "\$.b,invalid" | [jp().name('a').build()] | [jp().name('b').build()] + } + + // Subclass for setting Strictness of ConfigHelper when using fake configs static class ConfigTestWithFakes extends ConfigTest { def strictness - def setup(){ + def setup() { strictness = ConfigHelper.get().configInversionStrictFlag() ConfigHelper.get().setConfigInversionStrict(ConfigHelper.StrictnessPolicy.TEST) } - def cleanup(){ + def cleanup() { ConfigHelper.get().setConfigInversionStrict(strictness) } - def "set cloud payload tagging to null if invalid or empty value is passed as env"() { - setup: - environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, "") - environmentVariables.set(DD_TRACE_CLOUD_RESPONSE_PAYLOAD_TAGGING_ENV, "") - - when: - def config = new Config() - - then: - config.cloudRequestPayloadTagging == [] - config.@cloudRequestPayloadTagging == null - - config.cloudResponsePayloadTagging == [] - config.@cloudResponsePayloadTagging == null - - !config.isCloudRequestPayloadTaggingEnabled() - !config.isCloudResponsePayloadTaggingEnabled() - } def "verify rule config #name"() { setup: diff --git a/dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/JsonPathParserSpec.groovy b/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy similarity index 90% rename from dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/JsonPathParserSpec.groovy rename to internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy index d2c161426f9..e580b5a94cc 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/JsonPathParserSpec.groovy +++ b/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy @@ -1,4 +1,4 @@ -package datadog.trace.payloadtags.json +package datadog.trace.util.json import spock.lang.Specification @@ -8,6 +8,23 @@ class JsonPathParserSpec extends Specification { return JsonPath.Builder.start() } + def "parse lists of json-path patterns for payload tagging"() { + expect: + JsonPathParser.parseJsonPaths(paths).toString() == expected.toString() + + where: + paths | expected + ['$.a'] | [jp().name('a').build()] + ['$.a.b.c', '$.x.y.z'] | [ + jp().name('a').name('b').name('c').build(), + jp().name('x').name('y').name('z').build() + ] + ['$.BarFoo', "invalid"] | [jp().name("BarFoo").build()] + ['all'] | [] + ['all', 'all'] | [] + ['invalid1', "invalid2"] | [] + } + def "parse correct json-path patterns"() { expect: JsonPathParser.parse(path).toString() == expected.toString() diff --git a/dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/JsonPathSpec.groovy b/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathSpec.groovy similarity index 99% rename from dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/JsonPathSpec.groovy rename to internal-api/src/test/groovy/datadog/trace/util/json/JsonPathSpec.groovy index 42293126bdf..38412ca4dbf 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/JsonPathSpec.groovy +++ b/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathSpec.groovy @@ -1,4 +1,4 @@ -package datadog.trace.payloadtags.json +package datadog.trace.util.json import spock.lang.Specification diff --git a/dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/PathCursorSpec.groovy b/internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy similarity index 98% rename from dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/PathCursorSpec.groovy rename to internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy index eba5530beb3..a1088496f4c 100644 --- a/dd-trace-core/src/test/groovy/datadog/trace/payloadtags/json/PathCursorSpec.groovy +++ b/internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy @@ -1,4 +1,4 @@ -package datadog.trace.payloadtags.json +package datadog.trace.util.json import spock.lang.Specification From 1a04992777803a6757debdebcebca423c1eea142 Mon Sep 17 00:00:00 2001 From: Olivier John Ndjike Nzia Date: Sun, 9 Nov 2025 13:17:27 -0500 Subject: [PATCH 3/4] add branch coverage for pathcursor --- .../trace/util/json/PathCursorSpec.groovy | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy b/internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy index a1088496f4c..e2c70858755 100644 --- a/internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy +++ b/internal-api/src/test/groovy/datadog/trace/util/json/PathCursorSpec.groovy @@ -133,4 +133,38 @@ class PathCursorSpec extends Specification { then: p2.push("c").toString("") == ".a.3.b.c" } + + def "get item at index of path cursor"() { + expect: + p().push("a").push(3).get(0) == "a" + p().push("a").push(3).get(1) == 3 + } + + def "pop on empty cursor does nothing"() { + def p = p() + when: + p.pop() + + then: + p.length() == 0 + p.toString("") == "" + + when: + p.push("a").pop() + p.pop() + + then: + p.length() == 0 + p.toString("") == "" + } + + def "advance on empty cursor does nothing"() { + when: + def p = p() + p.advance() + + then: + p.length() == 0 + p.toString("") == "" + } } From 3cfbecacf9a19a08e4e8bf08c917a06eb0225277 Mon Sep 17 00:00:00 2001 From: Olivier John Ndjike Nzia Date: Tue, 11 Nov 2025 12:07:19 -0500 Subject: [PATCH 4/4] apply pr suggestions --- .../tagprocessor/PayloadTagsProcessor.java | 4 ++ .../trace/util/json/JsonPathParser.java | 6 +-- .../datadog/trace/api/ConfigTest.groovy | 41 ++++++++----------- .../trace/util/json/JsonPathParserSpec.groovy | 17 ++++---- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java index 9f2c9a95b61..87544b62aeb 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/tagprocessor/PayloadTagsProcessor.java @@ -146,6 +146,10 @@ public RedactionRules.Builder addRedactionJsonPaths(List jsonPaths) { } public RedactionRules.Builder addParsedRedactionJsonPaths(List jsonPaths) { + if (null == jsonPaths) { + log.warn("Provided JsonPaths list is null, skipping."); + return this; + } this.redactionRules.addAll(jsonPaths); return this; } diff --git a/internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java b/internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java index ddf4f102d3a..4bb2218da49 100644 --- a/internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java +++ b/internal-api/src/main/java/datadog/trace/util/json/JsonPathParser.java @@ -35,16 +35,16 @@ public ParseError(CharSequence path, int position, String error) { private static final Logger log = LoggerFactory.getLogger(JsonPathParser.class); public static List parseJsonPaths(List rules) { - if (rules.isEmpty() || rules.size() == 1 && rules.get(0).equalsIgnoreCase("all")) { + if (null == rules || rules.isEmpty()) { return Collections.emptyList(); } List result = new ArrayList<>(rules.size()); for (String rule : rules) { try { - JsonPath jp = JsonPathParser.parse(rule); + JsonPath jp = parse(rule); result.add(jp); } catch (Exception ex) { - log.warn("Skipping failed to parse redaction rule '{}'. {}", rule, ex.getMessage()); + log.warn("Failed to parse redaction rule '{}'. {}. Skipping rule.", rule, ex.getMessage()); } } return result; diff --git a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index e9e7ce9865c..028e5f0e29b 100644 --- a/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/internal-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -5,7 +5,6 @@ import datadog.trace.bootstrap.config.provider.AgentArgsInjector import datadog.trace.bootstrap.config.provider.ConfigConverter import datadog.trace.bootstrap.config.provider.ConfigProvider import datadog.trace.test.util.DDSpecification -import datadog.trace.util.json.JsonPath import datadog.trace.util.throwable.FatalAgentMisconfigurationError import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_CLIENT_ERROR_STATUSES import static datadog.trace.api.ConfigDefaults.DEFAULT_HTTP_SERVER_ERROR_STATUSES @@ -937,8 +936,8 @@ class ConfigTest extends DDSpecification { config.longRunningTraceFlushInterval == 120 config.cloudRequestPayloadTagging == [] config.cloudResponsePayloadTagging == [] - config.@cloudRequestPayloadTagging == null - config.@cloudResponsePayloadTagging == [] + !config.isCloudRequestPayloadTaggingEnabled() + config.isCloudResponsePayloadTaggingEnabled() } def "default when configured incorrectly"() { @@ -2902,10 +2901,6 @@ class ConfigTest extends DDSpecification { "datad0g.com" | "https://all-http-intake.logs.datad0g.com/api/v2/apmtelemetry" } - static jp() { - return JsonPath.Builder.start() - } - def "set cloud payload tagging config"() { setup: environmentVariables.set(DD_TRACE_CLOUD_REQUEST_PAYLOAD_TAGGING_ENV, reqEnv) @@ -2915,29 +2910,29 @@ class ConfigTest extends DDSpecification { def config = new Config() then: - if(config.@cloudRequestPayloadTagging == null){ - expectedreqconfig == null - }else{ - config.@cloudRequestPayloadTagging.toString()== expectedreqconfig.toString() + if (expectedReqConfig == null) { + // if expected config is null, then the feature should be disabled + assert !config.isCloudRequestPayloadTaggingEnabled() + } else { + assert config.cloudRequestPayloadTagging.toString() == expectedReqConfig } - if(config.@cloudResponsePayloadTagging == null){ - expectedrespconfig == null - }else{ - config.@cloudResponsePayloadTagging.toString()== expectedrespconfig.toString() + if (expectedRespConfig == null) { + assert !config.isCloudResponsePayloadTaggingEnabled() + } else { + assert config.cloudResponsePayloadTagging.toString() == expectedRespConfig } where: - reqEnv | respEnv | expectedreqconfig | expectedrespconfig - "all" | "all" | [] | [] - "all,invalid" | "all,invalid" | null | null - "" | "" | null | null - "invalid" | "invalid" | null | null - "\$.a" | "\$.b" | [jp().name('a').build()] | [jp().name('b').build()] - "\$.a,invalid" | "\$.b,invalid" | [jp().name('a').build()] | [jp().name('b').build()] + reqEnv | respEnv | expectedReqConfig | expectedRespConfig + "all" | "all" | '[]' | '[]' + "all,invalid" | "all,invalid" | null | null + "" | "" | null | null + "invalid" | "invalid" | null | null + "\$.a" | "\$.b" | '[$[\'a\']]' | '[$[\'b\']]' + "\$.a,invalid" | "\$.b,invalid" | '[$[\'a\']]' | '[$[\'b\']]' } - // Subclass for setting Strictness of ConfigHelper when using fake configs static class ConfigTestWithFakes extends ConfigTest { diff --git a/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy b/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy index e580b5a94cc..5c3be6fc1c2 100644 --- a/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy +++ b/internal-api/src/test/groovy/datadog/trace/util/json/JsonPathParserSpec.groovy @@ -10,19 +10,16 @@ class JsonPathParserSpec extends Specification { def "parse lists of json-path patterns for payload tagging"() { expect: - JsonPathParser.parseJsonPaths(paths).toString() == expected.toString() + JsonPathParser.parseJsonPaths(paths).toString() == expected where: paths | expected - ['$.a'] | [jp().name('a').build()] - ['$.a.b.c', '$.x.y.z'] | [ - jp().name('a').name('b').name('c').build(), - jp().name('x').name('y').name('z').build() - ] - ['$.BarFoo', "invalid"] | [jp().name("BarFoo").build()] - ['all'] | [] - ['all', 'all'] | [] - ['invalid1', "invalid2"] | [] + ['$.a'] | '[$[\'a\']]' + ['$.a.b.c', '$.x.y.z'] | '[$[\'a\'][\'b\'][\'c\'], $[\'x\'][\'y\'][\'z\']]' + ['$.BarFoo', "invalid"] | '[$[\'BarFoo\']]' + ['all'] | '[]' + ['all', 'all'] | '[]' + ['invalid1', "invalid2"] | '[]' } def "parse correct json-path patterns"() {