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