@contrast/cli 1.53.0 → 1.55.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/bin/config-diagnostics.mjs +29 -0
- package/bin/rewrite.mjs +29 -0
- package/bin/system-diagnostics.mjs +28 -0
- package/lib/{config-diagnostics.js → config-diagnostics.mjs} +25 -30
- package/lib/{rewrite.js → rewrite.mjs} +75 -64
- package/lib/system-diagnostics.mjs +51 -0
- package/package.json +16 -14
- package/lib/system-diagnostics.js +0 -57
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright: 2025 Contrast Security, Inc
|
|
4
|
+
* Contact: support@contrastsecurity.com
|
|
5
|
+
* License: Commercial
|
|
6
|
+
|
|
7
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
8
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
9
|
+
* made available through public repositories, use of this Software is subject to
|
|
10
|
+
* the applicable End User Licensing Agreement found at
|
|
11
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
12
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
13
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
14
|
+
* way not consistent with the End User License Agreement.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { program } from 'commander';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { action, version } from '../lib/config-diagnostics.mjs';
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.name('config-diagnostics')
|
|
23
|
+
.version(version)
|
|
24
|
+
.description('The config-diagnostics utility returns the current effective node agent configuration.')
|
|
25
|
+
.argument('<entrypoint>', 'The entrypoint JavaScript or ESM file for the application')
|
|
26
|
+
.option('-q, --quiet', 'suppress logging to stdout')
|
|
27
|
+
.option('-o, --output <string>', 'output directory for generated JSON file', path.join(process.cwd(), 'contrast_effective_config.json'))
|
|
28
|
+
.action(action)
|
|
29
|
+
.parse(process.argv);
|
package/bin/rewrite.mjs
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node --experimental-import-meta-resolve
|
|
2
|
+
/*
|
|
3
|
+
* Copyright: 2025 Contrast Security, Inc
|
|
4
|
+
* Contact: support@contrastsecurity.com
|
|
5
|
+
* License: Commercial
|
|
6
|
+
|
|
7
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
8
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
9
|
+
* made available through public repositories, use of this Software is subject to
|
|
10
|
+
* the applicable End User Licensing Agreement found at
|
|
11
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
12
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
13
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
14
|
+
* way not consistent with the End User License Agreement.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { program } from 'commander';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { action, version } from '../lib/rewrite.mjs';
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.name('npx -p @contrast/cli rewrite')
|
|
23
|
+
.version(version)
|
|
24
|
+
.description('Rewrites application files, caching them so that rewriting does not need to occur when the application runs.')
|
|
25
|
+
.argument('<entrypoint>', 'The entrypoint for the application', entrypoint => path.resolve(entrypoint))
|
|
26
|
+
.option('-a, --assess', 'rewrite in assess mode')
|
|
27
|
+
.option('-p, --protect', 'rewrite in protect mode')
|
|
28
|
+
.action(action)
|
|
29
|
+
.parse(process.argv);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright: 2025 Contrast Security, Inc
|
|
4
|
+
* Contact: support@contrastsecurity.com
|
|
5
|
+
* License: Commercial
|
|
6
|
+
|
|
7
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
8
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
9
|
+
* made available through public repositories, use of this Software is subject to
|
|
10
|
+
* the applicable End User Licensing Agreement found at
|
|
11
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
12
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
13
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
14
|
+
* way not consistent with the End User License Agreement.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { program } from 'commander';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { action, version } from '../lib/system-diagnostics.mjs';
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.name('system-diagnostics')
|
|
23
|
+
.version(version)
|
|
24
|
+
.description('The system-diagnostics utility returns the system info for the server/container the agent is running on.')
|
|
25
|
+
.option('-q, --quiet', 'suppress logging to stdout')
|
|
26
|
+
.option('-o, --output <string>', 'output directory for generated JSON file', path.join(process.cwd(), 'contrast_system_info.json'))
|
|
27
|
+
.action(action)
|
|
28
|
+
.parse(process.argv);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/*
|
|
3
2
|
* Copyright: 2025 Contrast Security, Inc
|
|
4
3
|
* Contact: support@contrastsecurity.com
|
|
@@ -13,36 +12,34 @@
|
|
|
13
12
|
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
14
13
|
* way not consistent with the End User License Agreement.
|
|
15
14
|
*/
|
|
16
|
-
'use strict';
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
import { primordials } from '@contrast/common';
|
|
17
|
+
import configInit from '@contrast/config';
|
|
18
|
+
import appInfoInit from '@contrast/core/lib/app-info.js';
|
|
19
|
+
import coreMessagesInit from '@contrast/core/lib/messages.js';
|
|
20
|
+
import reporterInit from '@contrast/reporter';
|
|
21
|
+
import scopesInit from '@contrast/scopes';
|
|
22
|
+
import loggerInit from '@contrast/logger';
|
|
23
|
+
import Perf from '@contrast/perf';
|
|
24
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { EOL } from 'node:os';
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
.option('-q, --quiet', 'suppress logging to stdout')
|
|
31
|
-
.option('-o, --output <string>', 'output directory for generated JSON file', path.join(process.cwd(), 'contrast_effective_config.json'))
|
|
32
|
-
.action(action)
|
|
33
|
-
.parse();
|
|
34
|
-
}
|
|
27
|
+
const { JSONStringify } = primordials;
|
|
28
|
+
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url)));
|
|
29
|
+
const { name: agentName, version: agentVersion } = packageJson;
|
|
30
|
+
|
|
31
|
+
export { agentVersion as version };
|
|
35
32
|
|
|
36
|
-
async function action(entrypoint, options) {
|
|
37
|
-
const core = { agentName, agentVersion, Perf
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
export async function action(entrypoint, options) {
|
|
34
|
+
const core = { agentName, agentVersion, Perf };
|
|
35
|
+
coreMessagesInit(core);
|
|
36
|
+
configInit(core);
|
|
37
|
+
loggerInit.default(core);
|
|
38
|
+
appInfoInit(core);
|
|
42
39
|
if (core.appInfo._errors?.length) throw core.appInfo._errors[0];
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
scopesInit(core);
|
|
42
|
+
reporterInit.default(core, { reporters: ['ui'] });
|
|
46
43
|
|
|
47
44
|
for (const err of core.config._errors) {
|
|
48
45
|
throw err;
|
|
@@ -62,11 +59,9 @@ async function action(entrypoint, options) {
|
|
|
62
59
|
const content = JSONStringify({ ...core.config.getReport({ redact: true }), Status }, null, 2) + EOL;
|
|
63
60
|
|
|
64
61
|
if (!options.quiet) {
|
|
65
|
-
|
|
62
|
+
writeFileSync(process.stdout.fd, content, 'utf8');
|
|
66
63
|
}
|
|
67
64
|
if (options.output) {
|
|
68
|
-
|
|
65
|
+
writeFileSync(options.output, content, 'utf-8');
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
|
-
|
|
72
|
-
module.exports.action = action;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
/*
|
|
3
2
|
* Copyright: 2025 Contrast Security, Inc
|
|
4
3
|
* Contact: support@contrastsecurity.com
|
|
@@ -14,38 +13,45 @@
|
|
|
14
13
|
* way not consistent with the End User License Agreement.
|
|
15
14
|
*/
|
|
16
15
|
// @ts-check
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
import { primordials } from '@contrast/common';
|
|
17
|
+
import configInit from '@contrast/config';
|
|
18
|
+
import appInfoInit from '@contrast/core/lib/app-info.js';
|
|
19
|
+
import coreMessagesInit from '@contrast/core/lib/messages.js';
|
|
20
|
+
import { findPackageJson } from '@contrast/find-package-json';
|
|
21
|
+
import loggerInit from '@contrast/logger';
|
|
22
|
+
import Perf from '@contrast/perf';
|
|
23
|
+
import rewriterInit from '@contrast/rewriter';
|
|
24
|
+
import { rewriteIsDeadzoned } from '@contrast/rewriter/lib/rewrite-is-deadzoned.js';
|
|
25
|
+
import swc from '@swc/core';
|
|
26
|
+
import { Visitor } from '@swc/core/Visitor.js';
|
|
27
|
+
import { readFile } from 'node:fs/promises';
|
|
28
|
+
import { createRequire } from 'node:module';
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
31
|
+
|
|
32
|
+
const { RegExpPrototypeTest, JSONParse } = primordials;
|
|
30
33
|
const JS_FILE_REGEX = /\.[cm]?js$/;
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
require('@contrast/core/lib/messages')(core);
|
|
35
|
-
const config = require('@contrast/config')(core);
|
|
36
|
-
if (core.config._errors?.length) throw core.config._errors[0];
|
|
35
|
+
const packageJson = JSON.parse((await readFile(new URL('../package.json', import.meta.url))).toString());
|
|
36
|
+
const { name: agentName, version: agentVersion } = packageJson;
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
/** @type {any} */
|
|
39
|
+
const core = { agentName, agentVersion, Perf };
|
|
40
|
+
coreMessagesInit(core);
|
|
41
|
+
const config = configInit(core);
|
|
42
|
+
if (config._errors?.length) throw config._errors[0];
|
|
43
|
+
// @ts-expect-error ESM interop doesn't handle this `default` very well.
|
|
44
|
+
const logger = loggerInit.default(core, { name: 'contrast:rewriter:cli' });
|
|
39
45
|
|
|
40
46
|
if (!config.agent.node.rewrite.enable || !config.agent.node.rewrite.cache.enable) {
|
|
41
47
|
logger.warn({ 'config.agent.node.rewrite': config.agent.node.rewrite }, 'rewriter config');
|
|
42
48
|
throw new Error('Rewriting disabled.');
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
const appInfo =
|
|
51
|
+
const appInfo = appInfoInit(core);
|
|
46
52
|
if (appInfo._errors?.length) throw appInfo._errors[0];
|
|
47
53
|
|
|
48
|
-
const rewriter =
|
|
54
|
+
const rewriter = rewriterInit(core);
|
|
49
55
|
|
|
50
56
|
/**
|
|
51
57
|
* Keeps track of visited files so we don't bother rewriting multiple times.
|
|
@@ -83,27 +89,23 @@ class RewriteVisitor extends Visitor {
|
|
|
83
89
|
constructor(filename) {
|
|
84
90
|
super();
|
|
85
91
|
visited.add(filename);
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Create a local `require` function so we can resolve relative filenames.
|
|
89
|
-
* @type {NodeRequire}
|
|
90
|
-
*/
|
|
92
|
+
this.parentUrl = pathToFileURL(filename);
|
|
91
93
|
this.require = createRequire(filename);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
96
|
/**
|
|
95
|
-
* Visit the
|
|
96
|
-
*
|
|
97
|
-
* @param {
|
|
97
|
+
* Visit the argument of an import declaration or `import()` function to
|
|
98
|
+
* rewrite the arg if it's a valid (i.e., absolute) path.
|
|
99
|
+
* @param {string} source
|
|
98
100
|
*/
|
|
99
|
-
|
|
101
|
+
visitImportSource(source) {
|
|
100
102
|
try {
|
|
101
|
-
const filename =
|
|
103
|
+
const filename = fileURLToPath(import.meta.resolve(source, this.parentUrl));
|
|
102
104
|
if (path.isAbsolute(filename)) {
|
|
103
105
|
rewriteFile(filename);
|
|
104
106
|
}
|
|
105
107
|
} catch (err) {
|
|
106
|
-
logger.
|
|
108
|
+
logger.error({ err }, 'unable to resolve %s', source);
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -113,20 +115,41 @@ class RewriteVisitor extends Visitor {
|
|
|
113
115
|
* @param {swc.ImportDeclaration} n
|
|
114
116
|
*/
|
|
115
117
|
visitImportDeclaration(n) {
|
|
116
|
-
this.
|
|
118
|
+
this.visitImportSource(n.source.value);
|
|
117
119
|
return super.visitImportDeclaration(n);
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Visit the argument of a `require()` call to rewrite the arg if it's a valid
|
|
124
|
+
* (i.e., absolute) path.
|
|
125
|
+
* @param {string} arg
|
|
126
|
+
*/
|
|
127
|
+
visitRequireArg(arg) {
|
|
128
|
+
try {
|
|
129
|
+
const filename = this.require.resolve(arg);
|
|
130
|
+
if (path.isAbsolute(filename)) {
|
|
131
|
+
rewriteFile(filename);
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
logger.error({ err }, 'unable to resolve %s', arg);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
120
138
|
/**
|
|
121
139
|
* Visits `import(...)` or `require(...)` call expressions, recursively
|
|
122
140
|
* rewriting the resolved path of the first argument if it's a string literal.
|
|
123
141
|
* @param {swc.CallExpression} n
|
|
124
142
|
*/
|
|
125
143
|
visitCallExpression(n) {
|
|
126
|
-
if (n.callee.type === 'Import'
|
|
144
|
+
if (n.callee.type === 'Import') {
|
|
127
145
|
const { expression } = n.arguments[0];
|
|
128
146
|
if (expression.type === 'StringLiteral') {
|
|
129
|
-
this.
|
|
147
|
+
this.visitImportSource(expression.value);
|
|
148
|
+
}
|
|
149
|
+
} else if (n.callee.type === 'Identifier' && n.callee.value === 'require') {
|
|
150
|
+
const { expression } = n.arguments[0];
|
|
151
|
+
if (expression.type === 'StringLiteral') {
|
|
152
|
+
this.visitRequireArg(expression.value);
|
|
130
153
|
}
|
|
131
154
|
}
|
|
132
155
|
|
|
@@ -135,12 +158,17 @@ class RewriteVisitor extends Visitor {
|
|
|
135
158
|
}
|
|
136
159
|
|
|
137
160
|
/** @param {string} filename */
|
|
138
|
-
|
|
139
|
-
|
|
161
|
+
function shouldSkipFile(filename) {
|
|
162
|
+
return (
|
|
140
163
|
!RegExpPrototypeTest.call(JS_FILE_REGEX, filename) ||
|
|
141
164
|
visited.has(filename) ||
|
|
142
|
-
rewriteIsDeadzoned(filename)
|
|
143
|
-
)
|
|
165
|
+
!!rewriteIsDeadzoned(filename)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** @param {string} filename */
|
|
170
|
+
async function rewriteFile(filename) {
|
|
171
|
+
if (shouldSkipFile(filename)) return;
|
|
144
172
|
|
|
145
173
|
try {
|
|
146
174
|
const content = (await readFile(filename)).toString();
|
|
@@ -156,24 +184,24 @@ async function rewriteFile(filename) {
|
|
|
156
184
|
rewriter.cache.write(filename, result);
|
|
157
185
|
|
|
158
186
|
/** @type {swc.Module | swc.Script} */
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
isModule
|
|
162
|
-
});
|
|
187
|
+
// @ts-expect-error SWC types expect `isModule` to be a `false` literal.
|
|
188
|
+
const program = await swc.parse(content, { isModule });
|
|
163
189
|
const visitor = new RewriteVisitor(filename);
|
|
164
190
|
visitor.visitProgram(program);
|
|
165
191
|
} catch (err) {
|
|
166
|
-
logger.
|
|
192
|
+
logger.error({ err }, 'unable to parse or rewrite %s', filename);
|
|
167
193
|
}
|
|
168
194
|
}
|
|
169
195
|
|
|
196
|
+
export { agentVersion as version };
|
|
197
|
+
|
|
170
198
|
/**
|
|
171
199
|
* @param {string} filename
|
|
172
200
|
* @param {object} opts
|
|
173
201
|
* @param {boolean=} opts.assess
|
|
174
202
|
* @param {boolean=} opts.protect
|
|
175
203
|
*/
|
|
176
|
-
async function action(filename, opts) {
|
|
204
|
+
export async function action(filename, opts) {
|
|
177
205
|
if (!opts.protect && (config.assess.enable || opts.assess)) {
|
|
178
206
|
rewriter.install('assess');
|
|
179
207
|
} else {
|
|
@@ -182,25 +210,8 @@ async function action(filename, opts) {
|
|
|
182
210
|
|
|
183
211
|
logger.info(
|
|
184
212
|
'Caching rewriter results to %s',
|
|
185
|
-
path.join(
|
|
186
|
-
rewriter.cache.cacheDir,
|
|
187
|
-
rewriter.modes.has('assess') ? 'assess' : 'protect'
|
|
188
|
-
),
|
|
213
|
+
path.join(rewriter.cache.cacheDir, rewriter.modes.has('assess') ? 'assess' : 'protect'),
|
|
189
214
|
);
|
|
190
215
|
logger.debug({ config }, 'Agent configuration');
|
|
191
216
|
return rewriteFile(filename);
|
|
192
217
|
}
|
|
193
|
-
|
|
194
|
-
if (require.main === module) {
|
|
195
|
-
program
|
|
196
|
-
.name('npx -p @contrast/cli rewrite')
|
|
197
|
-
.version(version)
|
|
198
|
-
.description('Rewrites application files, caching them so that rewriting does not need to occur when the application runs.')
|
|
199
|
-
.argument('<entrypoint>', 'The entrypoint for the application', entrypoint => path.resolve(entrypoint))
|
|
200
|
-
.option('-a, --assess', 'rewrite in assess mode')
|
|
201
|
-
.option('-p, --protect', 'rewrite in protect mode')
|
|
202
|
-
.action(action)
|
|
203
|
-
.parse(process.argv);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
module.exports.action = action;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2025 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { primordials } from '@contrast/common';
|
|
17
|
+
import configInit from '@contrast/config';
|
|
18
|
+
import appInfoInit from '@contrast/core/lib/app-info.js';
|
|
19
|
+
import systemInfoInit from '@contrast/core/lib/system-info/index.js';
|
|
20
|
+
import loggerInit from '@contrast/logger';
|
|
21
|
+
import Perf from '@contrast/perf';
|
|
22
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { EOL } from 'node:os';
|
|
24
|
+
|
|
25
|
+
const { JSONStringify } = primordials;
|
|
26
|
+
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
27
|
+
const { name: agentName, version: agentVersion } = packageJson;
|
|
28
|
+
|
|
29
|
+
export { agentVersion as version };
|
|
30
|
+
|
|
31
|
+
export async function action(options) {
|
|
32
|
+
const core = { agentName, agentVersion, Perf };
|
|
33
|
+
configInit(core);
|
|
34
|
+
loggerInit.default(core, { level: 'silent' });
|
|
35
|
+
appInfoInit(core);
|
|
36
|
+
systemInfoInit(core);
|
|
37
|
+
|
|
38
|
+
for (const err of core.config._errors) {
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const systemInfo = await core.getSystemInfo();
|
|
43
|
+
const content = JSONStringify(systemInfo, null, 2) + EOL;
|
|
44
|
+
|
|
45
|
+
if (!options.quiet) {
|
|
46
|
+
writeFileSync(process.stdout.fd, content, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
if (options.output) {
|
|
49
|
+
writeFileSync(options.output, content, 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
}
|
package/package.json
CHANGED
|
@@ -1,38 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.55.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "A collection of agent related CLI utilities",
|
|
5
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
7
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
7
8
|
"files": [
|
|
9
|
+
"bin/",
|
|
8
10
|
"lib/",
|
|
9
11
|
"!*.test.*",
|
|
10
12
|
"!tsconfig.*",
|
|
11
13
|
"!*.map"
|
|
12
14
|
],
|
|
13
15
|
"bin": {
|
|
14
|
-
"config-diagnostics": "
|
|
15
|
-
"rewrite": "
|
|
16
|
-
"system-diagnostics": "
|
|
16
|
+
"config-diagnostics": "bin/config-diagnostics.mjs",
|
|
17
|
+
"rewrite": "bin/rewrite.mjs",
|
|
18
|
+
"system-diagnostics": "bin/system-diagnostics.mjs"
|
|
17
19
|
},
|
|
18
20
|
"engines": {
|
|
19
21
|
"npm": ">=6.13.7 <7 || >= 8.3.1",
|
|
20
|
-
"node": ">=
|
|
22
|
+
"node": ">= 18.7.0"
|
|
21
23
|
},
|
|
22
24
|
"scripts": {
|
|
23
25
|
"test": "bash ../scripts/test.sh"
|
|
24
26
|
},
|
|
25
27
|
"dependencies": {
|
|
26
28
|
"@contrast/find-package-json": "^1.1.0",
|
|
27
|
-
"@contrast/rewriter": "1.
|
|
28
|
-
"@contrast/common": "1.
|
|
29
|
-
"@contrast/config": "1.
|
|
30
|
-
"@contrast/core": "1.
|
|
31
|
-
"@contrast/logger": "1.
|
|
32
|
-
"@contrast/perf": "1.
|
|
33
|
-
"@contrast/reporter": "1.
|
|
34
|
-
"@contrast/scopes": "1.
|
|
35
|
-
"@swc/core": "1.
|
|
29
|
+
"@contrast/rewriter": "1.33.0",
|
|
30
|
+
"@contrast/common": "1.37.0",
|
|
31
|
+
"@contrast/config": "1.52.0",
|
|
32
|
+
"@contrast/core": "1.57.0",
|
|
33
|
+
"@contrast/logger": "1.30.0",
|
|
34
|
+
"@contrast/perf": "1.4.0",
|
|
35
|
+
"@contrast/reporter": "1.55.0",
|
|
36
|
+
"@contrast/scopes": "1.27.0",
|
|
37
|
+
"@swc/core": "1.13.3",
|
|
36
38
|
"commander": "^9.4.1"
|
|
37
39
|
}
|
|
38
40
|
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/*
|
|
3
|
-
* Copyright: 2025 Contrast Security, Inc
|
|
4
|
-
* Contact: support@contrastsecurity.com
|
|
5
|
-
* License: Commercial
|
|
6
|
-
|
|
7
|
-
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
8
|
-
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
9
|
-
* made available through public repositories, use of this Software is subject to
|
|
10
|
-
* the applicable End User Licensing Agreement found at
|
|
11
|
-
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
12
|
-
* between Contrast Security and the End User. The Software may not be reverse
|
|
13
|
-
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
14
|
-
* way not consistent with the End User License Agreement.
|
|
15
|
-
*/
|
|
16
|
-
'use strict';
|
|
17
|
-
|
|
18
|
-
const commander = require('commander');
|
|
19
|
-
const fs = require('fs');
|
|
20
|
-
const { EOL } = require('os');
|
|
21
|
-
const path = require('path');
|
|
22
|
-
const { name: agentName, version: agentVersion } = require('../package.json');
|
|
23
|
-
const { primordials: { JSONStringify } } = require('@contrast/common');
|
|
24
|
-
|
|
25
|
-
if (require.main === module) {
|
|
26
|
-
commander.program
|
|
27
|
-
.name('system-diagnostics')
|
|
28
|
-
.description('The system-diagnostics utility returns the system info for the server/container the agent is running on.')
|
|
29
|
-
.option('-q, --quiet', 'suppress logging to stdout')
|
|
30
|
-
.option('-o, --output <string>', 'output directory for generated JSON file', path.join(process.cwd(), 'contrast_system_info.json'))
|
|
31
|
-
.action(action)
|
|
32
|
-
.parse();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function action(options) {
|
|
36
|
-
const core = { agentName, agentVersion, Perf: require('@contrast/perf') };
|
|
37
|
-
require('@contrast/config')(core);
|
|
38
|
-
require('@contrast/logger').default(core, { level: 'silent' });
|
|
39
|
-
require('@contrast/core/lib/app-info')(core);
|
|
40
|
-
require('@contrast/core/lib/system-info')(core);
|
|
41
|
-
|
|
42
|
-
for (const err of core.config._errors) {
|
|
43
|
-
throw err;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const systemInfo = await core.getSystemInfo();
|
|
47
|
-
const content = JSONStringify(systemInfo, null, 2) + EOL;
|
|
48
|
-
|
|
49
|
-
if (!options.quiet) {
|
|
50
|
-
fs.writeFileSync(1, content, 'utf8');
|
|
51
|
-
}
|
|
52
|
-
if (options.output) {
|
|
53
|
-
fs.writeFileSync(options.output, content, 'utf-8');
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
module.exports.action = action;
|