Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/apm-integrations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ jobs:
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: ./.github/actions/testagent/start
- uses: ./.github/actions/node/active-lts # TODO: change this to latest once we figure out the `fresh` bug
- uses: ./.github/actions/node/latest
- uses: ./.github/actions/install
- run: yarn test:plugins:ci

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/appsec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ jobs:
- uses: ./.github/actions/node/oldest-maintenance-lts
- uses: ./.github/actions/install
- run: yarn test:appsec:plugins:ci
- uses: ./.github/actions/node/active-lts # TODO: change this to latest once we figure out the `fresh` bug
- uses: ./.github/actions/node/latest
- run: yarn test:appsec:plugins:ci
- uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Check code meets quality and security standards
id: datadog-static-analysis
uses: DataDog/datadog-static-analyzer-github-action@v1
uses: DataDog/datadog-static-analyzer-github-action@2707598b1182dce1d1792186477b5b4132338e1c # v1.2.3
with:
dd_api_key: ${{ secrets.DD_API_KEY }}
dd_app_key: ${{ secrets.DD_APP_KEY }}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dd-trace",
"version": "5.63.0",
"version": "5.63.1",
"description": "Datadog APM tracing client for JavaScript",
"main": "index.js",
"typings": "index.d.ts",
Expand Down
5 changes: 2 additions & 3 deletions packages/datadog-instrumentations/src/moleculer/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,18 @@ function wrapCall (call) {
ctx.promiseCtx = promise.ctx
ctx.broker = broker

return promise
promise
.then(
result => {
finishChannel.publish(ctx)
return result
},
error => {
ctx.error = error
errorChannel.publish(ctx)
finishChannel.publish(ctx)
throw error
}
)
return promise
})
}
}
Expand Down
7 changes: 7 additions & 0 deletions packages/datadog-plugin-express/test/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'

