@contrast/telemetry 1.20.2 → 1.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/index.js +84 -96
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -30,17 +30,40 @@ const TELEMETRY_URL = 'https://telemetry.nodejs.contrastsecurity.com/';
30
30
  const TELEMETRY_VERSION = 'v0'; // TODO: set this to v1 when format is established.
31
31
 
32
32
  /**
33
- * @param {{
34
- * agentName: string,
35
- * agentVersion: string,
36
- * appInfo: import('@contrast/common').AppInfo,
33
+ * @param {import("@contrast/core").Core} core
34
+ */
35
+ async function getIds(core) {
36
+ const { Host: { Docker: { containerID: containerId } } } = await core.getSystemInfo();
37
+ let applicationId, instance;
38
+
39
+ try {
40
+ const hash = createHash('sha256').update(getMac());
41
+ if (containerId) hash.update(containerId);
42
+ instance = hash.copy().digest('hex');
43
+ applicationId = hash.update(core.appInfo.name).digest('hex');
44
+ } catch {
45
+ // If getMac fails we fall back to generating a UUID. "Unstable"
46
+ // identifiers such as these are prefixed with an underscore.
47
+ let id = randomUUID();
48
+ if (containerId) {
49
+ id += `_${containerId}`;
50
+ }
51
+ applicationId = instance = `_${id}`;
52
+ }
53
+
54
+ return { applicationId, containerId, instance };
55
+ }
56
+
57
+ /**
58
+ * @param {import('@contrast/core').Core & {
37
59
  * config: import('@contrast/config').Config,
38
60
  * logger: import('@contrast/logger').Logger,
39
61
  * messages: import('@contrast/common').Messages,
62
+ * telemetry?: import('@contrast/common').Installable
40
63
  * }} core
41
64
  * @returns {import('@contrast/common').Installable}
42
65
  */
