@@ -10,6 +10,36 @@ export const SENTRY_TRACE_REGEX = new RegExp(
10
10
'[ \\t]*$' , // whitespace
11
11
) ;
12
12
13
+ // This is a normal base64 regex, modified to reflect that fact that we strip the
14
+ // trailing = or == off
15
+ const base64Stripped =
16
+ // any of the characters in the base64 "alphabet", in multiples of 4
17
+ '([a-zA-Z0-9+/]{4})*' +
18
+ // either nothing or 2 or 3 base64-alphabet characters (see
19
+ // https://en.wikipedia.org/wiki/Base64#Decoding_Base64_without_padding
20
+ // forwhy there's never only 1 extra character)
21
+ '([a-zA-Z0-9+/]{2,3})?' ;
22
+
23
+ // comma-delimited list of entries of the form `xxx=yyy`
24
+ const tracestateEntry = '[^=]+=[^=]+' ;
25
+ const TRACESTATE_ENTRIES_REGEX = new RegExp (
26
+ // one or more xxxxx=yyyy entries
27
+ `^(${ tracestateEntry } )+` +
28
+ // each entry except the last must be followed by a comma
29
+ '(,|$)' ,
30
+ ) ;
31
+
32
+ // this doesn't check that the value is valid, just that there's something there of the form `sentry=xxxx`
33
+ const SENTRY_TRACESTATE_ENTRY_REGEX = new RegExp (
34
+ // either sentry is the first entry or there's stuff immediately before it,
35
+ // ending in a commma (this prevents matching something like `coolsentry=xxx`)
36
+ '(?:^|.+,)' +
37
+ // sentry's part, not including the potential comma
38
+ '(sentry=[^,]*)' +
39
+ // either there's a comma and another vendor's entry or we end
40
+ '(?:,.+|$)' ,
41
+ ) ;
42
+
13
43
/**
14
44
* Determines if tracing is currently enabled.
15
45
*
@@ -28,6 +58,7 @@ export function hasTracingEnabled(options: Options): boolean {
28
58
*/
29
59
export function extractSentrytraceData ( header : string ) : TraceparentData | undefined {
30
60
const matches = header . match ( SENTRY_TRACE_REGEX ) ;
61
+
31
62
if ( matches ) {
32
63
let parentSampled : boolean | undefined ;
33
64
if ( matches [ 3 ] === '1' ) {
@@ -41,9 +72,56 @@ export function extractSentrytraceData(header: string): TraceparentData | undefi
41
72
parentSpanId : matches [ 2 ] ,
42
73
} ;
43
74
}
75
+
44
76
return undefined ;
45
77
}
46
78
79
+ type TracestateHeaderData = { sentry ?: string ; thirdparty ?: string } ;
80
+
81
+ /**
82
+ * Extract data from an incoming `tracestate` header
83
+ *
84
+ * @param header
85
+ * @returns Object containing data from the header
86
+ */
87
+ export function extractTracestateData ( header : string ) : TracestateHeaderData {
88
+ let sentryEntry , thirdPartyEntry ;
89
+
90
+ if ( header ) {
91
+ let before , after ;
92
+
93
+ // find sentry's entry, if any
94
+ const sentryMatch = SENTRY_TRACESTATE_ENTRY_REGEX . exec ( header ) ;
95
+
96
+ if ( sentryMatch !== null ) {
97
+ sentryEntry = sentryMatch [ 1 ] ;
98
+
99
+ // remove the commas after the split so we don't end up with `xxx=yyy,,zzz=qqq` (double commas) when we put them
100
+ // back together
101
+ [ before , after ] = header . split ( sentryEntry ) . map ( s => s . replace ( / ^ , * | , * $ / g, '' ) ) ;
102
+
103
+ // extract sentry's value from its entry and test to make sure it's valid; if it isn't, discard the entire entry
104
+ // so that a new one will be created by the Transaction contrcutor
105
+ const sentryValue = sentryEntry . replace ( 'sentry=' , '' ) ;
106
+ if ( ! new RegExp ( `^${ base64Stripped } $` ) . test ( sentryValue ) ) {
107
+ sentryEntry = undefined ;
108
+ }
109
+ } else {
110
+ // this could just as well be `before`; we just need to get the thirdparty data into one or the other since
111
+ // there's no valid Sentry entry
112
+ after = header ;
113
+ }
114
+
115
+ // if either thirdparty part is invalid or empty, remove it before gluing them together
116
+ const validThirdpartyEntries = [ before , after ] . filter ( x => TRACESTATE_ENTRIES_REGEX . test ( x || '' ) ) ;
117
+ if ( validThirdpartyEntries . length ) {
118
+ thirdPartyEntry = validThirdpartyEntries . join ( ',' ) ;
119
+ }
120
+ }
121
+
122
+ return { sentry : sentryEntry , thirdparty : thirdPartyEntry } ;
123
+ }
124
+
47
125
/** Grabs active transaction off scope, if any */
48
126
export function getActiveTransaction < T extends Transaction > ( hub : Hub = getCurrentHub ( ) ) : T | undefined {
49
127
return hub ?. getScope ( ) ?. getTransaction ( ) as T | undefined ;
0 commit comments