const { NODE_MAJOR } = require('../../../version')
const { AsyncLocalStorage } = require('async_hooks')
const axios = require('axios')
const semver = require('semver')
Expand All @@ -17,6 +18,12 @@ describe('Plugin', () => {

describe('express', () => {
withVersions('express', 'express', version => {
// Express.js 4.10.5 and below have a Node.js incompatibility in the `fresh` package RE res._headers missing
if (semver.intersects(version, '<=4.10.5') && NODE_MAJOR >= 24) {
describe.skip(`refusing to run tests as express@${version} is incompatible with Node.js ${NODE_MAJOR}`)
return
}

beforeEach(() => {
tracer = require('../../dd-trace')
})
Expand Down
124 changes: 106 additions & 18 deletions packages/datadog-plugin-moleculer/test/index.spec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use strict'

const { expect } = require('chai')
const assert = require('node:assert')
const getPort = require('get-port')
const os = require('node:os')
const { withNamingSchema, withPeerService, withVersions } = require('../../dd-trace/test/setup/mocha')
const agent = require('../../dd-trace/test/plugins/agent')
const { expectedSchema, rawExpectedSchema } = require('./naming')
const { assertObjectContains } = require('../../../integration-tests/helpers')

const sort = trace => trace.sort((a, b) => Number(a.start - b.start))

Expand All @@ -30,10 +32,10 @@ describe('Plugin', () => {
broker.createService({
name: 'math',
actions: {
add (ctx) {
async add (ctx) {
const numerify = this.actions.numerify

return numerify(ctx.params.a) + numerify(ctx.params.b)
return await numerify(ctx.params.a) + await numerify(ctx.params.b)
},

numerify (ctx) {
Expand All @@ -42,6 +44,15 @@ describe('Plugin', () => {
}
})

broker.createService({
name: 'error',
actions: {
async error (ctx) {
throw new Error('Invalid number')
}
}
})

return broker.start()
}

Expand Down Expand Up @@ -159,25 +170,61 @@ describe('Plugin', () => {
'out.host'
)

it('should do automatic instrumentation', done => {
it('should do automatic instrumentation', async () => {
const result = await broker.call('math.add', { a: 5, b: 3 })
assert.strictEqual(result, 8)

agent.assertSomeTraces(traces => {
const spans = sort(traces[0])
const span = traces[0][0]

assertObjectContains(span, {
name: expectedSchema.client.opName,
service: expectedSchema.client.serviceName,
resource: 'math.add',
meta: {
'span.kind': 'client',
'out.host': hostname,
'moleculer.context.action': 'math.add',
'moleculer.context.node_id': `server-${process.pid}`,
'moleculer.context.service': 'math',
'moleculer.namespace': 'multi',
'moleculer.node_id': `server-${process.pid}`,
},
metrics: {
'network.destination.port': port
}
})

assert.strictEqual(typeof span.meta['moleculer.context.request_id'], 'string')
})
})

expect(spans[0]).to.have.property('name', expectedSchema.client.opName)
expect(spans[0]).to.have.property('service', expectedSchema.client.serviceName)
expect(spans[0]).to.have.property('resource', 'math.add')
expect(spans[0].meta).to.have.property('span.kind', 'client')
expect(spans[0].meta).to.have.property('out.host', hostname)
expect(spans[0].meta).to.have.property('moleculer.context.action', 'math.add')
expect(spans[0].meta).to.have.property('moleculer.context.node_id', `server-${process.pid}`)
expect(spans[0].meta).to.have.property('moleculer.context.request_id')
expect(spans[0].meta).to.have.property('moleculer.context.service', 'math')
expect(spans[0].meta).to.have.property('moleculer.namespace', 'multi')
expect(spans[0].meta).to.have.property('moleculer.node_id', `server-${process.pid}`)
expect(spans[0].metrics).to.have.property('network.destination.port', port)
}).then(done, done)
it('should handle error cases', async () => {
await assert.rejects(broker.call('error.error'), { message: 'Invalid number' })

broker.call('math.add', { a: 5, b: 3 }).catch(done)
agent.assertSomeTraces(traces => {
const span = traces[0][0]

assertObjectContains(span, {
name: expectedSchema.client.opName,
service: expectedSchema.client.serviceName,
resource: 'error.error',
meta: {
'span.kind': 'client',
'out.host': hostname,
'moleculer.context.action': 'error.error',
'moleculer.context.node_id': `server-${process.pid}`,
'moleculer.context.service': 'error',
'moleculer.namespace': 'multi',
'moleculer.node_id': `server-${process.pid}`,
},
metrics: {
'network.destination.port': port
}
})

assert.strictEqual(typeof span.meta['moleculer.context.request_id'], 'string')
})
})

withNamingSchema(
Expand Down Expand Up @@ -331,6 +378,47 @@ describe('Plugin', () => {
expect(spanId.toString()).to.equal(parentId.toString())
})
})
describe('meta propagation', () => {
before(() => agent.load('moleculer', {
meta: true
}))

before(async () => {
const { ServiceBroker } = require(`../../../versions/moleculer@${version}`).get()
broker = new ServiceBroker({
nodeID: `server-${process.pid}`,
logger: false
})

broker.createService({
name: 'test',
actions: {
async first (ctx) {
await ctx.call('test.second', null, {
meta: {
a: 'John'
}
})
return ctx.meta.a
},
second (ctx) {
ctx.meta.a = 'Doe'
}
}
})

return broker.start()
})

after(() => broker.stop())

after(() => agent.close({ ritmReset: false }))

it('should propagate meta from child to parent', async () => {
const result = await broker.call('test.first')
assert.strictEqual(result, 'Doe')
})
})
})
})
})
6 changes: 5 additions & 1 deletion packages/dd-trace/src/llmobs/writers/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,11 @@ class BaseLLMObsWriter {
}

const { hostname, port } = this._config
const base = this._config.url || new URL(format({

const overrideOriginEnv = getEnvironmentVariable('_DD_LLMOBS_OVERRIDE_ORIGIN')
const overrideOriginUrl = overrideOriginEnv && new URL(overrideOriginEnv)

const base = overrideOriginUrl ?? this._config.url ?? new URL(format({
protocol: 'http:',
hostname,
port
Expand Down
4 changes: 2 additions & 2 deletions packages/dd-trace/src/opentracing/propagation/text_map.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,13 @@ class TextMapPropagator {

// Check for item count limit exceeded
if (itemCounter > this._config.baggageMaxItems) {
tracerMetrics.count('context_header_style.truncated', ['truncation_reason:baggage_item_count_exceeded']).inc()
tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_item_count_exceeded']).inc()
break
}

// Check for byte count limit exceeded
if (byteCounter > this._config.baggageMaxBytes) {
tracerMetrics.count('context_header_style.truncated', ['truncation_reason:baggage_byte_count_exceeded']).inc()
tracerMetrics.count('context_header.truncated', ['truncation_reason:baggage_byte_count_exceeded']).inc()
break
}

Expand Down
4 changes: 4 additions & 0 deletions packages/dd-trace/src/supported-configurations.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
"DD_PLAYWRIGHT_WORKER": ["A"],
"DD_PROFILING_CODEHOTSPOTS_ENABLED": ["A"],
"DD_PROFILING_CPU_ENABLED": ["A"],
"DD_PROFILING_DEBUG_SOURCE_MAPS": ["A"],
"DD_PROFILING_DEBUG_UPLOAD_COMPRESSION": ["A"],
"DD_PROFILING_ENABLED": ["A"],
"DD_PROFILING_ENDPOINT_COLLECTION_ENABLED": ["A"],
Expand All @@ -127,10 +128,13 @@
"DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED": ["A"],
"DD_PROFILING_EXPORTERS": ["A"],
"DD_PROFILING_HEAP_ENABLED": ["A"],
"DD_PROFILING_HEAP_SAMPLING_INTERVAL": ["A"],
"DD_PROFILING_PPROF_PREFIX": ["A"],
"DD_PROFILING_PROFILERS": ["A"],
"DD_PROFILING_SOURCE_MAP": ["A"],
"DD_PROFILING_TIMELINE_ENABLED": ["A"],
"DD_PROFILING_UPLOAD_PERIOD": ["A"],
"DD_PROFILING_UPLOAD_TIMEOUT": ["A"],
"DD_PROFILING_V8_PROFILER_BUG_WORKAROUND": ["A"],
"DD_PROFILING_WALLTIME_ENABLED": ["A"],
"DD_REMOTE_CONFIG_POLL_INTERVAL_SECONDS": ["A"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const { NODE_MAJOR } = require('../../../../../../version')
const semver = require('semver')
const { prepareTestServerForIastInExpress } = require('../utils')
const axios = require('axios')
const path = require('path')
Expand All @@ -14,6 +16,11 @@ const { withVersions } = require('../../../setup/mocha')

describe('Code injection vulnerability', () => {
withVersions('express', 'express', version => {
if (semver.intersects(version, '<=4.10.5') && NODE_MAJOR >= 24) {
describe.skip(`refusing to run tests as express@${version} is incompatible with Node.js ${NODE_MAJOR}`)
return
}

describe('Eval', () => {
let i = 0
let evalFunctionsPath
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict'

const { NODE_MAJOR } = require('../../../../../../../version')
const axios = require('axios')
const semver = require('semver')
const agent = require('../../../../plugins/agent')
Expand All @@ -19,6 +20,11 @@ describe('URI sourcing with express', () => {
let appListener

withVersions('express', 'express', version => {
if (semver.intersects(version, '<=4.10.5') && NODE_MAJOR >= 24) {
describe.skip(`refusing to run tests as express@${version} is incompatible with Node.js ${NODE_MAJOR}`)
return
}

before(() => {
return agent.load(['http', 'express'], { client: false })
})
Expand Down Expand Up @@ -78,6 +84,11 @@ describe('Path params sourcing with express', () => {
let appListener

withVersions('express', 'express', version => {
if (semver.intersects(version, '<=4.10.5') && NODE_MAJOR >= 24) {
describe.skip(`refusing to run tests as express@${version} is incompatible with Node.js ${NODE_MAJOR}`)
return
}

const checkParamIsTaintedAndNext = (req, res, next, param, name) => {
const store = storage('legacy').getStore()
const iastContext = iastContextFunctions.getIastContext(store)
Expand Down
7 changes: 7 additions & 0 deletions packages/dd-trace/test/appsec/index.express.plugin.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const { NODE_MAJOR } = require('../../../../version')
const semver = require('semver')
const Axios = require('axios')
const { assert } = require('chai')
const path = require('path')
Expand All @@ -11,6 +13,11 @@ const { json } = require('../../src/appsec/blocked_templates')
const { withVersions } = require('../setup/mocha')

withVersions('express', 'express', version => {
if (semver.intersects(version, '<=4.10.5') && NODE_MAJOR >= 24) {
describe.skip(`refusing to run tests as express@${version} is incompatible with Node.js ${NODE_MAJOR}`)
return
}

describe('Suspicious request blocking - path parameters', () => {
let server, paramCallbackSpy, axios

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict'

const { NODE_MAJOR } = require('../../../../../version')
const semver = require('semver')
const Axios = require('axios')
const os = require('os')
const fs = require('fs')
Expand Down Expand Up @@ -31,6 +33,11 @@ describe('RASP - lfi', () => {
}

withVersions('express', 'express', expressVersion => {
if (semver.intersects(expressVersion, '<=4.10.5') && NODE_MAJOR >= 24) {
describe.skip(`refusing to run tests as express@${expressVersion} is incompatible with Node.js ${NODE_MAJOR}`)
return
}

withVersions('express', 'ejs', ejsVersion => {
let app, server

Expand Down
Loading
Loading