43
- module.exports = function(core) {
66
+ module.exports = function (core) {
44
67
  const {
45
68
  agentName,
46
69
  agentVersion,
@@ -50,58 +73,48 @@ module.exports = function(core) {
50
73
  messages,
51
74
  } = core;
52
75
 
53
- const { instanceId, applicationId } = getIds(appInfo);
54
-
55
- const telemetry = core.telemetry = {
56
- /** @type {axios.AxiosInstance} */
57
- client: null,
58
- instanceId,
59
- tags: {
60
- applicationId,
61
- isCustomerEnv: true,
62
- osArch: appInfo.os.architecture,
63
- osPlatform: appInfo.os.platform,
64
- osRelease: appInfo.os.release,
65
- isContainer: !!appInfo.containerId, // TODO: this is undefined
66
- agent: agentName,
67
- agentVersion,
68
- isAssess: config.getEffectiveValue('assess.enable'),
69
- isProtect: config.getEffectiveValue('protect.enable'),
70
- nodeVersion: appInfo.nodeVersion,
71
- telemetryVersion: TELEMETRY_VERSION,
72
- },
73
- isEnabled() {
74
- return (
75
- process.env.CONTRAST_AGENT_TELEMETRY_OPTOUT !== 'true' &&
76
- process.env.CONTRAST_AGENT_TELEMETRY_OPTOUT !== '1'
77
- );
78
- },
79
- install() {
80
- if (!telemetry.isEnabled()) {
76
+ return core.telemetry = {
77
+ async install() {
78
+ if (
79
+ process.env.CONTRAST_AGENT_TELEMETRY_OPTOUT === 'true' ||
80
+ process.env.CONTRAST_AGENT_TELEMETRY_OPTOUT === '1'
81
+ ) {
81
82
  logger.info('Telemetry opt-out in effect. All usage data collection is suppressed.');
82
83
  return;
83
84
  }
84
85
 
85
86
  logger.info(DISCLAIMER);
86
87
 
87
- telemetry.client = axios.create({
88
+ const { instance, containerId, applicationId } = await getIds(core);
89
+ const tags = {
90
+ applicationId,
91
+ isCustomerEnv: true,
92
+ osArch: appInfo.os.architecture,
93
+ osPlatform: appInfo.os.platform,
94
+ osRelease: appInfo.os.release,
95
+ isContainer: !!containerId,
96
+ agent: agentName,
97
+ agentVersion,
98
+ isAssess: config.getEffectiveValue('assess.enable'),
99
+ isProtect: config.getEffectiveValue('protect.enable'),
100
+ nodeVersion: appInfo.nodeVersion,
101
+ telemetryVersion: TELEMETRY_VERSION,
102
+ };
103
+
104
+ const client = axios.create({
88
105
  baseURL: new URL('/api/v1/telemetry/metrics/node', TELEMETRY_URL).href,
89
106
  headers: {
90
107
  'User-Agent': `${agentName}/${agentVersion}`,
91
108
  }
92
109
  });
93
110
 
94
- telemetry.startup();
95
- },
96
-
97
- async startup() {
98
111
  try {
99
112
  const { external, heapTotal, heapUsed, rss } = process.memoryUsage();
100
- await telemetry.client.post('/startup', [
113
+ await client.post('/startup', [
101
114
  {
102
115
  timestamp: new Date().toISOString(),
103
- instance: telemetry.instanceId,
104
- tags: telemetry.tags,
116
+ instance,
117
+ tags,
105
118
  fields: {
106
119
  numCpus: os.cpus().length,
107
120
  memTotal: os.totalmem(),
@@ -116,66 +129,41 @@ module.exports = function(core) {
116
129
  } catch (err) {
117
130
  logger.error({ err }, 'error occurred while reporting telemetry: /startup');
118
131
  }
119
- },
120
- };
121
132
 
122
- messages.on(Event.SERVER_SETTINGS_UPDATE, () => {
123
- telemetry.tags.isAssess = config.getEffectiveValue('assess.enable');
124
- telemetry.tags.isProtect = config.getEffectiveValue('protect.enable');
125
- });
126
-
127
- let unsupportedLibs = [];
128
- messages.on(Event.UNSUPPORTED_LIBRARY, (metadata) => {
129
- unsupportedLibs.push({
130
- timestamp: new Date().toISOString(),
131
- instance: telemetry.instanceId,
132
- tags: {
133
- ...telemetry.tags,
134
- name: metadata.name,
135
- version: metadata.version,
136
- },
137
- fields: {
138
- supported: 0
139
- }
140
- });
141
- });
133
+ messages.on(Event.SERVER_SETTINGS_UPDATE, () => {
134
+ tags.isAssess = config.getEffectiveValue('assess.enable');
135
+ tags.isProtect = config.getEffectiveValue('protect.enable');
136
+ });
142
137
 
143
- setInterval(async () => {
144
- if (unsupportedLibs.length < 1) return;
138
+ let unsupportedLibs = [];
139
+ messages.on(Event.UNSUPPORTED_LIBRARY, (metadata) => {
140
+ unsupportedLibs.push({
141
+ timestamp: new Date().toISOString(),
142
+ instance,
143
+ tags: {
144
+ ...tags,
145
+ name: metadata.name,
146
+ version: metadata.version,
147
+ },
148
+ fields: {
149
+ supported: 0
150
+ }
151
+ });
152
+ });
145
153
 
146
- try {
147
- await telemetry.client.post('/dependencies', unsupportedLibs);
148
- } catch (err) {
149
- logger.error({ err }, 'error occurred while reporting telemetry: /dependencies');
150
- }
154
+ setInterval(async () => {
155
+ if (unsupportedLibs.length < 1) return;
151
156
 
152
- unsupportedLibs = [];
153
- }, 20_000).unref();
157
+ try {
158
+ await client.post('/dependencies', unsupportedLibs);
159
+ } catch (err) {
160
+ logger.error({ err }, 'error occurred while reporting telemetry: /dependencies');
161
+ }
154
162
 
155
- return core.telemetry;
163
+ unsupportedLibs = [];
164
+ }, 20_000).unref();
165
+ },
166
+ };
156
167
  };
157
168
 
158
- module.exports.getIds = getIds;
159
-
160
- function getIds (appInfo) {
161
- let applicationId, instanceId;
162
-
163
- try {
164
- const hash = createHash('sha256').update(getMac());
165
- if (appInfo.containerId) hash.update(appInfo.containerId);
166
- instanceId = hash.copy().digest('hex');
167
- applicationId = hash.update(appInfo.name).digest('hex');
168
- } catch {
169
- // If getMac fails we fall back to generating a UUID. "Unstable"
170
- // identifiers such as these are prefixed with an underscore.
171
- let id = randomUUID();
172
- if (appInfo.containerId) {
173
- id += `_${appInfo.containerId}`;
174
- }
175
- applicationId = instanceId = `_${id}`;
176
- }
177
-
178
- return { applicationId, instanceId };
179
- }
180
-
181
169
  module.exports.DISCLAIMER = DISCLAIMER;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/telemetry",
3
- "version": "1.20.2",
3
+ "version": "1.20.3",
4
4
  "description": "Telemetry reporting for the Contrast Node.js agent.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",