@contrast/contrast 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -1,33 +1,43 @@
1
- # Contrast Command Line Interface
1
+ # Contrast Free
2
2
 
3
- - [About Contrast CLI](#about-contrast-cli)
4
- - [Requirements](#requirements)
5
- - [Step 1 Install](#step-1--install)
6
- - [Installation via Homebrew](#installation-via-homebrew)
7
- - [Install NPM / YARN](#install-npm--yarn)
8
- - [Install with binaries](#install-with-binaries)
9
- - [Step 2 – Authenticate](#step-2--authenticate)
10
- - [Step 3 – Scan](#step-3--scan)
11
- - [Usage](#usage)
12
- - [Commands](#commands)
13
- - [Examples of usage](#examples-of-usage)
14
- - [Example of Results](#example-of-results)
15
- - [Dependencies Vulnerabilities](#dependencies-vulnerabilities)
16
- - [Least Privilege](#least-privilege)
3
+ - The fastest and most accurate SAST scanner.
4
+ - Immediate and actionable results — scan code and serverless environments.
5
+ - A frictionless and seamless sign-in process with GitHub or Google Account. From start to finish in minutes.
6
+ - By running a scan on your lambda functions, you can find: Least privilege identity and access management (IAM) vulnerabilities (over permissive policies) and remediation.
17
7
 
18
- ## About Contrast CLI
8
+ ## Install
19
9
 
20
- Contrast CLI helps you find & fix security issues on AWS lambda functions (supports **Java** and **Python**)
21
- By running a scan on your lambda functions, you can find:
10
+ ```
11
+ npm install -g @contrast/contrast
12
+ ```
13
+
14
+ ## Authenticate
15
+
16
+ Authenticate by entering contrast auth in the terminal.
17
+
18
+ In the resulting browser window, log in and authenticate with your GitHub or Google credentials.
19
+
20
+ ## Run a scan
21
+
22
+ ### SAST scan
23
+
24
+ ####Requirements
25
+ Make sure you have the correct file types to scan.
22
26
 
23
- - Least privilege identity and access management (IAM) vulnerabilities (over permissive policies) and remediation
24
- - The Common Vulnerabilities and Exposures (CVE) from your libraries (Vulnerable Dependencies) and remediation
27
+ - Upload a .jar or .war file to scan a Java project for analysis
28
+ - Upload a .js or .zip file to scan a JavaScript project for analysis
29
+ - Upload a .exe. or .zip file to scan a .NET c# web forms project
25
30
 
26
- ## Requirements
31
+ Start scanning
27
32
 
28
- - AWS credentials should be **available** on your local configure (usually `~/.aws/credentials`)
29
- - You have an option run lambda scan with your aws-profile to pass `--profile`
30
- - You also can export different credentials
33
+ Use the Contrast scan command `contrast scan`
34
+
35
+ ### Lambda function scan
36
+
37
+ ####Requirements
38
+
39
+ - Currently supports Java and Python functions on AWS.
40
+ Configure AWS credentials on your local environment by running the commands with your credentials:
31
41
 
32
42
  ```shell
33
43
  export AWS_DEFAULT_REGION=<YOUR_AWS_REGION>
@@ -35,148 +45,100 @@ export AWS_ACCESS_KEY_ID=<YOUR_ACCESS_KEY_ID>
35
45
  export AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>
36
46
  ```
37
47
 
38
- These permissions are required to gather all required information on an AWS Lambda to use the `contrast lambda` command:
39
-
40
- - Lambda: [GetFunction](https://docs.aws.amazon.com/lambda/latest/dg/API_GetFunction.html) | [GetLayerVersion](https://docs.aws.amazon.com/lambda/latest/dg/API_GetLayerVersion.html)
41
- - IAM: [GetRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetRolePolicy.html) | [GetPolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicy.html) | [GetPolicyVersion](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicyVersion.html) | [ListRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListRolePolicies.html) | [ListAttachedRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAttachedRolePolicies.html)
42
-
43
- Policy example:
44
-
45
- ```json
46
- {
47
- "Version": "2012-10-17",
48
- "Statement": [
49
- {
50
- "Sid": "VisualEditor0",
51
- "Effect": "Allow",
52
- "Action": [
53
- "iam:GetPolicyVersion",
54
- "iam:GetPolicy",
55
- "lambda:GetLayerVersion",
56
- "lambda:GetFunction",
57
- "iam:ListAttachedRolePolicies",
58
- "iam:ListRolePolicies",
59
- "iam:GetRolePolicy"
60
- ],
61
- "Resource": [
62
- "arn:aws:lambda:*:YOUR_ACCOUNT:layer:*:*",
63
- "arn:aws:lambda:*:YOUR_ACCOUNT:function:*",
64
- "arn:aws:iam::YOUR_ACCOUNT:role/*",
65
- "arn:aws:iam::YOUR_ACCOUNT:policy/*"
66
- ]
67
- }
68
- ]
69
- }
70
- ```
48
+ - AWS credentials should be available on your local configure (usually **~/.aws/credentials**). You have an option to run a lambda scan with your aws-profile to pass --profile. You also can export different credentials.
71
49
 
72
- ## Step 1 Install
50
+ - These permissions are required to gather all required information on an AWS Lambda to use the `contrast lambda` command:
73
51
 
74
- ### Installation via Homebrew
52
+ - Lambda: [GetFunction](https://docs.aws.amazon.com/lambda/latest/dg/API_GetFunction.html) | [GetLayerVersion](https://docs.aws.amazon.com/lambda/latest/dg/API_GetLayerVersion.html)
53
+ - IAM: [GetRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetRolePolicy.html) | [GetPolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicy.html) | [GetPolicyVersion](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicyVersion.html) | [ListRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListRolePolicies.html) | [ListAttachedRolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAttachedRolePolicies.html)
75
54
 
76
- - `brew tap Contrast-Security-OSS/homebrew-contrast`
77
- - `brew install contrast`
55
+ ### Start scanning
78
56
 
79
- ### Install NPM / YARN
57
+ Use contrast lambda to scan your AWS Lambda functions.
58
+ `contrast lambda --function-name MyFunctionName --region my-aws-region`
80
59
 
81
- - `npm install -g @contrast/contrast`
82
- - `yarn global add @contrast/contrast`
60
+ ## Contrast Free commands
83
61
 
84
- ### Install with binaries
62
+ ### auth
85
63
 
86
- - Go to [https://pkg.contrastsecurity.com/ui/repos/tree/General/cli](https://pkg.contrastsecurity.com/ui/repos/tree/General/cli)
87
- - Select your operating system and download the package
88
- - You must allow **execute permissions** on the file depending on your OS
64
+ Authenticate Contrast using your GitHub or Google account. A new browser window will open for login.
89
65
 
90
- | Architecture | Link |
91
- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
92
- | macOS | [https://pkg.contrastsecurity.com/ui/repos/tree/General/cli/1.0.0/mac/contrast](https://pkg.contrastsecurity.com/ui/repos/tree/General/cli/1.0.0/mac/contrast) |
93
- | Linux | [https://pkg.contrastsecurity.com/ui/repos/tree/General/cli/1.0.0/linux/contrast](https://pkg.contrastsecurity.com/ui/repos/tree/General/cli/1.0.0/linux/contrast) |
94
- | Windows | [https://pkg.contrastsecurity.com/ui/repos/tree/General/cli/1.0.0/windows/contrast.exe](https://pkg.contrastsecurity.com/ui/repos/tree/General/cli/1.0.0/windows/contrast.exe) |
66
+ **Usage:** `contrast auth`
95
67
 
96
- ## Step 2 – Authenticate
68
+ ### config
97
69
 
98
- Authenticate using your existing [GitHub](https://github.com/) or [Google](https://www.google.com/) account.
70
+ Displays stored credentials.
99
71
 
100
- ```shell
101
- contrast auth
102
- ```
72
+ **Usage:** `contrast config`
103
73
 
104
- ## Step 3 – Scan
74
+ **Options:**
105
75
 
106
- Use contrast lambda to scan your AWS Lambda functions:
76
+ - **-c, --clear** - Removes stored credentials.
107
77
 
108
- ```shell
109
- contrast lambda --function-name MyFunctionName --region my-aws-region
110
- ```
78
+ ### scan
111
79
 
112
- ### Usage
80
+ Performs a security SAST scan.
113
81
 
114
- - `contrast [command] [option]`
115
- - `contrast lambda --list-functions`
116
- - `contrast lambda --function-name <function> [options]`
117
- - `contrast lambda --help`
82
+ **Usage:** `contrast scan [option]`
118
83
 
119
- ### Commands
84
+ **Options:**
120
85
 
121
- - `auth` - Authenticate Contrast using your `Github` or `Google` account
122
- - `lambda` - Perform scan on AWS Lambda function
123
- - `--function-name` - Name of AWS lambda function to scan
124
- - `--list-functions` - List all available lambda functions to scan
125
- - `--endpoint-url` (optional) - AWS Endpoint override, works like in AWS CLI
126
- - `--region` (optional) - Region override, default to AWS_DEAFAULT_REGION env var, works like in AWS CLI
127
- - `--profile` (optional) - AWS configuration profile override, works like in AWS CLI
128
- - `--json-output` (optional) - Return response in JSON (versus default human readable format)
129
- - `--verbose` (optional) - Returns extended information to the terminal
130
- - `--help` Displays usage guide
131
- - `version` - Displays version of Contrast CLI
132
- - `config` - Displays stored credentials
133
- - `config --clear` - Removes stored credentials
134
- - `help` - Displays usage guide
86
+ - **contrast scan --file** Path of the file you want to scan. Contrast searches for a .jar, .war, .js. or .zip file in the working directory if a file is not specified.
87
+ Alias: **--f**
135
88
 
136
- ### Examples of usage
89
+ - **contrast scan --name**
90
+ Contrast project name. If not specified, Contrast uses contrast.settings to identify the project or creates a project.
91
+ Alias: **–n**
92
+ - **contrast scan --save**
93
+ Download the results to a Static Analysis Results Interchange Format (SARIF) file.
94
+ Alias: **-s**
137
95
 
138
- show help for lambda
96
+ - **contrast scan --timeout**
97
+ Time in seconds to wait for the scan to complete. Default value is 300 seconds.
98
+ Alias: **-t**
139
99
 
140
- ```shell
141
- contrast lambda --help
142
- ```
100
+ ### lambda
143
101
 
144
- get list of all available functions for scan
102
+ Name of AWS lambda function to scan.
145
103
 
146
- ```shell
147
- contrast lambda --list-functions
148
- ```
149
-
150
- scan lambda function in specific region
104
+ **Usage:** `contrast lambda --function-name`
151
105
 
152
- ```shell
153
- contrast lambda -f myFunctionName --region eu-cental-1
154
- ```
106
+ **Options:**
155
107
 
156
- scan lambda function with different profile
108
+ - **contrast lambda --function-name --endpoint-url**
109
+ AWS Endpoint override. Similar to AWS CLI.
110
+ Alias: **-e**
157
111
 
158
- ```shell
159
- contrast lambda -f myFunctionName --profile myDevProfile
160
- ```
112
+ - **contrast lambda --function-name --region**
113
+ Region override. Defaults to AWS_DEFAULT_REGION. Similar to AWS CLI.
114
+ Alias: **-r**
161
115
 
162
- scan lambda function with full details in json format
116
+ - **contrast lambda --function-name --profile**
117
+ AWS configuration profile override. Similar to AWS CLI.
118
+ Alias: **-p**
163
119
 
164
- ```shell
165
- contrast lambda -f myFunctionName --json-output
166
- ```
120
+ - **contrast lambda --function-name --json**
121
+ Return response in JSON (versus default human-readable format).
122
+ Alias: **-j**
167
123
 
168
- scan lambda function with all possible flags
124
+ - **contrast lambda -–function-name -–verbose**
125
+ Returns extended information to the terminal.
126
+ Alias: **-v**
169
127
 
170
- ```shell
171
- contrast lambda -f myFunctionName --region eu-cental-1 --profile myDevProfile --endpoint-url https://adbb-94-188-169-138.eu.ngrok.io/ --verbose --json-output
172
- ```
128
+ - **contrast lambda -–function-name --list-functions**
129
+ Lists all available lambda functions to scan.
173
130
 
174
- ## Example of Results
131
+ - **contrast lambda --function-name -–help**
132
+ Displays usage guide.
133
+ Alias: **-h**
175
134
 
176
- ### Dependency vulnerabilities
135
+ ### help
177
136
 
178
- ![image](https://user-images.githubusercontent.com/289035/166472066-fa4a179c-cbef-436f-bc8e-9cbb8a4b465e.png)
137
+ Displays usage guide. To list detailed help for any CLI command, add the -h or --help flag to the command.
138
+ **Usage:** `contrast scan --help`
139
+ Alias: **-h**
179
140
 
180
- ### Least Privilege
141
+ ### version
181
142
 
182
- ![image](https://user-images.githubusercontent.com/289035/166480331-54ec133f-3379-4023-9fe4-c0db352c5b16.png)
143
+ Displays version of Contrast CLI.
144
+ **Usage:** `contrast version` Alias: **-v**, **--version**
@@ -3,8 +3,9 @@ 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.libraryAnalysisError = exports.handleResponseErrors = exports.getErrorMessage = exports.generalError = exports.hostWarningError = exports.failOptionError = exports.proxyError = exports.forbiddenError = exports.badRequestError = exports.unauthenticatedError = exports.genericError = void 0;
6
+ exports.approximateCommandOnError = 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");
8
9
  const handleResponseErrors = (res, api) => {
9
10
  if (res.statusCode === 400) {
10
11
  api === 'catalogue' ? badRequestError(true) : badRequestError(false);
@@ -106,3 +107,32 @@ const generalError = (header, message) => {
106
107
  console.log(finalMessage);
107
108
  };
108
109
  exports.generalError = generalError;
110
+ const approximateCommandOnError = (unknownOptions) => {
111
+ const commandKeywords = {
112
+ auth: 'auth',
113
+ audit: 'audit',
114
+ scan: 'scan',
115
+ lambda: 'lambda',
116
+ config: 'config'
117
+ };
118
+ const sortedUnknownOptions = (0, lodash_1.sortBy)(unknownOptions, param => param === 'auth' ||
119
+ param === 'audit' ||
120
+ param === 'scan' ||
121
+ param === 'lambda' ||
122
+ param === 'config'
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
+ };
137
+ };
138
+ exports.approximateCommandOnError = approximateCommandOnError;
@@ -12,7 +12,7 @@ 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.1';
15
+ const APP_VERSION = '1.0.2';
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';
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  const { lambda } = require('./lambda');
3
+ const chalk = require('chalk');
3
4
  const en_locales = () => {
4
5
  return {
5
6
  successHeader: 'SUCCESS',
@@ -187,6 +188,7 @@ const en_locales = () => {
187
188
  noFileFoundScan: "We could't find a suitable file in your directories (we go 3 deep)",
188
189
  specifyFileScanError: 'Java Scan requires a .war or .jar file. Javascript Scan requires a .js or .zip file.\nTo start a Scan enter "contrast scan -f <path-to-file>"',
189
190
  populateProjectIdMessage: 'project ID is %s',
191
+ genericServiceError: 'returned with status code %s',
190
192
  permissionsError: 'You do not have the correct permissions here. \n Contact support@contrastsecurity.com to get this fixed.',
191
193
  scanErrorFileMessage: 'We only accept the following file types: \nJava - .jar, .war \nJavaScript - .js or .zip files',
192
194
  helpAuthSummary: 'Authenticate Contrast using your Github or Google account',
@@ -207,13 +209,17 @@ const en_locales = () => {
207
209
  scanOptionsFileNameSummary: 'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .js, .exe or .zip file in the working directory.',
208
210
  scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
209
211
  authSuccessMessage: 'Authentication successful',
210
- runAuthSuccessMessage: 'Now you can use Contrast CLI',
212
+ runAuthSuccessMessage: "Now you can use Contrast CLI \nRun 'contrast scan' on your file \n" +
213
+ "or 'contrast help' to learn more about the capabilities.",
211
214
  authWaitingMessage: 'Waiting for auth...',
212
215
  authTimedOutMessage: 'Auth Timed out, try again',
213
216
  zipErrorScan: 'We only support zip files for JAVASCRIPT language, please set the flag --language JAVASCRIPT',
214
217
  unknownFileErrorScan: 'Unsupported file selected for Scan.',
215
218
  foundScanFile: 'found: %s',
216
- foundDetailedVulnerabilities: '%s Critical %s High %s Medium %s Low %s Note',
219
+ foundDetailedVulnerabilities: chalk.bold('%s Critical') +
220
+ ' | ' +
221
+ chalk.bold('%s High') +
222
+ ' | %s Medium | %s Low | %s Note',
217
223
  requiredParams: 'All required parameters are not present.',
218
224
  timeoutScan: 'Timeout set to 5 minutes.',
219
225
  searchingScanFileDirectory: 'Searching for file to scan from %s...',
package/dist/index.js CHANGED
@@ -30,7 +30,9 @@ const getMainOption = () => {
30
30
  const start = async () => {
31
31
  const { mainOptions, argv: argvMain } = getMainOption();
32
32
  const command = mainOptions.command != undefined ? mainOptions.command.toLowerCase() : '';
33
- if (command === 'version') {
33
+ if (command === 'version' ||
34
+ argvMain.includes('--v') ||
35
+ argvMain.includes('--version')) {
34
36
  console.log(constants_2.APP_VERSION);
35
37
  return;
36
38
  }
@@ -57,6 +59,7 @@ const start = async () => {
57
59
  }
58
60
  else {
59
61
  console.log('Unknown Command: ' + command + ' \nUse --help for the full list');
62
+ process.exit(9);
60
63
  }
61
64
  };
62
65
  start();
package/dist/scan/scan.js CHANGED
@@ -18,6 +18,7 @@ const stripMustacheTags = oldString => {
18
18
  return oldString
19
19
  .replace(/\n/g, ' ')
20
20
  .replace(/{{.*?}}/g, '\n')
21
+ .replace(/\$\$LINK_DELIM\$\$/g, '\n')
21
22
  .replace(/\s+/g, ' ')
22
23
  .trim();
23
24
  };
@@ -50,6 +51,7 @@ const sendScan = async (config) => {
50
51
  if (res.statusCode === 403) {
51
52
  console.log(i18n.__('permissionsError'));
52
53
  }
54
+ console.log(i18n.__('genericServiceError', res.statusCode));
53
55
  process.exit(1);
54
56
  }
55
57
  })
@@ -64,7 +66,10 @@ const formatScanOutput = (overview, results) => {
64
66
  console.log(i18n.__('scanNoVulnerabilitiesFound'));
65
67
  }
66
68
  else {
67
- console.log(chalk.bold('Here are your top priorities to fix'));
69
+ let message = overview.critical || overview.high
70
+ ? 'Here are your top priorities to fix'
71
+ : "No major issues, here's what we found";
72
+ console.log(chalk.bold(message));
68
73
  console.log();
69
74
  const groups = getGroups(results.content);
70
75
  groups.forEach(entry => {
@@ -102,7 +107,7 @@ const getGroups = content => {
102
107
  groupResultsObj.severity = resultEntry.severity;
103
108
  groupResultsObj.recommendation = resultEntry.recommendation
104
109
  ? stripMustacheTags(resultEntry.recommendation)
105
- : '';
110
+ : 'No Recommendations Data Found';
106
111
  groupResultsObj.lineInfoSet.add(formattedCodeLine(resultEntry));
107
112
  }
108
113
  });
@@ -40,6 +40,7 @@ const returnScanResults = async (config, codeArtifactId, timeout, startScanSpinn
40
40
  if (result.body.status === 'FAILED') {
41
41
  complete = true;
42
42
  oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.');
43
+ console.log(result.body.errorMessage);
43
44
  process.exit(1);
44
45
  }
45
46
  }
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const getAuth = parsedCLIOptions => {
2
+ const getAuth = (parsedCLIOptions = {}) => {
3
3
  let params = {};
4
4
  params.apiKey = parsedCLIOptions['apiKey'];
5
5
  params.authorization = parsedCLIOptions['authorization'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/contrast",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Contrast Security's command line tool",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,4 +1,5 @@
1
1
  import i18n from 'i18n'
2
+ import { sortBy } from 'lodash'
2
3
 
3
4
  const handleResponseErrors = (res: any, api: string) => {
4
5
  if (res.statusCode === 400) {
@@ -104,7 +105,7 @@ const getErrorMessage = (header: string, message?: string) => {
104
105
  const multiLine = message?.includes('\n')
105
106
  let finalMessage = ''
106
107
 
107
- // i18n split the line if it includes "\n"
108
+ // i18n split the line if it includes '\n'
108
109
  if (multiLine) {
109
110
  finalMessage = `\n${message}`
110
111
  } else if (message) {
@@ -119,6 +120,45 @@ const generalError = (header: string, message?: string) => {
119
120
  console.log(finalMessage)
120
121
  }
121
122
 
123
+ const approximateCommandOnError = (unknownOptions: string[]) => {
124
+ const commandKeywords = {
125
+ auth: 'auth',
126
+ audit: 'audit',
127
+ scan: 'scan',
128
+ lambda: 'lambda',
129
+ config: 'config'
130
+ }
131
+ const sortedUnknownOptions = sortBy(unknownOptions, param =>
132
+ param === 'auth' ||
133
+ param === 'audit' ||
134
+ param === 'scan' ||
135
+ param === 'lambda' ||
136
+ param === 'config'
137
+ ? 0
138
+ : 1
139
+ )
140
+
141
+ const foundCommands = sortedUnknownOptions.filter(
142
+ // @ts-ignore
143
+ command => commandKeywords[command]
144
+ )
145
+
146
+ const parsedUnknownOptions = sortedUnknownOptions
147
+ .toString()
148
+ .replace(/,/g, ' ')
149
+
150
+ const approximateParams = parsedUnknownOptions
151
+ .replace(new RegExp(foundCommands.join('|'), 'g'), '')
152
+ .trim()
153
+
154
+ const approximateCommand = `${foundCommands[0]} ${approximateParams}`
155
+
156
+ return {
157
+ approximateCommand,
158
+ approximateCommandKeyword: foundCommands[0]
159
+ }
160
+ }
161
+
122
162
  export {
123
163
  genericError,
124
164
  unauthenticatedError,
@@ -130,5 +170,6 @@ export {
130
170
  generalError,
131
171
  getErrorMessage,
132
172
  handleResponseErrors,
133
- libraryAnalysisError
173
+ libraryAnalysisError,
174
+ approximateCommandOnError
134
175
  }
@@ -15,7 +15,7 @@ const HIGH = 'HIGH'
15
15
  const CRITICAL = 'CRITICAL'
16
16
 
17
17
  const APP_NAME = 'contrast'
18
- const APP_VERSION = '1.0.1'
18
+ const APP_VERSION = '1.0.2'
19
19
  const TIMEOUT = 120000
20
20
  const AUTH_UI_URL = 'https://cli-auth.contrastsecurity.com'
21
21
  const AUTH_CALLBACK_URL = 'https://cli-auth-api.contrastsecurity.com'
@@ -1,4 +1,5 @@
1
1
  const { lambda } = require('./lambda')
2
+ const chalk = require('chalk')
2
3
 
3
4
  const en_locales = () => {
4
5
  return {
@@ -288,6 +289,7 @@ const en_locales = () => {
288
289
  specifyFileScanError:
289
290
  'Java Scan requires a .war or .jar file. Javascript Scan requires a .js or .zip file.\nTo start a Scan enter "contrast scan -f <path-to-file>"',
290
291
  populateProjectIdMessage: 'project ID is %s',
292
+ genericServiceError: 'returned with status code %s',
291
293
  permissionsError:
292
294
  'You do not have the correct permissions here. \n Contact support@contrastsecurity.com to get this fixed.',
293
295
  scanErrorFileMessage:
@@ -316,7 +318,9 @@ const en_locales = () => {
316
318
  'Path of the file you want to scan. If no file is specified, Contrast searches for a .jar, .war, .js, .exe or .zip file in the working directory.',
317
319
  scanOptionsVerboseSummary: ' Returns extended information to the terminal.',
318
320
  authSuccessMessage: 'Authentication successful',
319
- runAuthSuccessMessage: 'Now you can use Contrast CLI',
321
+ runAuthSuccessMessage:
322
+ "Now you can use Contrast CLI \nRun 'contrast scan' on your file \n" +
323
+ "or 'contrast help' to learn more about the capabilities.",
320
324
  authWaitingMessage: 'Waiting for auth...',
321
325
  authTimedOutMessage: 'Auth Timed out, try again',
322
326
  zipErrorScan:
@@ -324,7 +328,10 @@ const en_locales = () => {
324
328
  unknownFileErrorScan: 'Unsupported file selected for Scan.',
325
329
  foundScanFile: 'found: %s',
326
330
  foundDetailedVulnerabilities:
327
- '%s Critical %s High %s Medium %s Low %s Note',
331
+ chalk.bold('%s Critical') +
332
+ ' | ' +
333
+ chalk.bold('%s High') +
334
+ ' | %s Medium | %s Low | %s Note',
328
335
  requiredParams: 'All required parameters are not present.',
329
336
  timeoutScan: 'Timeout set to 5 minutes.',
330
337
  searchingScanFileDirectory: 'Searching for file to scan from %s...',
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ import { APP_NAME, APP_VERSION } from './constants/constants'
8
8
  import { processLambda } from './lambda/lambda'
9
9
  import { localConfig } from './utils/getConfig'
10
10
  import findLatestCLIVersion from './common/findLatestCLIVersion'
11
+ import { approximateCommandOnError } from './common/errorHandling'
11
12
 
12
13
  const {
13
14
  commandLineDefinitions: { mainUsageGuide, mainDefinition }
@@ -33,7 +34,11 @@ const start = async () => {
33
34
  const { mainOptions, argv: argvMain } = getMainOption()
34
35
  const command =
35
36
  mainOptions.command != undefined ? mainOptions.command.toLowerCase() : ''
36
- if (command === 'version') {
37
+ if (
38
+ command === 'version' ||
39
+ argvMain.includes('--v') ||
40
+ argvMain.includes('--version')
41
+ ) {
37
42
  console.log(APP_VERSION)
38
43
  return
39
44
  }
@@ -70,6 +75,7 @@ const start = async () => {
70
75
  console.log(
71
76
  'Unknown Command: ' + command + ' \nUse --help for the full list'
72
77
  )
78
+ process.exit(9)
73
79
  }
74
80
  }
75
81
 
package/src/scan/scan.js CHANGED
@@ -19,6 +19,7 @@ const stripMustacheTags = oldString => {
19
19
  return oldString
20
20
  .replace(/\n/g, ' ')
21
21
  .replace(/{{.*?}}/g, '\n')
22
+ .replace(/\$\$LINK_DELIM\$\$/g, '\n')
22
23
  .replace(/\s+/g, ' ')
23
24
  .trim()
24
25
  }
@@ -58,6 +59,7 @@ const sendScan = async config => {
58
59
  if (res.statusCode === 403) {
59
60
  console.log(i18n.__('permissionsError'))
60
61
  }
62
+ console.log(i18n.__('genericServiceError', res.statusCode))
61
63
  process.exit(1)
62
64
  }
63
65
  })
@@ -69,12 +71,15 @@ const sendScan = async config => {
69
71
 
70
72
  const formatScanOutput = (overview, results) => {
71
73
  console.log()
72
- //check for no vulnerabilities and show a different message
73
74
 
74
75
  if (results.content.length === 0) {
75
76
  console.log(i18n.__('scanNoVulnerabilitiesFound'))
76
77
  } else {
77
- console.log(chalk.bold('Here are your top priorities to fix'))
78
+ let message =
79
+ overview.critical || overview.high
80
+ ? 'Here are your top priorities to fix'
81
+ : "No major issues, here's what we found"
82
+ console.log(chalk.bold(message))
78
83
  console.log()
79
84
 
80
85
  const groups = getGroups(results.content)
@@ -130,7 +135,7 @@ const getGroups = content => {
130
135
  groupResultsObj.severity = resultEntry.severity
131
136
  groupResultsObj.recommendation = resultEntry.recommendation
132
137
  ? stripMustacheTags(resultEntry.recommendation)
133
- : ''
138
+ : 'No Recommendations Data Found'
134
139
  groupResultsObj.lineInfoSet.add(formattedCodeLine(resultEntry))
135
140
  }
136
141
  })
@@ -47,6 +47,7 @@ const returnScanResults = async (
47
47
  if (result.body.status === 'FAILED') {
48
48
  complete = true
49
49
  oraFunctions.failSpinner(startScanSpinner, 'Contrast Scan Failed.')
50
+ console.log(result.body.errorMessage)
50
51
  process.exit(1)
51
52
  }
52
53
  }
@@ -1,4 +1,4 @@
1
- const getAuth = parsedCLIOptions => {
1
+ const getAuth = (parsedCLIOptions = {}) => {
2
2
  let params = {}
3
3
  params.apiKey = parsedCLIOptions['apiKey']
4
4
  params.authorization = parsedCLIOptions['authorization']
@@ -1,30 +0,0 @@
1
- 'use strict'
2
- Object.defineProperty(exports, '__esModule', { value: true })
3
- exports.pollScanDetail = void 0
4
- const requestUtils_1 = require('../utils/requestUtils')
5
- const pollScanDetail = async (
6
- config,
7
- params,
8
- scanId,
9
- httpClient,
10
- pollCount,
11
- showProgress = false
12
- ) => {
13
- await (0, requestUtils_1.sleep)(5000)
14
- return httpClient.getFunctionScan(config, params, scanId).then(res => {
15
- const { resultsCount = 0 } = res?.body?.data?.scan || {}
16
- if (showProgress) {
17
- process.stdout.write(
18
- `\rScanning (${resultsCount} results found so far)${'.'.repeat(
19
- pollCount
20
- )}`
21
- )
22
- }
23
- if (res.statusCode === 200) {
24
- return res
25
- } else {
26
- throw Error(`Failed to get scan detail: ${res.statusCode} ${res.body}`)
27
- }
28
- })
29
- }
30
- exports.pollScanDetail = pollScanDetail
@@ -1,15 +0,0 @@
1
- 'use strict'
2
- const fg = require('fast-glob')
3
- const i18n = require('i18n')
4
- const findFile = async () => {
5
- console.log(i18n.__('searchingScanFileDirectory', process.cwd()))
6
- const entries = fg(['**/*.jar', '**/*.war', '**/*.zip', '**/*.dll'], {
7
- dot: false,
8
- deep: 3,
9
- onlyFiles: true
10
- })
11
- return entries
12
- }
13
- module.exports = {
14
- findFile
15
- }
@@ -1,31 +0,0 @@
1
- "use strict";
2
- const fg = require('fast-glob');
3
- const fs = require('fs');
4
- const i18n = require('i18n');
5
- const findFile = async () => {
6
- console.log(i18n.__('searchingScanFileDirectory', process.cwd()));
7
- return fg(['**/*.jar', '**/*.war', '**/*.zip', '**/*.dll'], {
8
- dot: false,
9
- deep: 3,
10
- onlyFiles: true
11
- });
12
- };
13
- const checkFilePermissions = file => {
14
- let readableFile = false;
15
- try {
16
- fs.accessSync(file, fs.constants.R_OK);
17
- return (readableFile = true);
18
- }
19
- catch (err) {
20
- console.log('Invalid permissions found on ', file);
21
- process.exit(0);
22
- }
23
- };
24
- const fileExists = path => {
25
- return fs.existsSync(path);
26
- };
27
- module.exports = {
28
- findFile,
29
- fileExists,
30
- checkFilePermissions
31
- };
@@ -1,12 +0,0 @@
1
- "use strict";
2
- const cliOptions = require('../parsedCLIOptions');
3
- const getGenericCommandLineParams = () => {
4
- return cliOptions.getCommandLineArgsGeneric();
5
- };
6
- const getSpecificCommandLineParams = (parameterList, optionDefinition) => {
7
- return cliOptions.getCommandLineArgsCustom(parameterList, optionDefinition);
8
- };
9
- module.exports = {
10
- getSpecificCommandLineParams,
11
- getGenericCommandLineParams
12
- };
@@ -1,6 +0,0 @@
1
- 'use strict'
2
- const fs = require('fs')
3
- const yaml = require('js-yaml')
4
- const getAuth = yamlPath => {
5
- const yamlParams = yaml.load(fs.readFileSync(yamlPath, 'utf8'))
6
- }