dd-trace 4.36.0 → 4.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/README.md +8 -1
  2. package/ci/init.js +7 -0
  3. package/ext/exporters.d.ts +2 -1
  4. package/ext/exporters.js +2 -1
  5. package/index.d.ts +21 -6
  6. package/init.js +27 -3
  7. package/package.json +6 -6
  8. package/packages/datadog-esbuild/index.js +8 -2
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +4 -1
  10. package/packages/datadog-instrumentations/src/cucumber.js +182 -105
  11. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  12. package/packages/datadog-instrumentations/src/lodash.js +31 -0
  13. package/packages/datadog-instrumentations/src/playwright.js +6 -1
  14. package/packages/datadog-plugin-aws-sdk/src/services/index.js +3 -0
  15. package/packages/datadog-plugin-aws-sdk/src/services/sfn.js +7 -0
  16. package/packages/datadog-plugin-aws-sdk/src/services/states.js +7 -0
  17. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +64 -0
  18. package/packages/datadog-plugin-cucumber/src/index.js +83 -11
  19. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -0
  20. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +4 -0
  21. package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -33
  22. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +3 -0
  23. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +10 -1
  24. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +55 -1
  25. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/hardcoded-password-analyzer.js +13 -0
  26. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +8 -2
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +6 -6
  28. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +56 -0
  29. package/packages/dd-trace/src/ci-visibility/exporters/{jest-worker → test-worker}/writer.js +7 -0
  30. package/packages/dd-trace/src/config.js +25 -8
  31. package/packages/dd-trace/src/encode/0.4.js +50 -3
  32. package/packages/dd-trace/src/exporter.js +2 -1
  33. package/packages/dd-trace/src/plugins/database.js +20 -5
  34. package/packages/dd-trace/src/plugins/util/test.js +7 -0
  35. package/packages/dd-trace/src/profiling/profiler.js +23 -7
  36. package/packages/dd-trace/src/proxy.js +7 -1
  37. package/packages/dd-trace/src/serverless.js +3 -5
  38. package/packages/dd-trace/src/telemetry/index.js +9 -3
  39. package/packages/dd-trace/src/telemetry/logs/log-collector.js +42 -1
  40. package/packages/dd-trace/src/ci-visibility/exporters/jest-worker/index.js +0 -33
@@ -0,0 +1,31 @@
1
+ 'use strict'
2
+
3
+ const { channel, addHook } = require('./helpers/instrument')
4
+
5
+ const shimmer = require('../../datadog-shimmer')
6
+
7
+ addHook({ name: 'lodash', versions: ['>=4'] }, lodash => {
8
+ const lodashOperationCh = channel('datadog:lodash:operation')
9
+
10
+ const instrumentedLodashFn = ['trim', 'trimStart', 'trimEnd', 'toLower', 'toUpper', 'join']
11
+
12
+ shimmer.massWrap(
13
+ lodash,
14
+ instrumentedLodashFn,
15
+ lodashFn => {
16
+ return function () {
17
+ if (!lodashOperationCh.hasSubscribers) {
18
+ return lodashFn.apply(this, arguments)
19
+ }
20
+
21
+ const result = lodashFn.apply(this, arguments)
22
+ const message = { operation: lodashFn.name, arguments, result }
23
+ lodashOperationCh.publish(message)
24
+
25
+ return message.result
26
+ }
27
+ }
28
+ )
29
+
30
+ return lodash
31
+ })
@@ -297,7 +297,12 @@ function dispatcherRunWrapper (run) {
297
297
  }
298
298
 
