Socket
Socket
Sign inDemoInstall

dd-trace

Package Overview
Dependencies
Maintainers
1
Versions
574
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

dd-trace - npm Package Compare versions

Comparing version 6.0.0-pre-31c56e7 to 6.0.0-pre-332b216

packages/datadog-instrumentations/src/process.js

1

ext/formats.d.ts

@@ -8,4 +8,5 @@ import * as opentracing from 'opentracing'

LOG: 'log'
TEXT_MAP_DSM: 'text_map_dsm'
}
export = formats

3

ext/formats.js

@@ -7,3 +7,4 @@ 'use strict'

BINARY: 'binary',
LOG: 'log'
LOG: 'log',
TEXT_MAP_DSM: 'text_map_dsm'
}

@@ -5,19 +5,7 @@ 'use strict'

const Module = require('module')
const semver = require('semver')
const log = require('./packages/dd-trace/src/log')
const { isTrue } = require('./packages/dd-trace/src/util')
const telemetry = require('./packages/dd-trace/src/telemetry/init-telemetry')
const semver = require('semver')
function isTrue (envVar) {
return ['1', 'true', 'True'].includes(envVar)
}
// eslint-disable-next-line no-console
let log = { info: isTrue(process.env.DD_TRACE_DEBUG) ? console.log : () => {} }
if (semver.satisfies(process.versions.node, '>=16')) {
const Config = require('./packages/dd-trace/src/config')
log = require('./packages/dd-trace/src/log')
// eslint-disable-next-line no-new
new Config() // we need this to initialize the logger
}
let initBailout = false

@@ -24,0 +12,0 @@ let clobberBailout = false

{
"name": "dd-trace",
"version": "6.0.0-pre-31c56e7",
"version": "6.0.0-pre-332b216",
"description": "Datadog APM tracing client for JavaScript",

@@ -17,16 +17,16 @@ "main": "index.js",

"services": "node ./scripts/install_plugin_modules && node packages/dd-trace/test/setup/services",
"test": "SERVICES=* yarn services && mocha --colors --exit --expose-gc 'packages/dd-trace/test/setup/node.js' 'packages/*/test/**/*.spec.js'",
"test:appsec": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" \"packages/dd-trace/test/appsec/**/*.spec.js\"",
"test": "SERVICES=* yarn services && mocha --expose-gc 'packages/dd-trace/test/setup/node.js' 'packages/*/test/**/*.spec.js'",
"test:appsec": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" \"packages/dd-trace/test/appsec/**/*.spec.js\"",
"test:appsec:ci": "nyc --no-clean --include \"packages/dd-trace/src/appsec/**/*.js\" --exclude \"packages/dd-trace/test/appsec/**/*.plugin.spec.js\" -- npm run test:appsec",
"test:appsec:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/appsec/**/*.@($(echo $PLUGINS)).plugin.spec.js\"",
"test:appsec:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/appsec/**/*.@($(echo $PLUGINS)).plugin.spec.js\"",
"test:appsec:plugins:ci": "yarn services && nyc --no-clean --include \"packages/dd-trace/src/appsec/**/*.js\" -- npm run test:appsec:plugins",
"test:trace:core": "tap packages/dd-trace/test/*.spec.js \"packages/dd-trace/test/{ci-visibility,datastreams,encode,exporters,opentelemetry,opentracing,plugins,service-naming,telemetry}/**/*.spec.js\"",
"test:trace:core:ci": "npm run test:trace:core -- --coverage --nyc-arg=--include=\"packages/dd-trace/src/**/*.js\"",
"test:instrumentations": "mocha --colors -r 'packages/dd-trace/test/setup/mocha.js' 'packages/datadog-instrumentations/test/**/*.spec.js'",
"test:instrumentations": "mocha -r 'packages/dd-trace/test/setup/mocha.js' 'packages/datadog-instrumentations/test/**/*.spec.js'",
"test:instrumentations:ci": "nyc --no-clean --include 'packages/datadog-instrumentations/src/**/*.js' -- npm run test:instrumentations",
"test:core": "tap \"packages/datadog-core/test/**/*.spec.js\"",
"test:core:ci": "npm run test:core -- --coverage --nyc-arg=--include=\"packages/datadog-core/src/**/*.js\"",
"test:lambda": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/lambda/**/*.spec.js\"",
"test:lambda": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/dd-trace/test/lambda/**/*.spec.js\"",
"test:lambda:ci": "nyc --no-clean --include \"packages/dd-trace/src/lambda/**/*.js\" -- npm run test:lambda",
"test:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\"",
"test:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\"",
"test:plugins:ci": "yarn services && nyc --no-clean --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS)).js\" --include \"packages/datadog-instrumentations/src/@($(echo $PLUGINS))/**/*.js\" --include \"packages/datadog-plugin-@($(echo $PLUGINS))/src/**/*.js\" -- npm run test:plugins",

@@ -36,18 +36,17 @@ "test:plugins:upstream": "node ./packages/dd-trace/test/plugins/suite.js",

"test:profiler:ci": "npm run test:profiler -- --coverage --nyc-arg=--include=\"packages/dd-trace/src/profiling/**/*.js\"",
"test:integration": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/*.spec.js\"",
"test:integration:cucumber": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cucumber/*.spec.js\"",
"test:integration:cypress": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cypress/*.spec.js\"",
"test:integration:jest": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/jest/*.spec.js\"",
"test:integration:mocha": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/mocha/*.spec.js\"",
"test:integration:playwright": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/playwright/*.spec.js\"",
"test:integration:selenium": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/selenium/*.spec.js\"",
"test:integration:vitest": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/vitest/*.spec.js\"",
"test:integration:profiler": "mocha --colors --timeout 180000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/profiler/*.spec.js\"",
"test:integration:serverless": "mocha --colors --timeout 30000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/serverless/*.spec.js\"",
"test:integration:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
"test:unit:plugins": "mocha --colors --exit -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
"test:shimmer": "mocha --colors 'packages/datadog-shimmer/test/**/*.spec.js'",
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer",
"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'",
"leak:plugins": "yarn services && (cd packages/memwatch && yarn) && NODE_PATH=./packages/memwatch/node_modules node --no-warnings ./node_modules/.bin/tape \"packages/datadog-plugin-@($(echo $PLUGINS))/test/leak.js\""
"test:integration": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/*.spec.js\"",
"test:integration:appsec": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/appsec/*.spec.js\"",
"test:integration:cucumber": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cucumber/*.spec.js\"",
"test:integration:cypress": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/cypress/*.spec.js\"",
"test:integration:jest": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/jest/*.spec.js\"",
"test:integration:mocha": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/mocha/*.spec.js\"",
"test:integration:playwright": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/playwright/*.spec.js\"",
"test:integration:selenium": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/selenium/*.spec.js\"",
"test:integration:vitest": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/vitest/*.spec.js\"",
"test:integration:profiler": "mocha --timeout 180000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/profiler/*.spec.js\"",
"test:integration:serverless": "mocha --timeout 60000 -r \"packages/dd-trace/test/setup/core.js\" \"integration-tests/serverless/*.spec.js\"",
"test:integration:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
"test:unit:plugins": "mocha -r \"packages/dd-trace/test/setup/mocha.js\" \"packages/datadog-instrumentations/test/@($(echo $PLUGINS)).spec.js\" \"packages/datadog-plugin-@($(echo $PLUGINS))/test/**/*.spec.js\" --exclude \"packages/datadog-plugin-@($(echo $PLUGINS))/test/integration-test/**/*.spec.js\"",
"test:shimmer": "mocha 'packages/datadog-shimmer/test/**/*.spec.js'",
"test:shimmer:ci": "nyc --no-clean --include 'packages/datadog-shimmer/src/**/*.js' -- npm run test:shimmer"
},

@@ -79,4 +78,4 @@ "repository": {

"@datadog/native-appsec": "8.0.1",
"@datadog/native-iast-rewriter": "2.3.1",
"@datadog/native-iast-taint-tracking": "3.0.0",
"@datadog/native-iast-rewriter": "2.4.0",
"@datadog/native-iast-taint-tracking": "3.1.0",
"@datadog/native-metrics": "^2.0.0",

@@ -110,3 +109,3 @@ "@datadog/pprof": "5.3.0",

"devDependencies": {
"@types/node": ">=18",
"@types/node": "^16.18.103",
"autocannon": "^4.5.2",

@@ -123,9 +122,8 @@ "aws-sdk": "^2.1446.0",

"esbuild": "0.16.12",
"eslint": "^8.23.0",
"eslint": "^8.57.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-mocha": "^10.1.0",
"eslint-plugin-n": "^15.7.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-mocha": "^10.4.3",
"eslint-plugin-n": "^16.6.2",
"eslint-plugin-promise": "^6.4.0",
"express": "^4.18.2",

@@ -147,5 +145,4 @@ "get-port": "^3.2.0",

"tap": "^16.3.7",
"tape": "^5.6.5",
"tiktoken": "^1.0.15"
}
}

@@ -42,6 +42,6 @@ 'use strict'

childProcessInfo.command = childProcessInfo.command + ' ' + args[1].join(' ')
if (args[2] != null && typeof args[2] === 'object') {
if (args[2] !== null && typeof args[2] === 'object') {
childProcessInfo.options = args[2]
}
} else if (args[1] != null && typeof args[1] === 'object') {
} else if (args[1] !== null && typeof args[1] === 'object') {
childProcessInfo.options = args[1]

@@ -48,0 +48,0 @@ }

'use strict'
const { createCoverageMap } = require('istanbul-lib-coverage')
const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
const { addHook, channel, AsyncResource } = require('./helpers/instrument')

@@ -9,2 +10,3 @@ const shimmer = require('../../datadog-shimmer')

const testStartCh = channel('ci:cucumber:test:start')
const testRetryCh = channel('ci:cucumber:test:retry')
const testFinishCh = channel('ci:cucumber:test:finish') // used for test steps too

@@ -51,2 +53,3 @@

const numRetriesByPickleId = new Map()
const numAttemptToAsyncResource = new Map()

@@ -65,2 +68,3 @@ let pickleByFile = {}

let earlyFlakeDetectionNumRetries = 0
let isFlakyTestRetriesEnabled = false
let knownTests = []

@@ -168,43 +172,76 @@ let skippedSuites = []

