@contrast/core 1.41.1 → 1.42.0

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/lib/index.d.ts CHANGED
@@ -30,6 +30,7 @@ interface CreateSnapshotOpts {
30
30
  }
31
31
 
32
32
  export interface Core {
33
+ threadTransferData: any;
33
34
  agentName: string;
34
35
  agentVersion: string;
35
36
  reportingInstance: string;
@@ -19,6 +19,7 @@ const fs = require('fs/promises');
19
19
  const os = require('os');
20
20
  const { getCloudProviderMetadata } = require('./cloud-provider-metadata');
21
21
  const { primordials: { StringPrototypeConcat, StringPrototypeMatch } } = require('@contrast/common');
22
+ const getLinuxOsInfo = require('./linux-os-info');
22
23
 
23
24
  const MOUNTINFO_REGEX = /\/docker\/containers\/(.*?)\//;
24
25
  const CGROUP_REGEX = /:\/docker\/([^/]+)$/;
@@ -33,7 +34,7 @@ function isUsingPM2(pkg) {
33
34
  return result;
34
35
  }
35
36
 
36
- async function isDocker() {
37
+ async function getDockerInfo() {
37
38
  try {
38
39
  const result = await fs.readFile('/proc/self/mountinfo', 'utf8');
39
40
  const matches = StringPrototypeMatch.call(result, MOUNTINFO_REGEX);
@@ -60,6 +61,10 @@ async function isDocker() {
60
61
  return { isDocker: false, containerID: null };
61
62
  }
62
63
 
64
+ function getKubernetesInfo() {
65
+ return { isKubernetes: !!process.env.KUBERNETES_SERVICE_HOST };
66
+ }
67
+
63
68
  module.exports = function(core) {
64
69
  const {
65
70
  agentName,
@@ -77,6 +82,16 @@ module.exports = function(core) {
77
82
  const totalmem = os.totalmem();
78
83
  const freemem = os.freemem();
79
84
 
85
+ let linuxOsInfo = undefined;
86
+ const osInfo = await getLinuxOsInfo();
87
+ if (osInfo && osInfo.file) {
88
+ linuxOsInfo = {
89
+ Id: osInfo.id,
90
+ VersionId: osInfo.version_id,
91
+ };
92
+ }
93
+
94
+
80
95
  /** @type {import('@contrast/common').SystemInfo} */
81
96
  const info = {
82
97
  ReportDate: new Date().toISOString(),
@@ -107,16 +122,19 @@ module.exports = function(core) {
107
122
  CPU: {
108
123
  Type: cpus[0].model,
109
124
  Count: cpus.length,
110
- }
125
+ },
126
+ // Id, VersionId if linux, else null
127
+ ...linuxOsInfo,
111
128
  },
112
129
  Host: {
113
- Docker: await isDocker(),
130
+ Docker: await getDockerInfo(),
131
+ Kubernetes: getKubernetesInfo(),
114
132
  PM2: isUsingPM2(appInfo.pkg),
115
133
  Memory: {
116
134
  Total: StringPrototypeConcat.call((totalmem / 1e6).toFixed(0), ' MB'),
117
135
  Free: StringPrototypeConcat.call((freemem / 1e6).toFixed(0), ' MB'),
118
136
  Used: StringPrototypeConcat.call(((totalmem - freemem) / 1e6).toFixed(0), ' MB'),
119
- }
137
+ },
120
138
  },
121
139
  Application: appInfo.pkg,
122
140
  Cloud: {
@@ -0,0 +1,134 @@
1
+ /* eslint-disable header/header */
2
+ /**
3
+ * MIT License
4
+ *
5
+ * Copyright (c) 2018 Samuel Carreira
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in all
15
+ * copies or substantial portions of the Software.
16
+ *
17
+ * This code is modified from https://github.com/bmacnaughton/linux-os-info, a
18
+ * fork of https://github.com/samuelcarreira/linux-release-info.
19
+ */
20
+
21
+ /*
22
+ * Copyright: 2024 Contrast Security, Inc
23
+ * Contact: support@contrastsecurity.com
24
+ * License: Commercial
25
+
26
+ * NOTICE: This Software and the patented inventions embodied within may only be
27
+ * used as part of Contrast Security’s commercial offerings. Even though it is
28
+ * made available through public repositories, use of this Software is subject to
29
+ * the applicable End User Licensing Agreement found at
30
+ * https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
31
+ * between Contrast Security and the End User. The Software may not be reverse
32
+ * engineered, modified, repackaged, sold, redistributed or otherwise used in a
33
+ * way not consistent with the End User License Agreement.
34
+ */
35
+ 'use strict';
36
+
37
+ const fsp = require('node:fs/promises');
38
+ const os = require('node:os');
39
+
40
+ //
41
+ // the key pieces of information from os-release are:
42
+ // - id: 'ubuntu' or 'alpine' or 'arch' etc.
43
+ // - version_id: ubuntu '22.04', alpine '3.20.3', arch '20241110.0.278197'
44
+ // - version: ubuntu '24.04.1 LTS (Noble Numbat)', alpine undefined, arch undefined
45
+ // NOTE: only ubuntu omits the patch field of the version in version ID. ubuntu
46
+ // includes a version field that has that information, but that's not present in
47
+ // most other distributions.
48
+ // the alpine-release file only contains the version string
49
+ // so fill in the basics based on that.
50
+ function addEtcAlpineReleaseToOutputData(data, outputData) {
51
+ if (data[data.length - 1] === '\n') {
52
+ data = data.slice(0, -1);
53
+ }
54
+ outputData.name = 'Alpine';
55
+ outputData.id = 'alpine';
56
+ outputData.version = data;
57
+ outputData.version_id = data;
58
+ }
59
+
60
+ const defaultList = [
61
+ { path: '/etc/os-release', parser: addOsReleaseToOutputData },
62
+ { path: '/usr/lib/os-release', parser: addOsReleaseToOutputData },
63
+ { path: '/etc/alpine-release', parser: addEtcAlpineReleaseToOutputData }
64
+ ];
65
+
66
+ /**
67
+ * Get OS release info with information from '/etc/os-release', '/usr/lib/os-release',
68
+ * or '/etc/alpine-release'. The information in that file is distribution-dependent.
69
+ *
70
+ * @returns Promise<Object> - where object is null if not Linux, or an object with
71
+ * the a file property and the key-value pairs from the file. Any quotes around the
72
+ * values are removed.
73
+ *
74
+ * the file property in the info object will be filled in with one of:
75
+ * - the file path (above) used
76
+ * - undefined if no file was found/could be read
77
+ */
78
+ async function linuxOsInfo(opts = {}) {
79
+ // allow searching for other files and parsers.
80
+ const list = Array.isArray(opts.list) ? opts.list : defaultList;
81
+
82
+ if (os.type() !== 'Linux') {
83
+ return null;
84
+ }
85
+
86
+ let i = 0;
87
+ while (i < list.length) {
88
+ try {
89
+ const file = list[i].path;
90
+ const data = await fsp.readFile(file, 'utf8');
91
+ const outputData = { file };
92
+ list[i].parser(data, outputData);
93
+
94
+ return outputData;
95
+
96
+ } catch (e) {
97
+ i += 1;
98
+ continue;
99
+ }
100
+ }
101
+
102
+ // no file could be found and read
103
+ return { file: undefined };
104
+ }
105
+
106
+
107
+ function addOsReleaseToOutputData(data, outputData) {
108
+ const lines = data.split('\n');
109
+
110
+ for (let i = 0; i < lines.length; i++) {
111
+ const line = lines[i];
112
+ const index = line.indexOf('=');
113
+ // only look at lines with a key of length 1 or greater
114
+ if (index < 1) {
115
+ continue;
116
+ }
117
+
118
+ // lowercase key. all the keys i've seen have been UPPERCASE but it's safer
119
+ // to normalize them.
120
+ const key = line.slice(0, index).toLowerCase();
121
+ // remove quotes around value. this handles a quoted value with embedded
122
+ // quotes even though i've never seen that in the wild.
123
+ let value = line.slice(index + 1).trim();
124
+ if (value[0] === '"' && value[value.length - 1] === '"') {
125
+ value = value.slice(1, -1);
126
+ value = value.replace(/\\"/g, '"');
127
+ }
128
+
129
+ outputData[key] = value;
130
+ }
131
+ }
132
+
133
+ module.exports = linuxOsInfo;
134
+
@@ -0,0 +1,121 @@
1
+ 'use strict';
2
+
3
+ const fsp = require('node:fs/promises');
4
+ const os = require('node:os');
5
+ const { expect } = require('chai');
6
+ const sinon = require('sinon');
7
+
8
+ /**
9
+ # cat /etc/os-release
10
+ NAME="Alpine Linux"
11
+ ID=alpine
12
+ VERSION_ID=3.20.3
13
+ PRETTY_NAME="Alpine Linux v3.20"
14
+ HOME_URL="https://alpinelinux.org/"
15
+ BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"
16
+ # cat /etc/alpine-release
17
+ 3.20.3
18
+
19
+ # cat /etc/os-release
20
+ PRETTY_NAME="Ubuntu 22.04.3 LTS"
21
+ NAME="Ubuntu"
22
+ VERSION_ID="22.04"
23
+ VERSION="22.04.3 LTS (Jammy Jellyfish)"
24
+ VERSION_CODENAME=jammy
25
+ ID=ubuntu
26
+ ID_LIKE=debian
27
+ HOME_URL="https://www.ubuntu.com/"
28
+ SUPPORT_URL="https://help.ubuntu.com/"
29
+ BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
30
+ PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
31
+ UBUNTU_CODENAME=jammy
32
+ */
33
+
34
+ const linuxOsInfo = require('./linux-os-info');
35
+
36
+ describe('linux-os-info', function() {
37
+ let fsStub;
38
+ let osStub;
39
+
40
+ beforeEach(function() {
41
+ fsStub = sinon.stub(fsp, 'readFile');
42
+ // fake up so test passes on windows.
43
+ osStub = sinon.stub(os, 'type').returns('Linux');
44
+ });
45
+ afterEach(function() {
46
+ fsStub.restore();
47
+ osStub.restore();
48
+ });
49
+
50
+ it('should return info for Ubuntu', async function() {
51
+ fsStub.withArgs('/etc/os-release').resolves('PRETTY_NAME="Ubuntu 22.04.3 LTS"\nNAME="Ubuntu"\nVERSION_ID="22.04"\nVERSION="22.04.3 LTS (Jammy Jellyfish)"\nVERSION_CODENAME=jammy\nID=ubuntu\nID_LIKE=debian\nHOME_URL="https://www.ubuntu.com/"\nSUPPORT_URL="https://help.ubuntu.com/"\nBUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"\nPRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"\nUBUNTU_CODENAME=jammy\n');
52
+ fsStub.withArgs('/etc/alpine-release').rejects(new Error('ENOENT'));
53
+
54
+ const result = await linuxOsInfo();
55
+ expect(result).to.deep.equal({
56
+ file: '/etc/os-release',
57
+ pretty_name: 'Ubuntu 22.04.3 LTS',
58
+ name: 'Ubuntu',
59
+ id: 'ubuntu',
60
+ version_id: '22.04',
61
+ version: '22.04.3 LTS (Jammy Jellyfish)',
62
+ version_codename: 'jammy',
63
+ id_like: 'debian',
64
+ home_url: 'https://www.ubuntu.com/',
65
+ support_url: 'https://help.ubuntu.com/',
66
+ bug_report_url: 'https://bugs.launchpad.net/ubuntu/',
67
+ privacy_policy_url: 'https://www.ubuntu.com/legal/terms-and-policies/privacy-policy',
68
+ ubuntu_codename: 'jammy',
69
+ });
70
+ });
71
+
72
+ it('should return info for Alpine without an os-release file', async function() {
73
+ fsStub.withArgs('/etc/os-release').rejects(new Error('ENOENT'));
74
+ fsStub.withArgs('/usr/lib/os-release').rejects(new Error('ENOENT'));
75
+ fsStub.withArgs('/etc/alpine-release').resolves('3.20.3\n');
76
+
77
+ const result = await linuxOsInfo();
78
+ expect(result).to.deep.equal({
79
+ file: '/etc/alpine-release',
80
+ name: 'Alpine',
81
+ id: 'alpine',
82
+ version: '3.20.3',
83
+ version_id: '3.20.3',
84
+ });
85
+ });
86
+
87
+ it('should return info for Alpine with an os-release file', async function() {
88
+ fsStub.withArgs('/etc/os-release').resolves('NAME="Alpine Linux"\nID=alpine\nVERSION_ID=3.20.3\nPRETTY_NAME="Alpine Linux v3.20"\nHOME_URL="https://alpinelinux.org/"\nBUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues"\n');
89
+ fsStub.withArgs('/usr/lib/os-release').rejects(new Error('ENOENT'));
90
+ fsStub.withArgs('/etc/alpine-release').resolves('3.20.3\n');
91
+
92
+ const result = await linuxOsInfo();
93
+ expect(result).to.deep.equal({
94
+ file: '/etc/os-release',
95
+ name: 'Alpine Linux',
96
+ id: 'alpine',
97
+ version_id: '3.20.3',
98
+ pretty_name: 'Alpine Linux v3.20',
99
+ home_url: 'https://alpinelinux.org/',
100
+ bug_report_url: 'https://gitlab.alpinelinux.org/alpine/aports/-/issues',
101
+ });
102
+ });
103
+
104
+ it('should return info when no os-release or alpine-release files are found', async function() {
105
+ fsStub.withArgs('/etc/os-release').rejects(new Error('ENOENT'));
106
+ fsStub.withArgs('/usr/lib/os-release').rejects(new Error('ENOENT'));
107
+ fsStub.withArgs('/etc/alpine-release').rejects(new Error('ENOENT'));
108
+
109
+ const result = await linuxOsInfo();
110
+ // no file found
111
+ expect(result).deep.equal({ file: undefined });
112
+ });
113
+
114
+ it('should return null for non-linux systems', async function() {
115
+ osStub.returns('Windows_NT');
116
+
117
+ const result = await linuxOsInfo();
118
+
119
+ expect(result).to.be.null;
120
+ });
121
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/core",
3
- "version": "1.41.1",
3
+ "version": "1.42.0",
4
4
  "description": "Preconfigured Contrast agent core services and models",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -16,12 +16,12 @@
16
16
  "test": "../scripts/test.sh"
17
17
  },
18
18
  "dependencies": {
19
- "@contrast/common": "1.26.0",
20
- "@contrast/config": "1.36.0",
19
+ "@contrast/common": "1.27.0",
20
+ "@contrast/config": "1.37.0",
21
21
  "@contrast/find-package-json": "^1.1.0",
22
22
  "@contrast/fn-inspect": "^4.3.0",
23
- "@contrast/logger": "1.14.0",
24
- "@contrast/patcher": "1.13.0",
23
+ "@contrast/logger": "1.15.0",
24
+ "@contrast/patcher": "1.14.0",
25
25
  "axios": "^1.7.4",
26
26
  "semver": "^7.6.0"
27
27
  }