@contrast/contrast 1.0.2 → 1.0.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.
- package/.prettierignore +1 -0
- package/README.md +6 -4
- package/dist/audit/languageAnalysisEngine/langugageAnalysisFactory.js +25 -0
- package/dist/commands/audit/saveFile.js +11 -0
- package/dist/commands/auth/auth.js +19 -1
- package/dist/commands/config/config.js +19 -8
- package/dist/commands/scan/processScan.js +4 -22
- package/dist/common/HTTPClient.js +11 -0
- package/dist/common/errorHandling.js +9 -23
- package/dist/common/{findLatestCLIVersion.js → versionChecker.js} +10 -3
- package/dist/constants/constants.js +4 -2
- package/dist/constants/locales.js +17 -7
- package/dist/constants.js +34 -2
- package/dist/index.js +48 -30
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scan/help.js +4 -2
- package/dist/scan/populateProjectIdAndProjectName.js +1 -0
- package/dist/scan/saveResults.js +9 -10
- package/dist/scan/scan.js +27 -2
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +7 -2
- package/dist/scan/scanResults.js +5 -0
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +1 -1
- package/src/audit/languageAnalysisEngine/langugageAnalysisFactory.js +32 -0
- package/src/commands/audit/processAudit.ts +0 -1
- package/src/commands/audit/saveFile.ts +6 -0
- package/src/commands/auth/auth.js +25 -1
- package/src/commands/config/config.js +22 -8
- package/src/commands/scan/processScan.js +4 -23
- package/src/common/HTTPClient.js +13 -0
- package/src/common/errorHandling.ts +11 -24
- package/src/common/versionChecker.ts +39 -0
- package/src/constants/constants.js +5 -4
- package/src/constants/locales.js +23 -8
- package/src/constants.js +37 -2
- package/src/index.ts +63 -36
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scan/help.js +4 -2
- package/src/scan/populateProjectIdAndProjectName.js +1 -0
- package/src/scan/saveResults.js +8 -9
- package/src/scan/scan.js +30 -2
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +9 -2
- package/src/scan/scanResults.js +10 -0
- package/src/utils/getConfig.ts +2 -0
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/src/common/findLatestCLIVersion.ts +0 -27
package/.prettierignore
CHANGED
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# Contrast
|
|
1
|
+
# CodeSec by Contrast Security
|
|
2
|
+
|
|
3
|
+
CodeSec delivers:
|
|
2
4
|
|
|
3
5
|
- The fastest and most accurate SAST scanner.
|
|
4
6
|
- Immediate and actionable results — scan code and serverless environments.
|
|
@@ -57,7 +59,7 @@ export AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>
|
|
|
57
59
|
Use contrast lambda to scan your AWS Lambda functions.
|
|
58
60
|
`contrast lambda --function-name MyFunctionName --region my-aws-region`
|
|
59
61
|
|
|
60
|
-
## Contrast
|
|
62
|
+
## Contrast commands
|
|
61
63
|
|
|
62
64
|
### auth
|
|
63
65
|
|
|
@@ -83,11 +85,11 @@ Performs a security SAST scan.
|
|
|
83
85
|
|
|
84
86
|
**Options:**
|
|
85
87
|
|
|
86
|
-
- **contrast scan --file** Path of the file you want to scan. Contrast searches for a .jar, .war
|
|
88
|
+
- **contrast scan --file** Path of the file you want to scan. Contrast searches for a .jar, .war .exe or .zip file in the working directory (and 3 folders deep) if a file is not specified.
|
|
87
89
|
Alias: **--f**
|
|
88
90
|
|
|
89
91
|
- **contrast scan --name**
|
|
90
|
-
Contrast project name. If not specified, Contrast
|
|
92
|
+
Contrast project name. If not specified, Contrast creates a project from the name of the file
|
|
91
93
|
Alias: **–n**
|
|
92
94
|
- **contrast scan --save**
|
|
93
95
|
Download the results to a Static Analysis Results Interchange Format (SARIF) file.
|
|
@@ -12,6 +12,10 @@ const { vulnerabilityReport } = require('./report/reportingFeature');
|
|
|
12
12
|
const { vulnReportWithoutDevDep } = require('./report/newReportingFeature');
|
|
13
13
|
const { checkDevDeps } = require('./report/checkIgnoreDevDep');
|
|
14
14
|
const { newSendSnapShot } = require('../languageAnalysisEngine/sendSnapshot');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const chalk = require('chalk');
|
|
17
|
+
const saveFile = require('../../commands/audit/saveFile').default;
|
|
18
|
+
const generateSbom = require('../../sbom/generateSbom').default;
|
|
15
19
|
module.exports = exports = (err, analysis) => {
|
|
16
20
|
const { identifiedLanguageInfo } = analysis.languageAnalysis;
|
|
17
21
|
const catalogueAppId = analysis.languageAnalysis.appId;
|
|
@@ -44,6 +48,7 @@ module.exports = exports = (err, analysis) => {
|
|
|
44
48
|
await vulnerabilityReport(analysis, catalogueAppId, config);
|
|
45
49
|
}
|
|
46
50
|
}
|
|
51
|
+
await auditSave(config);
|
|
47
52
|
console.log('\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n');
|
|
48
53
|
};
|
|
49
54
|
if (identifiedLanguageInfo.language === DOTNET) {
|
|
@@ -68,3 +73,23 @@ module.exports = exports = (err, analysis) => {
|
|
|
68
73
|
goAE(identifiedLanguageInfo, analysis.config, langCallback);
|
|
69
74
|
}
|
|
70
75
|
};
|
|
76
|
+
async function auditSave(config) {
|
|
77
|
+
if (config.save) {
|
|
78
|
+
if (config.save.toLowerCase() === 'sbom') {
|
|
79
|
+
saveFile(config, await generateSbom(config));
|
|
80
|
+
const filename = `${config.applicationId}-sbom-cyclonedx.json`;
|
|
81
|
+
if (fs.existsSync(filename)) {
|
|
82
|
+
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.log(chalk.yellow.bold(`\n Unable to save ${filename} Software Bill of Materials (SBOM)`));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(i18n.__('auditBadFiletypeSpecifiedForSave'));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.log(i18n.__('auditNoFiletypeSpecifiedForSave'));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = __importDefault(require("fs"));
|
|
7
|
+
function saveFile(config, rawResults) {
|
|
8
|
+
const fileName = `${config.applicationId}-sbom-cyclonedx.json`;
|
|
9
|
+
fs_1.default.writeFileSync(fileName, JSON.stringify(rawResults));
|
|
10
|
+
}
|
|
11
|
+
exports.default = saveFile;
|
|
@@ -7,7 +7,15 @@ const { sleep } = require('../../utils/requestUtils');
|
|
|
7
7
|
const i18n = require('i18n');
|
|
8
8
|
const { returnOra, startSpinner, failSpinner, succeedSpinner } = require('../../utils/oraWrapper');
|
|
9
9
|
const { TIMEOUT, AUTH_UI_URL } = require('../../constants/constants');
|
|
10
|
-
const
|
|
10
|
+
const parsedCLIOptions = require('../../utils/parsedCLIOptions');
|
|
11
|
+
const constants = require('../../constants');
|
|
12
|
+
const commandLineUsage = require('command-line-usage');
|
|
13
|
+
const processAuth = async (argv, config) => {
|
|
14
|
+
let authParams = parsedCLIOptions.getCommandLineArgsCustom(argv, constants.commandLineDefinitions.authOptionDefinitions);
|
|
15
|
+
if (authParams.help) {
|
|
16
|
+
console.log(authUsageGuide);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
11
19
|
const token = uuidv4();
|
|
12
20
|
const url = `${AUTH_UI_URL}/?token=${token}`;
|
|
13
21
|
console.log(i18n.__('redirectAuth', url));
|
|
@@ -56,6 +64,16 @@ const pollAuthResult = async (token, client) => {
|
|
|
56
64
|
console.log(err);
|
|
57
65
|
});
|
|
58
66
|
};
|
|
67
|
+
const authUsageGuide = commandLineUsage([
|
|
68
|
+
{
|
|
69
|
+
header: i18n.__('authHeader'),
|
|
70
|
+
content: [i18n.__('constantsAuthHeaderContents')]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
header: i18n.__('constantsAuthUsageHeader'),
|
|
74
|
+
content: [i18n.__('constantsAuthUsageContents')]
|
|
75
|
+
}
|
|
76
|
+
]);
|
|
59
77
|
module.exports = {
|
|
60
78
|
processAuth: processAuth
|
|
61
79
|
};
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const
|
|
2
|
+
const parsedCLIOptions = require('../../utils/parsedCLIOptions');
|
|
3
|
+
const constants = require('../../constants');
|
|
4
|
+
const commandLineUsage = require('command-line-usage');
|
|
5
|
+
const i18n = require('i18n');
|
|
3
6
|
const processConfig = (argv, config) => {
|
|
4
|
-
const options = [{ name: 'clear', alias: 'c', type: Boolean }];
|
|
5
7
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
if (
|
|
8
|
+
let configParams = parsedCLIOptions.getCommandLineArgsCustom(argv, constants.commandLineDefinitions.configOptionDefinitions);
|
|
9
|
+
if (configParams.help) {
|
|
10
|
+
console.log(configUsageGuide);
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
13
|
+
if (configParams.clear) {
|
|
12
14
|
config.clear();
|
|
13
15
|
}
|
|
14
16
|
else {
|
|
@@ -19,6 +21,15 @@ const processConfig = (argv, config) => {
|
|
|
19
21
|
console.log(e.message.toString());
|
|
20
22
|
}
|
|
21
23
|
};
|
|
24
|
+
const configUsageGuide = commandLineUsage([
|
|
25
|
+
{
|
|
26
|
+
header: i18n.__('configHeader')
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
content: [i18n.__('constantsConfigUsageContents')],
|
|
30
|
+
optionList: constants.commandLineDefinitions.configOptionDefinitions
|
|
31
|
+
}
|
|
32
|
+
]);
|
|
22
33
|
module.exports = {
|
|
23
34
|
processConfig: processConfig
|
|
24
35
|
};
|
|
@@ -3,35 +3,17 @@ const { startScan } = require('../../scan/scanController');
|
|
|
3
3
|
const { formatScanOutput } = require('../../scan/scan');
|
|
4
4
|
const { scanUsageGuide } = require('../../scan/help');
|
|
5
5
|
const scanConfig = require('../../scan/scanConfig');
|
|
6
|
-
const
|
|
7
|
-
const commonApi = require('../../utils/commonApi');
|
|
8
|
-
const i18n = require('i18n');
|
|
6
|
+
const { saveScanFile } = require('../../utils/saveFile');
|
|
9
7
|
const processScan = async (argvMain) => {
|
|
10
|
-
if (argvMain.indexOf('--help') !== -1) {
|
|
11
|
-
printHelpMessage();
|
|
12
|
-
process.exit(1);
|
|
13
|
-
}
|
|
14
8
|
let config = scanConfig.getScanConfig(argvMain);
|
|
15
9
|
let scanResults = await startScan(config);
|
|
16
10
|
if (scanResults) {
|
|
17
11
|
formatScanOutput(scanResults?.projectOverview, scanResults?.scanResultsInstances);
|
|
18
12
|
}
|
|
19
|
-
if (config.save) {
|
|
20
|
-
|
|
21
|
-
const scanId = scanResults.scanDetail.id;
|
|
22
|
-
const client = commonApi.getHttpClient(config);
|
|
23
|
-
const rawResults = await client.getSpecificScanResultSarif(config, scanId);
|
|
24
|
-
saveResults.writeResultsToFile(rawResults?.body);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
console.log(i18n.__('scanNoFiletypeSpecifiedForSave'));
|
|
28
|
-
}
|
|
13
|
+
if (config.save !== undefined) {
|
|
14
|
+
await saveScanFile(config, scanResults);
|
|
29
15
|
}
|
|
30
16
|
};
|
|
31
|
-
const printHelpMessage = () => {
|
|
32
|
-
console.log(scanUsageGuide);
|
|
33
|
-
};
|
|
34
17
|
module.exports = {
|
|
35
|
-
processScan
|
|
36
|
-
printHelpMessage
|
|
18
|
+
processScan
|
|
37
19
|
};
|
|
@@ -97,6 +97,9 @@ HTTPClient.prototype.createProjectId = function createProjectId(config) {
|
|
|
97
97
|
name: config.name,
|
|
98
98
|
archived: 'false'
|
|
99
99
|
};
|
|
100
|
+
if (config.language) {
|
|
101
|
+
options.body.language = config.language;
|
|
102
|
+
}
|
|
100
103
|
options.url = createHarmonyProjectsUrl(config);
|
|
101
104
|
return requestUtils.sendRequest({ method: 'post', options });
|
|
102
105
|
};
|
|
@@ -224,6 +227,11 @@ HTTPClient.prototype.checkLibrary = function checkLibrary(data) {
|
|
|
224
227
|
options.body = data;
|
|
225
228
|
return requestUtils.sendRequest({ method: 'post', options });
|
|
226
229
|
};
|
|
230
|
+
HTTPClient.prototype.getSbom = function getSbom(config) {
|
|
231
|
+
const options = _.cloneDeep(this.requestOptions);
|
|
232
|
+
options.url = createSbomCycloneDXUrl(config);
|
|
233
|
+
return requestUtils.sendRequest({ method: 'get', options });
|
|
234
|
+
};
|
|
227
235
|
const createGetScanIdURL = config => {
|
|
228
236
|
return `${config.host}/Contrast/api/sast/v1/organizations/${config.organizationId}/projects/${config.projectId}/scans/`;
|
|
229
237
|
};
|
|
@@ -275,6 +283,9 @@ function createDataUrl() {
|
|
|
275
283
|
const createGetDependencyTree = (protocol, orgUuid, appId, reportId) => {
|
|
276
284
|
return `${protocol}/Contrast/api/ng/sca/organizations/${orgUuid}/applications/${appId}/reports/${reportId}`;
|
|
277
285
|
};
|
|
286
|
+
function createSbomCycloneDXUrl(config) {
|
|
287
|
+
return `${config.host}/Contrast/api/ng/${config.organizationId}/applications/${config.applicationId}/libraries/sbom/cyclonedx`;
|
|
288
|
+
}
|
|
278
289
|
module.exports = HTTPClient;
|
|
279
290
|
module.exports.pollForAuthUrl = pollForAuthUrl;
|
|
280
291
|
module.exports.getServerlessHost = getServerlessHost;
|
|
@@ -3,9 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.findCommandOnError = exports.libraryAnalysisError = exports.handleResponseErrors = exports.getErrorMessage = exports.generalError = exports.hostWarningError = exports.failOptionError = exports.proxyError = exports.forbiddenError = exports.badRequestError = exports.unauthenticatedError = exports.genericError = void 0;
|
|
7
7
|
const i18n_1 = __importDefault(require("i18n"));
|
|
8
|
-
const lodash_1 = require("lodash");
|
|
9
8
|
const handleResponseErrors = (res, api) => {
|
|
10
9
|
if (res.statusCode === 400) {
|
|
11
10
|
api === 'catalogue' ? badRequestError(true) : badRequestError(false);
|
|
@@ -72,6 +71,7 @@ const badRequestError = (catalogue) => {
|
|
|
72
71
|
exports.badRequestError = badRequestError;
|
|
73
72
|
const forbiddenError = () => {
|
|
74
73
|
generalError('forbiddenRequestErrorHeader', 'forbiddenRequestErrorMessage');
|
|
74
|
+
process.exit(1);
|
|
75
75
|
};
|
|
76
76
|
exports.forbiddenError = forbiddenError;
|
|
77
77
|
const proxyError = () => {
|
|
@@ -107,7 +107,7 @@ const generalError = (header, message) => {
|
|
|
107
107
|
console.log(finalMessage);
|
|
108
108
|
};
|
|
109
109
|
exports.generalError = generalError;
|
|
110
|
-
const
|
|
110
|
+
const findCommandOnError = (unknownOptions) => {
|
|
111
111
|
const commandKeywords = {
|
|
112
112
|
auth: 'auth',
|
|
113
113
|
audit: 'audit',
|
|
@@ -115,24 +115,10 @@ const approximateCommandOnError = (unknownOptions) => {
|
|
|
115
115
|
lambda: 'lambda',
|
|
116
116
|
config: 'config'
|
|
117
117
|
};
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
? 0
|
|
124
|
-
: 1);
|
|
125
|
-
const foundCommands = sortedUnknownOptions.filter(command => commandKeywords[command]);
|
|
126
|
-
const parsedUnknownOptions = sortedUnknownOptions
|
|
127
|
-
.toString()
|
|
128
|
-
.replace(/,/g, ' ');
|
|
129
|
-
const approximateParams = parsedUnknownOptions
|
|
130
|
-
.replace(new RegExp(foundCommands.join('|'), 'g'), '')
|
|
131
|
-
.trim();
|
|
132
|
-
const approximateCommand = `${foundCommands[0]} ${approximateParams}`;
|
|
133
|
-
return {
|
|
134
|
-
approximateCommand,
|
|
135
|
-
approximateCommandKeyword: foundCommands[0]
|
|
136
|
-
};
|
|
118
|
+
const containsCommandKeyword = unknownOptions.some(command => commandKeywords[command]);
|
|
119
|
+
if (containsCommandKeyword) {
|
|
120
|
+
const foundCommands = unknownOptions.filter(command => commandKeywords[command]);
|
|
121
|
+
return foundCommands[0];
|
|
122
|
+
}
|
|
137
123
|
};
|
|
138
|
-
exports.
|
|
124
|
+
exports.findCommandOnError = findCommandOnError;
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isCorrectNodeVersion = exports.findLatestCLIVersion = void 0;
|
|
6
7
|
const latest_version_1 = __importDefault(require("latest-version"));
|
|
7
8
|
const constants_1 = require("../constants/constants");
|
|
8
9
|
const boxen_1 = __importDefault(require("boxen"));
|
|
@@ -12,12 +13,18 @@ async function findLatestCLIVersion() {
|
|
|
12
13
|
const latestCLIVersion = await (0, latest_version_1.default)('@contrast/contrast');
|
|
13
14
|
if (semver_1.default.lt(constants_1.APP_VERSION, latestCLIVersion)) {
|
|
14
15
|
const updateAvailableMessage = `Update available ${chalk_1.default.yellow(constants_1.APP_VERSION)} → ${chalk_1.default.green(latestCLIVersion)}`;
|
|
15
|
-
const
|
|
16
|
-
|
|
16
|
+
const npmUpdateAvailableCommand = `Run ${chalk_1.default.cyan('npm i @contrast/contrast -g')} to update via npm`;
|
|
17
|
+
const homebrewUpdateAvailableCommand = `Run ${chalk_1.default.cyan('brew install contrastsecurity/tap/contrast')} to update via brew`;
|
|
18
|
+
console.log((0, boxen_1.default)(`${updateAvailableMessage}\n${npmUpdateAvailableCommand}\n\n${homebrewUpdateAvailableCommand}`, {
|
|
19
|
+
titleAlignment: 'center',
|
|
17
20
|
margin: 1,
|
|
18
21
|
padding: 1,
|
|
19
22
|
align: 'center'
|
|
20
23
|
}));
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
|
-
exports.
|
|
26
|
+
exports.findLatestCLIVersion = findLatestCLIVersion;
|
|
27
|
+
async function isCorrectNodeVersion(currentVersion) {
|
|
28
|
+
return semver_1.default.satisfies(currentVersion, '>=16.13.2 <17');
|
|
29
|
+
}
|
|
30
|
+
exports.isCorrectNodeVersion = isCorrectNodeVersion;
|
|
@@ -12,10 +12,11 @@ const MEDIUM = 'MEDIUM';
|
|
|
12
12
|
const HIGH = 'HIGH';
|
|
13
13
|
const CRITICAL = 'CRITICAL';
|
|
14
14
|
const APP_NAME = 'contrast';
|
|
15
|
-
const APP_VERSION = '1.0.
|
|
15
|
+
const APP_VERSION = '1.0.3';
|
|
16
16
|
const TIMEOUT = 120000;
|
|
17
17
|
const AUTH_UI_URL = 'https://cli-auth.contrastsecurity.com';
|
|
18
18
|
const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com';
|
|
19
|
+
const SARIF_FILE = 'SARIF';
|
|
19
20
|
module.exports = {
|
|
20
21
|
supportedLanguages: { NODE, DOTNET, JAVA, RUBY, PYTHON, GO, PHP, JAVASCRIPT },
|
|
21
22
|
LOW,
|
|
@@ -26,5 +27,6 @@ module.exports = {
|
|
|
26
27
|
APP_NAME,
|
|
27
28
|
TIMEOUT,
|
|
28
29
|
AUTH_UI_URL,
|
|
29
|
-
AUTH_CALLBACK_URL
|
|
30
|
+
AUTH_CALLBACK_URL,
|
|
31
|
+
SARIF_FILE
|
|
30
32
|
};
|
|
@@ -109,17 +109,22 @@ const en_locales = () => {
|
|
|
109
109
|
failOptionErrorMessage: " FAIL - CVE's have been detected that match at least the cve_severity or cve_threshold option specified.",
|
|
110
110
|
constantsSeverity: 'Combined with the --report command, allows the user to report libraries with vulnerabilities above a chosen severity level. For example, cve_severity medium only reports libraries with vulnerabilities at medium or higher severity. Values for level are high, medium or low.',
|
|
111
111
|
constantsCount: "The number of CVE's that must be exceeded to fail a build",
|
|
112
|
-
constantsHeader: 'Contrast
|
|
112
|
+
constantsHeader: 'CodeSec by Contrast Security',
|
|
113
113
|
constantsPrerequisitesContentScanLanguages: 'Java & JavaScript supported',
|
|
114
|
-
constantsContrastContent: 'Use the Contrast CLI,
|
|
114
|
+
constantsContrastContent: 'Use the Contrast CLI to run a scan(Java, JavaScript and .NET ) or lambda command (Java and Python) to find your vulnerabilities and start securing your code.',
|
|
115
115
|
constantsUsageGuideContentRecommendation: 'Our recommendation is that this is invoked as part of a CI pipeline so that running the cli is automated as part of your build process.',
|
|
116
116
|
constantsPrerequisitesHeader: 'Pre-requisites',
|
|
117
|
+
constantsAuthUsageHeader: 'Usage',
|
|
118
|
+
constantsAuthUsageContents: 'contrast auth',
|
|
119
|
+
constantsAuthHeaderContents: 'Authorize with external identity provider to perform scans on code',
|
|
120
|
+
configHeader: 'Config',
|
|
121
|
+
constantsConfigUsageContents: 'view / clear the configuration',
|
|
117
122
|
constantsPrerequisitesContent: 'To scan a Java project you will need a .jar or .war file for analysis\n' +
|
|
118
123
|
'To scan a Javascript project you will need a .js or.zip file for analysis\n' +
|
|
119
124
|
'To scan a .NET c# webforms project you will need a .exe or a .zip file for analysis\n',
|
|
120
125
|
constantsUsage: 'Usage',
|
|
121
126
|
constantsUsageCommandExample: 'contrast [command] [options]',
|
|
122
|
-
constantsUsageCommandInfo: 'The file argument is optional. If no file is given, Contrast will search for a .jar, .war, .
|
|
127
|
+
constantsUsageCommandInfo: 'The file argument is optional. If no file is given, Contrast will search for a .jar, .war, .exe or .zip file in the working directory.\n',
|
|
123
128
|
constantsUsageCommandInfo24Hours: 'Submitted files are encrypted during upload and deleted in 24 hours.',
|
|
124
129
|
constantsAnd: 'AND',
|
|
125
130
|
constantsJava: 'AND Maven build platform, including the dependency plugin. For a Gradle project, use build.gradle. A gradle-wrapper.properties file is also required. Kotlin is also supported requiring a build.gradle.kts file.',
|
|
@@ -192,7 +197,7 @@ const en_locales = () => {
|
|
|
192
197
|
permissionsError: 'You do not have the correct permissions here. \n Contact support@contrastsecurity.com to get this fixed.',
|
|
193
198
|
scanErrorFileMessage: 'We only accept the following file types: \nJava - .jar, .war \nJavaScript - .js or .zip files',
|
|
194
199
|
helpAuthSummary: 'Authenticate Contrast using your Github or Google account',
|
|
195
|
-
helpScanSummary: '
|
|
200
|
+
helpScanSummary: 'Perform static analysis on binaries / code artifacts',
|
|
196
201
|
helpLambdaSummary: 'Perform scan on AWS Lambda functions',
|
|
197
202
|
helpVersionSummary: 'Displays version of Contrast CLI',
|
|
198
203
|
helpConfigSummary: 'Displays stored credentials',
|
|
@@ -203,10 +208,11 @@ const en_locales = () => {
|
|
|
203
208
|
versionName: 'version',
|
|
204
209
|
configName: 'config',
|
|
205
210
|
helpName: 'help',
|
|
211
|
+
scanOptionsLanguageSummary: 'Valid values are JAVA, JAVASCRIPT and DOTNET',
|
|
206
212
|
scanOptionsLanguageSummaryOptional: 'Language of file to send for analysis. ',
|
|
207
213
|
scanOptionsLanguageSummaryRequired: 'If you scan a .zip file or you use the --file option.',
|
|
208
214
|
scanOptionsTimeoutSummary: 'Time in seconds to wait for scan to complete. Default value is 300 seconds.',
|
|
209
|
-
scanOptionsFileNameSummary: 'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .
|
|
215
|
+
scanOptionsFileNameSummary: 'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .exe or .zip file in the working directory.',
|
|
210
216
|
scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
|
|
211
217
|
authSuccessMessage: 'Authentication successful',
|
|
212
218
|
runAuthSuccessMessage: "Now you can use Contrast CLI \nRun 'contrast scan' on your file \n" +
|
|
@@ -215,7 +221,7 @@ const en_locales = () => {
|
|
|
215
221
|
authTimedOutMessage: 'Auth Timed out, try again',
|
|
216
222
|
zipErrorScan: 'We only support zip files for JAVASCRIPT language, please set the flag --language JAVASCRIPT',
|
|
217
223
|
unknownFileErrorScan: 'Unsupported file selected for Scan.',
|
|
218
|
-
foundScanFile: '
|
|
224
|
+
foundScanFile: 'Found: %s',
|
|
219
225
|
foundDetailedVulnerabilities: chalk.bold('%s Critical') +
|
|
220
226
|
' | ' +
|
|
221
227
|
chalk.bold('%s High') +
|
|
@@ -224,6 +230,7 @@ const en_locales = () => {
|
|
|
224
230
|
timeoutScan: 'Timeout set to 5 minutes.',
|
|
225
231
|
searchingScanFileDirectory: 'Searching for file to scan from %s...',
|
|
226
232
|
scanHeader: 'Contrast Scan CLI',
|
|
233
|
+
authHeader: 'Auth',
|
|
227
234
|
lambdaHeader: 'Contrast lambda help',
|
|
228
235
|
lambdaSummary: 'Performs static security scan on an AWS Lambda Function.\nProduces CVE (Vulnerable Dependencies) and Least Privilege violations/remediation results.',
|
|
229
236
|
lambdaUsage: 'contrast lambda --function-name <function> [options]',
|
|
@@ -279,7 +286,10 @@ const en_locales = () => {
|
|
|
279
286
|
auditOptionsSave: '-s, --save',
|
|
280
287
|
auditOptionsSaveDescription: 'saves the output in specified format Txt text, sbom',
|
|
281
288
|
scanNoVulnerabilitiesFound: '👏 No vulnerabilities found',
|
|
282
|
-
scanNoFiletypeSpecifiedForSave: 'Please specify file type to save results to',
|
|
289
|
+
scanNoFiletypeSpecifiedForSave: 'Please specify file type to save results to, accepted value is SARIF',
|
|
290
|
+
auditSBOMSaveSuccess: '\n Software Bill of Materials (SBOM) saved successfully',
|
|
291
|
+
auditNoFiletypeSpecifiedForSave: `\n ${chalk.yellow.bold('No file type specified for --save option to save audit results to. Use audit --help to see valid --save options.')}`,
|
|
292
|
+
auditBadFiletypeSpecifiedForSave: `\n ${chalk.yellow.bold('Bad file type specified for --save option. Use audit --help to see valid --save options.')}`,
|
|
283
293
|
...lambda
|
|
284
294
|
};
|
|
285
295
|
};
|
package/dist/constants.js
CHANGED
|
@@ -17,6 +17,14 @@ const scanOptionDefinitions = [
|
|
|
17
17
|
'}: ' +
|
|
18
18
|
i18n.__('constantsProjectName')
|
|
19
19
|
},
|
|
20
|
+
{
|
|
21
|
+
name: 'language',
|
|
22
|
+
alias: 'l',
|
|
23
|
+
description: '{bold ' +
|
|
24
|
+
i18n.__('constantsOptional') +
|
|
25
|
+
'}: ' +
|
|
26
|
+
i18n.__('scanOptionsLanguageSummary')
|
|
27
|
+
},
|
|
20
28
|
{
|
|
21
29
|
name: 'file',
|
|
22
30
|
alias: 'f',
|
|
@@ -66,7 +74,6 @@ const scanOptionDefinitions = [
|
|
|
66
74
|
},
|
|
67
75
|
{
|
|
68
76
|
name: 'host',
|
|
69
|
-
alias: 'h',
|
|
70
77
|
description: '{bold ' +
|
|
71
78
|
i18n.__('constantsRequired') +
|
|
72
79
|
'}: ' +
|
|
@@ -111,6 +118,7 @@ const scanOptionDefinitions = [
|
|
|
111
118
|
},
|
|
112
119
|
{
|
|
113
120
|
name: 'help',
|
|
121
|
+
alias: 'h',
|
|
114
122
|
type: Boolean
|
|
115
123
|
},
|
|
116
124
|
{
|
|
@@ -119,6 +127,27 @@ const scanOptionDefinitions = [
|
|
|
119
127
|
type: Boolean
|
|
120
128
|
}
|
|
121
129
|
];
|
|
130
|
+
const authOptionDefinitions = [
|
|
131
|
+
{
|
|
132
|
+
name: 'help',
|
|
133
|
+
alias: 'h',
|
|
134
|
+
type: Boolean
|
|
135
|
+
}
|
|
136
|
+
];
|
|
137
|
+
const configOptionDefinitions = [
|
|
138
|
+
{
|
|
139
|
+
name: 'help',
|
|
140
|
+
alias: 'h',
|
|
141
|
+
type: Boolean,
|
|
142
|
+
description: 'Help text'
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'clear',
|
|
146
|
+
alias: 'c',
|
|
147
|
+
type: Boolean,
|
|
148
|
+
description: 'Clear the currently stored config'
|
|
149
|
+
}
|
|
150
|
+
];
|
|
122
151
|
const auditOptionDefinitions = [
|
|
123
152
|
{
|
|
124
153
|
name: 'application-id',
|
|
@@ -257,6 +286,7 @@ const mainUsageGuide = commandLineUsage([
|
|
|
257
286
|
header: i18n.__('constantsCommands'),
|
|
258
287
|
content: [
|
|
259
288
|
{ name: i18n.__('authName'), summary: i18n.__('helpAuthSummary') },
|
|
289
|
+
{ name: i18n.__('scanName'), summary: i18n.__('helpScanSummary') },
|
|
260
290
|
{ name: i18n.__('lambdaName'), summary: i18n.__('helpLambdaSummary') },
|
|
261
291
|
{ name: i18n.__('versionName'), summary: i18n.__('helpVersionSummary') },
|
|
262
292
|
{ name: i18n.__('configName'), summary: i18n.__('helpConfigSummary') },
|
|
@@ -273,6 +303,8 @@ module.exports = {
|
|
|
273
303
|
mainUsageGuide,
|
|
274
304
|
mainDefinition,
|
|
275
305
|
scanOptionDefinitions,
|
|
276
|
-
auditOptionDefinitions
|
|
306
|
+
auditOptionDefinitions,
|
|
307
|
+
authOptionDefinitions,
|
|
308
|
+
configOptionDefinitions
|
|
277
309
|
}
|
|
278
310
|
};
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,8 @@ const constants_1 = __importDefault(require("./constants"));
|
|
|
12
12
|
const constants_2 = require("./constants/constants");
|
|
13
13
|
const lambda_1 = require("./lambda/lambda");
|
|
14
14
|
const getConfig_1 = require("./utils/getConfig");
|
|
15
|
-
const
|
|
15
|
+
const versionChecker_1 = require("./common/versionChecker");
|
|
16
|
+
const errorHandling_1 = require("./common/errorHandling");
|
|
16
17
|
const { commandLineDefinitions: { mainUsageGuide, mainDefinition } } = constants_1.default;
|
|
17
18
|
const config = (0, getConfig_1.localConfig)(constants_2.APP_NAME, constants_2.APP_VERSION);
|
|
18
19
|
const getMainOption = () => {
|
|
@@ -28,37 +29,54 @@ const getMainOption = () => {
|
|
|
28
29
|
};
|
|
29
30
|
};
|
|
30
31
|
const start = async () => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
32
|
+
if (await (0, versionChecker_1.isCorrectNodeVersion)(process.version)) {
|
|
33
|
+
const { mainOptions, argv: argvMain } = getMainOption();
|
|
34
|
+
const command = mainOptions.command != undefined ? mainOptions.command.toLowerCase() : '';
|
|
35
|
+
if (command === 'version' ||
|
|
36
|
+
argvMain.includes('--v') ||
|
|
37
|
+
argvMain.includes('--version')) {
|
|
38
|
+
console.log(constants_2.APP_VERSION);
|
|
39
|
+
await (0, versionChecker_1.findLatestCLIVersion)();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
config.set('numOfRuns', config.get('numOfRuns') + 1);
|
|
43
|
+
if (config.get('numOfRuns') >= 5) {
|
|
44
|
+
await (0, versionChecker_1.findLatestCLIVersion)();
|
|
45
|
+
config.set('numOfRuns', 0);
|
|
46
|
+
}
|
|
47
|
+
if (command === 'config') {
|
|
48
|
+
return (0, config_1.processConfig)(argvMain, config);
|
|
49
|
+
}
|
|
50
|
+
if (command === 'auth') {
|
|
51
|
+
return await (0, auth_1.processAuth)(argvMain, config);
|
|
52
|
+
}
|
|
53
|
+
if (command === 'lambda') {
|
|
54
|
+
return await (0, lambda_1.processLambda)(argvMain);
|
|
55
|
+
}
|
|
56
|
+
if (command === 'scan') {
|
|
57
|
+
return await (0, processScan_1.processScan)(argvMain);
|
|
58
|
+
}
|
|
59
|
+
if (command === 'audit') {
|
|
60
|
+
return await (0, processAudit_1.processAudit)(argvMain);
|
|
61
|
+
}
|
|
62
|
+
if (command === 'help' ||
|
|
63
|
+
argvMain.includes('--help') ||
|
|
64
|
+
Object.keys(mainOptions).length === 0) {
|
|
65
|
+
console.log(mainUsageGuide);
|
|
66
|
+
}
|
|
67
|
+
else if (mainOptions._unknown !== undefined) {
|
|
68
|
+
const foundCommand = (0, errorHandling_1.findCommandOnError)(mainOptions._unknown);
|
|
69
|
+
foundCommand
|
|
70
|
+
? console.log(`Unknown Command: Did you mean "${foundCommand}"? \nUse "${foundCommand} --help" for the full list of options`)
|
|
71
|
+
: console.log(`Unknown Command: ${command} \nUse --help for the full list`);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.log(`Unknown Command: ${command} \nUse --help for the full list`);
|
|
75
|
+
}
|
|
76
|
+
process.exit(9);
|
|
59
77
|
}
|
|
60
78
|
else {
|
|
61
|
-
console.log('
|
|
79
|
+
console.log('Contrast supports Node versions >=16.13.2 <17. Please use one of those versions.');
|
|
62
80
|
process.exit(9);
|
|
63
81
|
}
|
|
64
82
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const commonApi_1 = require("../utils/commonApi");
|
|
4
|
+
function generateSbom(config) {
|
|
5
|
+
const client = (0, commonApi_1.getHttpClient)(config);
|
|
6
|
+
return client
|
|
7
|
+
.getSbom(config)
|
|
8
|
+
.then((res) => {
|
|
9
|
+
if (res.statusCode === 200) {
|
|
10
|
+
return res.body;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
console.log('Unable to retrieve Software Bill of Materials (SBOM)');
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
.catch((err) => {
|
|
17
|
+
console.log(err);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
exports.default = generateSbom;
|
package/dist/scan/help.js
CHANGED
|
@@ -21,14 +21,16 @@ const scanUsageGuide = commandLineUsage([
|
|
|
21
21
|
optionList: constants.commandLineDefinitions.scanOptionDefinitions,
|
|
22
22
|
hide: [
|
|
23
23
|
'project-id',
|
|
24
|
-
'language',
|
|
25
24
|
'organization-id',
|
|
26
25
|
'api-key',
|
|
27
26
|
'authorization',
|
|
28
27
|
'host',
|
|
29
28
|
'proxy',
|
|
29
|
+
'help',
|
|
30
30
|
'ff',
|
|
31
|
-
'ignore-cert-errors'
|
|
31
|
+
'ignore-cert-errors',
|
|
32
|
+
'verbose',
|
|
33
|
+
'debug'
|
|
32
34
|
]
|
|
33
35
|
},
|
|
34
36
|
{
|