let numAttempt = 0
const asyncResource = new AsyncResource('bound-anonymous-fn')
return asyncResource.runInAsyncScope(() => {
const testFileAbsolutePath = this.pickle.uri
const testSourceLine = this.gherkinDocument?.feature?.location?.line
numAttemptToAsyncResource.set(numAttempt, asyncResource)
testStartCh.publish({
testName: this.pickle.name,
testFileAbsolutePath,
testSourceLine,
isParallel: !!process.env.CUCUMBER_WORKER_ID
})
try {
const promise = run.apply(this, arguments)
promise.finally(() => {
const result = this.getWorstStepResult()
const { status, skipReason, errorMessage } = isLatestVersion
? getStatusFromResultLatest(result)
: getStatusFromResult(result)
const testFileAbsolutePath = this.pickle.uri
if (lastStatusByPickleId.has(this.pickle.id)) {
lastStatusByPickleId.get(this.pickle.id).push(status)
} else {
lastStatusByPickleId.set(this.pickle.id, [status])
const testSourceLine = this.gherkinDocument?.feature?.location?.line
const testStartPayload = {
testName: this.pickle.name,
testFileAbsolutePath,
testSourceLine,
isParallel: !!process.env.CUCUMBER_WORKER_ID
}
asyncResource.runInAsyncScope(() => {
testStartCh.publish(testStartPayload)
})
try {
this.eventBroadcaster.on('envelope', (testCase) => {
// Only supported from >=8.0.0
if (testCase?.testCaseFinished) {
const { testCaseFinished: { willBeRetried } } = testCase
if (willBeRetried) { // test case failed and will be retried
const failedAttemptAsyncResource = numAttemptToAsyncResource.get(numAttempt)
failedAttemptAsyncResource.runInAsyncScope(() => {
testRetryCh.publish(numAttempt++ > 0) // the current span will be finished and a new one will be created
})
const newAsyncResource = new AsyncResource('bound-anonymous-fn')
numAttemptToAsyncResource.set(numAttempt, newAsyncResource)
newAsyncResource.runInAsyncScope(() => {
testStartCh.publish(testStartPayload) // a new span will be created
})
}
let isNew = false
let isEfdRetry = false
if (isEarlyFlakeDetectionEnabled && status !== 'skip') {
const numRetries = numRetriesByPickleId.get(this.pickle.id)
}
})
let promise
isNew = numRetries !== undefined
isEfdRetry = numRetries > 0
}
testFinishCh.publish({ status, skipReason, errorMessage, isNew, isEfdRetry })
asyncResource.runInAsyncScope(() => {
promise = run.apply(this, arguments)
})
promise.finally(() => {
const result = this.getWorstStepResult()
const { status, skipReason, errorMessage } = isLatestVersion
? getStatusFromResultLatest(result)
: getStatusFromResult(result)
if (lastStatusByPickleId.has(this.pickle.id)) {
lastStatusByPickleId.get(this.pickle.id).push(status)
} else {
lastStatusByPickleId.set(this.pickle.id, [status])
}
let isNew = false
let isEfdRetry = false
if (isEarlyFlakeDetectionEnabled && status !== 'skip') {
const numRetries = numRetriesByPickleId.get(this.pickle.id)
isNew = numRetries !== undefined
isEfdRetry = numRetries > 0
}
const attemptAsyncResource = numAttemptToAsyncResource.get(numAttempt)
attemptAsyncResource.runInAsyncScope(() => {
testFinishCh.publish({ status, skipReason, errorMessage, isNew, isEfdRetry, isFlakyRetry: numAttempt > 0 })
})
return promise
} catch (err) {
errorCh.publish(err)
throw err
}
})
})
return promise
} catch (err) {
errorCh.publish(err)
throw err
}
})

@@ -274,2 +311,3 @@ shimmer.wrap(pl.prototype, 'runStep', runStep => function () {

isSuitesSkippingEnabled = configurationResponse.libraryConfig?.isSuitesSkippingEnabled
isFlakyTestRetriesEnabled = configurationResponse.libraryConfig?.isFlakyTestRetriesEnabled

@@ -312,2 +350,6 @@ if (isEarlyFlakeDetectionEnabled) {

if (isFlakyTestRetriesEnabled && !this.options.retry) {
this.options.retry = NUM_FAILED_TEST_RETRIES
}
sessionAsyncResource.runInAsyncScope(() => {

@@ -314,0 +356,0 @@ sessionStartCh.publish({ command, frameworkVersion })

@@ -275,3 +275,3 @@ 'use strict'

arguments[lastIndex] = innerResource.bind(function (e) {
if (typeof e === 'object') { // fs.exists receives a boolean
if (e !== null && typeof e === 'object') { // fs.exists receives a boolean
errorChannel.publish(e)

@@ -278,0 +278,0 @@ }

@@ -41,3 +41,3 @@ 'use strict'

return function (events, method, options) {
if (typeof events === 'object') {
if (events !== null && typeof events === 'object') {
arguments[0] = wrapEvents(events)

@@ -44,0 +44,0 @@ } else {

@@ -32,2 +32,6 @@ 'use strict'

if (!disabledInstrumentations.has('process')) {
require('../process')
}
const HOOK_SYMBOL = Symbol('hookExportsMap')

@@ -92,6 +96,3 @@

if (!Object.hasOwnProperty(namesAndSuccesses, name)) {
namesAndSuccesses[name] = {
success: false,
version
}
namesAndSuccesses[`${name}@${version}`] = false
}

@@ -102,3 +103,3 @@

if (hook[HOOK_SYMBOL].has(moduleExports)) {
namesAndSuccesses[name].success = true
namesAndSuccesses[`${name}@${version}`] = true
return moduleExports

@@ -123,9 +124,10 @@ }

}
namesAndSuccesses[name].success = true
namesAndSuccesses[`${name}@${version}`] = true
}
}
}
for (const name of Object.keys(namesAndSuccesses)) {
const { success, version } = namesAndSuccesses[name]
if (!success && !seenCombo.has(`${name}@${version}`)) {
for (const nameVersion of Object.keys(namesAndSuccesses)) {
const [name, version] = nameVersion.split('@')
const success = namesAndSuccesses[nameVersion]
if (!success && !seenCombo.has(nameVersion)) {
telemetry('abort.integration', [

@@ -135,4 +137,4 @@ `integration:${name}`,

])
log.info(`Found incompatible integration version: ${name}@${version}`)
seenCombo.add(`${name}@${version}`)
log.info(`Found incompatible integration version: ${nameVersion}`)
seenCombo.add(nameVersion)
}

@@ -139,0 +141,0 @@ }

@@ -46,4 +46,6 @@ 'use strict'

const ctx = { args, http }
const abortController = new AbortController()
const ctx = { args, http, abortController }
return startChannel.runStores(ctx, () => {

@@ -111,2 +113,6 @@ let finished = false

if (abortController.signal.aborted) {
req.destroy(abortController.signal.reason || new Error('Aborted'))
}
return req

@@ -137,3 +143,3 @@ } catch (e) {

function combineOptions (inputURL, inputOptions) {
if (typeof inputOptions === 'object') {
if (inputOptions !== null && typeof inputOptions === 'object') {
return Object.assign(inputURL || {}, inputOptions)

@@ -140,0 +146,0 @@ } else {

@@ -15,2 +15,3 @@ 'use strict'

const finishSetHeaderCh = channel('datadog:http:server:response:set-header:finish')
const startSetHeaderCh = channel('datadog:http:server:response:set-header:start')

@@ -28,2 +29,8 @@ const requestFinishedSet = new WeakSet()

shimmer.wrap(http.ServerResponse.prototype, 'end', wrapEnd)
shimmer.wrap(http.ServerResponse.prototype, 'setHeader', wrapSetHeader)
shimmer.wrap(http.ServerResponse.prototype, 'removeHeader', wrapAppendOrRemoveHeader)
// Added in node v16.17.0
if (http.ServerResponse.prototype.appendHeader) {
shimmer.wrap(http.ServerResponse.prototype, 'appendHeader', wrapAppendOrRemoveHeader)
}
return http

@@ -70,5 +77,3 @@ })

}
if (finishSetHeaderCh.hasSubscribers) {
wrapSetHeader(res)
}
return emit.apply(this, arguments)

@@ -87,12 +92,2 @@ } catch (err) {

function wrapSetHeader (res) {
shimmer.wrap(res, 'setHeader', setHeader => {
return function (name, value) {
const setHeaderResult = setHeader.apply(this, arguments)
finishSetHeaderCh.publish({ name, value, res })
return setHeaderResult
}
})
}
function wrapWriteHead (writeHead) {

@@ -166,2 +161,44 @@ return function wrappedWriteHead (statusCode, reason, obj) {

function wrapSetHeader (setHeader) {
return function wrappedSetHeader (name, value) {
if (!startSetHeaderCh.hasSubscribers && !finishSetHeaderCh.hasSubscribers) {
return setHeader.apply(this, arguments)
}
if (startSetHeaderCh.hasSubscribers) {
const abortController = new AbortController()
startSetHeaderCh.publish({ res: this, abortController })
if (abortController.signal.aborted) {
return
}
}
const setHeaderResult = setHeader.apply(this, arguments)
if (finishSetHeaderCh.hasSubscribers) {
finishSetHeaderCh.publish({ name, value, res: this })
}
return setHeaderResult
}
}
function wrapAppendOrRemoveHeader (originalMethod) {
return function wrappedAppendOrRemoveHeader () {
if (!startSetHeaderCh.hasSubscribers) {
return originalMethod.apply(this, arguments)
}
const abortController = new AbortController()
startSetHeaderCh.publish({ res: this, abortController })
if (abortController.signal.aborted) {
return this
}
return originalMethod.apply(this, arguments)
}
}
function wrapEnd (end) {

@@ -168,0 +205,0 @@ return function wrappedEnd () {

@@ -15,3 +15,4 @@ 'use strict'

removeEfdStringFromTestName,
getIsFaultyEarlyFlakeDetection
getIsFaultyEarlyFlakeDetection,
NUM_FAILED_TEST_RETRIES
} = require('../../dd-trace/src/plugins/util/test')

@@ -53,2 +54,5 @@ const {

const FLUSH_TIMEOUT = 10000
// eslint-disable-next-line
// https://github.com/jestjs/jest/blob/41f842a46bb2691f828c3a5f27fc1d6290495b82/packages/jest-circus/src/types.ts#L9C8-L9C54
const RETRY_TIMES = Symbol.for('RETRY_TIMES')

@@ -132,2 +136,3 @@ let skippableSuites = []

this.isEarlyFlakeDetectionEnabled = this.testEnvironmentOptions._ddIsEarlyFlakeDetectionEnabled
this.isFlakyTestRetriesEnabled = this.testEnvironmentOptions._ddIsFlakyTestRetriesEnabled

@@ -146,2 +151,9 @@ if (this.isEarlyFlakeDetectionEnabled) {

}
if (this.isFlakyTestRetriesEnabled) {
const currentNumRetries = this.global[RETRY_TIMES]
if (!currentNumRetries) {
this.global[RETRY_TIMES] = NUM_FAILED_TEST_RETRIES
}
}
}

@@ -225,2 +237,3 @@

}
const isJestRetry = event.test?.invocations > 1
asyncResource.runInAsyncScope(() => {

@@ -236,3 +249,4 @@ testStartCh.publish({

isNew: isNewTest,
isEfdRetry: numEfdRetry > 0
isEfdRetry: numEfdRetry > 0,
isJestRetry
})

@@ -767,2 +781,3 @@ originalTestFns.set(event.test, event.test.fn)

_ddRepositoryRoot,
_ddIsFlakyTestRetriesEnabled,
...restOfTestEnvironmentOptions

@@ -769,0 +784,0 @@ } = testEnvironmentOptions

@@ -62,3 +62,3 @@ 'use strict'

for (const message of messages) {
if (typeof message === 'object') {
if (message !== null && typeof message === 'object') {
message.headers = message.headers || {}

@@ -65,0 +65,0 @@ }

@@ -64,3 +64,3 @@ 'use strict'

filter = options
} else if (typeof options === 'object' && options.filter) {
} else if (options !== null && typeof options === 'object' && options.filter) {
if (isString(options.filter)) {

@@ -82,3 +82,3 @@ filter = options.filter

arguments[callbackIndex] = shimmer.wrap(callback, function (err, corkedEmitter) {
if (typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
if (corkedEmitter !== null && typeof corkedEmitter === 'object' && typeof corkedEmitter.on === 'function') {
wrapEmitter(corkedEmitter)

@@ -85,0 +85,0 @@ }

@@ -398,5 +398,9 @@ 'use strict'

const asyncResource = testFileToSuiteAr.get(suite.file)
asyncResource.runInAsyncScope(() => {
testSuiteFinishCh.publish(status)
})
if (asyncResource) {
asyncResource.runInAsyncScope(() => {
testSuiteFinishCh.publish(status)
})
} else {
log.warn(() => `No AsyncResource found for suite ${suite.file}`)
}
})

@@ -428,12 +432,17 @@

}, (workerHandlerPackage) => {
shimmer.wrap(workerHandlerPackage.prototype, 'exec', exec => function (message, [testSuiteAbsolutePath]) {
shimmer.wrap(workerHandlerPackage.prototype, 'exec', exec => function (_, path) {
if (!testStartCh.hasSubscribers) {
return exec.apply(this, arguments)
}
if (!path?.length) {
return exec.apply(this, arguments)
}
const [testSuiteAbsolutePath] = path
const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
this.worker.on('message', function (message) {
function onMessage (message) {
if (Array.isArray(message)) {
const [messageCode, payload] = message
if (messageCode === MOCHA_WORKER_TRACE_PAYLOAD_CODE) {
testSessionAsyncResource.runInAsyncScope(() => {
testSuiteAsyncResource.runInAsyncScope(() => {
workerReportTraceCh.publish(payload)

@@ -443,5 +452,6 @@ })

}
})
}
const testSuiteAsyncResource = new AsyncResource('bound-anonymous-fn')
this.worker.on('message', onMessage)
testSuiteAsyncResource.runInAsyncScope(() => {

@@ -461,2 +471,3 @@ testSuiteStartCh.publish({

})
this.worker.off('message', onMessage)
},

@@ -468,2 +479,3 @@ (err) => {

})
this.worker.off('message', onMessage)
}

@@ -477,2 +489,3 @@ )

})
this.worker.off('message', onMessage)
throw err

@@ -479,0 +492,0 @@ }

@@ -6,3 +6,4 @@ 'use strict'

removeEfdStringFromTestName,
addEfdStringToTestName
addEfdStringToTestName,
NUM_FAILED_TEST_RETRIES
} = require('../../../dd-trace/src/plugins/util/test')

@@ -29,4 +30,2 @@ const { channel, AsyncResource } = require('../helpers/instrument')

const NUM_FAILED_TEST_RETRIES = 5
function isNewTest (test, knownTests) {

@@ -33,0 +32,0 @@ const testSuite = getTestSuitePath(test.file, process.cwd())

@@ -28,5 +28,5 @@ 'use strict'

const filters = arg0 && typeof arg0 === 'object' ? [arg0] : []
const filters = arg0 !== null && typeof arg0 === 'object' ? [arg0] : []
if (arg1 && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
if (arg1 !== null && typeof arg1 === 'object' && methodsOptionalArgs.includes(methodName)) {
filters.push(arg1)

@@ -33,0 +33,0 @@ }

@@ -49,3 +49,3 @@ 'use strict'

return instrument(req, res, () => {
const page = (typeof match === 'object' && typeof match.definition === 'object')
const page = (match !== null && typeof match === 'object' && typeof match.definition === 'object')
? match.definition.pathname

@@ -52,0 +52,0 @@ : undefined

@@ -38,3 +38,3 @@ 'use strict'

const pgQuery = arguments[0] && typeof arguments[0] === 'object'
const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object'
? arguments[0]

@@ -113,3 +113,3 @@ : { text: arguments[0] }

const pgQuery = arguments[0] && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] }
const pgQuery = arguments[0] !== null && typeof arguments[0] === 'object' ? arguments[0] : { text: arguments[0] }

@@ -116,0 +116,0 @@ return asyncResource.runInAsyncScope(() => {

@@ -5,3 +5,3 @@ const semver = require('semver')

const shimmer = require('../../datadog-shimmer')
const { parseAnnotations, getTestSuitePath } = require('../../dd-trace/src/plugins/util/test')
const { parseAnnotations, getTestSuitePath, NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')
const log = require('../../dd-trace/src/log')

@@ -25,2 +25,3 @@

const testSuiteToErrors = new Map()
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')

@@ -40,2 +41,3 @@ let applyRepeatEachIndex = null

let isEarlyFlakeDetectionEnabled = false
let isFlakyTestRetriesEnabled = false
let earlyFlakeDetectionNumRetries = 0

@@ -202,2 +204,27 @@ let knownTests = {}

function getTestByTestId (dispatcher, testId) {
if (dispatcher._testById) {
return dispatcher._testById.get(testId)?.test
}
const allTests = dispatcher._allTests || dispatcher._ddAllTests
if (allTests) {
return allTests.find(({ id }) => id === testId)
}
}
function getChannelPromise (channelToPublishTo) {
return new Promise(resolve => {
testSessionAsyncResource.runInAsyncScope(() => {
channelToPublishTo.publish({ onDone: resolve })
})
})
}
// eslint-disable-next-line
// Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
// We can't use test.outcome() directly because it's set on follow up handlers:
// our `testEndHandler` is called before the outcome is set.
function testWillRetry (test, testStatus) {
return testStatus === 'fail' && test.results.length <= test.retries
}
function testBeginHandler (test, browserName) {

@@ -257,2 +284,3 @@ const {

steps: testResult?.steps || [],
isRetry: testResult?.retry > 0,
error,

@@ -275,4 +303,6 @@ extraTags: annotationTags,

remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
.filter(currentTest => currentTest !== test)
if (!testWillRetry(test, testStatus)) {
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
.filter(currentTest => currentTest !== test)
}

@@ -344,12 +374,2 @@ // Last test, we finish the suite

function getTestByTestId (dispatcher, testId) {
if (dispatcher._testById) {
return dispatcher._testById.get(testId)?.test
}
const allTests = dispatcher._allTests || dispatcher._ddAllTests
if (allTests) {
return allTests.find(({ id }) => id === testId)
}
}
function dispatcherHookNew (dispatcherExport, runWrapper) {

@@ -383,4 +403,2 @@ shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper)

const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
rootDir = getRootDir(this)

@@ -394,17 +412,11 @@

const configurationPromise = new Promise((resolve) => {
onDone = resolve
})
testSessionAsyncResource.runInAsyncScope(() => {
libraryConfigurationCh.publish({ onDone })
})
try {
const { err, libraryConfig } = await configurationPromise
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
if (!err) {
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
}
} catch (e) {
isEarlyFlakeDetectionEnabled = false
log.error(e)

@@ -414,11 +426,4 @@ }

if (isEarlyFlakeDetectionEnabled && semver.gte(playwrightVersion, MINIMUM_SUPPORTED_VERSION_EFD)) {
const knownTestsPromise = new Promise((resolve) => {
onDone = resolve
})
testSessionAsyncResource.runInAsyncScope(() => {
knownTestsCh.publish({ onDone })
})
try {
const { err, knownTests: receivedKnownTests } = await knownTestsPromise
const { err, knownTests: receivedKnownTests } = await getChannelPromise(knownTestsCh)
if (!err) {

@@ -430,2 +435,3 @@ knownTests = receivedKnownTests

} catch (err) {
isEarlyFlakeDetectionEnabled = false
log.error(err)

@@ -437,2 +443,10 @@ }

if (isFlakyTestRetriesEnabled) {
projects.forEach(project => {
if (project.retries === 0) { // Only if it hasn't been set by the user
project.retries = NUM_FAILED_TEST_RETRIES
}
})
}
const runAllTestsReturn = await runAllTests.apply(this, arguments)

@@ -439,0 +453,0 @@

@@ -54,3 +54,3 @@ 'use strict'

const result = fn.apply(this, arguments)
if (result && typeof result === 'object' && typeof result.then === 'function') {
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
return result.then(function () {

@@ -57,0 +57,0 @@ nextChannel.publish({ req })

const { addHook, channel, AsyncResource } = require('./helpers/instrument')
const shimmer = require('../../datadog-shimmer')
const { NUM_FAILED_TEST_RETRIES } = require('../../dd-trace/src/plugins/util/test')

@@ -19,2 +20,3 @@ // test hooks

const testSessionFinishCh = channel('ci:vitest:session:finish')
const libraryConfigurationCh = channel('ci:vitest:library-configuration')

@@ -34,2 +36,14 @@ const taskToAsync = new WeakMap()

function isReporterPackageNewest (vitestPackage) {
return vitestPackage.h?.name === 'BaseSequencer'
}
function getChannelPromise (channelToPublishTo) {
return new Promise(resolve => {
sessionAsyncResource.runInAsyncScope(() => {
channelToPublishTo.publish({ onDone: resolve })
})
})
}
function getSessionStatus (state) {

@@ -95,2 +109,34 @@ if (state.getCountOfFailedTests() > 0) {

}
// There isn't any other async function that we seem to be able to hook into
// So we will use the sort from BaseSequencer. This means that a custom sequencer
// will not work. This will be a known limitation.
let isFlakyTestRetriesEnabled = false
try {
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh)
if (!err) {
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
}
} catch (e) {
isFlakyTestRetriesEnabled = false
}
if (isFlakyTestRetriesEnabled && !this.ctx.config.retry) {
this.ctx.config.retry = NUM_FAILED_TEST_RETRIES
}
let testCodeCoverageLinesTotal
if (this.ctx.coverageProvider?.generateCoverage) {
shimmer.wrap(this.ctx.coverageProvider, 'generateCoverage', generateCoverage => async function () {
const totalCodeCoverage = await generateCoverage.apply(this, arguments)
try {
testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
} catch (e) {
// ignore errors
}
return totalCodeCoverage
})
}
shimmer.wrap(this.ctx, 'exit', exit => async function () {

@@ -111,4 +157,5 @@ let onFinish

status: getSessionStatus(this.state),
onFinish,
error
testCodeCoverageLinesTotal,
error,
onFinish
})

@@ -126,2 +173,17 @@ })

function getCreateCliWrapper (vitestPackage, frameworkVersion) {
shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
if (!testSessionStartCh.hasSubscribers) {
return oldCreateCli.apply(this, arguments)
}
sessionAsyncResource.runInAsyncScope(() => {
const processArgv = process.argv.slice(2).join(' ')
testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
})
return oldCreateCli.apply(this, arguments)
})
return vitestPackage
}
addHook({

@@ -134,6 +196,18 @@ name: 'vitest',

// test start (only tests that are not marked as skip or todo)
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task) {
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
if (!testStartCh.hasSubscribers) {
return onBeforeTryTask.apply(this, arguments)
}
const { retry: numAttempt } = retryInfo
// We finish the previous test here because we know it has failed already
if (numAttempt > 0) {
const asyncResource = taskToAsync.get(task)
const testError = task.result?.errors?.[0]
if (asyncResource) {
asyncResource.runInAsyncScope(() => {
testErrorCh.publish({ error: testError })
})
}
}
const asyncResource = new AsyncResource('bound-anonymous-fn')

@@ -143,3 +217,7 @@ taskToAsync.set(task, asyncResource)

asyncResource.runInAsyncScope(() => {
testStartCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath })
testStartCh.publish({
testName: getTestName(task),
testSuiteAbsolutePath: task.file.filepath,
isRetry: numAttempt > 0
})
})

@@ -173,8 +251,21 @@ return onBeforeTryTask.apply(this, arguments)

// There are multiple index* files across different versions of vitest,
// so we check for the existence of BaseSequencer to determine if we are in the right file
addHook({
name: 'vitest',
versions: ['>=2.0.0'],
versions: ['>=1.6.0 <2.0.0'],
filePattern: 'dist/vendor/index.*'
}, (vitestPackage) => {
// there are multiple index* files so we have to check the exported values
if (isReporterPackage(vitestPackage)) {
shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
}
return vitestPackage
})
addHook({
name: 'vitest',
versions: ['>=2.0.0 <2.0.5'],
filePattern: 'dist/vendor/index.*'
}, (vitestPackage) => {
if (isReporterPackageNew(vitestPackage)) {

@@ -189,8 +280,7 @@ shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)

name: 'vitest',
versions: ['>=1.6.0'],
filePattern: 'dist/vendor/index.*'
versions: ['>=2.0.5'],
filePattern: 'dist/chunks/index.*'
}, (vitestPackage) => {
// there are multiple index* files so we have to check the exported values
if (isReporterPackage(vitestPackage)) {
shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
if (isReporterPackageNewest(vitestPackage)) {
shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
}

@@ -204,18 +294,11 @@

name: 'vitest',
versions: ['>=1.6.0'],
versions: ['>=1.6.0 <2.0.5'],
filePattern: 'dist/vendor/cac.*'
}, (vitestPackage, frameworkVersion) => {
shimmer.wrap(vitestPackage, 'c', oldCreateCli => function () {
if (!testSessionStartCh.hasSubscribers) {
return oldCreateCli.apply(this, arguments)
}
sessionAsyncResource.runInAsyncScope(() => {
const processArgv = process.argv.slice(2).join(' ')
testSessionStartCh.publish({ command: `vitest ${processArgv}`, frameworkVersion })
})
return oldCreateCli.apply(this, arguments)
})
}, getCreateCliWrapper)
return vitestPackage
})
addHook({
name: 'vitest',
versions: ['>=2.0.5'],
filePattern: 'dist/chunks/cac.*'
}, getCreateCliWrapper)

@@ -228,3 +311,3 @@ // test suite start and finish

file: 'dist/index.js'
}, vitestPackage => {
}, (vitestPackage, frameworkVersion) => {
shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPath) {

@@ -238,3 +321,3 @@ let testSuiteError = null

testSuiteAsyncResource.runInAsyncScope(() => {
testSuiteStartCh.publish(testPath[0])
testSuiteStartCh.publish({ testSuiteAbsolutePath: testPath[0], frameworkVersion })
})

@@ -250,2 +333,3 @@ const startTestsResponse = await startTests.apply(this, arguments)

// Only one test task per test, even if there are retries
testTasks.forEach(task => {

@@ -258,3 +342,3 @@ const testAsyncResource = taskToAsync.get(task)

if (state === 'skip') { // programmatic skip
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath })
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
} else if (state === 'pass') {

@@ -275,4 +359,6 @@ if (testAsyncResource) {

if (testAsyncResource) {
const isRetry = task.result?.retryCount > 0
// `duration` is the duration of all the retries, so it can't be used if there are retries
testAsyncResource.runInAsyncScope(() => {
testErrorCh.publish({ duration, error: testError })
testErrorCh.publish({ duration: !isRetry ? duration : undefined, error: testError })
})

@@ -285,3 +371,3 @@ }

} else { // test.skip or test.todo
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.suite.file.filepath })
testSkipCh.publish({ testName: getTestName(task), testSuiteAbsolutePath: task.file.filepath })
}

@@ -288,0 +374,0 @@ })

@@ -7,2 +7,3 @@ 'use strict'

const { isTrue } = require('../../dd-trace/src/util')
const coalesce = require('koalas')

@@ -77,3 +78,3 @@ class BaseAwsSdkPlugin extends ClientPlugin {

const operation = response.request.operation
this.responseExtractDSMContext(operation, params, response.data, span)
this.responseExtractDSMContext(operation, params, response.data ?? response, span)
}

@@ -168,5 +169,18 @@ this.addResponseTags(span, response)

// check if AWS batch propagation or AWS_[SERVICE] batch propagation is enabled via env variable
const serviceId = serviceIdentifier.toUpperCase()
const batchPropagationEnabled = isTrue(
coalesce(
specificConfig.batchPropagationEnabled,
process.env[`DD_TRACE_AWS_SDK_${serviceId}_BATCH_PROPAGATION_ENABLED`],
config.batchPropagationEnabled,
process.env.DD_TRACE_AWS_SDK_BATCH_PROPAGATION_ENABLED,
false
)
)
// Merge the specific config back into the main config
return Object.assign({}, config, specificConfig, {
splitByAwsService: config.splitByAwsService !== false,
batchPropagationEnabled: config.batchPropagationEnabled !== false,
batchPropagationEnabled,
hooks

@@ -173,0 +187,0 @@ })

@@ -24,3 +24,3 @@ 'use strict'

// dynamoDB batch TableName
if (params.RequestItems) {
if (params.RequestItems !== null) {
if (typeof params.RequestItems === 'object') {

@@ -27,0 +27,0 @@ if (Object.keys(params.RequestItems).length === 1) {

@@ -159,3 +159,3 @@ 'use strict'

stream,
i === 0 || (this.config.kinesis && this.config.kinesis.batchPropagationEnabled)
i === 0 || (this.config.batchPropagationEnabled)
)

@@ -162,0 +162,0 @@ }

@@ -66,3 +66,3 @@ 'use strict'

params.TopicArn,
i === 0 || (this.config.sns && this.config.sns.batchPropagationEnabled)
i === 0 || (this.config.batchPropagationEnabled)
)

@@ -69,0 +69,0 @@ }

@@ -160,4 +160,4 @@ 'use strict'

return JSON.parse(textMap)
} else if (attributes.Type === 'Binary') {
const buffer = Buffer.from(attributes.Value, 'base64')
} else if (attributes.Type === 'Binary' || attributes.DataType === 'Binary') {
const buffer = Buffer.from(attributes.Value ?? attributes.BinaryValue, 'base64')
return JSON.parse(buffer)

@@ -226,3 +226,3 @@ }

params.QueueUrl,
i === 0 || (this.config.sqs && this.config.sqs.batchPropagationEnabled)
i === 0 || (this.config.batchPropagationEnabled)
)

@@ -229,0 +229,0 @@ }

@@ -50,3 +50,3 @@ 'use strict'

const inputObj = JSON.parse(input)
if (inputObj && typeof inputObj === 'object') {
if (inputObj !== null && typeof inputObj === 'object') {
// We've parsed the input JSON string

@@ -53,0 +53,0 @@ inputObj._datadog = {}

@@ -10,6 +10,6 @@ 'use strict'

// eslint-disable-next-line max-len
const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|api_?key|secret|a(?:ccess|uth)_token|mysql_pwd|credentials|(?:stripe)?token)$'
const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|address|api[-_]?key|e?mail|secret(?:[-_]?key)?|a(?:ccess|uth)[-_]?token|mysql_pwd|credentials|(?:stripe)?token)$'
const regexParam = new RegExp(PARAM_PATTERN, 'i')
const ENV_PATTERN = '^(\\w+=\\w+;)*\\w+=\\w+;?$'
const envvarRegex = new RegExp(ENV_PATTERN)
const envVarRegex = new RegExp(ENV_PATTERN)
const REDACTED = '?'

@@ -65,3 +65,5 @@

if (typeof token === 'object') {
if (token === null) {
continue
} else if (typeof token === 'object') {
if (token.pattern) {

@@ -75,3 +77,3 @@ result.push(token.pattern)

} else if (!foundBinary) {
if (envvarRegex.test(token)) {
if (envVarRegex.test(token)) {
const envSplit = token.split('=')

@@ -78,0 +80,0 @@

@@ -198,2 +198,13 @@ 'use strict'

this.addSub('ci:cucumber:test:retry', (isFlakyRetry) => {
const store = storage.getStore()
const span = store.span
if (isFlakyRetry) {
span.setTag(TEST_IS_RETRY, 'true')
}
span.setTag(TEST_STATUS, 'fail')
span.finish()
finishAllTraceSpans(span)
})
this.addSub('ci:cucumber:test-step:start', ({ resource }) => {

@@ -243,3 +254,11 @@ const store = storage.getStore()

this.addSub('ci:cucumber:test:finish', ({ isStep, status, skipReason, errorMessage, isNew, isEfdRetry }) => {
this.addSub('ci:cucumber:test:finish', ({
isStep,
status,
skipReason,
errorMessage,
isNew,
isEfdRetry,
isFlakyRetry
}) => {
const span = storage.getStore().span

@@ -265,2 +284,6 @@ const statusTag = isStep ? 'step.status' : TEST_STATUS

if (isFlakyRetry > 0) {
span.setTag(TEST_IS_RETRY, 'true')
}
span.finish()

@@ -267,0 +290,0 @@ if (!isStep) {

@@ -31,3 +31,4 @@ const {

TEST_IS_RETRY,
TEST_EARLY_FLAKE_ENABLED
TEST_EARLY_FLAKE_ENABLED,
NUM_FAILED_TEST_RETRIES
} = require('../../dd-trace/src/plugins/util/test')

@@ -211,2 +212,5 @@ const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')

// Init function returns a promise that resolves with the Cypress configuration
// Depending on the received configuration, the Cypress configuration can be modified:
// for example, to enable retries for failed tests.
init (tracer, cypressConfig) {

@@ -216,2 +220,29 @@ this._isInit = true

this.cypressConfig = cypressConfig
this.libraryConfigurationPromise = getLibraryConfiguration(this.tracer, this.testConfiguration)
.then((libraryConfigurationResponse) => {
if (libraryConfigurationResponse.err) {
log.error(libraryConfigurationResponse.err)
} else {
const {
libraryConfig: {
isSuitesSkippingEnabled,
isCodeCoverageEnabled,
isEarlyFlakeDetectionEnabled,
earlyFlakeDetectionNumRetries,
isFlakyTestRetriesEnabled
}
} = libraryConfigurationResponse
this.isSuitesSkippingEnabled = isSuitesSkippingEnabled
this.isCodeCoverageEnabled = isCodeCoverageEnabled
this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
this.isFlakyTestRetriesEnabled = isFlakyTestRetriesEnabled
if (this.isFlakyTestRetriesEnabled) {
this.cypressConfig.retries.runMode = NUM_FAILED_TEST_RETRIES
}
}
return this.cypressConfig
})
return this.libraryConfigurationPromise
}

@@ -233,3 +264,3 @@

getTestSpan (testName, testSuite, isUnskippable, isForcedToRun) {
getTestSpan ({ testName, testSuite, isUnskippable, isForcedToRun, testSourceFile }) {
const testSuiteTags = {

@@ -258,4 +289,7 @@ [TEST_COMMAND]: this.command,

const codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
if (testSourceFile) {
testSpanMetadata[TEST_SOURCE_FILE] = testSourceFile
}
const codeOwners = this.getTestCodeOwners({ testSuite, testSourceFile })
if (codeOwners) {

@@ -305,2 +339,5 @@ testSpanMetadata[TEST_CODE_OWNERS] = codeOwners

async beforeRun (details) {
// We need to make sure that the plugin is initialized before running the tests
// This is for the case where the user has not returned the promise from the init function
await this.libraryConfigurationPromise
this.command = getCypressCommand(details)

@@ -310,21 +347,2 @@ this.frameworkVersion = getCypressVersion(details)

const libraryConfigurationResponse = await getLibraryConfiguration(this.tracer, this.testConfiguration)
if (libraryConfigurationResponse.err) {
log.error(libraryConfigurationResponse.err)
} else {
const {
libraryConfig: {
isSuitesSkippingEnabled,
isCodeCoverageEnabled,
isEarlyFlakeDetectionEnabled,
earlyFlakeDetectionNumRetries
}
} = libraryConfigurationResponse
this.isSuitesSkippingEnabled = isSuitesSkippingEnabled
this.isCodeCoverageEnabled = isCodeCoverageEnabled
this.isEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
this.earlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
}
if (this.isEarlyFlakeDetectionEnabled) {

@@ -475,8 +493,12 @@ const knownTestsResponse = await getKnownTests(

)
const skippedTestSpan = this.getTestSpan(cypressTestName, spec.relative)
let testSourceFile
if (spec.absolute && this.repositoryRoot) {
skippedTestSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, this.repositoryRoot))
testSourceFile = getTestSuitePath(spec.absolute, this.repositoryRoot)
} else {
skippedTestSpan.setTag(TEST_SOURCE_FILE, spec.relative)
testSourceFile = spec.relative
}
const skippedTestSpan = this.getTestSpan({ testName: cypressTestName, testSuite: spec.relative, testSourceFile })
skippedTestSpan.setTag(TEST_STATUS, 'skip')

@@ -496,25 +518,57 @@ if (isSkippedByItr) {

let latestError
finishedTests.forEach((finishedTest) => {
const cypressTest = cypressTests.find(test => test.title.join(' ') === finishedTest.testName)
if (!cypressTest) {
return
const finishedTestsByTestName = finishedTests.reduce((acc, finishedTest) => {
if (!acc[finishedTest.testName]) {
acc[finishedTest.testName] = []
}
if (cypressTest.displayError) {
latestError = new Error(cypressTest.displayError)
}
const cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
// update test status
if (cypressTestStatus !== finishedTest.testStatus) {
finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
finishedTest.testSpan.setTag('error', latestError)
}
if (this.itrCorrelationId) {
finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
}
if (spec.absolute && this.repositoryRoot) {
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, getTestSuitePath(spec.absolute, this.repositoryRoot))
} else {
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, spec.relative)
}
finishedTest.testSpan.finish(finishedTest.finishTime)
acc[finishedTest.testName].push(finishedTest)
return acc
}, {})
Object.entries(finishedTestsByTestName).forEach(([testName, finishedTestAttempts]) => {
finishedTestAttempts.forEach((finishedTest, attemptIndex) => {
// TODO: there could be multiple if there have been retries!
// potentially we need to match the test status!
const cypressTest = cypressTests.find(test => test.title.join(' ') === testName)
if (!cypressTest) {
return
}
// finishedTests can include multiple tests with the same name if they have been retried
// by early flake detection. Cypress is unaware of this so .attempts does not necessarily have
// the same length as `finishedTestAttempts`
let cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.state]
if (cypressTest.attempts && cypressTest.attempts[attemptIndex]) {
cypressTestStatus = CYPRESS_STATUS_TO_TEST_STATUS[cypressTest.attempts[attemptIndex].state]
if (attemptIndex > 0) {
finishedTest.testSpan.setTag(TEST_IS_RETRY, 'true')
}
}
if (cypressTest.displayError) {
latestError = new Error(cypressTest.displayError)
}
// Update test status
if (cypressTestStatus !== finishedTest.testStatus) {
finishedTest.testSpan.setTag(TEST_STATUS, cypressTestStatus)
finishedTest.testSpan.setTag('error', latestError)
}
if (this.itrCorrelationId) {
finishedTest.testSpan.setTag(ITR_CORRELATION_ID, this.itrCorrelationId)
}
let testSourceFile
if (spec.absolute && this.repositoryRoot) {
testSourceFile = getTestSuitePath(spec.absolute, this.repositoryRoot)
} else {
testSourceFile = spec.relative
}
if (testSourceFile) {
finishedTest.testSpan.setTag(TEST_SOURCE_FILE, testSourceFile)
}
const codeOwners = this.getTestCodeOwners({ testSuite: spec.relative, testSourceFile })
if (codeOwners) {
finishedTest.testSpan.setTag(TEST_CODE_OWNERS, codeOwners)
}
finishedTest.testSpan.finish(finishedTest.finishTime)
})
})

@@ -566,3 +620,8 @@

if (!this.activeTestSpan) {
this.activeTestSpan = this.getTestSpan(testName, testSuite, isUnskippable, isForcedToRun)
this.activeTestSpan = this.getTestSpan({
testName,
testSuite,
isUnskippable,
isForcedToRun
})
}

@@ -634,4 +693,11 @@

}
getTestCodeOwners ({ testSuite, testSourceFile }) {
if (testSourceFile) {
return getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries)
}
return getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
}
}
module.exports = new CypressPlugin()

@@ -25,7 +25,6 @@ const NoopTracer = require('../../dd-trace/src/noop/tracer')

// We still need to register these tasks or the support file will fail
return on('task', noopTask)
on('task', noopTask)
return config
}
cypressPlugin.init(tracer, config)
on('before:run', cypressPlugin.beforeRun.bind(cypressPlugin))

@@ -35,2 +34,4 @@ on('after:spec', cypressPlugin.afterSpec.bind(cypressPlugin))

on('task', cypressPlugin.getTasks())
return cypressPlugin.init(tracer, config)
}

@@ -32,3 +32,3 @@ 'use strict'

meta: {
'file.descriptor': (typeof fd === 'object' || typeof fd === 'number') ? fd.toString() : '',
'file.descriptor': ((fd !== null && typeof fd === 'object') || typeof fd === 'number') ? fd.toString() : '',
'file.dest': params.dest || params.newPath || (params.target && params.path),

@@ -35,0 +35,0 @@ 'file.flag': String(flag || defaultFlag || ''),

@@ -151,2 +151,3 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')

config._ddRepositoryRoot = this.repositoryRoot
config._ddIsFlakyTestRetriesEnabled = this.libraryConfig?.isFlakyTestRetriesEnabled ?? false
})

@@ -328,3 +329,4 @@ })

isNew,
isEfdRetry
isEfdRetry,
isJestRetry
} = test

@@ -354,2 +356,6 @@

if (isJestRetry) {
extraTags[TEST_IS_RETRY] = 'true'
}
return super.startTestSpan(name, suite, this.testSuiteSpan, extraTags)

@@ -356,0 +362,0 @@ }

@@ -84,3 +84,3 @@ 'use strict'

for (const message of messages) {
if (typeof message === 'object') {
if (message !== null && typeof message === 'object') {
this.tracer.inject(span, 'text_map', message.headers)

@@ -87,0 +87,0 @@ if (this.config.dsmEnabled) {

@@ -118,3 +118,3 @@ 'use strict'

function isObject (val) {
return typeof val === 'object' && val !== null && !(val instanceof Array)
return val !== null && typeof val === 'object' && !Array.isArray(val)
}

@@ -121,0 +121,0 @@

@@ -121,3 +121,3 @@ 'use strict'

// createChatCompletion, createCompletion
if (typeof payload.logit_bias === 'object' && payload.logit_bias) {
if (payload.logit_bias !== null && typeof payload.logit_bias === 'object') {
for (const [tokenId, bias] of Object.entries(payload.logit_bias)) {

@@ -431,3 +431,3 @@ tags[`openai.request.logit_bias.${tokenId}`] = bias

const img = payload.file || payload.image
if (img && typeof img === 'object' && img.path) {
if (img !== null && typeof img === 'object' && img.path) {
const file = path.basename(img.path)

@@ -439,3 +439,3 @@ tags['openai.request.image'] = file

// createImageEdit
if (payload.mask && typeof payload.mask === 'object' && payload.mask.path) {
if (payload.mask !== null && typeof payload.mask === 'object' && payload.mask.path) {
const mask = path.basename(payload.mask.path)

@@ -639,3 +639,3 @@ tags['openai.request.mask'] = mask

if (body.file && typeof body.file === 'object' && body.file.path) {
if (body.file !== null && typeof body.file === 'object' && body.file.path) {
const filename = path.basename(body.file.path)

@@ -653,3 +653,3 @@ tags['openai.request.filename'] = filename

// This is a best effort attempt to extract the filename during the request
if (body.file && typeof body.file === 'object' && body.file.path) {
if (body.file !== null && typeof body.file === 'object' && body.file.path) {
tags['openai.request.filename'] = path.basename(body.file.path)

@@ -656,0 +656,0 @@ }

@@ -119,3 +119,3 @@ 'use strict'

})
this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error, extraTags, isNew, isEfdRetry }) => {
this.addSub('ci:playwright:test:finish', ({ testStatus, steps, error, extraTags, isNew, isEfdRetry, isRetry }) => {
const store = storage.getStore()

@@ -139,2 +139,5 @@ const span = store && store.span

}
if (isRetry) {
span.setTag(TEST_IS_RETRY, 'true')
}

@@ -141,0 +144,0 @@ steps.forEach(step => {

@@ -57,5 +57,5 @@ 'use strict'

function isObject (val) {
return typeof val === 'object' && val !== null && !(val instanceof Array)
return val !== null && typeof val === 'object' && !Array.isArray(val)
}
module.exports = SharedbPlugin

@@ -9,3 +9,5 @@ const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')

getTestSuiteCommonTags,
TEST_SOURCE_FILE
TEST_SOURCE_FILE,
TEST_IS_RETRY,
TEST_CODE_COVERAGE_LINES_PCT
} = require('../../dd-trace/src/plugins/util/test')

@@ -29,5 +31,13 @@ const { COMPONENT } = require('../../dd-trace/src/constants')

this.addSub('ci:vitest:test:start', ({ testName, testSuiteAbsolutePath }) => {
this.addSub('ci:vitest:test:start', ({ testName, testSuiteAbsolutePath, isRetry }) => {
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
const store = storage.getStore()
const extraTags = {
[TEST_SOURCE_FILE]: testSuite
}
if (isRetry) {
extraTags[TEST_IS_RETRY] = 'true'
}
const span = this.startTestSpan(

@@ -37,5 +47,3 @@ testName,

this.testSuiteSpan,
{
[TEST_SOURCE_FILE]: testSuite
}
extraTags
)

@@ -79,3 +87,7 @@

}
span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds
if (duration) {
span.finish(span._startTime + duration - MILLISECONDS_TO_SUBTRACT_FROM_FAILED_TEST_DURATION) // milliseconds
} else {
span.finish() // retries will not have a duration
}
finishAllTraceSpans(span)

@@ -98,3 +110,4 @@ }

this.addSub('ci:vitest:test-suite:start', (testSuiteAbsolutePath) => {
this.addSub('ci:vitest:test-suite:start', ({ testSuiteAbsolutePath, frameworkVersion }) => {
this.frameworkVersion = frameworkVersion
const testSessionSpanContext = this.tracer.extract('text_map', {

@@ -146,3 +159,3 @@ 'x-datadog-trace-id': process.env.DD_CIVISIBILITY_TEST_SESSION_ID,

this.addSub('ci:vitest:session:finish', ({ status, onFinish, error }) => {
this.addSub('ci:vitest:session:finish', ({ status, onFinish, error, testCodeCoverageLinesTotal }) => {
this.testSessionSpan.setTag(TEST_STATUS, status)

@@ -154,2 +167,6 @@ this.testModuleSpan.setTag(TEST_STATUS, status)

}
if (testCodeCoverageLinesTotal) {
this.testModuleSpan.setTag(TEST_CODE_COVERAGE_LINES_PCT, testCodeCoverageLinesTotal)
this.testSessionSpan.setTag(TEST_CODE_COVERAGE_LINES_PCT, testCodeCoverageLinesTotal)
}
this.testModuleSpan.finish()

@@ -156,0 +173,0 @@ this.testSessionSpan.finish()

@@ -7,3 +7,3 @@ 'use strict'

sample (span, measured, measuredByDefault) {
if (typeof measured === 'object') {
if (measured !== null && typeof measured === 'object') {
this.sample(span, measured[span.context()._name], measuredByDefault)

@@ -10,0 +10,0 @@ } else if (measured !== undefined) {

@@ -12,2 +12,4 @@ 'use strict'

const responseBlockedSet = new WeakSet()
const specificBlockingTypes = {

@@ -121,2 +123,4 @@ GRAPHQL: 'graphql'

responseBlockedSet.add(res)
abortController?.abort()

@@ -149,2 +153,6 @@ }

function isBlocked (res) {
return responseBlockedSet.has(res)
}
module.exports = {

@@ -156,3 +164,4 @@ addSpecificEndpoint,

getBlockingAction,
setTemplates
setTemplates,
isBlocked
}

@@ -22,3 +22,6 @@ 'use strict'

responseWriteHead: dc.channel('apm:http:server:response:writeHead:start'),
httpClientRequestStart: dc.channel('apm:http:client:request:start')
httpClientRequestStart: dc.channel('apm:http:client:request:start'),
responseSetHeader: dc.channel('datadog:http:server:response:set-header:start'),
setUncaughtExceptionCaptureCallbackStart: dc.channel('datadog:process:setUncaughtExceptionCaptureCallback:start')
}
'use strict'
module.exports = {
CODE_INJECTION_ANALYZER: require('./code-injection-analyzer'),
COMMAND_INJECTION_ANALYZER: require('./command-injection-analyzer'),

@@ -5,0 +6,0 @@ HARCODED_PASSWORD_ANALYZER: require('./hardcoded-password-analyzer'),

@@ -16,3 +16,3 @@ 'use strict'

function iterateObjectStrings (target, fn, levelKeys = [], depth = 20, visited = new Set()) {
if (target && typeof target === 'object') {
if (target !== null && typeof target === 'object') {
Object.keys(target).forEach((key) => {

@@ -19,0 +19,0 @@ const nextLevelKeys = [...levelKeys, key]

@@ -50,2 +50,4 @@ 'use strict'

_isExcluded (location) {
if (!location) return false
return EXCLUDED_LOCATIONS.some(excludedLocation => {

@@ -52,0 +54,0 @@ return location.path.includes(excludedLocation)

@@ -17,3 +17,4 @@ 'use strict'

{ src: 'trimEnd' },
{ src: 'trimStart', dst: 'trim' }
{ src: 'trimStart', dst: 'trim' },
{ src: 'eval', allowedWithoutCallee: true }
]

@@ -20,0 +21,0 @@

@@ -48,3 +48,3 @@ 'use strict'

({ req }) => {
if (req && req.body && typeof req.body === 'object') {
if (req && req.body !== null && typeof req.body === 'object') {
const iastContext = getIastContext(storage.getStore())

@@ -67,3 +67,3 @@ if (iastContext && iastContext.body !== req.body) {

({ req }) => {
if (req && req.params && typeof req.params === 'object') {
if (req && req.params !== null && typeof req.params === 'object') {
this._taintTrackingHandler(HTTP_REQUEST_PATH_PARAM, req, 'params')

@@ -70,0 +70,0 @@ }

@@ -30,3 +30,3 @@ 'use strict'

if (key && typeof key === 'object') {
if (key !== null && typeof key === 'object') {
shimmer.wrap(key, 'toString',

@@ -38,3 +38,3 @@ toString => this.getToStringWrap(toString, iastContext, KAFKA_MESSAGE_KEY))

if (value && typeof value === 'object') {
if (value !== null && typeof value === 'object') {
shimmer.wrap(value, 'toString',

@@ -41,0 +41,0 @@ toString => this.getToStringWrap(toString, iastContext, KAFKA_MESSAGE_VALUE))

@@ -13,2 +13,3 @@ 'use strict'

const mathRandomCallCh = dc.channel('datadog:random:call')
const evalCallCh = dc.channel('datadog:eval:call')

@@ -22,2 +23,3 @@ const JSON_VALUE = 'json.value'

concat: noop,
eval: noop,
join: noop,

@@ -141,2 +143,11 @@ parse: noop,

eval: function (res, fn, target, script) {
// eslint-disable-next-line no-eval
if (evalCallCh.hasSubscribers && fn === globalThis.eval) {
evalCallCh.publish({ script })
}
return res
},
parse: function (res, fn, target, json) {

@@ -143,0 +154,0 @@ if (fn === JSON.parse) {

@@ -8,2 +8,3 @@ 'use strict'

const codeInjectionSensitiveAnalyzer = require('./sensitive-analyzers/code-injection-sensitive-analyzer')
const commandSensitiveAnalyzer = require('./sensitive-analyzers/command-sensitive-analyzer')

@@ -27,2 +28,3 @@ const hardcodedPasswordAnalyzer = require('./sensitive-analyzers/hardcoded-password-analyzer')

this._sensitiveAnalyzers = new Map()
this._sensitiveAnalyzers.set(vulnerabilities.CODE_INJECTION, codeInjectionSensitiveAnalyzer)
this._sensitiveAnalyzers.set(vulnerabilities.COMMAND_INJECTION, commandSensitiveAnalyzer)

@@ -29,0 +31,0 @@ this._sensitiveAnalyzers.set(vulnerabilities.NOSQL_MONGODB_INJECTION, jsonSensitiveAnalyzer)

// eslint-disable-next-line max-len
const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)'
const DEFAULT_IAST_REDACTION_NAME_PATTERN = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|(?:sur|last)name|user(?:name)?|address|e?mail)'
// eslint-disable-next-line max-len
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,}))'
const DEFAULT_IAST_REDACTION_VALUE_PATTERN = '(?:bearer\\s+[a-z0-9\\._\\-]+|glpat-[\\w\\-]{20}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=\\-]+\\.ey[I-L][\\w=\\-]+(?:\\.[\\w.+/=\\-]+)?|(?:[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY[\\-]{5}|ssh-rsa\\s*[a-z0-9/\\.+]{100,})|[\\w\\.-]+@[a-zA-Z\\d\\.-]+\\.[a-zA-Z]{2,})'

@@ -6,0 +6,0 @@ module.exports = {

@@ -46,2 +46,4 @@ 'use strict'

if (evidence.value == null) return { valueParts }
if (typeof evidence.value === 'object' && evidence.rangesToApply) {

@@ -73,3 +75,3 @@ const { value, ranges } = stringifyWithRanges(evidence.value, evidence.rangesToApply)

formatEvidence (type, evidence, sourcesIndexes, sources) {
if (typeof evidence.value === 'undefined') {
if (evidence.value === undefined) {
return undefined

@@ -76,0 +78,0 @@ }

module.exports = {
COMMAND_INJECTION: 'COMMAND_INJECTION',
CODE_INJECTION: 'CODE_INJECTION',
HARDCODED_PASSWORD: 'HARDCODED_PASSWORD',

@@ -4,0 +5,0 @@ HARDCODED_SECRET: 'HARDCODED_SECRET',

@@ -16,3 +16,4 @@ 'use strict'

responseBody,
responseWriteHead
responseWriteHead,
responseSetHeader
} = require('./channels')

@@ -27,3 +28,3 @@ const waf = require('./waf')

const { HTTP_CLIENT_IP } = require('../../../../ext/tags')
const { block, setTemplates, getBlockingAction } = require('./blocking')
const { isBlocked, block, setTemplates, getBlockingAction } = require('./blocking')
const { passportTrackEvent } = require('./passport')

@@ -67,2 +68,3 @@ const { storage } = require('../../../datadog-core')

responseWriteHead.subscribe(onResponseWriteHead)
responseSetHeader.subscribe(onResponseSetHeader)

@@ -127,3 +129,3 @@ if (_config.appsec.eventTracking.enabled) {

// TODO: temporary express instrumentation, will use express plugin later
if (req.params && typeof req.params === 'object') {
if (req.params !== null && typeof req.params === 'object') {
persistent[addresses.HTTP_INCOMING_PARAMS] = req.params

@@ -133,7 +135,7 @@ }

// we need to keep this to support other cookie parsers
if (req.cookies && typeof req.cookies === 'object') {
if (req.cookies !== null && typeof req.cookies === 'object') {
persistent[addresses.HTTP_INCOMING_COOKIES] = req.cookies
}
if (req.query && typeof req.query === 'object') {
if (req.query !== null && typeof req.query === 'object') {
persistent[addresses.HTTP_INCOMING_QUERY] = req.query

@@ -231,7 +233,6 @@ }

const responseAnalyzedSet = new WeakSet()
const responseBlockedSet = new WeakSet()
function onResponseWriteHead ({ req, res, abortController, statusCode, responseHeaders }) {
// avoid "write after end" error
if (responseBlockedSet.has(res)) {
if (isBlocked(res)) {
abortController?.abort()

@@ -264,2 +265,8 @@ return

function onResponseSetHeader ({ res, abortController }) {
if (isBlocked(res)) {
abortController?.abort()
}
}
function handleResults (actions, req, res, rootSpan, abortController) {

@@ -271,5 +278,2 @@ if (!actions || !req || !res || !rootSpan || !abortController) return

block(req, res, rootSpan, abortController, blockingAction)
if (!abortController.signal || abortController.signal.aborted) {
responseBlockedSet.add(res)
}
}

@@ -301,2 +305,3 @@ }

if (responseWriteHead.hasSubscribers) responseWriteHead.unsubscribe(onResponseWriteHead)
if (responseSetHeader.hasSubscribers) responseSetHeader.unsubscribe(onResponseSetHeader)
}

@@ -303,0 +308,0 @@

@@ -16,3 +16,3 @@ 'use strict'

if (tags && typeof tags === 'object') {
if (tags !== null && typeof tags === 'object') {
called = Object.entries(tags).some(([key, value]) => regexSdkEvent.test(key) && value === 'true')

@@ -19,0 +19,0 @@ }

@@ -6,5 +6,7 @@ 'use strict'

const addresses = require('./addresses')
const { httpClientRequestStart } = require('./channels')
const { httpClientRequestStart, setUncaughtExceptionCaptureCallbackStart } = require('./channels')
const { reportStackTrace } = require('./stack_trace')
const waf = require('./waf')
const { getBlockingAction, block } = require('./blocking')
const log = require('../log')

@@ -15,7 +17,98 @@ const RULE_TYPES = {

let config
class DatadogRaspAbortError extends Error {
constructor (req, res, blockingAction) {
super('DatadogRaspAbortError')
this.name = 'DatadogRaspAbortError'
this.req = req
this.res = res
this.blockingAction = blockingAction
}
}
let config, abortOnUncaughtException
function removeAllListeners (emitter, event) {
const listeners = emitter.listeners(event)
emitter.removeAllListeners(event)
let cleaned = false
return function () {
if (cleaned === true) {
return
}
cleaned = true
for (let i = 0; i < listeners.length; ++i) {
emitter.on(event, listeners[i])
}
}
}
function findDatadogRaspAbortError (err, deep = 10) {
if (err instanceof DatadogRaspAbortError) {
return err
}
if (err.cause && deep > 0) {
return findDatadogRaspAbortError(err.cause, deep - 1)
}
}
function handleUncaughtExceptionMonitor (err) {
const abortError = findDatadogRaspAbortError(err)
if (!abortError) return
const { req, res, blockingAction } = abortError
block(req, res, web.root(req), null, blockingAction)
if (!process.hasUncaughtExceptionCaptureCallback()) {
const cleanUp = removeAllListeners(process, 'uncaughtException')
const handler = () => {
process.removeListener('uncaughtException', handler)
}
setTimeout(() => {
process.removeListener('uncaughtException', handler)
cleanUp()
})
process.on('uncaughtException', handler)
} else {
// uncaughtException event is not executed when hasUncaughtExceptionCaptureCallback is true
let previousCb
const cb = ({ currentCallback, abortController }) => {
setUncaughtExceptionCaptureCallbackStart.unsubscribe(cb)
if (!currentCallback) {
abortController.abort()
return
}
previousCb = currentCallback
}
setUncaughtExceptionCaptureCallbackStart.subscribe(cb)
process.setUncaughtExceptionCaptureCallback(null)
// For some reason, previous callback was defined before the instrumentation
// We can not restore it, so we let the app decide
if (previousCb) {
process.setUncaughtExceptionCaptureCallback(() => {
process.setUncaughtExceptionCaptureCallback(null)
process.setUncaughtExceptionCaptureCallback(previousCb)
})
}
}
}
function enable (_config) {
config = _config
httpClientRequestStart.subscribe(analyzeSsrf)
process.on('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
abortOnUncaughtException = process.execArgv?.includes('--abort-on-uncaught-exception')
if (abortOnUncaughtException) {
log.warn('The --abort-on-uncaught-exception flag is enabled. The RASP module will not block operations.')
}
}

@@ -25,2 +118,4 @@

if (httpClientRequestStart.hasSubscribers) httpClientRequestStart.unsubscribe(analyzeSsrf)
process.off('uncaughtExceptionMonitor', handleUncaughtExceptionMonitor)
}

@@ -38,6 +133,7 @@

}
// TODO: Currently this is only monitoring, we should
// block the request if SSRF attempt
const result = waf.run({ persistent }, req, RULE_TYPES.SSRF)
handleResult(result, req)
const res = store?.res
handleResult(result, req, res, ctx.abortController)
}

@@ -49,3 +145,3 @@

function handleResult (actions, req) {
function handleResult (actions, req, res, abortController) {
const generateStackTraceAction = getGenerateStackTraceAction(actions)

@@ -61,2 +157,19 @@ if (generateStackTraceAction && config.appsec.stackTrace.enabled) {

}
if (!abortController || abortOnUncaughtException) return
const blockingAction = getBlockingAction(actions)
if (blockingAction) {
const rootSpan = web.root(req)
// Should block only in express
if (rootSpan?.context()._name === 'express.request') {
const abortError = new DatadogRaspAbortError(req, res, blockingAction)
abortController.abort(abortError)
// TODO Delete this when support for node 16 is removed
if (!abortController.signal.reason) {
abortController.signal.reason = abortError
}
}
}
}

@@ -67,3 +180,4 @@

disable,
handleResult
handleResult,
handleUncaughtExceptionMonitor // exported only for testing purpose
}

@@ -210,6 +210,2 @@ 'use strict'

const mandatoryTags = filterHeaders(req.headers, REQUEST_HEADERS_MAP)
const ua = mandatoryTags['http.request.headers.user-agent']
if (ua) {
mandatoryTags['http.useragent'] = ua
}
rootSpan.addTags(mandatoryTags)

@@ -216,0 +212,0 @@

@@ -28,3 +28,3 @@ 'use strict'

if (persistent && typeof persistent === 'object') {
if (persistent !== null && typeof persistent === 'object') {
// TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext

@@ -31,0 +31,0 @@ for (const key of Object.keys(persistent)) {

@@ -32,10 +32,10 @@ 'use strict'

const tags = []
const ddVarPrefix = 'config.datadog:'
const otelVarPrefix = 'config.opentelemetry:'
const ddVarPrefix = 'config_datadog:'
const otelVarPrefix = 'config_opentelemetry:'
if (ddVar) {
ddVar = ddVarPrefix + ddVar
ddVar = ddVarPrefix + ddVar.toLowerCase()
tags.push(ddVar)
}
if (otelVar) {
otelVar = otelVarPrefix + otelVar
otelVar = otelVarPrefix + otelVar.toLowerCase()
tags.push(otelVar)

@@ -138,3 +138,3 @@ }

// eslint-disable-next-line max-len
const defaultWafObfuscatorKeyRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key)|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt'
const defaultWafObfuscatorKeyRegex = '(?i)pass|pw(?:or)?d|secret|(?:api|private|public|access)[_-]?key|token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)|bearer|authorization|jsessionid|phpsessid|asp\\.net[_-]sessionid|sid|jwt'
// eslint-disable-next-line max-len

@@ -190,3 +190,3 @@ const defaultWafObfuscatorValueRegex = '(?i)(?:p(?:ass)?w(?:or)?d|pass(?:[_-]?phrase)?|secret(?:[_-]?key)?|(?:(?:api|private|public|access)[_-]?)key(?:[_-]?id)?|(?:(?:auth|access|id|refresh)[_-]?)?token|consumer[_-]?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?|jsessionid|phpsessid|asp\\.net(?:[_-]|-)sessionid|sid|jwt)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}'

// Extract by key if in object-form value
if (typeof option === 'object' && !Array.isArray(option)) {
if (option !== null && typeof option === 'object' && !Array.isArray(option)) {
option = option[key]

@@ -199,3 +199,3 @@ }

// If it's not an array but not undefined there's something wrong with the input
if (typeof option !== 'undefined') {
if (option !== undefined) {
log.warn('Unexpected input for config.tracePropagationStyle')

@@ -208,3 +208,3 @@ }

const envVar = coalesce(process.env[envKey], process.env.DD_TRACE_PROPAGATION_STYLE, process.env.OTEL_PROPAGATORS)
if (typeof envVar !== 'undefined') {
if (envVar !== undefined) {
return envVar.split(',')

@@ -219,30 +219,20 @@ .filter(v => v !== '')

class Config {
constructor (options) {
options = options || {}
options = this.options = {
constructor (options = {}) {
options = {
...options,
appsec: options.appsec != null ? options.appsec : options.experimental?.appsec,
iastOptions: options.experimental?.iast
iast: options.iast != null ? options.iast : options.experimental?.iast
}
checkIfBothOtelAndDdEnvVarSet()
// Configure the logger first so it can be used to warn about other configs
this.debug = isTrue(coalesce(
process.env.DD_TRACE_DEBUG,
process.env.OTEL_LOG_LEVEL && process.env.OTEL_LOG_LEVEL === 'debug',
false
))
this.logger = options.logger
const logConfig = log.getConfig()
this.debug = logConfig.enabled
this.logger = coalesce(options.logger, logConfig.logger)
this.logLevel = coalesce(options.logLevel, logConfig.logLevel)
this.logLevel = coalesce(
options.logLevel,
process.env.DD_TRACE_LOG_LEVEL,
process.env.OTEL_LOG_LEVEL,
'debug'
)
log.use(this.logger)
log.toggle(this.debug, this.logLevel, this)
log.toggle(this.debug, this.logLevel)
checkIfBothOtelAndDdEnvVarSet()
const DD_TRACE_MEMCACHED_COMMAND_ENABLED = coalesce(

@@ -495,3 +485,3 @@ process.env.DD_TRACE_MEMCACHED_COMMAND_ENABLED,

const defaults = this._defaults = {}
const defaults = setHiddenProperty(this, '_defaults', {})

@@ -503,3 +493,3 @@ this._setValue(defaults, 'appsec.blockedTemplateHtml', undefined)

this._setValue(defaults, 'appsec.obfuscatorValueRegex', defaultWafObfuscatorValueRegex)
this._setValue(defaults, 'appsec.rasp.enabled', false)
this._setValue(defaults, 'appsec.rasp.enabled', true)
this._setValue(defaults, 'appsec.rateLimit', 100)

@@ -682,4 +672,4 @@ this._setValue(defaults, 'appsec.rules', undefined)

const tags = {}
const env = this._env = {}
this._envUnprocessed = {}
const env = setHiddenProperty(this, '_env', {})
setHiddenProperty(this, '_envUnprocessed', {})

@@ -824,7 +814,7 @@ tagger.add(tags, OTEL_RESOURCE_ATTRIBUTES, true)

_applyOptions (options) {
const opts = this._options = this._options || {}
const opts = setHiddenProperty(this, '_options', this._options || {})
const tags = {}
this._optsUnprocessed = {}
setHiddenProperty(this, '_optsUnprocessed', {})
options = this.options = Object.assign({ ingestion: {} }, options, opts)
options = setHiddenProperty(this, '_optionsArg', Object.assign({ ingestion: {} }, options, opts))

@@ -871,19 +861,19 @@ tagger.add(tags, options.tags)

this._setString(opts, 'hostname', options.hostname)
this._setBoolean(opts, 'iast.deduplicationEnabled', options.iastOptions && options.iastOptions.deduplicationEnabled)
this._setBoolean(opts, 'iast.deduplicationEnabled', options.iast && options.iast.deduplicationEnabled)
this._setBoolean(opts, 'iast.enabled',
options.iastOptions && (options.iastOptions === true || options.iastOptions.enabled === true))
options.iast && (options.iast === true || options.iast.enabled === true))
this._setValue(opts, 'iast.maxConcurrentRequests',
maybeInt(options.iastOptions?.maxConcurrentRequests))
this._optsUnprocessed['iast.maxConcurrentRequests'] = options.iastOptions?.maxConcurrentRequests
this._setValue(opts, 'iast.maxContextOperations', maybeInt(options.iastOptions?.maxContextOperations))
this._optsUnprocessed['iast.maxContextOperations'] = options.iastOptions?.maxContextOperations
this._setBoolean(opts, 'iast.redactionEnabled', options.iastOptions?.redactionEnabled)
this._setString(opts, 'iast.redactionNamePattern', options.iastOptions?.redactionNamePattern)
this._setString(opts, 'iast.redactionValuePattern', options.iastOptions?.redactionValuePattern)
const iastRequestSampling = maybeInt(options.iastOptions?.requestSampling)
maybeInt(options.iast?.maxConcurrentRequests))
this._optsUnprocessed['iast.maxConcurrentRequests'] = options.iast?.maxConcurrentRequests
this._setValue(opts, 'iast.maxContextOperations', maybeInt(options.iast?.maxContextOperations))
this._optsUnprocessed['iast.maxContextOperations'] = options.iast?.maxContextOperations
this._setBoolean(opts, 'iast.redactionEnabled', options.iast?.redactionEnabled)
this._setString(opts, 'iast.redactionNamePattern', options.iast?.redactionNamePattern)
this._setString(opts, 'iast.redactionValuePattern', options.iast?.redactionValuePattern)
const iastRequestSampling = maybeInt(options.iast?.requestSampling)
if (iastRequestSampling > -1 && iastRequestSampling < 101) {
this._setValue(opts, 'iast.requestSampling', iastRequestSampling)
this._optsUnprocessed['iast.requestSampling'] = options.iastOptions?.requestSampling
this._optsUnprocessed['iast.requestSampling'] = options.iast?.requestSampling
}
this._setString(opts, 'iast.telemetryVerbosity', options.iastOptions && options.iastOptions.telemetryVerbosity)
this._setString(opts, 'iast.telemetryVerbosity', options.iast && options.iast.telemetryVerbosity)
this._setBoolean(opts, 'isCiVisibility', options.isCiVisibility)

@@ -918,3 +908,3 @@ this._setBoolean(opts, 'logInjection', options.logInjection)

const hasTelemetryLogsUsingFeatures =
(options.iastOptions && (options.iastOptions === true || options.iastOptions?.enabled === true)) ||
(options.iast && (options.iast === true || options.iast?.enabled === true)) ||
(options.profiling && options.profiling === true)

@@ -929,3 +919,3 @@ this._setBoolean(opts, 'telemetry.logCollection', hasTelemetryLogsUsingFeatures)

return coalesce(
this.options.isCiVisibility,
this._optionsArg.isCiVisibility,
this._defaults.isCiVisibility

@@ -946,5 +936,5 @@ )

? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
: getAgentUrl(this._getTraceAgentUrl(), this.options)
: getAgentUrl(this._getTraceAgentUrl(), this._optionsArg)
const DD_AGENT_HOST = coalesce(
this.options.hostname,
this._optionsArg.hostname,
process.env.DD_AGENT_HOST,

@@ -960,3 +950,3 @@ process.env.DD_TRACE_AGENT_HOSTNAME,

coalesce(
this.options.spanAttributeSchema,
this._optionsArg.spanAttributeSchema,
process.env.DD_TRACE_SPAN_ATTRIBUTE_SCHEMA

@@ -967,7 +957,7 @@ )

const peerServiceSet = (
this.options.hasOwnProperty('spanComputePeerService') ||
this._optionsArg.hasOwnProperty('spanComputePeerService') ||
process.env.hasOwnProperty('DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED')
)
const peerServiceValue = coalesce(
this.options.spanComputePeerService,
this._optionsArg.spanComputePeerService,
process.env.DD_TRACE_PEER_SERVICE_DEFAULTS_ENABLED

@@ -1003,3 +993,3 @@ )

return coalesce(
this.options.stats,
this._optionsArg.stats,
process.env.DD_TRACE_STATS_COMPUTATION_ENABLED,

@@ -1012,3 +1002,3 @@ getIsGCPFunction() || getIsAzureFunction()

return coalesce(
this.options.url,
this._optionsArg.url,
process.env.DD_TRACE_AGENT_URL,

@@ -1022,3 +1012,3 @@ process.env.DD_TRACE_URL,

_applyCalculated () {
const calc = this._calculated = {}
const calc = setHiddenProperty(this, '_calculated', {})

@@ -1032,3 +1022,3 @@ const {

} else {
this._setValue(calc, 'url', getAgentUrl(this._getTraceAgentUrl(), this.options))
this._setValue(calc, 'url', getAgentUrl(this._getTraceAgentUrl(), this._optionsArg))
}

@@ -1049,4 +1039,4 @@ if (this._isCiVisibility()) {

_applyRemote (options) {
const opts = this._remote = this._remote || {}
this._remoteUnprocessed = {}
const opts = setHiddenProperty(this, '_remote', this._remote || {})
setHiddenProperty(this, '_remoteUnprocessed', {})
const tags = {}

@@ -1067,11 +1057,14 @@ const headerTags = options.tracing_header_tags

this._setBoolean(opts, 'tracing', options.tracing_enabled)
// ignore tags for now since rc sampling rule tags format is not supported
this._setSamplingRule(opts, 'sampler.rules', this._ignoreTags(options.tracing_sampling_rules))
this._remoteUnprocessed['sampler.rules'] = options.tracing_sampling_rules
this._setSamplingRule(opts, 'sampler.rules', this._reformatTags(options.tracing_sampling_rules))
}
_ignoreTags (samplingRules) {
if (samplingRules) {
for (const rule of samplingRules) {
delete rule.tags
_reformatTags (samplingRules) {
for (const rule of (samplingRules || [])) {
const reformattedTags = {}
if (rule.tags) {
for (const tag of (rule.tags || {})) {
reformattedTags[tag.key] = tag.value_glob
}
rule.tags = reformattedTags
}

@@ -1218,2 +1211,11 @@ }

function setHiddenProperty (obj, name, value) {
Object.defineProperty(obj, name, {
value,
enumerable: false,
writable: true
})
return obj[name]
}
module.exports = Config

@@ -20,7 +20,9 @@ // encoding used here is sha256

function computeHash (service, env, edgeTags, parentHash) {
const key = `${service}${env}` + edgeTags.join('') + parentHash.toString()
const hashableEdgeTags = edgeTags.filter(item => item !== 'manual_checkpoint:true')
const key = `${service}${env}` + hashableEdgeTags.join('') + parentHash.toString()
if (cache.get(key)) {
return cache.get(key)
}
const currentHash = shaHash(`${service}${env}` + edgeTags.join(''))
const currentHash = shaHash(`${service}${env}` + hashableEdgeTags.join(''))
const buf = Buffer.concat([currentHash, parentHash], 16)

@@ -27,0 +29,0 @@ const val = shaHash(buf.toString())

@@ -135,3 +135,3 @@ const os = require('os')

}
if (typeof obj === 'object') {
if (obj !== null && typeof obj === 'object') {
try {

@@ -215,3 +215,4 @@ return getHeadersSize(obj)

Version: this.version,
Lang: 'javascript'
Lang: 'javascript',
Tags: Object.entries(this.tags).map(([key, value]) => `${key}:${value}`)
}

@@ -218,0 +219,0 @@ this.writer.flush(payload)

'use strict'
const coalesce = require('koalas')
const { isTrue } = require('../util')
const { debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')

@@ -23,4 +25,18 @@ const logWriter = require('./writer')

const config = {
enabled: false,
logger: undefined,
logLevel: 'debug'
}
const log = {
/**
* @returns Read-only version of logging config. To modify config, call `log.use` and `log.toggle`
*/
getConfig () {
return { ...config }
},
use (logger) {
config.logger = logger
logWriter.use(logger)

@@ -31,2 +47,4 @@ return this

toggle (enabled, logLevel) {
config.enabled = enabled
config.logLevel = logLevel
logWriter.toggle(enabled, logLevel)

@@ -81,2 +99,16 @@ return this

const enabled = isTrue(coalesce(
process.env.DD_TRACE_DEBUG,
process.env.OTEL_LOG_LEVEL === 'debug',
config.enabled
))
const logLevel = coalesce(
process.env.DD_TRACE_LOG_LEVEL,
process.env.OTEL_LOG_LEVEL,
config.logLevel
)
log.toggle(enabled, logLevel)
module.exports = log

@@ -5,2 +5,3 @@ 'use strict'

const { trace, ROOT_CONTEXT } = require('@opentelemetry/api')
const DataDogSpanContext = require('../opentracing/span_context')

@@ -10,31 +11,2 @@ const SpanContext = require('./span_context')

// Horrible hack to acquire the otherwise inaccessible SPAN_KEY so we can redirect it...
// This is used for getting the current span context in OpenTelemetry, but the SPAN_KEY value is
// not exposed as it's meant to be read-only from outside the module. We want to hijack this logic
// so we can instead get the span context from the datadog context manager instead.
let SPAN_KEY
trace.getSpan({
getValue (key) {
SPAN_KEY = key
}
})
// Whenever a value is acquired from the context map we should mostly delegate to the real getter,
// but when accessing the current span we should hijack that access to instead provide a fake span
// which we can use to get an OTel span context wrapping the datadog active scope span context.
function wrappedGetValue (target) {
return (key) => {
if (key === SPAN_KEY) {
return {
spanContext () {
const activeSpan = tracer.scope().active()
const context = activeSpan && activeSpan.context()
return new SpanContext(context)
}
}
}
return target.getValue(key)
}
}
class ContextManager {

@@ -46,9 +18,18 @@ constructor () {

active () {
const active = this._store.getStore() || ROOT_CONTEXT
const activeSpan = tracer.scope().active()
const store = this._store.getStore()
const context = (activeSpan && activeSpan.context()) || store || ROOT_CONTEXT
return new Proxy(active, {
get (target, key) {
return key === 'getValue' ? wrappedGetValue(target) : target[key]
}
})
if (!(context instanceof DataDogSpanContext)) {
return context
}
if (!context._otelSpanContext) {
const newSpanContext = new SpanContext(context)
context._otelSpanContext = newSpanContext
}
if (store && trace.getSpanContext(store) === context._otelSpanContext) {
return store
}
return trace.setSpanContext(store || ROOT_CONTEXT, context._otelSpanContext)
}

@@ -59,6 +40,10 @@

const ddScope = tracer.scope()
return ddScope.activate(span._ddSpan, () => {
const run = () => {
const cb = thisArg == null ? fn : fn.bind(thisArg)
return this._store.run(context, cb, ...args)
})
}
if (span && span._ddSpan) {
return ddScope.activate(span._ddSpan, run)
}
return run()
}

@@ -73,7 +58,5 @@

// Not part of the spec but the Node.js API expects these
enable () {}
disable () {}
}
module.exports = ContextManager

@@ -27,7 +27,7 @@ 'use strict'

get traceId () {
return this._ddContext._traceId.toString(16)
return this._ddContext.toTraceId(true)
}
get spanId () {
return this._ddContext._spanId.toString(16)
return this._ddContext.toSpanId(true)
}

@@ -34,0 +34,0 @@

@@ -286,3 +286,3 @@ 'use strict'

get ended () {
return typeof this.duration !== 'undefined'
return this.duration !== undefined
}

@@ -289,0 +289,0 @@ }

'use strict'
const { trace, context } = require('@opentelemetry/api')
const { trace, context, propagation } = require('@opentelemetry/api')
const { W3CTraceContextPropagator } = require('@opentelemetry/core')

@@ -55,2 +56,9 @@ const tracer = require('../../')

}
// The default propagator used is the W3C Trace Context propagator, users should be able to pass in others
// as needed
if (config.propagator) {
propagation.setGlobalPropagator(config.propagator)
} else {
propagation.setGlobalPropagator(new W3CTraceContextPropagator())
}
}

@@ -57,0 +65,0 @@

@@ -10,2 +10,3 @@ 'use strict'

const SpanContext = require('./span_context')
const TextMapPropagator = require('../opentracing/propagation/text_map')

@@ -19,2 +20,3 @@ class Tracer {

this.instrumentationLibrary = library
this._spanLimits = {}
}

@@ -26,2 +28,20 @@

_createSpanContextFromParent (parentSpanContext) {
return new SpanContext({
traceId: parentSpanContext._traceId,
spanId: id(),
parentId: parentSpanContext._spanId,
sampling: parentSpanContext._sampling,
baggageItems: Object.assign({}, parentSpanContext._baggageItems),
trace: parentSpanContext._trace,
tracestate: parentSpanContext._tracestate
})
}
// Extracted method to create span context for a new span
_createSpanContextForNewSpan (context) {
const { traceId, spanId, traceFlags, traceState } = context
return TextMapPropagator._convertOtelContextToDatadog(traceId, spanId, traceFlags, traceState)
}
startSpan (name, options = {}, context = api.context.active()) {

@@ -34,17 +54,7 @@ // remove span from context in case a root span is requested via options

const parentSpanContext = parentSpan && parentSpan.spanContext()
let spanContext
// TODO: Need a way to get 128-bit trace IDs for the validity check API to work...
// if (parent && api.trace.isSpanContextValid(parent)) {
if (parentSpanContext && parentSpanContext.traceId) {
const parent = parentSpanContext._ddContext
spanContext = new SpanContext({
traceId: parent._traceId,
spanId: id(),
parentId: parent._spanId,
sampling: parent._sampling,
baggageItems: Object.assign({}, parent._baggageItems),
trace: parent._trace,
tracestate: parent._tracestate
})
if (parentSpanContext && api.trace.isSpanContextValid(parentSpanContext)) {
spanContext = parentSpanContext._ddContext
? this._createSpanContextFromParent(parentSpanContext._ddContext)
: this._createSpanContextForNewSpan(parentSpanContext)
} else {

@@ -125,4 +135,9 @@ spanContext = new SpanContext()

}
// not used in our codebase but needed for compatibility. See issue #1244
getSpanLimits () {
return this._spanLimits
}
}
module.exports = Tracer

@@ -6,2 +6,3 @@ 'use strict'

const DatadogSpanContext = require('../span_context')
const OtelSpanContext = require('../../opentelemetry/span_context')
const log = require('../../log')

@@ -622,4 +623,63 @@ const TraceState = require('./tracestate')

}
static _convertOtelContextToDatadog (traceId, spanId, traceFlag, ts, meta = {}) {
const origin = null
let samplingPriority = traceFlag
ts = ts?.traceparent || null
if (ts) {
// Use TraceState.fromString to parse the tracestate header
const traceState = TraceState.fromString(ts)
let ddTraceStateData = null
// Extract Datadog specific trace state data
traceState.forVendor('dd', (state) => {
ddTraceStateData = state
return state // You might need to adjust this part based on actual logic needed
})
if (ddTraceStateData) {
// Assuming ddTraceStateData is now a Map or similar structure containing Datadog trace state data
// Extract values as needed, similar to the original logic
const samplingPriorityTs = ddTraceStateData.get('s')
const origin = ddTraceStateData.get('o')
// Convert Map to object for meta
const otherPropagatedTags = Object.fromEntries(ddTraceStateData.entries())
// Update meta and samplingPriority based on extracted values
Object.assign(meta, otherPropagatedTags)
samplingPriority = TextMapPropagator._getSamplingPriority(traceFlag, parseInt(samplingPriorityTs, 10), origin)
} else {
log.debug(`no dd list member in tracestate from incoming request: ${ts}`)
}
}
const spanContext = new OtelSpanContext({
traceId: id(traceId, 16), spanId: id(), tags: meta, parentId: id(spanId, 16)
})
spanContext._sampling = { priority: samplingPriority }
spanContext._trace = { origin }
return spanContext
}
static _getSamplingPriority (traceparentSampled, tracestateSamplingPriority, origin = null) {
const fromRumWithoutPriority = !tracestateSamplingPriority && origin === 'rum'
let samplingPriority
if (!fromRumWithoutPriority && traceparentSampled === 0 &&
(!tracestateSamplingPriority || tracestateSamplingPriority >= 0)) {
samplingPriority = 0
} else if (!fromRumWithoutPriority && traceparentSampled === 1 &&
(!tracestateSamplingPriority || tracestateSamplingPriority < 0)) {
samplingPriority = 1
} else {
samplingPriority = tracestateSamplingPriority
}
return samplingPriority
}
}
module.exports = TextMapPropagator

@@ -30,2 +30,3 @@ 'use strict'

}
this._otelSpanContext = undefined
}

@@ -32,0 +33,0 @@

@@ -8,2 +8,3 @@ 'use strict'

const TextMapPropagator = require('./propagation/text_map')
const DSMTextMapPropagator = require('./propagation/text_map_dsm')
const HttpPropagator = require('./propagation/http')

@@ -42,3 +43,4 @@ const BinaryPropagator = require('./propagation/binary')

[formats.BINARY]: new BinaryPropagator(config),
[formats.LOG]: new LogPropagator(config)
[formats.LOG]: new LogPropagator(config),
[formats.TEXT_MAP_DSM]: new DSMTextMapPropagator(config)
}

@@ -76,10 +78,12 @@ if (config.reportHostname) {

inject (spanContext, format, carrier) {
if (spanContext instanceof Span) {
spanContext = spanContext.context()
inject (context, format, carrier) {
if (context instanceof Span) {
context = context.context()
}
try {
this._prioritySampler.sample(spanContext)
this._propagators[format].inject(spanContext, carrier)
if (format !== 'text_map_dsm') {
this._prioritySampler.sample(context)
}
this._propagators[format].inject(context, carrier)
} catch (e) {

@@ -86,0 +90,0 @@ log.error(e)

@@ -19,3 +19,4 @@ const {

TEST_SKIPPED_BY_ITR,
ITR_CORRELATION_ID
ITR_CORRELATION_ID,
TEST_SOURCE_FILE
} = require('./util/test')

@@ -211,3 +212,9 @@ const Plugin = require('./plugin')

const codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
const { [TEST_SOURCE_FILE]: testSourceFile } = extraTags
// We'll try with the test source file if available (it could be different from the test suite)
let codeOwners = getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries)
if (!codeOwners) {
codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
}
if (codeOwners) {

@@ -214,0 +221,0 @@ testTags[TEST_CODE_OWNERS] = codeOwners

@@ -6,2 +6,3 @@ 'use strict'

const dc = require('dc-polyfill')
const logger = require('../log')
const { storage } = require('../../../datadog-core')

@@ -76,3 +77,13 @@

addSub (channelName, handler) {
this._subscriptions.push(new Subscription(channelName, handler))
const plugin = this
const wrappedHandler = function () {
try {
return handler.apply(this, arguments)
} catch (e) {
logger.error('Error in plugin handler:', e)
logger.info('Disabling plugin:', plugin.id)
plugin.configure(false)
}
}
this._subscriptions.push(new Subscription(channelName, wrappedHandler))
}

@@ -79,0 +90,0 @@

@@ -98,2 +98,5 @@ const path = require('path')

// Flaky test retries
const NUM_FAILED_TEST_RETRIES = 5
module.exports = {

@@ -171,3 +174,4 @@ TEST_CODE_OWNERS,

TEST_BROWSER_NAME,
TEST_BROWSER_VERSION
TEST_BROWSER_VERSION,
NUM_FAILED_TEST_RETRIES
}

@@ -174,0 +178,0 @@

@@ -72,3 +72,3 @@ 'use strict'

mapper = await maybeSourceMap(config.sourceMap, SourceMapper, config.debugSourceMaps)
if (config.SourceMap && config.debugSourceMaps) {
if (config.sourceMap && config.debugSourceMaps) {
this._logger.debug(() => {

@@ -75,0 +75,0 @@ return mapper.infoMap.size === 0

@@ -184,2 +184,3 @@ 'use strict'

this._tracer = new DatadogTracer(config, prioritySampler)
this.dataStreamsCheckpointer = this._tracer.dataStreamsCheckpointer
this.appsec = new AppsecSdk(this._tracer, config)

@@ -186,0 +187,0 @@ this._tracingInitialized = true

@@ -14,2 +14,3 @@ 'use strict'

const DataStreamsContext = require('./data_streams_context')
const { DataStreamsCheckpointer } = require('./data_streams')
const { flushStartupLogs } = require('../../datadog-instrumentations/src/check_require_cache')

@@ -27,2 +28,3 @@ const log = require('./log/writer')

this._dataStreamsProcessor = new DataStreamsProcessor(config)
this.dataStreamsCheckpointer = new DataStreamsCheckpointer(this)
this._scope = new Scope()

@@ -29,0 +31,0 @@ setStartupLogConfig(config)

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc