dd-trace 4.0.0-pre-5e89884 → 4.0.0-pre-4e7da80

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 (42) hide show
  1. package/index.d.ts +16 -0
  2. package/package.json +5 -4
  3. package/packages/datadog-instrumentations/src/jest.js +1 -0
  4. package/packages/datadog-instrumentations/src/next.js +6 -1
  5. package/packages/datadog-plugin-aws-sdk/src/base.js +3 -0
  6. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -1
  7. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +4 -2
  8. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +4 -3
  9. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -1
  10. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -0
  11. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -1
  12. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -1
  13. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +8 -1
  14. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +7 -1
  15. package/packages/datadog-plugin-http/src/client.js +2 -1
  16. package/packages/datadog-plugin-http2/src/client.js +2 -1
  17. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -0
  18. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +2 -1
  19. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +2 -1
  20. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +22 -5
  21. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +40 -4
  22. package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +2 -1
  23. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +2 -1
  24. package/packages/dd-trace/src/appsec/iast/path-line.js +2 -1
  25. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/range-utils.js +37 -0
  26. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +29 -0
  27. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/ldap-sensitive-analyzer.js +35 -0
  28. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +95 -0
  29. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +144 -0
  30. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +113 -0
  31. package/packages/dd-trace/src/appsec/iast/vulnerabilities.js +8 -0
  32. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +4 -76
  33. package/packages/dd-trace/src/config.js +44 -7
  34. package/packages/dd-trace/src/constants.js +3 -1
  35. package/packages/dd-trace/src/git_metadata_tagger.js +17 -0
  36. package/packages/dd-trace/src/plugins/util/ci.js +62 -7
  37. package/packages/dd-trace/src/plugins/util/tags.js +5 -1
  38. package/packages/dd-trace/src/proxy.js +4 -0
  39. package/packages/dd-trace/src/serverless.js +25 -0
  40. package/packages/dd-trace/src/span_processor.js +3 -0
  41. package/packages/dd-trace/src/tracer.js +9 -0
  42. package/version.js +9 -0
package/index.d.ts CHANGED
@@ -77,6 +77,10 @@ export declare interface Tracer extends opentracing.Tracer {
77
77
  * span will finish when that callback is called.
78
78
  * * The function doesn't accept a callback and doesn't return a promise, in
79
79
  * which case the span will finish at the end of the function execution.
80
+ *
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. Note that this
83
+ * option is deprecated and has been removed in version 4.0.
80
84
  */
81
85
  trace<T> (name: string, fn: (span?: Span, fn?: (error?: Error) => any) => T): T;
82
86
  trace<T> (name: string, options: TraceOptions & SpanOptions, fn: (span?: Span, done?: (error?: Error) => string) => T): T;
@@ -440,6 +444,11 @@ export declare interface TracerOptions {
440
444
  * Whether to enable vulnerability deduplication
441
445
  */
442
446
  deduplicationEnabled?: boolean
447
+ /**
448
+ * Whether to enable vulnerability redaction
449
+ * @default true
450
+ */
451
+ redactionEnabled?: boolean
443
452
  }
444
453
  };
445
454
 
