Skip to content

Commit 88b2334

Browse files
security: Bumped minimum version of security agent (#3713)
Co-authored-by: Bob Evans <revans@newrelic.com>
1 parent c3c5ed5 commit 88b2334

25 files changed

+291
-248
lines changed

THIRD_PARTY_NOTICES.md

Lines changed: 39 additions & 39 deletions
Large diffs are not rendered by default.

esm-loader.mjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
export * from 'import-in-the-middle/hook.mjs'
6+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
7+
import { register } from 'node:module'
8+
9+
register('import-in-the-middle/hook.mjs', import.meta.url, {})

lib/instrumentation/when/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ module.exports = function initialize(shim, when) {
296296
ret = agent.tracer.bindFunction(fn, context, true).apply(this, arguments)
297297
} finally {
298298
if (ret && typeof ret.then === 'function') {
299-
// eslint-disable-next-line sonarjs/no-dead-store
300299
ret = ctx.next[symbols.context].continue(ret)
301300
}
302301
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@
203203
"dependencies": {
204204
"@grpc/grpc-js": "^1.13.2",
205205
"@grpc/proto-loader": "^0.7.5",
206-
"@newrelic/security-agent": "^2.4.2",
206+
"@newrelic/security-agent": "^2.4.4",
207207
"@opentelemetry/api": "^1.9.0",
208208
"@opentelemetry/core": "^2.0.0",
209209
"@opentelemetry/exporter-metrics-otlp-proto": "^0.201.1",

test/integration/instrumentation/http-outbound.test.js

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const helper = require('../../lib/agent_helper')
99
const test = require('node:test')
1010
const assert = require('node:assert')
1111
const symbols = require('../../../lib/symbols')
12+
const nock = require('nock')
1213

1314
test('external requests', async function (t) {
1415
t.beforeEach((ctx) => {
@@ -102,10 +103,15 @@ test('external requests', async function (t) {
102103
let connectChildren = tx.trace.getChildren(connect.id)
103104
assert.equal(connectChildren.length, 1, 'connect should have 1 child')
104105

106+
// as of Node 24.5.0 there's yet another layer of net segments
107+
if (connectChildren[0].name === 'net.createConnection') {
108+
connectChildren = tx.trace.getChildren(connectChildren[0].id)
109+
}
105110
// There is potentially an extra layer of create/connect segments.
106111
if (connectChildren[0].name === 'net.Socket.connect') {
107112
connect = connectChildren[0]
108113
}
114+
109115
connectChildren = tx.trace.getChildren(connect.id)
110116

111117
const dnsLookup = connectChildren[0]
@@ -159,42 +165,15 @@ test('external requests', async function (t) {
159165
})
160166
})
161167

162-
await t.test('should not duplicate the external segment', function (t, end) {
163-
const { agent } = t.nr
164-
const https = require('https')
165-
166-
helper.runInTransaction(agent, function inTransaction() {
167-
https.get('https://example.com:443/', function onResponse(res) {
168-
res.once('end', check)
169-
res.resume()
170-
})
171-
})
172-
173-
function check() {
174-
const tx = agent.getTransaction()
175-
const [segment] = tx.trace.getChildren(tx.trace.root.id)
176-
177-
assert.equal(segment.name, 'External/example.com/', 'should be named')
178-
assert.ok(segment.timer.start, 'should have started')
179-
assert.ok(segment.timer.hasEnd(), 'should have ended')
180-
const segmentChildren = tx.trace.getChildren(segment.id)
181-
assert.equal(segmentChildren.length, 1, 'should have 1 child')
182-
183-
const notDuped = segmentChildren[0]
184-
assert.notEqual(
185-
notDuped.name,
186-
segment.name,
187-
'child should not be named the same as the external segment'
188-
)
189-
190-
end()
191-
}
192-
})
193-
194168
await t.test('NODE-1647 should not interfere with `got`', { timeout: 5000 }, function (t, end) {
195169
const { agent } = t.nr
196170
// Our way of wrapping HTTP response objects caused `got` to hang. This was
197171
// resolved in agent 2.5.1.
172+
nock.disableNetConnect()
173+
t.after(() => {
174+
nock.enableNetConnect()
175+
})
176+
nock('https://example.com').get('/').reply(200)
198177
const got = require('got')
199178
helper.runInTransaction(agent, function () {
200179
const req = got('https://example.com/')
@@ -215,8 +194,13 @@ test('external requests', async function (t) {
215194

216195
await t.test('should record requests to default ports', (t, end) => {
217196
const { agent, http } = t.nr
197+
nock.disableNetConnect()
198+
t.after(() => {
199+
nock.enableNetConnect()
200+
})
201+
nock('http://example.com').get('/').reply(200)
218202
helper.runInTransaction(agent, (tx) => {
219-
http.get('http://example.com', (res) => {
203+
http.get('http://example.com/', (res) => {
220204
res.resume()
221205
res.on('end', () => {
222206
const [segment] = tx.trace.getChildren(tx.trace.root.id)
@@ -229,9 +213,14 @@ test('external requests', async function (t) {
229213

230214
await t.test('should expose the external segment on the http request', (t, end) => {
231215
const { agent, http } = t.nr
216+
nock.disableNetConnect()
217+
t.after(() => {
218+
nock.enableNetConnect()
219+
})
220+
nock('http://example.com').get('/').reply(200)
232221
helper.runInTransaction(agent, (tx) => {
233222
let reqSegment = null
234-
const req = http.get('http://example.com', (res) => {
223+
const req = http.get('http://example.com/', (res) => {
235224
res.resume()
236225
res.on('end', () => {
237226
const [segment] = tx.trace.getChildren(tx.trace.root.id)

test/integration/otel/metrics.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,9 @@ test('sends metrics', { timeout: 5_000 }, async (t) => {
119119
assert.equal(found.length, 1)
120120
let metric = found[0]
121121
assert.equal(metric.name, 'test-counter')
122-
assert.equal(metric.sum.dataPoints.length, 2)
122+
assert.equal(metric.sum.dataPoints.length, 1)
123123
assert.equal(metric.sum.dataPoints[0].attributes[0].key, 'ready')
124124
assert.deepEqual(metric.sum.dataPoints[0].attributes[0].value, { stringValue: 'no' })
125-
assert.equal(metric.sum.dataPoints[1].attributes[0].key, 'ready')
126-
assert.deepEqual(metric.sum.dataPoints[1].attributes[0].value, { stringValue: 'yes' })
127125

128126
await once(server, 'requestComplete')
129127
payload = requestSchema.decode(
@@ -134,9 +132,11 @@ test('sends metrics', { timeout: 5_000 }, async (t) => {
134132
assert.deepEqual(resource.attributes[0].value, { stringValue: 'guid-123456' })
135133
metric = payload.resourceMetrics[0].scopeMetrics[0].metrics[0]
136134
assert.equal(metric.name, 'test-counter')
137-
assert.equal(metric.sum.dataPoints.length, 1)
138-
assert.equal(metric.sum.dataPoints[0].attributes[0].key, 'otel')
135+
assert.equal(metric.sum.dataPoints.length, 2)
136+
assert.equal(metric.sum.dataPoints[0].attributes[0].key, 'ready')
139137
assert.deepEqual(metric.sum.dataPoints[0].attributes[0].value, { stringValue: 'yes' })
138+
assert.equal(metric.sum.dataPoints[1].attributes[0].key, 'otel')
139+
assert.deepEqual(metric.sum.dataPoints[1].attributes[0].value, { stringValue: 'yes' })
140140

141141
const supportMetrics = agent.metrics._metrics.unscoped
142142
const expectedMetricNames = [

test/lib/fake-cert.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@ const selfCert = require('self-cert')
3232
*/
3333
module.exports = function fakeCert({ commonName = null } = {}) {
3434
const cert = selfCert({
35-
// We set the certificate bits to 1,024 because we don't need 4,096 bit
35+
// We set the certificate bits to 2,048 because we don't need 4,096 bit
3636
// certificates for tests. This speeds up certificate generation time by
3737
// a significant amount, and thus speeds up tests that rely on these
3838
// certificates.
39-
bits: 1_024,
39+
bits: 2_048,
4040
attrs: {
4141
commonName,
4242
stateName: 'Georgia',

test/lib/read-package-version.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2026 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
'use strict'
7+
8+
module.exports = readPackageVersion
9+
10+
const fs = require('node:fs')
11+
const path = require('node:path')
12+
13+
/**
14+
* Used to get version from package.json.
15+
* Some packages define exports and omit `package.json` so `require` or `import`
16+
* will fail when trying to read package.json. This instead just reads file and parses to json
17+
*
18+
* @param {string} dirname value of `__dirname` in caller
19+
* @param {string} pkg name of package
20+
* @returns {string} package version
21+
*/
22+
function readPackageVersion(dirname, pkg) {
23+
const parsedPath = path.join(dirname, 'node_modules', pkg, 'package.json')
24+
const packageFile = fs.readFileSync(parsedPath)
25+
const { version } = JSON.parse(packageFile.toString())
26+
return version
27+
}

test/unit/spans/span-event.test.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ const assert = require('node:assert')
88
const test = require('node:test')
99
const DatastoreShim = require('../../../lib/shim/datastore-shim')
1010
const helper = require('../../lib/agent_helper')
11-
const https = require('https')
11+
const http = require('http')
1212
const SpanEvent = require('../../../lib/spans/span-event')
1313
const DatastoreParameters = require('../../../lib/shim/specs/params/datastore')
1414
const { QuerySpec } = require('../../../lib/shim/specs')
15+
const nock = require('nock')
1516

1617
test('#constructor() should construct an empty span event', () => {
1718
const attrs = {}
@@ -43,6 +44,7 @@ test('#constructor() should construct an empty span event', () => {
4344

4445
test('fromSegment()', async (t) => {
4546
t.beforeEach((ctx) => {
47+
nock.disableNetConnect()
4648
ctx.nr = {}
4749
ctx.nr.agent = helper.instrumentMockedAgent({
4850
distributed_tracing: {
@@ -52,6 +54,7 @@ test('fromSegment()', async (t) => {
5254
})
5355

5456
t.afterEach((ctx) => {
57+
nock.enableNetConnect()
5558
helper.unloadAgent(ctx.nr.agent)
5659
})
5760

@@ -132,8 +135,9 @@ test('fromSegment()', async (t) => {
132135
helper.runInTransaction(agent, (transaction) => {
133136
transaction.sampled = true
134137
transaction.priority = 42
138+
nock('http://example.com').get('/?foo=bar').reply(200)
135139

136-
https.get('https://example.com?foo=bar', (res) => {
140+
http.get('http://example.com?foo=bar', (res) => {
137141
res.resume()
138142
res.on('end', () => {
139143
const tx = agent.tracer.getTransaction()
@@ -159,7 +163,7 @@ test('fromSegment()', async (t) => {
159163
assert.equal(span.intrinsics.name, 'External/example.com/')
160164
assert.equal(span.intrinsics.timestamp, segment.timer.start)
161165

162-
assert.ok(span.intrinsics.duration >= 0.01 && span.intrinsics.duration <= 2)
166+
assert.ok(span.intrinsics.duration > 0 && span.intrinsics.duration <= 2)
163167

164168
// Should have type-specific intrinsics
165169
assert.equal(span.intrinsics.component, 'http')
@@ -169,13 +173,12 @@ test('fromSegment()', async (t) => {
169173
const attributes = span.attributes
170174

171175
// Should have (most) http properties.
172-
assert.equal(attributes['http.url'], 'https://example.com/')
176+
assert.equal(attributes['http.url'], 'http://example.com/')
173177
assert.equal(attributes['server.address'], 'example.com')
174-
assert.equal(attributes['server.port'], 443)
178+
assert.equal(attributes['server.port'], 80)
175179
assert.ok(attributes['http.method'])
176180
assert.ok(attributes['http.request.method'])
177181
assert.equal(attributes['http.statusCode'], 200)
178-
assert.equal(attributes['http.statusText'], 'OK')
179182

180183
// should nullify mapped properties
181184
assert.ok(!attributes.library)
@@ -353,8 +356,9 @@ test('fromSegment()', async (t) => {
353356

354357
await t.test('should handle truncated http spans', (t, end) => {
355358
const { agent } = t.nr
359+
nock('http://www.example.com').get('/path?foo=bar').reply(200)
356360
helper.runInTransaction(agent, (transaction) => {
357-
https.get('https://example.com?foo=bar', (res) => {
361+
http.get('http://www.example.com/path?foo=bar', (res) => {
358362
transaction.end() // prematurely end to truncate
359363

360364
res.resume()

test/versioned/elastic/elasticsearch.test.js

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
'use strict'
77
const test = require('node:test')
88
const assert = require('node:assert')
9-
const path = require('node:path')
109
const helper = require('../../lib/agent_helper')
1110
const params = require('../../lib/params')
1211
const urltils = require('../../../lib/util/urltils')
12+
const readPackageVersion = require('../../lib/read-package-version')
1313
const crypto = require('crypto')
14-
const { readFile } = require('fs/promises')
1514
const semver = require('semver')
1615
const DB_INDEX = `test-${randomString()}`
1716
const DB_INDEX_2 = `test2-${randomString()}`
@@ -53,8 +52,7 @@ test('Elasticsearch instrumentation', async (t) => {
5352
t.beforeEach(async (ctx) => {
5453
// Determine version. ElasticSearch v7 did not export package, so we have to read the file
5554
// instead of requiring it, as we can with 8+.
56-
const pkg = await readFile(path.join(__dirname, '/node_modules/@elastic/elasticsearch/package.json'))
57-
const { version: pkgVersion } = JSON.parse(pkg.toString())
55+
const pkgVersion = readPackageVersion(__dirname, '@elastic/elasticsearch')
5856

5957
const agent = helper.instrumentMockedAgent()
6058

@@ -66,23 +64,26 @@ test('Elasticsearch instrumentation', async (t) => {
6664
// need to capture attributes
6765
agent.config.attributes.enabled = true
6866

69-
const { Client } = require('@elastic/elasticsearch')
70-
const client = new Client({
71-
node: `http://${params.elastic_host}:${params.elastic_port}`
72-
})
67+
try {
68+
const { Client } = require('@elastic/elasticsearch')
69+
const client = new Client({
70+
node: `http://${params.elastic_host}:${params.elastic_port}`
71+
})
7372

74-
ctx.nr = {
75-
agent,
76-
client,
77-
pkgVersion,
78-
METRIC_HOST_NAME,
79-
HOST_ID
73+
ctx.nr = {
74+
agent,
75+
client,
76+
pkgVersion,
77+
METRIC_HOST_NAME,
78+
HOST_ID
79+
}
80+
return Promise.all([
81+
client.indices.create({ index: DB_INDEX }),
82+
client.indices.create({ index: DB_INDEX_2 })
83+
])
84+
} catch (error) {
85+
console.error(error)
8086
}
81-
82-
return Promise.all([
83-
client.indices.create({ index: DB_INDEX }),
84-
client.indices.create({ index: DB_INDEX_2 })
85-
])
8687
})
8788

8889
t.afterEach((ctx) => {

0 commit comments

Comments
 (0)