diff --git a/CHANGELOG.md b/CHANGELOG.md index f00f2d73736..a3250503787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Fixes + +- Do not use named capturing groups for regular expressions ([#4652](https://github.com/getsentry/sentry-java/pull/4652)) + - This fixes a crash on Android versions below 8.0 (API level 26) + ### Features - Add onDiscard to enable users to track the type and amount of data discarded before reaching Sentry ([#4612](https://github.com/getsentry/sentry-java/pull/4612)) diff --git a/sentry/src/main/java/io/sentry/SentryTraceHeader.java b/sentry/src/main/java/io/sentry/SentryTraceHeader.java index 71e6d01bc7c..a4fb1df68f5 100644 --- a/sentry/src/main/java/io/sentry/SentryTraceHeader.java +++ b/sentry/src/main/java/io/sentry/SentryTraceHeader.java @@ -15,10 +15,10 @@ public final class SentryTraceHeader { private final @NotNull SpanId spanId; private final @Nullable Boolean sampled; + // Use numbered capture groups for Android API level < 26 compatibility private static final Pattern SENTRY_TRACEPARENT_HEADER_REGEX = Pattern.compile( - "^[ \\t]*(?[0-9a-f]{32})-(?[0-9a-f]{16})(?-[01])?[ \\t]*$", - Pattern.CASE_INSENSITIVE); + "^[ \\t]*([0-9a-f]{32})-([0-9a-f]{16})(-[01])?[ \\t]*$", Pattern.CASE_INSENSITIVE); public SentryTraceHeader( final @NotNull SentryId traceId, @@ -33,14 +33,15 @@ public SentryTraceHeader(final @NotNull String value) throws InvalidSentryTraceH Matcher matcher = SENTRY_TRACEPARENT_HEADER_REGEX.matcher(value); boolean matchesExist = matcher.matches(); - if (!matchesExist || matcher.group("traceId") == null || matcher.group("spanId") == null) { + if (!matchesExist) { throw new InvalidSentryTraceHeaderException(value); } - this.traceId = new SentryId(matcher.group("traceId")); - this.spanId = new SpanId(matcher.group("spanId")); - this.sampled = - matcher.group("sampled") == null ? null : "1".equals(matcher.group("sampled").substring(1)); + this.traceId = new SentryId(matcher.group(1)); + this.spanId = new SpanId(matcher.group(2)); + + String sampled = matcher.group(3); + this.sampled = sampled == null ? null : "1".equals(sampled.substring(1)); } public @NotNull String getName() { diff --git a/sentry/src/test/java/io/sentry/SentryTraceHeaderTest.kt b/sentry/src/test/java/io/sentry/SentryTraceHeaderTest.kt index f63ef07f33d..7fd03ae80e9 100644 --- a/sentry/src/test/java/io/sentry/SentryTraceHeaderTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTraceHeaderTest.kt @@ -132,6 +132,26 @@ class SentryTraceHeaderTest { assertNull(header.isSampled) } + @Test + fun `handles header without sampling decision and leading whitespace`() { + val sentryId = SentryId() + val spanId = SpanId() + val header = SentryTraceHeader(" \t $sentryId-$spanId") + assertEquals(sentryId, header.traceId) + assertEquals(spanId, header.spanId) + assertNull(header.isSampled) + } + + @Test + fun `handles header without sampling decision and trailing whitespace`() { + val sentryId = SentryId() + val spanId = SpanId() + val header = SentryTraceHeader("$sentryId-$spanId \t ") + assertEquals(sentryId, header.traceId) + assertEquals(spanId, header.spanId) + assertNull(header.isSampled) + } + @Test fun `when sampling decision is not made, getValue returns header with traceId and spanId`() { val sentryId = SentryId()