@@ -11,7 +11,6 @@ import com.datadog.appsec.report.AppSecEvent
11
11
import com.datadog.appsec.report.AppSecEventWrapper
12
12
import datadog.trace.api.ProductTraceSource
13
13
import datadog.trace.api.config.GeneralConfig
14
- import static datadog.trace.api.config.IastConfig.IAST_DEDUPLICATION_ENABLED
15
14
import datadog.trace.api.function.TriConsumer
16
15
import datadog.trace.api.function.TriFunction
17
16
import datadog.trace.api.gateway.BlockResponseFunction
@@ -114,6 +113,8 @@ class GatewayBridgeSpecification extends DDSpecification {
114
113
BiFunction<RequestContext , String , Flow<Void > > shellCmdCB
115
114
BiFunction<RequestContext , String , Flow<Void > > userCB
116
115
TriFunction<RequestContext , LoginEvent , String , Flow<Void > > loginEventCB
116
+ BiFunction<RequestContext , StoredBodySupplier , Void > responseBodyStartCB
117
+ BiFunction<RequestContext , StoredBodySupplier , Flow<Void > > responseBodyDoneCB
117
118
118
119
WafMetricCollector wafMetricCollector = Mock (WafMetricCollector )
119
120
@@ -452,6 +453,8 @@ class GatewayBridgeSpecification extends DDSpecification {
452
453
1 * ig. registerCallback(EVENTS . responseStarted(), _) >> { responseStartedCB = it[1 ]; null }
453
454
1 * ig. registerCallback(EVENTS . responseHeader(), _) >> { respHeaderCB = it[1 ]; null }
454
455
1 * ig. registerCallback(EVENTS . responseHeaderDone(), _) >> { respHeadersDoneCB = it[1 ]; null }
456
+ 1 * ig. registerCallback(EVENTS . responseBodyStart(), _) >> { responseBodyStartCB = it[1 ]; null }
457
+ 1 * ig. registerCallback(EVENTS . responseBodyDone(), _) >> { responseBodyDoneCB = it[1 ]; null }
455
458
1 * ig. registerCallback(EVENTS . grpcServerMethod(), _) >> { grpcServerMethodCB = it[1 ]; null }
456
459
1 * ig. registerCallback(EVENTS . grpcServerRequestMessage(), _) >> { grpcServerRequestMessageCB = it[1 ]; null }
457
460
1 * ig. registerCallback(EVENTS . graphqlServerRequestMessage(), _) >> { graphqlServerRequestMessageCB = it[1 ]; null }
@@ -933,7 +936,7 @@ class GatewayBridgeSpecification extends DDSpecification {
933
936
}
934
937
935
938
@Override
936
- def <T> T getOrCreateMetaStructTop (String key , Function<String , T> defaultValue ) {
939
+ < T> T getOrCreateMetaStructTop(String key, Function<String , T> defaultValue) {
937
940
return null
938
941
}
939
942
@@ -991,7 +994,7 @@ class GatewayBridgeSpecification extends DDSpecification {
991
994
getTraceSegment() >> traceSegment
992
995
}
993
996
final spanInfo = Mock (AgentSpan ) {
994
- getTags() >> [' http.route' :' /' ]
997
+ getTags() >> [' http.route' : ' /' ]
995
998
}
996
999
997
1000
when :
@@ -1127,7 +1130,7 @@ class GatewayBridgeSpecification extends DDSpecification {
1127
1130
}
1128
1131
}
1129
1132
1130
- void "test onLoginFailure (# mode ) " () {
1133
+ void " test onLoginFailure" () {
1131
1134
setup :
1132
1135
eventDispatcher. getDataSubscribers(_) >> nonEmptyDsInfo
1133
1136
@@ -1222,4 +1225,95 @@ class GatewayBridgeSpecification extends DDSpecification {
1222
1225
1 * traceSegment. setTagTop(Tags . PROPAGATED_TRACE_SOURCE , ProductTraceSource . ASM )
1223
1226
}
1224
1227
1228
+ void ' forwards response body start events and stores the supplier' () {
1229
+ StoredBodySupplier supplier = Stub ()
1230
+
1231
+ setup :
1232
+ supplier. get() >> ' response content'
1233
+
1234
+ expect :
1235
+ ctx. data. storedResponseBody == null
1236
+
1237
+ when :
1238
+ responseBodyStartCB. apply(ctx, supplier)
1239
+
1240
+ then :
1241
+ ctx. data. storedResponseBody == ' response content'
1242
+ }
1243
+
1244
+ void ' forwards response body done events and distributes the body contents' () {
1245
+ DataBundle bundle
1246
+ GatewayContext gatewayContext
1247
+ StoredBodySupplier supplier = Stub ()
1248
+
1249
+ setup :
1250
+ supplier. get() >> ' response body content'
1251
+ eventDispatcher. getDataSubscribers({ KnownAddresses . RESPONSE_BODY_RAW in it }) >> nonEmptyDsInfo
1252
+ eventDispatcher. publishDataEvent(nonEmptyDsInfo, ctx. data, _ as DataBundle , _ as GatewayContext ) >>
1253
+ { bundle = it[2 ]; gatewayContext = it[3 ]; NoopFlow . INSTANCE }
1254
+
1255
+ when :
1256
+ responseBodyDoneCB. apply(ctx, supplier)
1257
+
1258
+ then :
1259
+ bundle. get(KnownAddresses . RESPONSE_BODY_OBJECT ) == ' response body content'
1260
+ gatewayContext. isTransient == false
1261
+ gatewayContext. isRasp == false
1262
+ }
1263
+
1264
+ void ' response body does not get published twice' () {
1265
+ StoredBodySupplier supplier = Stub ()
1266
+ Flow flow
1267
+
1268
+ given :
1269
+ supplier. get() >> ' response body content'
1270
+
1271
+ when :
1272
+ ctx. data. setRawResBodyPublished(true )
1273
+ flow = responseBodyDoneCB. apply(ctx, supplier)
1274
+
1275
+ then :
1276
+ flow == NoopFlow . INSTANCE
1277
+ 0 * eventDispatcher. getDataSubscribers(KnownAddresses . RESPONSE_BODY_RAW )
1278
+ }
1279
+
1280
+ void ' response body with empty content is not published' () {
1281
+ StoredBodySupplier supplier = Stub ()
1282
+ Flow flow
1283
+
1284
+ given :
1285
+ supplier. get() >> ' '
1286
+
1287
+ when :
1288
+ flow = responseBodyDoneCB. apply(ctx, supplier)
1289
+
1290
+ then :
1291
+ flow == NoopFlow . INSTANCE
1292
+ 1 * eventDispatcher. getDataSubscribers({ KnownAddresses . RESPONSE_BODY_RAW in it }) >> nonEmptyDsInfo
1293
+ 0 * eventDispatcher. publishDataEvent(_, _, _, _)
1294
+ }
1295
+
1296
+ void ' response data includes response body when available' () {
1297
+ setup :
1298
+ eventDispatcher. getDataSubscribers({ KnownAddresses . RESPONSE_STATUS in it }) >> nonEmptyDsInfo
1299
+ ctx. data. responseStatus = 200
1300
+ ctx. data. addResponseHeader(' Content-Type' , ' application/json' )
1301
+
1302
+ StoredBodySupplier supplier = Stub ()
1303
+ supplier. get() >> ' {"status":"success"}'
1304
+ ctx. data. storedResponseBodySupplier = supplier
1305
+
1306
+ DataBundle bundle
1307
+
1308
+ when :
1309
+ respHeadersDoneCB. apply(ctx)
1310
+
1311
+ then :
1312
+ 1 * eventDispatcher. publishDataEvent(nonEmptyDsInfo, ctx. data, _ as DataBundle , _ as GatewayContext ) >>
1313
+ { dsInfo , appSecCtx , db , gwCtx -> bundle = db; NoopFlow . INSTANCE }
1314
+ bundle. get(KnownAddresses . RESPONSE_STATUS ) == ' 200'
1315
+ bundle. get(KnownAddresses . RESPONSE_HEADERS_NO_COOKIES ) == [' content-type' : [' application/json' ]]
1316
+ bundle. get(KnownAddresses . RESPONSE_BODY_OBJECT ) == ' {"status":"success"}'
1317
+ }
1318
+
1225
1319
}
0 commit comments