dd-trace 3.20.0 → 3.21.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.
- package/index.d.ts +8 -1
- package/package.json +5 -4
- package/packages/datadog-instrumentations/src/grpc/client.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/server.js +8 -4
- package/packages/datadog-instrumentations/src/helpers/register.js +4 -0
- package/packages/datadog-instrumentations/src/jest.js +20 -17
- package/packages/datadog-instrumentations/src/next.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -0
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +4 -2
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +4 -3
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +8 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +7 -1
- package/packages/datadog-plugin-http/src/client.js +2 -1
- package/packages/datadog-plugin-http2/src/client.js +2 -1
- package/packages/datadog-plugin-jest/src/util.js +10 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +22 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +40 -4
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
- package/packages/dd-trace/src/appsec/iast/index.js +1 -1
- package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/range-utils.js +37 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +29 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +35 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +95 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +144 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +113 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +8 -0
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -76
- package/packages/dd-trace/src/config.js +58 -8
- package/packages/dd-trace/src/constants.js +3 -1
- package/packages/dd-trace/src/git_metadata_tagger.js +17 -0
- package/packages/dd-trace/src/git_properties.js +32 -0
- package/packages/dd-trace/src/plugins/util/ci.js +62 -7
- package/packages/dd-trace/src/plugins/util/tags.js +5 -1
- package/packages/dd-trace/src/profiling/constants.js +0 -1
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/serverless.js +25 -0
- package/packages/dd-trace/src/span_processor.js +3 -0
- package/packages/dd-trace/src/tracer.js +3 -2
- package/version.js +9 -0
package/index.d.ts
CHANGED
|
@@ -79,7 +79,8 @@ export declare interface Tracer extends opentracing.Tracer {
|
|
|
79
79
|
* which case the span will finish at the end of the function execution.
|
|
80
80
|
*
|
|
81
81
|
* If the `orphanable` option is set to false, the function will not be traced
|
|
82
|
-
* unless there is already an active span or `childOf` option.
|
|
82
|
+
* unless there is already an active span or `childOf` option. Note that this
|
|
83
|
+
* option is deprecated and has been removed in version 4.0.
|
|
83
84
|
*/
|
|
84
85
|
trace<T> (name: string, fn: (span?: Span, fn?: (error?: Error) => any) => T): T;
|
|
85
86
|
trace<T> (name: string, options: TraceOptions & SpanOptions, fn: (span?: Span, done?: (error?: Error) => string) => T): T;
|
|
@@ -443,6 +444,11 @@ export declare interface TracerOptions {
|
|
|
443
444
|
* Whether to enable vulnerability deduplication
|
|
444
445
|
*/
|
|
445
446
|
deduplicationEnabled?: boolean
|
|
447
|
+
/**
|
|
448
|
+
* Whether to enable vulnerability redaction
|
|
449
|
+
* @default true
|
|
450
|
+
*/
|
|
451
|
+
redactionEnabled?: boolean
|
|
446
452
|
}
|
|
447
453
|
};
|
|
448
454
|
|
|
@@ -491,6 +497,7 @@ export declare interface TracerOptions {
|
|
|
491
497
|
/**
|
|
492
498
|
* If false, require a parent in order to trace.
|
|
493
499
|
* @default true
|
|
500
|
+
* @deprecated since version 4.0
|
|
494
501
|
*/
|
|
495
502
|
orphanable?: boolean
|
|
496
503
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.21.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"test:integration:cucumber": "mocha --colors --timeout 30000 \"integration-tests/cucumber/*.spec.js\"",
|
|
37
37
|
"test:integration:cypress": "mocha --colors --timeout 30000 \"integration-tests/cypress/*.spec.js\"",
|
|
38
38
|
"test:integration:playwright": "mocha --colors --timeout 30000 \"integration-tests/playwright/*.spec.js\"",
|
|
39
|
+
"test:integration:serverless": "mocha --colors --timeout 30000 \"integration-tests/serverless/*.spec.js\"",
|
|
39
40
|
"test:shimmer": "mocha --colors 'packages/datadog-shimmer/test/**/*.spec.js'",
|
|
40
41
|
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer",
|
|
41
42
|
"leak:core": "node ./scripts/install_plugin_modules && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape 'packages/dd-trace/test/leak/**/*.js'",
|
|
@@ -67,9 +68,9 @@
|
|
|
67
68
|
"dependencies": {
|
|
68
69
|
"@datadog/native-appsec": "^3.1.0",
|
|
69
70
|
"@datadog/native-iast-rewriter": "2.0.1",
|
|
70
|
-
"@datadog/native-iast-taint-tracking": "^1.4.
|
|
71
|
-
"@datadog/native-metrics": "^
|
|
72
|
-
"@datadog/pprof": "^2.2.
|
|
71
|
+
"@datadog/native-iast-taint-tracking": "^1.4.1",
|
|
72
|
+
"@datadog/native-metrics": "^2.0.0",
|
|
73
|
+
"@datadog/pprof": "^2.2.1",
|
|
73
74
|
"@datadog/sketches-js": "^2.1.0",
|
|
74
75
|
"crypto-randomuuid": "^1.0.0",
|
|
75
76
|
"diagnostics_channel": "^1.1.0",
|
|
@@ -4,6 +4,8 @@ const types = require('./types')
|
|
|
4
4
|
const { addHook, channel, AsyncResource } = require('../helpers/instrument')
|
|
5
5
|
const shimmer = require('../../../datadog-shimmer')
|
|
6
6
|
|
|
7
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0])
|
|
8
|
+
|
|
7
9
|
const patched = new WeakSet()
|
|
8
10
|
const instances = new WeakMap()
|
|
9
11
|
|
|
@@ -232,13 +234,15 @@ function patch (grpc) {
|
|
|
232
234
|
return grpc
|
|
233
235
|
}
|
|
234
236
|
|
|
235
|
-
|
|
237
|
+
if (nodeMajor <= 14) {
|
|
238
|
+
addHook({ name: 'grpc', versions: ['>=1.24.3'] }, patch)
|
|
236
239
|
|
|
237
|
-
addHook({ name: 'grpc', versions: ['>=1.24.3'], file: 'src/client.js' }, client => {
|
|
238
|
-
|
|
240
|
+
addHook({ name: 'grpc', versions: ['>=1.24.3'], file: 'src/client.js' }, client => {
|
|
241
|
+
shimmer.wrap(client, 'makeClientConstructor', createWrapMakeClientConstructor())
|
|
239
242
|
|
|
240
|
-
|
|
241
|
-
})
|
|
243
|
+
return client
|
|
244
|
+
})
|
|
245
|
+
}
|
|
242
246
|
|
|
243
247
|
addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'] }, patch)
|
|
244
248
|
|
|
@@ -4,6 +4,8 @@ const types = require('./types')
|
|
|
4
4
|
const { channel, addHook, AsyncResource } = require('../helpers/instrument')
|
|
5
5
|
const shimmer = require('../../../datadog-shimmer')
|
|
6
6
|
|
|
7
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0])
|
|
8
|
+
|
|
7
9
|
const startChannel = channel('apm:grpc:server:request:start')
|
|
8
10
|
const errorChannel = channel('apm:grpc:server:request:error')
|
|
9
11
|
const updateChannel = channel('apm:grpc:server:request:update')
|
|
@@ -139,11 +141,13 @@ function isEmitter (obj) {
|
|
|
139
141
|
return typeof obj.emit === 'function' && typeof obj.once === 'function'
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
if (nodeMajor <= 14) {
|
|
145
|
+
addHook({ name: 'grpc', versions: ['>=1.24.3'], file: 'src/server.js' }, server => {
|
|
146
|
+
shimmer.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
144
147
|
|
|
145
|
-
|
|
146
|
-
})
|
|
148
|
+
return server
|
|
149
|
+
})
|
|
150
|
+
}
|
|
147
151
|
|
|
148
152
|
addHook({ name: '@grpc/grpc-js', versions: ['>=1.0.3'], file: 'build/src/server.js' }, server => {
|
|
149
153
|
shimmer.wrap(server.Server.prototype, 'register', wrapRegister)
|
|
@@ -22,6 +22,10 @@ for (const packageName of names) {
|
|
|
22
22
|
|
|
23
23
|
hooks[packageName]()
|
|
24
24
|
|
|
25
|
+
if (!instrumentations[packageName]) {
|
|
26
|
+
return moduleExports
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
for (const { name, file, versions, hook } of instrumentations[packageName]) {
|
|
26
30
|
const fullFilename = filename(name, file)
|
|
27
31
|
|
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
const semver = require('semver')
|
|
3
|
+
|
|
2
4
|
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
3
5
|
const shimmer = require('../../datadog-shimmer')
|
|
4
6
|
const log = require('../../dd-trace/src/log')
|
|
7
|
+
const { version: ddTraceVersion } = require('../../../package.json')
|
|
5
8
|
const {
|
|
6
9
|
getCoveredFilenamesFromCoverage,
|
|
7
10
|
JEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
8
11
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
9
|
-
getTestLineStart
|
|
12
|
+
getTestLineStart,
|
|
13
|
+
getTestSuitePath,
|
|
14
|
+
getTestParametersString
|
|
10
15
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
16
|
+
const {
|
|
17
|
+
getFormattedJestTestParameters,
|
|
18
|
+
getJestTestName,
|
|
19
|
+
getJestSuitesToRun
|
|
20
|
+
} = require('../../datadog-plugin-jest/src/util')
|
|
11
21
|
|
|
12
22
|
const testSessionStartCh = channel('ci:jest:session:start')
|
|
13
23
|
const testSessionFinishCh = channel('ci:jest:session:finish')
|
|
@@ -34,13 +44,6 @@ let skippableSuites = []
|
|
|
34
44
|
let isCodeCoverageEnabled = false
|
|
35
45
|
let isSuitesSkippingEnabled = false
|
|
36
46
|
|
|
37
|
-
const {
|
|
38
|
-
getTestSuitePath,
|
|
39
|
-
getTestParametersString
|
|
40
|
-
} = require('../../dd-trace/src/plugins/util/test')
|
|
41
|
-
|
|
42
|
-
const { getFormattedJestTestParameters, getJestTestName } = require('../../datadog-plugin-jest/src/util')
|
|
43
|
-
|
|
44
47
|
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
45
48
|
|
|
46
49
|
const specStatusToTestStatus = {
|
|
@@ -426,10 +429,7 @@ addHook({
|
|
|
426
429
|
const testPaths = await getTestPaths.apply(this, arguments)
|
|
427
430
|
const { tests } = testPaths
|
|
428
431
|
|
|
429
|
-
const filteredTests =
|
|
430
|
-
const relativePath = testPath.replace(`${rootDir}/`, '')
|
|
431
|
-
return !skippableSuites.includes(relativePath)
|
|
432
|
-
})
|
|
432
|
+
const filteredTests = getJestSuitesToRun(skippableSuites, tests, rootDir)
|
|
433
433
|
|
|
434
434
|
skippableSuites = []
|
|
435
435
|
|
|
@@ -451,6 +451,7 @@ addHook({
|
|
|
451
451
|
}, jestConfigSyncWrapper)
|
|
452
452
|
|
|
453
453
|
function jasmineAsyncInstallWraper (jasmineAsyncInstallExport, jestVersion) {
|
|
454
|
+
log.warn('jest-jasmine2 support is removed from dd-trace@v4. Consider changing to jest-circus as `testRunner`.')
|
|
454
455
|
return function (globalConfig, globalInput) {
|
|
455
456
|
globalInput._ddtrace = global._ddtrace
|
|
456
457
|
shimmer.wrap(globalInput.jasmine.Spec.prototype, 'execute', execute => function (onComplete) {
|
|
@@ -480,11 +481,13 @@ function jasmineAsyncInstallWraper (jasmineAsyncInstallExport, jestVersion) {
|
|
|
480
481
|
}
|
|
481
482
|
}
|
|
482
483
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
484
|
+
if (semver.lt(ddTraceVersion, '4.0.0')) {
|
|
485
|
+
addHook({
|
|
486
|
+
name: 'jest-jasmine2',
|
|
487
|
+
versions: ['>=24.8.0'],
|
|
488
|
+
file: 'build/jasmineAsyncInstall.js'
|
|
489
|
+
}, jasmineAsyncInstallWraper)
|
|
490
|
+
}
|
|
488
491
|
|
|
489
492
|
addHook({
|
|
490
493
|
name: 'jest-worker',
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const { channel, addHook, AsyncResource } = require('./helpers/instrument')
|
|
6
6
|
const shimmer = require('../../datadog-shimmer')
|
|
7
|
+
const { MAJOR } = require('../../../version')
|
|
7
8
|
|
|
8
9
|
const startChannel = channel('apm:next:request:start')
|
|
9
10
|
const finishChannel = channel('apm:next:request:finish')
|
|
@@ -168,7 +169,11 @@ addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-serv
|
|
|
168
169
|
return nextServer
|
|
169
170
|
})
|
|
170
171
|
|
|
171
|
-
addHook({
|
|
172
|
+
addHook({
|
|
173
|
+
name: 'next',
|
|
174
|
+
versions: MAJOR >= 4 ? ['>=10.2 <11.1'] : ['>=9.5 <11.1'],
|
|
175
|
+
file: 'dist/next-server/server/next-server.js'
|
|
176
|
+
}, nextServer => {
|
|
172
177
|
const Server = nextServer.default
|
|
173
178
|
|
|
174
179
|
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
|
|
@@ -38,6 +38,8 @@ class BaseAwsSdkPlugin extends Plugin {
|
|
|
38
38
|
'service.name': serviceName,
|
|
39
39
|
'aws.operation': operation,
|
|
40
40
|
'aws.region': awsRegion,
|
|
41
|
+
'region': awsRegion,
|
|
42
|
+
'aws_service': awsService,
|
|
41
43
|
'aws.service': awsService,
|
|
42
44
|
'component': 'aws-sdk'
|
|
43
45
|
}
|
|
@@ -60,6 +62,7 @@ class BaseAwsSdkPlugin extends Plugin {
|
|
|
60
62
|
const { span } = store
|
|
61
63
|
if (!span) return
|
|
62
64
|
span.setTag('aws.region', region)
|
|
65
|
+
span.setTag('region', region)
|
|
63
66
|
})
|
|
64
67
|
|
|
65
68
|
this.addSub(`apm:aws:request:complete:${this.serviceIdentifier}`, ({ response }) => {
|
|
@@ -12,7 +12,8 @@ class CloudwatchLogs extends BaseAwsSdkPlugin {
|
|
|
12
12
|
|
|
13
13
|
return Object.assign(tags, {
|
|
14
14
|
'resource.name': `${operation} ${params.logGroupName}`,
|
|
15
|
-
'aws.cloudwatch.logs.log_group_name': params.logGroupName
|
|
15
|
+
'aws.cloudwatch.logs.log_group_name': params.logGroupName,
|
|
16
|
+
'loggroupname': params.logGroupName
|
|
16
17
|
})
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -12,7 +12,8 @@ class DynamoDb extends BaseAwsSdkPlugin {
|
|
|
12
12
|
if (params.TableName) {
|
|
13
13
|
Object.assign(tags, {
|
|
14
14
|
'resource.name': `${operation} ${params.TableName}`,
|
|
15
|
-
'aws.dynamodb.table_name': params.TableName
|
|
15
|
+
'aws.dynamodb.table_name': params.TableName,
|
|
16
|
+
'tablename': params.TableName
|
|
16
17
|
})
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -27,7 +28,8 @@ class DynamoDb extends BaseAwsSdkPlugin {
|
|
|
27
28
|
// also add span type to match serverless convention
|
|
28
29
|
Object.assign(tags, {
|
|
29
30
|
'resource.name': `${operation} ${tableName}`,
|
|
30
|
-
'aws.dynamodb.table_name': tableName
|
|
31
|
+
'aws.dynamodb.table_name': tableName,
|
|
32
|
+
'tablename': tableName
|
|
31
33
|
})
|
|
32
34
|
}
|
|
33
35
|
}
|
|
@@ -7,10 +7,11 @@ class EventBridge extends BaseAwsSdkPlugin {
|
|
|
7
7
|
|
|
8
8
|
generateTags (params, operation, response) {
|
|
9
9
|
if (!params || !params.source) return {}
|
|
10
|
-
|
|
10
|
+
const rulename = params.Name ? params.Name : ''
|
|
11
11
|
return {
|
|
12
|
-
'resource.name': `${operation} ${params.source}
|
|
13
|
-
'aws.eventbridge.source': params.source
|
|
12
|
+
'resource.name': operation ? `${operation} ${params.source}` : params.source,
|
|
13
|
+
'aws.eventbridge.source': `${params.source}`,
|
|
14
|
+
'rulename': `${rulename}`
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -12,7 +12,8 @@ class Redshift extends BaseAwsSdkPlugin {
|
|
|
12
12
|
|
|
13
13
|
return Object.assign(tags, {
|
|
14
14
|
'resource.name': `${operation} ${params.ClusterIdentifier}`,
|
|
15
|
-
'aws.redshift.cluster_identifier': params.ClusterIdentifier
|
|
15
|
+
'aws.redshift.cluster_identifier': params.ClusterIdentifier,
|
|
16
|
+
'clusteridentifier': params.ClusterIdentifier
|
|
16
17
|
})
|
|
17
18
|
}
|
|
18
19
|
}
|
|
@@ -9,10 +9,17 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
9
9
|
if (!params) return {}
|
|
10
10
|
|
|
11
11
|
if (!params.TopicArn && !(response.data && response.data.TopicArn)) return {}
|
|
12
|
+
const TopicArn = params.TopicArn || response.data.TopicArn
|
|
13
|
+
// Split the ARN into its parts
|
|
14
|
+
// ex.'arn:aws:sns:us-east-1:123456789012:my-topic'
|
|
15
|
+
const arnParts = TopicArn.split(':')
|
|
12
16
|
|
|
17
|
+
// Get the topic name from the last part of the ARN
|
|
18
|
+
const topicName = arnParts[arnParts.length - 1]
|
|
13
19
|
return {
|
|
14
20
|
'resource.name': `${operation} ${params.TopicArn || response.data.TopicArn}`,
|
|
15
|
-
'aws.sns.topic_arn':
|
|
21
|
+
'aws.sns.topic_arn': TopicArn,
|
|
22
|
+
'topicname': topicName
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
// TODO: should arn be sanitized or quantized in some way here,
|
|
@@ -59,10 +59,16 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
59
59
|
const tags = {}
|
|
60
60
|
|
|
61
61
|
if (!params || (!params.QueueName && !params.QueueUrl)) return tags
|
|
62
|
+
// 'https://sqs.us-east-1.amazonaws.com/123456789012/my-queue';
|
|
63
|
+
let queueName = params.QueueName
|
|
64
|
+
if (params.QueueUrl) {
|
|
65
|
+
queueName = params.QueueUrl.split('/')[params.QueueUrl.split('/').length - 1]
|
|
66
|
+
}
|
|
62
67
|
|
|
63
68
|
Object.assign(tags, {
|
|
64
69
|
'resource.name': `${operation} ${params.QueueName || params.QueueUrl}`,
|
|
65
|
-
'aws.sqs.queue_name': params.QueueName || params.QueueUrl
|
|
70
|
+
'aws.sqs.queue_name': params.QueueName || params.QueueUrl,
|
|
71
|
+
'queuename': queueName
|
|
66
72
|
})
|
|
67
73
|
|
|
68
74
|
switch (operation) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* There are two ways to call `test.each` in `jest`:
|
|
3
5
|
* 1. With an array of arrays: https://jestjs.io/docs/api#1-testeachtablename-fn-timeout
|
|
@@ -45,4 +47,11 @@ function getJestTestName (test) {
|
|
|
45
47
|
return titles.join(' ')
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
function getJestSuitesToRun (skippableSuites, originalTests, rootDir) {
|
|
51
|
+
return originalTests.filter(({ path: testPath }) => {
|
|
52
|
+
const relativePath = getTestSuitePath(testPath, rootDir)
|
|
53
|
+
return !skippableSuites.includes(relativePath)
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { getFormattedJestTestParameters, getJestTestName, getJestSuitesToRun }
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
const { COMMAND_INJECTION } = require('../vulnerabilities')
|
|
3
4
|
|
|
4
5
|
class CommandInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
6
|
constructor () {
|
|
6
|
-
super(
|
|
7
|
+
super(COMMAND_INJECTION)
|
|
7
8
|
this.addSub('datadog:child_process:execution:start', ({ command }) => this.analyze(command))
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
3
|
+
const { LDAP_INJECTION } = require('../vulnerabilities')
|
|
3
4
|
|
|
4
5
|
class LdapInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
6
|
constructor () {
|
|
6
|
-
super(
|
|
7
|
+
super(LDAP_INJECTION)
|
|
7
8
|
this.addSub('datadog:ldapjs:client:search', ({ base, filter }) => this.analyzeAll(base, filter))
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -4,10 +4,11 @@ const path = require('path')
|
|
|
4
4
|
const { getIastContext } = require('../iast-context')
|
|
5
5
|
const { storage } = require('../../../../../datadog-core')
|
|
6
6
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
7
|
+
const { PATH_TRAVERSAL } = require('../vulnerabilities')
|
|
7
8
|
|
|
8
9
|
class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
9
10
|
constructor () {
|
|
10
|
-
super(
|
|
11
|
+
super(PATH_TRAVERSAL)
|
|
11
12
|
this.addSub('apm:fs:operation:start', obj => {
|
|
12
13
|
const pathArguments = []
|
|
13
14
|
if (obj.dest) {
|
|
@@ -40,13 +41,29 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
40
41
|
this.analyze(pathArguments)
|
|
41
42
|
})
|
|
42
43
|
|
|
43
|
-
this.exclusionList = [
|
|
44
|
+
this.exclusionList = [
|
|
45
|
+
path.join('node_modules', 'send') + path.sep
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
this.internalExclusionList = [
|
|
49
|
+
'node:fs',
|
|
50
|
+
'node:internal/fs',
|
|
51
|
+
'node:internal\\fs',
|
|
52
|
+
'fs.js',
|
|
53
|
+
'internal/fs',
|
|
54
|
+
'internal\\fs'
|
|
55
|
+
]
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
_isExcluded (location) {
|
|
47
|
-
let ret =
|
|
59
|
+
let ret = true
|
|
48
60
|
if (location && location.path) {
|
|
49
|
-
|
|
61
|
+
// Exclude from reporting those vulnerabilities which location is from an internal fs call
|
|
62
|
+
if (location.isInternal) {
|
|
63
|
+
ret = this.internalExclusionList.some(elem => location.path.includes(elem))
|
|
64
|
+
} else {
|
|
65
|
+
ret = this.exclusionList.some(elem => location.path.includes(elem))
|
|
66
|
+
}
|
|
50
67
|
}
|
|
51
68
|
return ret
|
|
52
69
|
}
|
|
@@ -59,7 +76,7 @@ class PathTraversalAnalyzer extends InjectionAnalyzer {
|
|
|
59
76
|
|
|
60
77
|
if (value && value.constructor === Array) {
|
|
61
78
|
for (const val of value) {
|
|
62
|
-
if (this._isVulnerable(val, iastContext)) {
|
|
79
|
+
if (this._isVulnerable(val, iastContext) && this._checkOCE(iastContext)) {
|
|
63
80
|
this._report(val, iastContext)
|
|
64
81
|
// no support several evidences in the same vulnerability, just report the 1st one
|
|
65
82
|
break
|
|
@@ -1,12 +1,48 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
+
|
|
2
3
|
const InjectionAnalyzer = require('./injection-analyzer')
|
|
4
|
+
const { SQL_INJECTION } = require('../vulnerabilities')
|
|
5
|
+
const { getRanges } = require('../taint-tracking/operations')
|
|
6
|
+
const { storage } = require('../../../../../datadog-core')
|
|
7
|
+
const { getIastContext } = require('../iast-context')
|
|
8
|
+
const { createVulnerability, addVulnerability } = require('../vulnerability-reporter')
|
|
3
9
|
|
|
4
10
|
class SqlInjectionAnalyzer extends InjectionAnalyzer {
|
|
5
11
|
constructor () {
|
|
6
|
-
super(
|
|
7
|
-
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql))
|
|
8
|
-
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql))
|
|
9
|
-
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text))
|
|
12
|
+
super(SQL_INJECTION)
|
|
13
|
+
this.addSub('apm:mysql:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
14
|
+
this.addSub('apm:mysql2:query:start', ({ sql }) => this.analyze(sql, 'MYSQL'))
|
|
15
|
+
this.addSub('apm:pg:query:start', ({ query }) => this.analyze(query.text, 'POSTGRES'))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_getEvidence (value, iastContext, dialect) {
|
|
19
|
+
const ranges = getRanges(iastContext, value)
|
|
20
|
+
return { value, ranges, dialect }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
analyze (value, dialect) {
|
|
24
|
+
const store = storage.getStore()
|
|
25
|
+
const iastContext = getIastContext(store)
|
|
26
|
+
if (store && !iastContext) return
|
|
27
|
+
this._reportIfVulnerable(value, iastContext, dialect)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_reportIfVulnerable (value, context, dialect) {
|
|
31
|
+
if (this._isVulnerable(value, context) && this._checkOCE(context)) {
|
|
32
|
+
this._report(value, context, dialect)
|
|
33
|
+
return true
|
|
34
|
+
}
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_report (value, context, dialect) {
|
|
39
|
+
const evidence = this._getEvidence(value, context, dialect)
|
|
40
|
+
const location = this._getLocation()
|
|
41
|
+
if (!this._isExcluded(location)) {
|
|
42
|
+
const spanId = context && context.rootSpan && context.rootSpan.context().toSpanId()
|
|
43
|
+
const vulnerability = createVulnerability(this._type, evidence, spanId, location)
|
|
44
|
+
addVulnerability(context, vulnerability)
|
|
45
|
+
}
|
|
10
46
|
}
|
|
11
47
|
}
|
|
12
48
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const Analyzer = require('./vulnerability-analyzer')
|
|
3
|
+
const { WEAK_CIPHER } = require('../vulnerabilities')
|
|
3
4
|
|
|
4
5
|
const INSECURE_CIPHERS = new Set([
|
|
5
6
|
'des', 'des-cbc', 'des-cfb', 'des-cfb1', 'des-cfb8', 'des-ecb', 'des-ede', 'des-ede-cbc', 'des-ede-cfb',
|
|
@@ -12,7 +13,7 @@ const INSECURE_CIPHERS = new Set([
|
|
|
12
13
|
|
|
13
14
|
class WeakCipherAnalyzer extends Analyzer {
|
|
14
15
|
constructor () {
|
|
15
|
-
super(
|
|
16
|
+
super(WEAK_CIPHER)
|
|
16
17
|
this.addSub('datadog:crypto:cipher:start', ({ algorithm }) => this.analyze(algorithm))
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const Analyzer = require('./vulnerability-analyzer')
|
|
3
|
+
const { WEAK_HASH } = require('../vulnerabilities')
|
|
3
4
|
|
|
4
5
|
const INSECURE_HASH_ALGORITHMS = new Set([
|
|
5
6
|
'md4', 'md4WithRSAEncryption', 'RSA-MD4',
|
|
@@ -9,7 +10,7 @@ const INSECURE_HASH_ALGORITHMS = new Set([
|
|
|
9
10
|
|
|
10
11
|
class WeakHashAnalyzer extends Analyzer {
|
|
11
12
|
constructor () {
|
|
12
|
-
super(
|
|
13
|
+
super(WEAK_HASH)
|
|
13
14
|
this.addSub('datadog:crypto:hashing:start', ({ algorithm }) => this.analyze(algorithm))
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -45,7 +45,8 @@ function getFirstNonDDPathAndLineFromCallsites (callsites) {
|
|
|
45
45
|
if (!isExcluded(callsite) && filepath.indexOf(pathLine.ddBasePath) === -1) {
|
|
46
46
|
return {
|
|
47
47
|
path: path.relative(process.cwd(), filepath),
|
|
48
|
-
line: callsite.getLineNumber()
|
|
48
|
+
line: callsite.getLineNumber(),
|
|
49
|
+
isInternal: !path.isAbsolute(filepath)
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function contains (rangeContainer, rangeContained) {
|
|
4
|
+
if (rangeContainer.start > rangeContained.start) {
|
|
5
|
+
return false
|
|
6
|
+
}
|
|
7
|
+
return rangeContainer.end >= rangeContained.end
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function intersects (rangeA, rangeB) {
|
|
11
|
+
return rangeB.start < rangeA.end && rangeB.end > rangeA.start
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function remove (range, rangeToRemove) {
|
|
15
|
+
if (!intersects(range, rangeToRemove)) {
|
|
16
|
+
return [range]
|
|
17
|
+
} else if (contains(rangeToRemove, range)) {
|
|
18
|
+
return []
|
|
19
|
+
} else {
|
|
20
|
+
const result = []
|
|
21
|
+
if (rangeToRemove.start > range.start) {
|
|
22
|
+
const offset = rangeToRemove.start - range.start
|
|
23
|
+
result.push({ start: range.start, end: range.start + offset })
|
|
24
|
+
}
|
|
25
|
+
if (rangeToRemove.end < range.end) {
|
|
26
|
+
const offset = range.end - rangeToRemove.end
|
|
27
|
+
result.push({ start: rangeToRemove.end, end: rangeToRemove.end + offset })
|
|
28
|
+
}
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
contains,
|
|
35
|
+
intersects,
|
|
36
|
+
remove
|
|
37
|
+
}
|