newrelic 10.5.0 → 10.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/NEWS.md CHANGED
@@ -1,3 +1,21 @@
1
+ ### v10.6.1 (2023-08-01)
2
+
3
+ #### Security Improvements
4
+
5
+ * updated ESM loader to track instrumentation by url in a map instead of in url to avoid remote code execution. ([#1741](https://github.com/newrelic/node-newrelic/pull/1741)) ([c8dc779](https://github.com/newrelic/node-newrelic/commit/c8dc779c7799b234290b6f7eb1d0a4e07d692ef9))
6
+
7
+ ### v10.6.0 (2023-07-26)
8
+
9
+ #### Miscellaneous Chores
10
+
11
+ * **deps:** Updated @newrelic/security-agent to v0.2.0 ([#1737](https://github.com/newrelic/node-newrelic/pull/1737)) ([9bf2a01](https://github.com/newrelic/node-newrelic/commit/9bf2a011aed846a024d6445f557eb8eb7d2e5efe))
12
+ * restored engines to >=14 until we actually drop support for Node 14 ([#1738](https://github.com/newrelic/node-newrelic/pull/1738)) ([8d66123](https://github.com/newrelic/node-newrelic/commit/8d66123105d25a3f949f0f5c2db53ecb7e9f2df0))
13
+
14
+ #### Continuous Integration
15
+
16
+ * Add Node.js 20.x to CI and remove 14.x ([#1603](https://github.com/newrelic/node-newrelic/pull/1603)) ([c4b008c](https://github.com/newrelic/node-newrelic/commit/c4b008c98c758ba3e669768a840e40dd8fe3e681))
17
+ * update support statement ([#1733](https://github.com/newrelic/node-newrelic/pull/1733)) ([3013da9](https://github.com/newrelic/node-newrelic/commit/3013da977ed6dae70bef81b1f1cf0dbf4acbb37b))
18
+
1
19
  ### v10.5.0 (2023-07-20)
2
20
 
3
21
  #### Features
@@ -953,7 +953,7 @@ Apache License
953
953
 
954
954
  ### @newrelic/security-agent
955
955
 
956
- This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v0.1.3](https://github.com/newrelic/csec-node-agent/tree/v0.1.3)), distributed under the [New Relic Pre-Release License](https://github.com/newrelic/csec-node-agent/blob/v0.1.3/LICENSE):
956
+ This product includes source derived from [@newrelic/security-agent](https://github.com/newrelic/csec-node-agent) ([v0.2.0](https://github.com/newrelic/csec-node-agent/tree/v0.2.0)), distributed under the [New Relic Pre-Release License](https://github.com/newrelic/csec-node-agent/blob/v0.2.0/LICENSE):
957
957
 
958
958
  ```
959
959
  ## New Relic Pre-Release Software Notice
package/esm-loader.mjs CHANGED
@@ -89,14 +89,10 @@ export async function resolve(specifier, context, nextResolve) {
89
89
  })
90
90
 
91
91
  // Keep track of what we've registered so we don't double register (see: https://github.com/newrelic/node-newrelic/issues/1646)
92
- registeredSpecifiers.set(url, specifier)
92
+ registeredSpecifiers.set(url, { specifier })
93
93
  } else if (format === 'module') {
94
- registeredSpecifiers.set(url, specifier)
95
- const modifiedUrl = new URL(url)
96
- // add a query param to the resolved url so the load hook below knows
97
- // to rewrite and wrap the source code
98
- modifiedUrl.searchParams.set('hasNrInstrumentation', 'true')
99
- resolvedModule.url = modifiedUrl.href
94
+ addNrInstrumentation(resolvedModule)
95
+ registeredSpecifiers.set(url, { specifier, hasNrInstrumentation: true })
100
96
  } else {
101
97
  logger.debug(`${specifier} is not a CommonJS nor ESM package, skipping for now.`)
102
98
  }
@@ -105,6 +101,38 @@ export async function resolve(specifier, context, nextResolve) {
105
101
  return resolvedModule
106
102
  }
107
103
 
104
+ /**
105
+ * This is purely done so that we can import the incoming specifier
106
+ * in the load hook. It used to be used to determine if instrumentation existed
107
+ * for specifier but we found a RCE with that solution.
108
+ *
109
+ * @param {object} resolvedModule the result of call resolve on a specifier
110
+ */
111
+ function addNrInstrumentation(resolvedModule) {
112
+ const modifiedUrl = new URL(resolvedModule.url)
113
+ modifiedUrl.searchParams.set('hasNrInstrumentation', 'true')
114
+ resolvedModule.url = modifiedUrl.href
115
+ }
116
+
117
+ /**
118
+ * Extracts the href without query params
119
+ *
120
+ * @param {string} url url of specifier
121
+ * @returns {string} new url without hasNrInstrumentation query param
122
+ */
123
+ function removeNrInstrumentation(url) {
124
+ let parsedUrl
125
+
126
+ try {
127
+ parsedUrl = new URL(url)
128
+ url = parsedUrl.href.split('?')[0]
129
+ } catch (err) {
130
+ logger.error('Unable to parse url: %s, msg: %s', url, err.message)
131
+ }
132
+
133
+ return url
134
+ }
135
+
108
136
  /**
109
137
  * Hook chain responsible for determining how a URL should be interpreted, retrieved, and parsed.
110
138
  *
@@ -123,30 +151,17 @@ export async function load(url, context, nextLoad) {
123
151
  return nextLoad(url, context, nextLoad)
124
152
  }
125
153
 
126
- let parsedUrl
127
-
128
- try {
129
- parsedUrl = new URL(url)
130
- } catch (err) {
131
- logger.error('Unable to parse url: %s, msg: %s', url, err.message)
132
- return nextLoad(url, context, nextLoad)
133
- }
154
+ url = removeNrInstrumentation(url)
134
155
 
135
- const hasNrInstrumentation = parsedUrl.searchParams.get('hasNrInstrumentation')
156
+ const pkg = registeredSpecifiers.get(url)
136
157
 
137
- if (!hasNrInstrumentation) {
158
+ if (!pkg?.hasNrInstrumentation) {
138
159
  return nextLoad(url, context, nextLoad)
139
160
  }
140
161
 
141
- /**
142
- * undo the work we did in the resolve hook above
143
- * so we can properly rewrite source and not get in an infinite loop
144
- */
145
- parsedUrl.searchParams.delete('hasNrInstrumentation')
162
+ const { specifier } = pkg
146
163
 
147
- const originalUrl = parsedUrl.href
148
- const specifier = registeredSpecifiers.get(originalUrl)
149
- const rewrittenSource = await wrapEsmSource(originalUrl, specifier)
164
+ const rewrittenSource = await wrapEsmSource(url, specifier)
150
165
  logger.debug(`Registered module instrumentation for ${specifier}.`)
151
166
 
152
167
  return {
package/index.js CHANGED
@@ -53,8 +53,8 @@ function initialize() {
53
53
  throw new Error(message)
54
54
  }
55
55
 
56
- // TODO: Update this check when Node v20 support is added
57
- if (psemver.satisfies('>=19.0.0')) {
56
+ // TODO: Update this check when Node v22 support is added
57
+ if (psemver.satisfies('>=21.0.0')) {
58
58
  logger.warn(
59
59
  'New Relic for Node.js %s has not been tested on Node.js %s. Please ' +
60
60
  'update the agent or downgrade your version of Node.js',
@@ -12,6 +12,7 @@ const logger = require('./logger').child({ component: 'environment' })
12
12
  const stringify = require('json-stringify-safe')
13
13
  const asyncEachLimit = require('./util/async-each-limit')
14
14
  const DISPATCHER_VERSION = 'Dispatcher Version'
15
+ const semver = require('semver')
15
16
 
16
17
  // As of 1.7.0 you can no longer dynamically link v8
17
18
  // https://github.com/nodejs/io.js/commit/d726a177ed
@@ -260,7 +261,7 @@ function flattenVersions(packages) {
260
261
  try {
261
262
  return stringify(pair)
262
263
  } catch (err) {
263
- logger.debug(err, 'Unabled to stringify package version')
264
+ logger.debug(err, 'Unable to stringify package version')
264
265
  return '<unknown>'
265
266
  }
266
267
  })
@@ -291,6 +292,19 @@ function remapConfigSettings() {
291
292
  addSetting(remapping[key], value)
292
293
  }
293
294
  })
295
+
296
+ maybeAddMissingProcessVars()
297
+ }
298
+ }
299
+
300
+ /**
301
+ * As of Node 19 DTrace and ETW are no longer bundled
302
+ * see: https://nodejs.org/en/blog/announcements/v19-release-announce#dtrace/systemtap/etw-support
303
+ */
304
+ function maybeAddMissingProcessVars() {
305
+ if (semver.gte(process.version, '19.0.0')) {
306
+ addSetting(remapping.node_use_dtrace, 'no')
307
+ addSetting(remapping.node_use_etw, 'no')
294
308
  }
295
309
  }
296
310
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "newrelic",
3
- "version": "10.5.0",
3
+ "version": "10.6.1",
4
4
  "author": "New Relic Node.js agent team <nodejs@newrelic.com>",
5
5
  "license": "Apache-2.0",
6
6
  "contributors": [
@@ -168,13 +168,11 @@
168
168
  "versioned-tests": "./bin/run-versioned-tests.sh",
169
169
  "update-changelog-version": "node ./bin/update-changelog-version",
170
170
  "checkout-external-versioned": "node ./test/versioned-external/checkout-external-tests.js",
171
- "versioned": "npm run versioned:npm7",
172
- "versioned:major": "VERSIONED_MODE=--major npm run versioned:npm7",
173
- "versioned:npm6": "npm run checkout-external-versioned && npm run prepare-test && time ./bin/run-versioned-tests.sh",
174
- "versioned:npm7": "npm run checkout-external-versioned && npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
175
- "versioned:async-local": "NEW_RELIC_FEATURE_FLAG_ASYNC_LOCAL_CONTEXT=1 npm run versioned:npm7",
171
+ "versioned:major": "VERSIONED_MODE=--major npm run versioned",
172
+ "versioned": "npm run checkout-external-versioned && npm run prepare-test && NPM7=1 time ./bin/run-versioned-tests.sh",
173
+ "versioned:async-local": "NEW_RELIC_FEATURE_FLAG_ASYNC_LOCAL_CONTEXT=1 npm run versioned",
176
174
  "versioned:async-local:major": "NEW_RELIC_FEATURE_FLAG_ASYNC_LOCAL_CONTEXT=1 npm run versioned:major",
177
- "versioned:security": "NEW_RELIC_SECURITY_AGENT_ENABLED=true npm run versioned:npm7",
175
+ "versioned:security": "NEW_RELIC_SECURITY_AGENT_ENABLED=true npm run versioned",
178
176
  "versioned:security:major": "NEW_RELIC_SECURITY_AGENT_ENABLED=true npm run versioned:major",
179
177
  "prepare": "husky install"
180
178
  },
@@ -187,7 +185,7 @@
187
185
  "@mrleebo/prisma-ast": "^0.5.2",
188
186
  "@newrelic/aws-sdk": "^6.0.0",
189
187
  "@newrelic/koa": "^7.1.1",
190
- "@newrelic/security-agent": "0.1.3",
188
+ "@newrelic/security-agent": "0.2.0",
191
189
  "@newrelic/superagent": "^6.0.0",
192
190
  "@tyriar/fibonacci-heap": "^2.0.7",
193
191
  "concat-stream": "^2.0.0",