@@ -485,6 +494,13 @@ export declare interface TracerOptions {
485
494
  */
486
495
  logLevel?: 'error' | 'debug'
487
496
 
497
+ /**
498
+ * If false, require a parent in order to trace.
499
+ * @default true
500
+ * @deprecated since version 4.0
501
+ */
502
+ orphanable?: boolean
503
+
488
504
  /**
489
505
  * Enables DBM to APM link using tag injection.
490
506
  * @default 'disabled'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "4.0.0-pre-5e89884",
3
+ "version": "4.0.0-pre-4e7da80",
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.0",
71
- "@datadog/native-metrics": "^1.6.0",
72
- "@datadog/pprof": "^2.2.0",
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",
@@ -454,6 +454,7 @@ addHook({
454
454
  }, jestConfigSyncWrapper)
455
455
 
456
456
  function jasmineAsyncInstallWraper (jasmineAsyncInstallExport, jestVersion) {
457
+ log.warn('jest-jasmine2 support is removed from dd-trace@v4. Consider changing to jest-circus as `testRunner`.')
457
458
  return function (globalConfig, globalInput) {
458
459
  globalInput._ddtrace = global._ddtrace
459
460
  shimmer.wrap(globalInput.jasmine.Spec.prototype, 'execute', execute => function (onComplete) {
@@ -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({ name: 'next', versions: ['>=9.5 <11.1'], file: 'dist/next-server/server/next-server.js' }, nextServer => {
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
 
@@ -9,7 +9,8 @@ class Kinesis extends BaseAwsSdkPlugin {
9
9
 
10
10
  return {
11
11
  'resource.name': `${operation} ${params.StreamName}`,
12
- 'aws.kinesis.stream_name': params.StreamName
12
+ 'aws.kinesis.stream_name': params.StreamName,
13
+ 'streamname': params.StreamName
13
14
  }
14
15
  }
15
16
 
@@ -13,6 +13,7 @@ class Lambda extends BaseAwsSdkPlugin {
13
13
 
14
14
  return Object.assign(tags, {
15
15
  'resource.name': `${operation} ${params.FunctionName}`,
16
+ 'functionname': params.FunctionName,
16
17
  'aws.lambda': params.FunctionName
17
18
  })
18
19
  }
@@ -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
  }
@@ -12,7 +12,8 @@ class S3 extends BaseAwsSdkPlugin {
12
12
 
13
13
  return Object.assign(tags, {
14
14
  'resource.name': `${operation} ${params.Bucket}`,
15
- 'aws.s3.bucket_name': params.Bucket
15
+ 'aws.s3.bucket_name': params.Bucket,
16
+ 'bucketname': params.Bucket
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': params.TopicArn || response.data.TopicArn
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) {
@@ -45,7 +45,8 @@ class HttpClientPlugin extends Plugin {
45
45
  'resource.name': method,
46
46
  'span.type': 'http',
47
47
  'http.method': method,
48
- 'http.url': uri
48
+ 'http.url': uri,
49
+ 'out.host': hostname
49
50
  }
50
51
  })
51
52
 
@@ -52,7 +52,8 @@ class Http2ClientPlugin extends Plugin {
52
52
  'resource.name': method,
53
53
  'span.type': 'http',
54
54
  'http.method': method,
55
- 'http.url': uri
55
+ 'http.url': uri,
56
+ 'out.host': sessionDetails.host
56
57
  }
57
58
  })
58
59
 
@@ -33,6 +33,8 @@ function extract (tracer, bufferMap) {
33
33
  const textMap = {}
34
34
 
35
35
  for (const key of Object.keys(bufferMap)) {
36
+ if (bufferMap[key] === null || bufferMap[key] === undefined) continue
37
+
36
38
  textMap[key] = bufferMap[key].toString()
37
39
  }
38
40
 
@@ -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('COMMAND_INJECTION')
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('LDAP_INJECTION')
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('PATH_TRAVERSAL')
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 = [ path.join('node_modules', 'send') + path.sep ]
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 = false
59
+ let ret = true
48
60
  if (location && location.path) {
49
- ret = this.exclusionList.some(elem => location.path.includes(elem))
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('SQL_INJECTION')
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('WEAK_CIPHER')
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('WEAK_HASH')
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
+ }
@@ -0,0 +1,29 @@
1
+ 'use strict'
2
+
3
+ const iastLog = require('../../../iast-log')
4
+
5
+ const COMMAND_PATTERN = '^(?:\\s*(?:sudo|doas)\\s+)?\\b\\S+\\b(.*)'
6
+
7
+ class CommandSensitiveAnalyzer {
8
+ constructor () {
9
+ this._pattern = new RegExp(COMMAND_PATTERN, 'gmi')
10
+ }
11
+
12
+ extractSensitiveRanges (evidence) {
13
+ try {
14
+ this._pattern.lastIndex = 0
15
+
16
+ const regexResult = this._pattern.exec(evidence.value)
17
+ if (regexResult && regexResult.length > 1) {
18
+ const start = regexResult.index + (regexResult[0].length - regexResult[1].length)
19
+ const end = start + regexResult[1].length
20
+ return [{ start, end }]
21
+ }
22
+ } catch (e) {
23
+ iastLog.debug(e)
24
+ }
25
+ return []
26
+ }
27
+ }
28
+
29
+ module.exports = CommandSensitiveAnalyzer
@@ -0,0 +1,35 @@
1
+ 'use strict'
2
+
3
+ const iastLog = require('../../../iast-log')
4
+
5
+ const LDAP_PATTERN = '\\(.*?(?:~=|=|<=|>=)(?<LITERAL>[^)]+)\\)'
6
+
7
+ class LdapSensitiveAnalyzer {
8
+ constructor () {
9
+ this._pattern = new RegExp(LDAP_PATTERN, 'gmi')
10
+ }
11
+
12
+ extractSensitiveRanges (evidence) {
13
+ try {
14
+ this._pattern.lastIndex = 0
15
+ const tokens = []
16
+
17
+ let regexResult = this._pattern.exec(evidence.value)
18
+ while (regexResult != null) {
19
+ if (!regexResult.groups.LITERAL) continue
20
+ // Computing indices manually since NodeJs 12 does not support d flag on regular expressions
21
+ // TODO Get indices from group by adding d flag in regular expression
22
+ const start = regexResult.index + (regexResult[0].length - regexResult.groups.LITERAL.length - 1)
23
+ const end = start + regexResult.groups.LITERAL.length
24
+ tokens.push({ start, end })
25
+ regexResult = this._pattern.exec(evidence.value)
26
+ }
27
+ return tokens
28
+ } catch (e) {
29
+ iastLog.debug(e)
30
+ }
31
+ return []
32
+ }
33
+ }
34
+
35
+ module.exports = LdapSensitiveAnalyzer
@@ -0,0 +1,95 @@
1
+ 'use strict'
2
+
3
+ const iastLog = require('../../../iast-log')
4
+
5
+ const STRING_LITERAL = '\'(?:\'\'|[^\'])*\''
6
+ const POSTGRESQL_ESCAPED_LITERAL = '\\$([^$]*)\\$.*?\\$\\1\\$'
7
+ const MYSQL_STRING_LITERAL = '"(?:\\\\"|[^"])*"|\'(?:\\\\\'|[^\'])*\''
8
+ const LINE_COMMENT = '--.*$'
9
+ const BLOCK_COMMENT = '/\\*[\\s\\S]*\\*/'
10
+ const EXPONENT = '(?:E[-+]?\\d+[fd]?)?'
11
+ const INTEGER_NUMBER = '(?<!\\w)\\d+'
12
+ const DECIMAL_NUMBER = '\\d*\\.\\d+'
13
+ const HEX_NUMBER = 'x\'[0-9a-f]+\'|0x[0-9a-f]+'
14
+ const BIN_NUMBER = 'b\'[0-9a-f]+\'|0b[0-9a-f]+'
15
+ const NUMERIC_LITERAL =
16
+ `[-+]?(?:${
17
+ [
18
+ HEX_NUMBER,
19
+ BIN_NUMBER,
20
+ DECIMAL_NUMBER + EXPONENT,
21
+ INTEGER_NUMBER + EXPONENT
22
+ ].join('|')
23
+ })`
24
+
25
+ class SqlSensitiveAnalyzer {
26
+ constructor () {
27
+ this._patterns = {
28
+ MYSQL: new RegExp(
29
+ [
30
+ NUMERIC_LITERAL,
31
+ MYSQL_STRING_LITERAL,
32
+ LINE_COMMENT,
33
+ BLOCK_COMMENT
34
+ ].join('|'),
35
+ 'gmi'
36
+ ),
37
+ POSTGRES: new RegExp(
38
+ [
39
+ NUMERIC_LITERAL,
40
+ POSTGRESQL_ESCAPED_LITERAL,
41
+ STRING_LITERAL,
42
+ LINE_COMMENT,
43
+ BLOCK_COMMENT
44
+ ].join('|'),
45
+ 'gmi'
46
+ )
47
+ }
48
+ }
49
+
50
+ extractSensitiveRanges (evidence) {
51
+ try {
52
+ const pattern = this._patterns[evidence.dialect]
53
+ pattern.lastIndex = 0
54
+ const tokens = []
55
+
56
+ let regexResult = pattern.exec(evidence.value)
57
+ while (regexResult != null) {
58
+ let start = regexResult.index
59
+ let end = regexResult.index + regexResult[0].length
60
+ const startChar = evidence.value.charAt(start)
61
+ if (startChar === '\'' || startChar === '"') {
62
+ start++
63
+ end--
64
+ } else if (end > start + 1) {
65
+ const nextChar = evidence.value.charAt(start + 1)
66
+ if (startChar === '/' && nextChar === '*') {
67
+ start += 2
68
+ end -= 2
69
+ } else if (startChar === '-' && startChar === nextChar) {
70
+ start += 2
71
+ } else if (startChar.toLowerCase() === 'q' && nextChar === '\'') {
72
+ start += 3
73
+ end -= 2
74
+ } else if (startChar === '$') {
75
+ const match = regexResult[0]
76
+ const size = match.indexOf('$', 1) + 1
77
+ if (size > 1) {
78
+ start += size
79
+ end -= size
80
+ }
81
+ }
82
+ }
83
+
84
+ tokens.push({ start, end })
85
+ regexResult = pattern.exec(evidence.value)
86
+ }
87
+ return tokens
88
+ } catch (e) {
89
+ iastLog.debug(e)
90
+ }
91
+ return []
92
+ }
93
+ }
94
+
95
+ module.exports = SqlSensitiveAnalyzer