11package com .datadog .appsec .gateway ;
22
33import static com .datadog .appsec .event .data .MapDataBundle .Builder .CAPACITY_0_2 ;
4- import static com .datadog .appsec .event .data .MapDataBundle .Builder .CAPACITY_3_4 ;
54import static com .datadog .appsec .event .data .MapDataBundle .Builder .CAPACITY_6_10 ;
65import static com .datadog .appsec .gateway .AppSecRequestContext .DEFAULT_REQUEST_HEADERS_ALLOW_LIST ;
76import static com .datadog .appsec .gateway .AppSecRequestContext .REQUEST_HEADERS_ALLOW_LIST ;
87import static com .datadog .appsec .gateway .AppSecRequestContext .RESPONSE_HEADERS_ALLOW_LIST ;
9- import static datadog .trace .api .UserIdCollectionMode .ANONYMIZATION ;
10- import static datadog .trace .api .UserIdCollectionMode .DISABLED ;
11- import static datadog .trace .api .UserIdCollectionMode .SDK ;
12- import static datadog .trace .api .telemetry .LogCollector .SEND_TELEMETRY ;
13- import static datadog .trace .util .Strings .toHexString ;
148
159import com .datadog .appsec .AppSecSystem ;
1610import com .datadog .appsec .api .security .ApiSecurityRequestSampler ;
2822import com .datadog .appsec .report .AppSecEventWrapper ;
2923import datadog .trace .api .Config ;
3024import datadog .trace .api .ProductTraceSource ;
31- import datadog .trace .api .UserIdCollectionMode ;
3225import datadog .trace .api .gateway .Events ;
3326import datadog .trace .api .gateway .Flow ;
3427import datadog .trace .api .gateway .IGSpanInfo ;
4841import java .net .URISyntaxException ;
4942import java .nio .charset .Charset ;
5043import java .nio .charset .StandardCharsets ;
51- import java .security .MessageDigest ;
52- import java .security .NoSuchAlgorithmException ;
5344import java .util .ArrayList ;
5445import java .util .Arrays ;
5546import java .util .Collection ;
5647import java .util .Collections ;
48+ import java .util .EnumMap ;
5749import java .util .HashMap ;
5850import java .util .HashSet ;
5951import java .util .List ;
6052import java .util .Map ;
6153import java .util .Set ;
6254import java .util .concurrent .ConcurrentHashMap ;
63- import java .util .concurrent .atomic .AtomicBoolean ;
6455import java .util .regex .Pattern ;
6556import java .util .stream .Collectors ;
6657import org .slf4j .Logger ;
@@ -76,21 +67,16 @@ public class GatewayBridge {
7667 private static final Pattern QUERY_PARAM_SPLITTER = Pattern .compile ("&" );
7768 private static final Map <String , List <String >> EMPTY_QUERY_PARAMS = Collections .emptyMap ();
7869
79- private static final int HASH_SIZE_BYTES = 16 ; // 128 bits
80- private static final String ANON_PREFIX = "anon_" ;
81- private static final AtomicBoolean SHA_MISSING_REPORTED = new AtomicBoolean (false );
82-
8370 /** User tracking tags that will force the collection of request headers */
8471 private static final String [] USER_TRACKING_TAGS = {
8572 "appsec.events.users.login.success.track" , "appsec.events.users.login.failure.track"
8673 };
8774
88- private static final Map <String , LoginEvent > EVENT_MAPPINGS = new HashMap <>();
75+ private static final Map <LoginEvent , Address <?>> EVENT_MAPPINGS = new EnumMap <>(LoginEvent . class );
8976
9077 static {
91- EVENT_MAPPINGS .put ("users.login.success" , LoginEvent .LOGIN_SUCCESS );
92- EVENT_MAPPINGS .put ("users.login.failure" , LoginEvent .LOGIN_FAILURE );
93- EVENT_MAPPINGS .put ("users.signup" , LoginEvent .SIGN_UP );
78+ EVENT_MAPPINGS .put (LoginEvent .LOGIN_SUCCESS , KnownAddresses .LOGIN_SUCCESS );
79+ EVENT_MAPPINGS .put (LoginEvent .LOGIN_FAILURE , KnownAddresses .LOGIN_FAILURE );
9480 }
9581
9682 private static final String METASTRUCT_EXPLOIT = "exploit" ;
@@ -198,44 +184,16 @@ public void reset() {
198184 shellCmdSubInfo = null ;
199185 }
200186
201- private Flow <Void > onUser (
202- final RequestContext ctx_ , final UserIdCollectionMode mode , final String originalUser ) {
203- if (mode == DISABLED ) {
204- return NoopFlow .INSTANCE ;
205- }
206- final String user = anonymizeUser (mode , originalUser );
207- if (user == null ) {
208- return NoopFlow .INSTANCE ;
209- }
187+ private Flow <Void > onUser (final RequestContext ctx_ , final String user ) {
210188 final AppSecRequestContext ctx = ctx_ .getData (RequestContextSlot .APPSEC );
211189 if (ctx == null ) {
212190 return NoopFlow .INSTANCE ;
213191 }
214- final TraceSegment segment = ctx_ .getTraceSegment ();
215-
216- // span with ASM data
217- segment .setTagTop (Tags .ASM_KEEP , true );
218- segment .setTagTop (Tags .PROPAGATED_TRACE_SOURCE , ProductTraceSource .ASM );
219-
220- // skip event if we have an SDK one
221- if (mode != SDK ) {
222- segment .setTagTop ("_dd.appsec.usr.id" , user );
223- if (ctx .getUserIdSource () == SDK ) {
224- return NoopFlow .INSTANCE ;
225- }
226- }
227-
228- // update span tags
229- segment .setTagTop ("usr.id" , user );
230- segment .setTagTop ("_dd.appsec.user.collection_mode" , mode .fullName ());
231192
232193 // update current context with new user id
233- ctx .setUserIdSource (mode );
234- final boolean newUserId = !user .equals (ctx .getUserId ());
235- if (!newUserId ) {
194+ if (!ctx .updateUserId (user )) {
236195 return NoopFlow .INSTANCE ;
237196 }
238- ctx .setUserId (user );
239197
240198 // call waf if we have a new user id
241199 while (true ) {
@@ -259,96 +217,29 @@ private Flow<Void> onUser(
259217 }
260218
261219 private Flow <Void > onLoginEvent (
262- final RequestContext ctx_ ,
263- final UserIdCollectionMode mode ,
264- final String eventName ,
265- final Boolean exists ,
266- final String originalUser ,
267- final Map <String , String > metadata ) {
268- if (mode == DISABLED ) {
269- return NoopFlow .INSTANCE ;
270- }
220+ final RequestContext ctx_ , final LoginEvent event , final String login ) {
271221 final AppSecRequestContext ctx = ctx_ .getData (RequestContextSlot .APPSEC );
272222 if (ctx == null ) {
273223 return NoopFlow .INSTANCE ;
274224 }
275- final TraceSegment segment = ctx_ .getTraceSegment ();
276-
277- // span with ASM data
278- segment .setTagTop (Tags .ASM_KEEP , true );
279- segment .setTagTop (Tags .PROPAGATED_TRACE_SOURCE , ProductTraceSource .ASM );
280-
281- // update span tags
282- segment .setTagTop ("appsec.events." + eventName + ".track" , true , true );
283- if (metadata != null && !metadata .isEmpty ()) {
284- segment .setTagTop ("appsec.events." + eventName , metadata , true );
285- }
286- if (mode == SDK ) {
287- segment .setTagTop ("_dd.appsec.events." + eventName + ".sdk" , true , true );
288- } else {
289- segment .setTagTop ("_dd.appsec.events." + eventName + ".auto.mode" , mode .fullName (), true );
290- }
291-
292- if (exists != null ) {
293- if (mode == SDK || ctx .getUserLoginSource () != SDK ) {
294- segment .setTagTop ("appsec.events." + eventName + ".usr.exists" , exists , true );
295- }
296- }
297-
298- final String user = anonymizeUser (mode , originalUser );
299- if (user == null ) {
300- // can happen in custom events
301- return NoopFlow .INSTANCE ;
302- }
303-
304- // parse the event (might be null for custom events sent via the SDK)
305- final LoginEvent sourceEvent = EVENT_MAPPINGS .get (eventName );
306-
307- // skip event if we have an SDK one
308- if (mode != SDK ) {
309- segment .setTagTop ("_dd.appsec.usr.login" , user );
310- if (ctx .getUserLoginSource () == SDK ) {
311- return NoopFlow .INSTANCE ;
312- }
313- } else {
314- if (sourceEvent == LoginEvent .LOGIN_SUCCESS ) {
315- segment .setTagTop ("usr.id" , user , false );
316- } else {
317- segment .setTagTop ("appsec.events." + eventName + ".usr.id" , user , true );
318- }
319- segment .setTagTop ("_dd.appsec.user.collection_mode" , mode .fullName ());
320- }
321-
322- // update user span tags
323- segment .setTagTop ("appsec.events." + eventName + ".usr.login" , user , true );
324225
325226 // update current context with new user login
326- ctx .setUserLoginSource (mode );
327- if (mode == SDK ) {
328- ctx .setUserIdSource (mode ); // we are setting the usr.id through the SDK
329- }
330- final boolean newUserLogin = !user .equals (ctx .getUserLogin ());
331- if (!newUserLogin ) {
227+ if (!ctx .updateUserLogin (login )) {
332228 return NoopFlow .INSTANCE ;
333229 }
334- ctx .setUserLogin (user );
335230
336231 // call waf if we have a new user login
337- final List <Address <?>> addresses = new ArrayList <>(3 );
338- final MapDataBundle .Builder bundleBuilder = new MapDataBundle .Builder (CAPACITY_3_4 );
232+ final List <Address <?>> addresses = new ArrayList <>(2 );
233+ final MapDataBundle .Builder bundleBuilder = new MapDataBundle .Builder (CAPACITY_0_2 );
339234 addresses .add (KnownAddresses .USER_LOGIN );
340- bundleBuilder .add (KnownAddresses .USER_LOGIN , user );
341- if (mode == SDK ) {
342- addresses .add (KnownAddresses .USER_ID );
343- bundleBuilder .add (KnownAddresses .USER_ID , user );
344- }
345- // we don't support null values for the address so we use an invalid placeholder here
346- if (sourceEvent == LoginEvent .LOGIN_SUCCESS ) {
347- addresses .add (KnownAddresses .LOGIN_SUCCESS );
348- bundleBuilder .add (KnownAddresses .LOGIN_SUCCESS , "invalid" );
349- } else if (sourceEvent == LoginEvent .LOGIN_FAILURE ) {
350- addresses .add (KnownAddresses .LOGIN_FAILURE );
351- bundleBuilder .add (KnownAddresses .LOGIN_FAILURE , "invalid" );
235+ bundleBuilder .add (KnownAddresses .USER_LOGIN , login );
236+
237+ // parse the event
238+ Address <?> address = EVENT_MAPPINGS .get (event );
239+ if (address != null ) {
240+ addresses .add (address );
241+ // we don't support null values for the address so we use an invalid placeholder here
242+ bundleBuilder .add (address , "invalid" );
352243 }
353244 final DataBundle bundle = bundleBuilder .build ();
354245 final String subInfoKey =
@@ -1158,33 +1049,6 @@ private static int byteToDigit(byte b) {
11581049 return -1 ;
11591050 }
11601051
1161- protected static String anonymizeUser (final UserIdCollectionMode mode , final String userId ) {
1162- if (mode != ANONYMIZATION || userId == null ) {
1163- return userId ;
1164- }
1165- MessageDigest digest ;
1166- try {
1167- // TODO avoid lookup a new instance every time
1168- digest = MessageDigest .getInstance ("SHA-256" );
1169- } catch (NoSuchAlgorithmException e ) {
1170- if (!SHA_MISSING_REPORTED .getAndSet (true )) {
1171- log .error (
1172- SEND_TELEMETRY ,
1173- "Missing SHA-256 digest, user collection in 'anon' mode cannot continue" ,
1174- e );
1175- }
1176- return null ;
1177- }
1178- digest .update (userId .getBytes ());
1179- byte [] hash = digest .digest ();
1180- if (hash .length > HASH_SIZE_BYTES ) {
1181- byte [] temp = new byte [HASH_SIZE_BYTES ];
1182- System .arraycopy (hash , 0 , temp , 0 , temp .length );
1183- hash = temp ;
1184- }
1185- return ANON_PREFIX + toHexString (hash );
1186- }
1187-
11881052 private static class IGAppSecEventDependencies {
11891053
11901054 private static final Map <Address <?>, Collection <datadog .trace .api .gateway .EventType <?>>>
0 commit comments