299
299
  function dispatcherRunWrapperNew (run) {
300
- return function () {
300
+ return function (testGroups) {
301
+ if (!this._allTests) {
302
+ // Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
303
+ // Not available from >=1.44.0
304
+ this._allTests = testGroups.map(g => g.tests).flat()
305
+ }
301
306
  remainingTestsByFile = getTestsBySuiteFromTestGroups(arguments[0])
302
307
  return run.apply(this, arguments)
303
308
  }
@@ -7,6 +7,9 @@ exports.kinesis = require('./kinesis')
7
7
  exports.lambda = require('./lambda')
8
8
  exports.redshift = require('./redshift')
9
9
  exports.s3 = require('./s3')
10
+ exports.sfn = require('./sfn')
10
11
  exports.sns = require('./sns')
11
12
  exports.sqs = require('./sqs')
13
+ exports.states = require('./states')
14
+ exports.stepfunctions = require('./stepfunctions')
12
15
  exports.default = require('./default')
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+ const Stepfunctions = require('./stepfunctions')
3
+ class Sfn extends Stepfunctions {
4
+ static get id () { return 'sfn' }
5
+ }
6
+
7
+ module.exports = Sfn
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+ const Stepfunctions = require('./stepfunctions')
3
+ class States extends Stepfunctions {
4
+ static get id () { return 'states' }
5
+ }
6
+
7
+ module.exports = States
@@ -0,0 +1,64 @@
1
+ 'use strict'
2
+ const log = require('../../../dd-trace/src/log')
3
+ const BaseAwsSdkPlugin = require('../base')
4
+
5
+ class Stepfunctions extends BaseAwsSdkPlugin {
6
+ static get id () { return 'stepfunctions' }
7
+
8
+ // This is the shape of StartExecutionInput, as defined in
9
+ // https://github.com/aws/aws-sdk-js/blob/master/apis/states-2016-11-23.normal.json
10
+ // "StartExecutionInput": {
11
+ // "type": "structure",
12
+ // "required": [
13
+ // "stateMachineArn"
14
+ // ],
15
+ // "members": {
16
+ // "stateMachineArn": {
17
+ // "shape": "Arn",
18
+ // },
19
+ // "name": {
20
+ // "shape": "Name",
21
+ // },
22
+ // "input": {
23
+ // "shape": "SensitiveData",
24
+ // },
25
+ // "traceHeader": {
26
+ // "shape": "TraceHeader",
27
+ // }
28
+ // }
29
+
30
+ generateTags (params, operation, response) {
31
+ if (!params) return {}
32
+ const tags = { 'resource.name': params.name ? `${operation} ${params.name}` : `${operation}` }
33
+ if (operation === 'startExecution' || operation === 'startSyncExecution') {
34
+ tags.statemachinearn = `${params.stateMachineArn}`
35
+ }
36
+ return tags
37
+ }
38
+
39
+ requestInject (span, request) {
40
+ const operation = request.operation
41
+ if (operation === 'startExecution' || operation === 'startSyncExecution') {
42
+ if (!request.params || !request.params.input) {
43
+ return
44
+ }
45
+
46
+ const input = request.params.input
47
+
48
+ try {
49
+ const inputObj = JSON.parse(input)
50
+ if (inputObj && typeof inputObj === 'object') {
51
+ // We've parsed the input JSON string
52
+ inputObj._datadog = {}
53
+ this.tracer.inject(span, 'text_map', inputObj._datadog)
54
+ const newInput = JSON.stringify(inputObj)
55
+ request.params.input = newInput
56
+ }
57
+ } catch (e) {
58
+ log.info('Unable to treat input as JSON')
59
+ }
60
+ }
61
+ }
62
+ }
63
+
64
+ module.exports = Stepfunctions
@@ -18,7 +18,14 @@ const {
18
18
  TEST_SOURCE_FILE,
19
19
  TEST_EARLY_FLAKE_ENABLED,
20
20
  TEST_IS_NEW,
21
- TEST_IS_RETRY
21
+ TEST_IS_RETRY,
22
+ TEST_SUITE_ID,
23
+ TEST_SESSION_ID,
24
+ TEST_COMMAND,
25
+ TEST_MODULE,
26
+ TEST_MODULE_ID,
27
+ TEST_SUITE,
28
+ CUCUMBER_IS_PARALLEL
22
29
  } = require('../../dd-trace/src/plugins/util/test')
23
30
  const { RESOURCE_NAME } = require('../../../ext/tags')
24
31
  const { COMPONENT, ERROR_MESSAGE } = require('../../dd-trace/src/constants')
@@ -32,6 +39,22 @@ const {
32
39
  TELEMETRY_ITR_UNSKIPPABLE,
33
40
  TELEMETRY_CODE_COVERAGE_NUM_FILES
34
41
  } = require('../../dd-trace/src/ci-visibility/telemetry')
42
+ const id = require('../../dd-trace/src/id')
43
+
44
+ const isCucumberWorker = !!process.env.CUCUMBER_WORKER_ID
45
+
46
+ function getTestSuiteTags (testSuiteSpan) {
47
+ const suiteTags = {
48
+ [TEST_SUITE_ID]: testSuiteSpan.context().toSpanId(),
49
+ [TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
50
+ [TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
51
+ [TEST_MODULE]: 'cucumber'
52
+ }
53
+ if (testSuiteSpan.context()._parentId) {
54
+ suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10)
55
+ }
56
+ return suiteTags
57
+ }
35
58
 
36
59
  class CucumberPlugin extends CiPlugin {
37
60
  static get id () {
@@ -43,6 +66,8 @@ class CucumberPlugin extends CiPlugin {
43
66
 
44
67
  this.sourceRoot = process.cwd()
45
68
 
69
+ this.testSuiteSpanByPath = {}
70
+
46
71
  this.addSub('ci:cucumber:session:finish', ({
47
72
  status,
48
73
  isSuitesSkipped,
@@ -50,7 +75,8 @@ class CucumberPlugin extends CiPlugin {
50
75
  testCodeCoverageLinesTotal,
51
76
  hasUnskippableSuites,
52
77
  hasForcedToRunSuites,
53
- isEarlyFlakeDetectionEnabled
78
+ isEarlyFlakeDetectionEnabled,
79
+ isParallel
54
80
  }) => {
55
81
  const { isSuitesSkippingEnabled, isCodeCoverageEnabled } = this.libraryConfig || {}
56
82
  addIntelligentTestRunnerSpanTags(
@@ -70,6 +96,9 @@ class CucumberPlugin extends CiPlugin {
70
96
  if (isEarlyFlakeDetectionEnabled) {
71
97
  this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
72
98
  }
99
+ if (isParallel) {
100
+ this.testSessionSpan.setTag(CUCUMBER_IS_PARALLEL, 'true')
101
+ }
73
102
 
74
103
  this.testSessionSpan.setTag(TEST_STATUS, status)
75
104
  this.testModuleSpan.setTag(TEST_STATUS, status)
@@ -101,7 +130,7 @@ class CucumberPlugin extends CiPlugin {
101
130
  if (itrCorrelationId) {
102
131
  testSuiteMetadata[ITR_CORRELATION_ID] = itrCorrelationId
103
132
  }
104
- this.testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
133
+ const testSuiteSpan = this.tracer.startSpan('cucumber.test_suite', {
105
134
  childOf: this.testModuleSpan,
106
135
  tags: {
107
136
  [COMPONENT]: this.constructor.id,
@@ -109,25 +138,29 @@ class CucumberPlugin extends CiPlugin {
109
138
  ...testSuiteMetadata
110
139
  }
111
140
  })
141
+ this.testSuiteSpanByPath[testSuitePath] = testSuiteSpan
142
+
112
143
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'suite')
113
144
  if (this.libraryConfig?.isCodeCoverageEnabled) {
114
145
  this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_STARTED, 'suite', { library: 'istanbul' })
115
146
  }
116
147
  })
117
148
 
118
- this.addSub('ci:cucumber:test-suite:finish', status => {
119
- this.testSuiteSpan.setTag(TEST_STATUS, status)
120
- this.testSuiteSpan.finish()
149
+ this.addSub('ci:cucumber:test-suite:finish', ({ status, testSuitePath }) => {
150
+ const testSuiteSpan = this.testSuiteSpanByPath[testSuitePath]
151
+ testSuiteSpan.setTag(TEST_STATUS, status)
152
+ testSuiteSpan.finish()
121
153
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_FINISHED, 'suite')
122
154
  })
123
155
 
124
- this.addSub('ci:cucumber:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
156
+ this.addSub('ci:cucumber:test-suite:code-coverage', ({ coverageFiles, suiteFile, testSuitePath }) => {
125
157
  if (!this.libraryConfig?.isCodeCoverageEnabled) {
126
158
  return
127
159
  }
128
160
  if (!coverageFiles.length) {
129
161
  this.telemetry.count(TELEMETRY_CODE_COVERAGE_EMPTY)
130
162
  }
163
+ const testSuiteSpan = this.testSuiteSpanByPath[testSuitePath]
131
164
 
132
165
  const relativeCoverageFiles = [...coverageFiles, suiteFile]
133
166
  .map(filename => getTestSuitePath(filename, this.repositoryRoot))
@@ -135,8 +168,8 @@ class CucumberPlugin extends CiPlugin {
135
168
  this.telemetry.distribution(TELEMETRY_CODE_COVERAGE_NUM_FILES, {}, relativeCoverageFiles.length)
136
169
 
137
170
  const formattedCoverage = {
138
- sessionId: this.testSuiteSpan.context()._traceId,
139
- suiteId: this.testSuiteSpan.context()._spanId,
171
+ sessionId: testSuiteSpan.context()._traceId,
172
+ suiteId: testSuiteSpan.context()._spanId,
140
173
  files: relativeCoverageFiles
141
174
  }
142
175
 
@@ -144,7 +177,7 @@ class CucumberPlugin extends CiPlugin {
144
177
  this.telemetry.ciVisEvent(TELEMETRY_CODE_COVERAGE_FINISHED, 'suite', { library: 'istanbul' })
145
178
  })
146
179
 
147
- this.addSub('ci:cucumber:test:start', ({ testName, testFileAbsolutePath, testSourceLine }) => {
180
+ this.addSub('ci:cucumber:test:start', ({ testName, testFileAbsolutePath, testSourceLine, isParallel }) => {
148
181
  const store = storage.getStore()
149
182
  const testSuite = getTestSuitePath(testFileAbsolutePath, this.sourceRoot)
150
183
  const testSourceFile = getTestSuitePath(testFileAbsolutePath, this.repositoryRoot)
@@ -153,6 +186,10 @@ class CucumberPlugin extends CiPlugin {
153
186
  [TEST_SOURCE_START]: testSourceLine,
154
187
  [TEST_SOURCE_FILE]: testSourceFile
155
188
  }
189
+ if (isParallel) {
190
+ extraTags[CUCUMBER_IS_PARALLEL] = 'true'
191
+ }
192
+
156
193
  const testSpan = this.startTestSpan(testName, testSuite, extraTags)
157
194
 
158
195
  this.enter(testSpan, store)
@@ -172,6 +209,36 @@ class CucumberPlugin extends CiPlugin {
172
209
  this.enter(span, store)
173
210
  })
174
211
 
212
+ this.addSub('ci:cucumber:worker-report:trace', (traces) => {
213
+ const formattedTraces = JSON.parse(traces).map(trace =>
214
+ trace.map(span => ({
215
+ ...span,
216
+ span_id: id(span.span_id),
217
+ trace_id: id(span.trace_id),
218
+ parent_id: id(span.parent_id)
219
+ }))
220
+ )
221
+
222
+ // We have to update the test session, test module and test suite ids
223
+ // before we export them in the main process
224
+ formattedTraces.forEach(trace => {
225
+ trace.forEach(span => {
226
+ if (span.name === 'cucumber.test') {
227
+ const testSuite = span.meta[TEST_SUITE]
228
+ const testSuiteSpan = this.testSuiteSpanByPath[testSuite]
229
+
230
+ const testSuiteTags = getTestSuiteTags(testSuiteSpan)
231
+ span.meta = {
232
+ ...span.meta,
233
+ ...testSuiteTags
234
+ }
235
+ }
236
+ })
237
+
238
+ this.tracer._exporter.export(trace)
239
+ })
240
+ })
241
+
175
242
  this.addSub('ci:cucumber:test:finish', ({ isStep, status, skipReason, errorMessage, isNew, isEfdRetry }) => {
176
243
  const span = storage.getStore().span
177
244
  const statusTag = isStep ? 'step.status' : TEST_STATUS
@@ -201,6 +268,10 @@ class CucumberPlugin extends CiPlugin {
201
268
  { hasCodeOwners: !!span.context()._tags[TEST_CODE_OWNERS] }
202
269
  )
203
270
  finishAllTraceSpans(span)
271
+ // If it's a worker, flushing is cheap, as it's just sending data to the main process
272
+ if (isCucumberWorker) {
273
+ this.tracer._exporter.flush()
274
+ }
204
275
  }
205
276
  })
206
277
 
@@ -213,10 +284,11 @@ class CucumberPlugin extends CiPlugin {
213
284
  }
214
285
 
215
286
  startTestSpan (testName, testSuite, extraTags) {
287
+ const testSuiteSpan = this.testSuiteSpanByPath[testSuite]
216
288
  return super.startTestSpan(
217
289
  testName,
218
290
  testSuite,
219
- this.testSuiteSpan,
291
+ testSuiteSpan,
220
292
  extraTags
221
293
  )
222
294
  }
@@ -48,6 +48,7 @@ class HardcodedBaseAnalyzer extends Analyzer {
48
48
  file,
49
49
  line: match.location.line,
50
50
  column: match.location.column,
51
+ ident: match.location.ident,
51
52
  data: match.ruleId
52
53
  }))
53
54
  }
@@ -9,6 +9,10 @@ class HardcodedPasswordAnalyzer extends HardcodedBaseAnalyzer {
9
9
  constructor () {
10
10
  super(HARDCODED_PASSWORD, allRules)
11
11
  }
12
+
13
+ _getEvidence (value) {
14
+ return { value: `${value.ident}` }
15
+ }
12
16
  }
13
17
 
14
18
  module.exports = new HardcodedPasswordAnalyzer()
@@ -2,35 +2,9 @@
2
2
 
3
3
  const dc = require('dc-polyfill')
4
4
  const log = require('../../log')
5
- const { calculateDDBasePath } = require('../../util')
6
5
 
7
6
  const telemetryLog = dc.channel('datadog:telemetry:log')
8
7
 
9
- const ddBasePath = calculateDDBasePath(__dirname)
10
- const EOL = '\n'
11
- const STACK_FRAME_LINE_REGEX = /^\s*at\s/gm
12
-
13
- function sanitize (logEntry, stack) {
14
- if (!stack) return logEntry
15
-
16
- let stackLines = stack.split(EOL)
17
-
18
- const firstIndex = stackLines.findIndex(l => l.match(STACK_FRAME_LINE_REGEX))
19
-
20
- const isDDCode = firstIndex > -1 && stackLines[firstIndex].includes(ddBasePath)
21
- stackLines = stackLines
22
- .filter((line, index) => (isDDCode && index < firstIndex) || line.includes(ddBasePath))
23
- .map(line => line.replace(ddBasePath, ''))
24
-
25
- logEntry.stack_trace = stackLines.join(EOL)
26
-
27
- if (!isDDCode) {
28
- logEntry.message = 'omitted'
29
- }
30
-
31
- return logEntry
32
- }
33
-
34
8
  function getTelemetryLog (data, level) {
35
9
  try {
36
10
  data = typeof data === 'function' ? data() : data
@@ -42,18 +16,13 @@ function getTelemetryLog (data, level) {
42
16
  message = String(data.message || data)
43
17
  }
44
18
 
45
- let logEntry = {
19
+ const logEntry = {
46
20
  message,
47
21
  level
48
22
  }
49
-
50
23
  if (data.stack) {
51
- logEntry = sanitize(logEntry, data.stack)
52
- if (logEntry.stack_trace === '') {
53
- return
54
- }
24
+ logEntry.stack_trace = data.stack
55
25
  }
56
-
57
26
  return logEntry
58
27
  } catch (e) {
59
28
  log.error(e)
@@ -2,6 +2,7 @@
2
2
 
3
3
  const csiMethods = [
4
4
  { src: 'concat' },
5
+ { src: 'join' },
5
6
  { src: 'parse' },
6
7
  { src: 'plusOperator', operator: true },
7
8
  { src: 'random' },
@@ -9,6 +10,8 @@ const csiMethods = [
9
10
  { src: 'slice' },
10
11
  { src: 'substr' },
11
12
  { src: 'substring' },
13
+ { src: 'toLowerCase', dst: 'stringCase' },
14
+ { src: 'toUpperCase', dst: 'stringCase' },
12
15
  { src: 'trim' },
13
16
  { src: 'trimEnd' },
14
17
  { src: 'trimStart', dst: 'trim' }
@@ -1,13 +1,20 @@
1
1
  'use strict'
2
2
 
3
+ const dc = require('dc-polyfill')
3
4
  const TaintedUtils = require('@datadog/native-iast-taint-tracking')
4
5
  const { IAST_TRANSACTION_ID } = require('../iast-context')
5
6
  const iastTelemetry = require('../telemetry')
6
7
  const { REQUEST_TAINTED } = require('../telemetry/iast-metric')
7
8
  const { isInfoAllowed } = require('../telemetry/verbosity')
8
- const { getTaintTrackingImpl, getTaintTrackingNoop } = require('./taint-tracking-impl')
9
+ const {
10
+ getTaintTrackingImpl,
11
+ getTaintTrackingNoop,
12
+ lodashTaintTrackingHandler
13
+ } = require('./taint-tracking-impl')
9
14
  const { taintObject } = require('./operations-taint-object')
10
15
 
16
+ const lodashOperationCh = dc.channel('datadog:lodash:operation')
17
+
11
18
  function createTransaction (id, iastContext) {
12
19
  if (id && iastContext) {
13
20
  iastContext[IAST_TRANSACTION_ID] = TaintedUtils.createTransaction(id)
@@ -92,10 +99,12 @@ function enableTaintOperations (telemetryVerbosity) {
92
99
  }
93
100
 
94
101
  global._ddiast = getTaintTrackingImpl(telemetryVerbosity)
102
+ lodashOperationCh.subscribe(lodashTaintTrackingHandler)
95
103
  }
96
104
 
97
105
  function disableTaintOperations () {
98
106
  global._ddiast = getTaintTrackingNoop()
107
+ lodashOperationCh.unsubscribe(lodashTaintTrackingHandler)
99
108
  }
100
109
 
101
110
  function setMaxTransactions (transactions) {
@@ -18,6 +18,7 @@ function noop (res) { return res }
18
18
  // Otherwise you may end up rewriting a method and not providing its rewritten implementation
19
19
  const TaintTrackingNoop = {
20
20
  concat: noop,
21
+ join: noop,
21
22
  parse: noop,
22
23
  plusOperator: noop,
23
24
  random: noop,
@@ -25,6 +26,7 @@ const TaintTrackingNoop = {
25
26
  slice: noop,
26
27
  substr: noop,
27
28
  substring: noop,
29
+ stringCase: noop,
28
30
  trim: noop,
29
31
  trimEnd: noop
30
32
  }
@@ -113,6 +115,13 @@ function csiMethodsOverrides (getContext) {
113
115
  return res
114
116
  },
115
117
 
118
+ stringCase: getCsiFn(
119
+ (transactionId, res, target) => TaintedUtils.stringCase(transactionId, res, target),
120
+ getContext,
121
+ String.prototype.toLowerCase,
122
+ String.prototype.toUpperCase
123
+ ),
124
+
116
125
  trim: getCsiFn(
117
126
  (transactionId, res, target) => TaintedUtils.trim(transactionId, res, target),
118
127
  getContext,
@@ -147,6 +156,22 @@ function csiMethodsOverrides (getContext) {
147
156
  }
148
157
  }
149
158
 
159
+ return res
160
+ },
161
+
162
+ join: function (res, fn, target, separator) {
163
+ if (fn === Array.prototype.join) {
164
+ try {
165
+ const iastContext = getContext()
166
+ const transactionId = getTransactionId(iastContext)
167
+ if (transactionId) {
168
+ res = TaintedUtils.arrayJoin(transactionId, res, target, separator)
169
+ }
170
+ } catch (e) {
171
+ iastLog.error(e)
172
+ }
173
+ }
174
+
150
175
  return res
151
176
  }
152
177
  }
@@ -176,7 +201,36 @@ function getTaintTrackingNoop () {
176
201
  return getTaintTrackingImpl(null, true)
177
202
  }
178
203
 
204
+ const lodashFns = {
205
+ join: TaintedUtils.arrayJoin,
206
+ toLower: TaintedUtils.stringCase,
207
+ toUpper: TaintedUtils.stringCase,
208
+ trim: TaintedUtils.trim,
209
+ trimEnd: TaintedUtils.trimEnd,
210
+ trimStart: TaintedUtils.trim
211
+
212
+ }
213
+
214
+ function getLodashTaintedUtilFn (lodashFn) {
215
+ return lodashFns[lodashFn] || ((transactionId, result) => result)
216
+ }
217
+
218
+ function lodashTaintTrackingHandler (message) {
219
+ try {
220
+ if (!message.result) return
221
+ const context = getContextDefault()
222
+ const transactionId = getTransactionId(context)
223
+ if (transactionId) {
224
+ message.result = getLodashTaintedUtilFn(message.operation)(transactionId, message.result, ...message.arguments)
225
+ }
226
+ } catch (e) {
227
+ iastLog.error(`Error invoking CSI lodash ${message.operation}`)
228
+ .errorAndPublish(e)
229
+ }
230
+ }
231
+
179
232
  module.exports = {
180
233
  getTaintTrackingImpl,
181
- getTaintTrackingNoop
234
+ getTaintTrackingNoop,
235
+ lodashTaintTrackingHandler
182
236
  }
@@ -0,0 +1,13 @@
1
+ 'use strict'
2
+
3
+ module.exports = function extractSensitiveRanges (evidence, valuePattern) {
4
+ const { value } = evidence
5
+ if (valuePattern.test(value)) {
6
+ return [{
7
+ start: 0,
8
+ end: value.length
9
+ }]
10
+ }
11
+
12
+ return []
13
+ }
@@ -6,6 +6,7 @@ const vulnerabilities = require('../../vulnerabilities')
6
6
  const { contains, intersects, remove } = require('./range-utils')
7
7
 
8
8
  const commandSensitiveAnalyzer = require('./sensitive-analyzers/command-sensitive-analyzer')
9
+ const hardcodedPasswordAnalyzer = require('./sensitive-analyzers/hardcoded-password-analyzer')
9
10
  const headerSensitiveAnalyzer = require('./sensitive-analyzers/header-sensitive-analyzer')
10
11
  const jsonSensitiveAnalyzer = require('./sensitive-analyzers/json-sensitive-analyzer')
11
12
  const ldapSensitiveAnalyzer = require('./sensitive-analyzers/ldap-sensitive-analyzer')
@@ -31,6 +32,9 @@ class SensitiveHandler {
31
32
  this._sensitiveAnalyzers.set(vulnerabilities.HEADER_INJECTION, (evidence) => {
32
33
  return headerSensitiveAnalyzer(evidence, this._namePattern, this._valuePattern)
33
34
  })
35
+ this._sensitiveAnalyzers.set(vulnerabilities.HARDCODED_PASSWORD, (evidence) => {
36
+ return hardcodedPasswordAnalyzer(evidence, this._valuePattern)
37
+ })
34
38
  }
35
39
 
36
40
  isSensibleName (name) {
@@ -51,7 +55,9 @@ class SensitiveHandler {
51
55
  const sensitiveAnalyzer = this._sensitiveAnalyzers.get(vulnerabilityType)
52
56
  if (sensitiveAnalyzer) {
53
57
  const sensitiveRanges = sensitiveAnalyzer(evidence)
54
- return this.toRedactedJson(evidence, sensitiveRanges, sourcesIndexes, sources)
58
+ if (evidence.ranges || sensitiveRanges?.length) {
59
+ return this.toRedactedJson(evidence, sensitiveRanges, sourcesIndexes, sources)
60
+ }
55
61
  }
56
62
  return null
57
63
  }
@@ -67,7 +73,7 @@ class SensitiveHandler {
67
73
  let nextTaintedIndex = 0
68
74
  let sourceIndex
69
75
 
70
- let nextTainted = ranges.shift()
76
+ let nextTainted = ranges?.shift()
71
77
  let nextSensitive = sensitive.shift()
72
78
 
73
79
  for (let i = 0; i < value.length; i++) {
@@ -49,6 +49,10 @@ class VulnerabilityFormatter {
49
49
  evidence.ranges = ranges
50
50
  }
51
51
 
52
+ if (!evidence.ranges) {
53
+ return { value: evidence.value }
54
+ }
55
+
52
56
  evidence.ranges.forEach((range, rangeIndex) => {
53
57
  if (fromIndex < range.start) {
54
58
  valueParts.push({ value: evidence.value.substring(fromIndex, range.start) })
@@ -65,12 +69,8 @@ class VulnerabilityFormatter {
65
69
  }
66
70
 
67
71
  formatEvidence (type, evidence, sourcesIndexes, sources) {
68
- if (!evidence.ranges && !evidence.rangesToApply) {
69
- if (typeof evidence.value === 'undefined') {
70
- return undefined
71
- } else {
72
- return { value: evidence.value }
73
- }
72
+ if (typeof evidence.value === 'undefined') {
73
+ return undefined
74
74
  }
75
75
 
76
76
  return this._redactVulnearbilities