@@ -38,14 +38,20 @@ const config = {
38
38
39
39
const metricsQueue = new Map ( )
40
40
41
+ const extendedDataCollectionRequest = new WeakMap ( )
42
+
41
43
// following header lists are ordered in the same way the spec orders them, it doesn't matter but it's easier to compare
42
44
const contentHeaderList = [
43
45
'content-length' ,
44
- 'content-type' ,
45
46
'content-encoding' ,
46
47
'content-language'
47
48
]
48
49
50
+ const responseHeaderList = [
51
+ ...contentHeaderList ,
52
+ 'content-type'
53
+ ]
54
+
49
55
const identificationHeaders = [
50
56
'x-amzn-trace-id' ,
51
57
'cloudfront-viewer-ja3-fingerprint' ,
@@ -75,15 +81,27 @@ const requestHeadersList = [
75
81
...identificationHeaders
76
82
]
77
83
84
+ const redactedHeadersList = [
85
+ 'authorization' ,
86
+ 'proxy-authorization' ,
87
+ 'www-authenticate' ,
88
+ 'proxy-authenticate' ,
89
+ 'authentication-info' ,
90
+ 'proxy-authentication-info' ,
91
+ 'cookie' ,
92
+ 'set-cookie'
93
+ ]
94
+
78
95
// these request headers are always collected - it breaks the expected spec orders
79
96
const REQUEST_HEADERS_MAP = mapHeaderAndTags ( requestHeadersList , REQUEST_HEADER_TAG_PREFIX )
80
97
81
98
const EVENT_HEADERS_MAP = mapHeaderAndTags ( eventHeadersList , REQUEST_HEADER_TAG_PREFIX )
82
99
83
- const RESPONSE_HEADERS_MAP = mapHeaderAndTags ( contentHeaderList , RESPONSE_HEADER_TAG_PREFIX )
100
+ const RESPONSE_HEADERS_MAP = mapHeaderAndTags ( responseHeaderList , RESPONSE_HEADER_TAG_PREFIX )
84
101
85
102
const NON_EXTENDED_REQUEST_HEADERS = new Set ( [ ...requestHeadersList , ...eventHeadersList ] )
86
- const NON_EXTENDED_RESPONSE_HEADERS = new Set ( contentHeaderList )
103
+ const NON_EXTENDED_RESPONSE_HEADERS = new Set ( responseHeaderList )
104
+ const REDACTED_HEADERS = new Set ( redactedHeadersList )
87
105
88
106
function init ( _config ) {
89
107
config . headersExtendedCollectionEnabled = _config . extendedHeadersCollection . enabled
@@ -132,15 +150,17 @@ function filterExtendedHeaders (headers, excludedHeaderNames, tagPrefix, limit =
132
150
for ( const [ headerName , headerValue ] of Object . entries ( headers ) ) {
133
151
if ( counter >= limit ) break
134
152
if ( ! excludedHeaderNames . has ( headerName ) ) {
135
- result [ getHeaderTag ( tagPrefix , headerName ) ] = String ( headerValue )
153
+ result [ getHeaderTag ( tagPrefix , headerName ) ] = REDACTED_HEADERS . has ( headerName )
154
+ ? '<redacted>'
155
+ : String ( headerValue )
136
156
counter ++
137
157
}
138
158
}
139
159
140
160
return result
141
161
}
142
162
143
- function getCollectedHeaders ( req , res , shouldCollectEventHeaders , storedResponseHeaders = { } ) {
163
+ function getCollectedHeaders ( req , res , shouldCollectEventHeaders , storedResponseHeaders = { } , extendedDataCollection ) {
144
164
// Mandatory
145
165
const mandatoryCollectedHeaders = filterHeaders ( req . headers , REQUEST_HEADERS_MAP )
146
166
@@ -154,7 +174,8 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
154
174
const requestEventCollectedHeaders = filterHeaders ( req . headers , EVENT_HEADERS_MAP )
155
175
const responseEventCollectedHeaders = filterHeaders ( responseHeaders , RESPONSE_HEADERS_MAP )
156
176
157
- if ( ! config . headersExtendedCollectionEnabled || config . headersRedaction ) {
177
+ // TODO headersExtendedCollectionEnabled and headersRedaction properties are deprecated to delete in a major
178
+ if ( ( ! config . headersExtendedCollectionEnabled || config . headersRedaction ) && ! extendedDataCollection ) {
158
179
// Standard collection
159
180
return Object . assign (
160
181
mandatoryCollectedHeaders ,
@@ -163,12 +184,15 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
163
184
)
164
185
}
165
186
187
+ // TODO config.maxHeadersCollected is deprecated to delete in a major
188
+ const maxHeadersCollected = extendedDataCollection ?. max_collected_headers ?? config . maxHeadersCollected
189
+
166
190
// Extended collection
167
- const requestExtendedHeadersAvailableCount =
168
- config . maxHeadersCollected -
169
- Object . keys ( mandatoryCollectedHeaders ) . length -
191
+ const collectedHeadersCount = Object . keys ( mandatoryCollectedHeaders ) . length +
170
192
Object . keys ( requestEventCollectedHeaders ) . length
171
193
194
+ const requestExtendedHeadersAvailableCount = maxHeadersCollected - collectedHeadersCount
195
+
172
196
const requestEventExtendedCollectedHeaders =
173
197
filterExtendedHeaders (
174
198
req . headers ,
@@ -178,7 +202,7 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
178
202
)
179
203
180
204
const responseExtendedHeadersAvailableCount =
181
- config . maxHeadersCollected -
205
+ maxHeadersCollected -
182
206
Object . keys ( responseEventCollectedHeaders ) . length
183
207
184
208
const responseEventExtendedCollectedHeaders =
@@ -199,15 +223,15 @@ function getCollectedHeaders (req, res, shouldCollectEventHeaders, storedRespons
199
223
200
224
// Check discarded headers
201
225
const requestHeadersCount = Object . keys ( req . headers ) . length
202
- if ( requestHeadersCount > config . maxHeadersCollected ) {
226
+ if ( requestHeadersCount > maxHeadersCollected ) {
203
227
headersTags [ '_dd.appsec.request.header_collection.discarded' ] =
204
- requestHeadersCount - config . maxHeadersCollected
228
+ requestHeadersCount - maxHeadersCollected
205
229
}
206
230
207
231
const responseHeadersCount = Object . keys ( responseHeaders ) . length
208
- if ( responseHeadersCount > config . maxHeadersCollected ) {
232
+ if ( responseHeadersCount > maxHeadersCollected ) {
209
233
headersTags [ '_dd.appsec.response.header_collection.discarded' ] =
210
- responseHeadersCount - config . maxHeadersCollected
234
+ responseHeadersCount - maxHeadersCollected
211
235
}
212
236
213
237
return headersTags
@@ -307,7 +331,7 @@ function reportTruncationMetrics (rootSpan, metrics) {
307
331
}
308
332
}
309
333
310
- function reportAttack ( attackData ) {
334
+ function reportAttack ( { events : attackData , actions } ) {
311
335
const store = storage ( 'legacy' ) . getStore ( )
312
336
const req = store ?. req
313
337
const rootSpan = web . root ( req )
@@ -338,8 +362,14 @@ function reportAttack (attackData) {
338
362
339
363
rootSpan . addTags ( newTags )
340
364
365
+ // TODO this should be deleted in a major
341
366
if ( config . raspBodyCollection && isRaspAttack ( attackData ) ) {
342
- reportRequestBody ( rootSpan , req . body )
367
+ reportRequestBody ( rootSpan , req . body , true )
368
+ }
369
+
370
+ const extendedDataCollection = actions ?. extended_data_collection
371
+ if ( extendedDataCollection ) {
372
+ extendedDataCollectionRequest . set ( req , extendedDataCollection )
343
373
}
344
374
}
345
375
@@ -398,18 +428,29 @@ function truncateRequestBody (target, depth = 0) {
398
428
}
399
429
}
400
430
401
- function reportRequestBody ( rootSpan , requestBody ) {
402
- if ( ! requestBody ) return
431
+ function reportRequestBody ( rootSpan , requestBody , comesFromRaspAction = false ) {
432
+ if ( ! requestBody || Object . keys ( requestBody ) . length === 0 ) return
403
433
404
434
if ( ! rootSpan . meta_struct ) {
405
435
rootSpan . meta_struct = { }
406
436
}
407
437
408
- if ( ! rootSpan . meta_struct [ 'http.request.body' ] ) {
438
+ if ( rootSpan . meta_struct [ 'http.request.body' ] ) {
439
+ // If the rasp.exceed metric exists, set also the same for the new tag
440
+ const currentTags = rootSpan . context ( ) . _tags
441
+ const sizeExceedTagValue = currentTags [ '_dd.appsec.rasp.request_body_size.exceeded' ]
442
+
443
+ if ( sizeExceedTagValue ) {
444
+ rootSpan . setTag ( '_dd.appsec.request_body_size.exceeded' , sizeExceedTagValue )
445
+ }
446
+ } else {
409
447
const { truncated, value } = truncateRequestBody ( requestBody )
410
448
rootSpan . meta_struct [ 'http.request.body' ] = value
411
449
if ( truncated ) {
412
- rootSpan . setTag ( '_dd.appsec.rasp.request_body_size.exceeded' , 'true' )
450
+ const sizeExceedTagKey = comesFromRaspAction
451
+ ? '_dd.appsec.rasp.request_body_size.exceeded' // TODO old metric to delete in a major
452
+ : '_dd.appsec.request_body_size.exceeded'
453
+ rootSpan . setTag ( sizeExceedTagKey , 'true' )
413
454
}
414
455
}
415
456
}
@@ -496,7 +537,15 @@ function finishRequest (req, res, storedResponseHeaders) {
496
537
497
538
const tags = rootSpan . context ( ) . _tags
498
539
499
- const newTags = getCollectedHeaders ( req , res , shouldCollectEventHeaders ( tags ) , storedResponseHeaders )
540
+ const extendedDataCollection = extendedDataCollectionRequest . get ( req )
541
+ const newTags = getCollectedHeaders (
542
+ req , res , shouldCollectEventHeaders ( tags ) , storedResponseHeaders , extendedDataCollection
543
+ )
544
+
545
+ if ( extendedDataCollection ) {
546
+ // TODO add support for fastify, req.body is not available in fastify
547
+ reportRequestBody ( rootSpan , req . body )
548
+ }
500
549
501
550
if ( tags [ 'appsec.event' ] === 'true' && typeof req . route ?. path === 'string' ) {
502
551
newTags [ 'http.endpoint' ] = req . route . path
0 commit comments