@contrast/contrast 1.0.1 → 1.0.4
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 +2 -0
- package/README.md +103 -133
- package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +26 -11
- package/dist/audit/languageAnalysisEngine/report/commonReportingFunctions.js +62 -234
- package/dist/audit/languageAnalysisEngine/report/models/reportLibraryModel.js +19 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportListModel.js +24 -0
- package/dist/audit/languageAnalysisEngine/report/models/reportSeverityModel.js +10 -0
- package/dist/audit/languageAnalysisEngine/report/reportingFeature.js +24 -129
- package/dist/audit/languageAnalysisEngine/report/utils/reportUtils.js +85 -0
- package/dist/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
- package/dist/commands/audit/auditController.js +6 -3
- 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 +8 -25
- package/dist/common/HTTPClient.js +30 -26
- package/dist/common/errorHandling.js +17 -1
- package/dist/common/versionChecker.js +32 -0
- package/dist/constants/constants.js +4 -2
- package/dist/constants/lambda.js +3 -1
- package/dist/constants/locales.js +41 -18
- package/dist/constants.js +39 -3
- package/dist/index.js +49 -28
- package/dist/lambda/help.js +22 -14
- package/dist/lambda/lambda.js +6 -0
- package/dist/sbom/generateSbom.js +20 -0
- package/dist/scan/help.js +4 -2
- package/dist/scan/models/groupedResultsModel.js +10 -0
- package/dist/scan/models/resultContentModel.js +2 -0
- package/dist/scan/models/scanResultsModel.js +11 -0
- package/dist/scan/populateProjectIdAndProjectName.js +1 -0
- package/dist/scan/saveResults.js +9 -10
- package/dist/scan/scan.js +99 -74
- package/dist/scan/scanConfig.js +20 -1
- package/dist/scan/scanController.js +7 -2
- package/dist/scan/scanResults.js +6 -0
- package/dist/utils/getConfig.js +3 -0
- package/dist/utils/paramsUtil/commandlineParams.js +1 -1
- package/dist/utils/requestUtils.js +1 -1
- package/dist/utils/saveFile.js +19 -0
- package/package.json +2 -2
- package/src/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js} +33 -15
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.ts +127 -0
- package/src/audit/languageAnalysisEngine/report/models/reportLibraryModel.ts +30 -0
- package/src/audit/languageAnalysisEngine/report/models/reportListModel.ts +32 -0
- package/src/audit/languageAnalysisEngine/report/models/reportSeverityModel.ts +9 -0
- package/src/audit/languageAnalysisEngine/report/reportingFeature.ts +56 -0
- package/src/audit/languageAnalysisEngine/report/utils/reportUtils.ts +110 -0
- package/src/audit/languageAnalysisEngine/sendSnapshot.js +3 -1
- package/src/commands/audit/auditController.ts +12 -3
- 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 +8 -29
- package/src/common/HTTPClient.js +42 -36
- package/src/common/errorHandling.ts +29 -2
- package/src/common/versionChecker.ts +41 -0
- package/src/constants/constants.js +5 -4
- package/src/constants/lambda.js +3 -1
- package/src/constants/locales.js +51 -19
- package/src/constants.js +44 -3
- package/src/index.ts +63 -31
- package/src/lambda/help.ts +22 -14
- package/src/lambda/lambda.ts +8 -0
- package/src/sbom/generateSbom.ts +17 -0
- package/src/scan/help.js +4 -2
- package/src/scan/models/groupedResultsModel.ts +18 -0
- package/src/scan/models/resultContentModel.ts +86 -0
- package/src/scan/models/scanResultsModel.ts +52 -0
- package/src/scan/populateProjectIdAndProjectName.js +1 -0
- package/src/scan/saveResults.js +8 -9
- package/src/scan/scan.ts +192 -0
- package/src/scan/scanConfig.js +26 -1
- package/src/scan/scanController.js +11 -2
- package/src/scan/scanResults.js +11 -0
- package/src/utils/getConfig.ts +12 -0
- package/src/utils/paramsUtil/commandlineParams.js +1 -1
- package/src/utils/requestUtils.js +1 -1
- package/src/utils/saveFile.js +19 -0
- package/dist/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -17
- package/dist/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -81
- package/dist/common/findLatestCLIVersion.js +0 -23
- package/dist/lambda/scanDetail.js +0 -30
- package/dist/scan/fileFinder.js +0 -15
- package/dist/utils/fileUtils.js +0 -31
- package/dist/utils/paramsUtil/genericCommandLineParams.js +0 -12
- package/dist/utils/paramsUtil/yamlParams.js +0 -6
- package/src/audit/languageAnalysisEngine/report/checkIgnoreDevDep.js +0 -27
- package/src/audit/languageAnalysisEngine/report/commonReportingFunctions.js +0 -303
- package/src/audit/languageAnalysisEngine/report/newReportingFeature.js +0 -124
- package/src/audit/languageAnalysisEngine/report/reportingFeature.js +0 -190
- package/src/common/findLatestCLIVersion.ts +0 -27
- package/src/scan/scan.js +0 -162
package/.prettierignore
CHANGED
package/README.md
CHANGED
|
@@ -1,33 +1,46 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CodeSec by Contrast Security
|
|
2
2
|
|
|
3
|
-
|
|
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
|
+
CodeSec delivers:
|
|
17
4
|
|
|
18
|
-
|
|
5
|
+
- The fastest and most accurate SAST scanner.
|
|
6
|
+
- Immediate and actionable results — scan code and serverless environments.
|
|
7
|
+
- A frictionless and seamless sign-in process with GitHub or Google Account. From start to finish in minutes.
|
|
8
|
+
- By running a scan on your lambda functions, you can find: Least privilege identity and access management (IAM) vulnerabilities (over permissive policies) and remediation.
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
By running a scan on your lambda functions, you can find:
|
|
10
|
+
## Install
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
```shell
|
|
13
|
+
npm install -g @contrast/contrast
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Authenticate
|
|
17
|
+
|
|
18
|
+
Authenticate by entering contrast auth in the terminal.
|
|
19
|
+
|
|
20
|
+
In the resulting browser window, log in and authenticate with your GitHub or Google credentials.
|
|
21
|
+
|
|
22
|
+
## Run a scan
|
|
23
|
+
|
|
24
|
+
### SAST scan
|
|
25
|
+
|
|
26
|
+
#### Scan Requirements
|
|
27
|
+
|
|
28
|
+
Make sure you have the correct file types to scan.
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
- Upload a .jar or .war file to scan a Java project for analysis
|
|
31
|
+
- Upload a .js or .zip file to scan a JavaScript project for analysis
|
|
32
|
+
- Upload a .exe. or .zip file to scan a .NET c# web forms project
|
|
27
33
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
Start scanning
|
|
35
|
+
|
|
36
|
+
Use the Contrast scan command `contrast scan`
|
|
37
|
+
|
|
38
|
+
### Lambda function scan
|
|
39
|
+
|
|
40
|
+
#### Lambda Requirements
|
|
41
|
+
|
|
42
|
+
- Currently supports Java and Python functions on AWS.
|
|
43
|
+
Configure AWS credentials on your local environment by running the commands with your credentials:
|
|
31
44
|
|
|
32
45
|
```shell
|
|
33
46
|
export AWS_DEFAULT_REGION=<YOUR_AWS_REGION>
|
|
@@ -35,148 +48,105 @@ export AWS_ACCESS_KEY_ID=<YOUR_ACCESS_KEY_ID>
|
|
|
35
48
|
export AWS_SECRET_ACCESS_KEY=<YOUR_SECRET_ACCESS_KEY>
|
|
36
49
|
```
|
|
37
50
|
|
|
38
|
-
|
|
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
|
-
```
|
|
51
|
+
- 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
52
|
|
|
72
|
-
|
|
53
|
+
- These permissions are required to gather all required information on an AWS Lambda to use the `contrast lambda` command:
|
|
73
54
|
|
|
74
|
-
|
|
55
|
+
- 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)
|
|
56
|
+
- 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
57
|
|
|
76
|
-
|
|
77
|
-
- `brew install contrast`
|
|
58
|
+
### Start scanning
|
|
78
59
|
|
|
79
|
-
|
|
60
|
+
Use contrast lambda to scan your AWS Lambda functions.
|
|
61
|
+
`contrast lambda --function-name MyFunctionName --region my-aws-region`
|
|
80
62
|
|
|
81
|
-
|
|
82
|
-
- `yarn global add @contrast/contrast`
|
|
63
|
+
## Contrast commands
|
|
83
64
|
|
|
84
|
-
###
|
|
65
|
+
### auth
|
|
85
66
|
|
|
86
|
-
|
|
87
|
-
- Select your operating system and download the package
|
|
88
|
-
- You must allow **execute permissions** on the file depending on your OS
|
|
67
|
+
Authenticate Contrast using your GitHub or Google account. A new browser window will open for login.
|
|
89
68
|
|
|
90
|
-
|
|
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) |
|
|
69
|
+
**Usage:** `contrast auth`
|
|
95
70
|
|
|
96
|
-
|
|
71
|
+
### config
|
|
97
72
|
|
|
98
|
-
|
|
73
|
+
Displays stored credentials.
|
|
99
74
|
|
|
100
|
-
|
|
101
|
-
contrast auth
|
|
102
|
-
```
|
|
75
|
+
**Usage:** `contrast config`
|
|
103
76
|
|
|
104
|
-
|
|
77
|
+
**Options:**
|
|
105
78
|
|
|
106
|
-
|
|
79
|
+
- **-c, --clear** - Removes stored credentials.
|
|
107
80
|
|
|
108
|
-
|
|
109
|
-
contrast lambda --function-name MyFunctionName --region my-aws-region
|
|
110
|
-
```
|
|
81
|
+
### scan
|
|
111
82
|
|
|
112
|
-
|
|
83
|
+
Performs a security SAST scan.
|
|
113
84
|
|
|
114
|
-
|
|
115
|
-
- `contrast lambda --list-functions`
|
|
116
|
-
- `contrast lambda --function-name <function> [options]`
|
|
117
|
-
- `contrast lambda --help`
|
|
85
|
+
**Usage:** `contrast scan [option]`
|
|
118
86
|
|
|
119
|
-
|
|
87
|
+
**Options:**
|
|
120
88
|
|
|
121
|
-
-
|
|
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
|
|
89
|
+
- **contrast scan --file**
|
|
135
90
|
|
|
136
|
-
|
|
91
|
+
- 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.
|
|
92
|
+
- Alias: **--f**
|
|
137
93
|
|
|
138
|
-
|
|
94
|
+
- **contrast scan --name**
|
|
139
95
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
```
|
|
96
|
+
- Contrast project name. If not specified, Contrast uses contrast.settings to identify the project or creates a project.
|
|
97
|
+
- Alias: **–n**
|
|
143
98
|
|
|
144
|
-
|
|
99
|
+
- **contrast scan --save**
|
|
145
100
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
```
|
|
101
|
+
- Download the results to a Static Analysis Results Interchange Format (SARIF) file. The file is downloaded to the current working directory with a default name of results.sarif. You can view the file with any text editor.
|
|
102
|
+
- Alias: **-s**
|
|
149
103
|
|
|
150
|
-
|
|
104
|
+
- **contrast scan --timeout**
|
|
105
|
+
- Time in seconds to wait for the scan to complete. Default value is 300 seconds.
|
|
106
|
+
- Alias: **-t**
|
|
151
107
|
|
|
152
|
-
|
|
153
|
-
contrast lambda -f myFunctionName --region eu-cental-1
|
|
154
|
-
```
|
|
108
|
+
### lambda
|
|
155
109
|
|
|
156
|
-
|
|
110
|
+
Name of AWS lambda function to scan.
|
|
157
111
|
|
|
158
|
-
|
|
159
|
-
contrast lambda -f myFunctionName --profile myDevProfile
|
|
160
|
-
```
|
|
112
|
+
**Usage:** `contrast lambda --function-name`
|
|
161
113
|
|
|
162
|
-
|
|
114
|
+
**Options:**
|
|
163
115
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```
|
|
116
|
+
- **contrast lambda --list-functions**
|
|
117
|
+
Lists all available lambda functions to scan.
|
|
167
118
|
|
|
168
|
-
|
|
119
|
+
- **contrast lambda --function-name --endpoint-url**
|
|
120
|
+
AWS Endpoint override. Similar to AWS CLI.
|
|
121
|
+
Alias: **-e**
|
|
169
122
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
123
|
+
- **contrast lambda --function-name --region**
|
|
124
|
+
Region override. Defaults to AWS_DEFAULT_REGION. Similar to AWS CLI.
|
|
125
|
+
Alias: **-r**
|
|
126
|
+
|
|
127
|
+
- **contrast lambda --function-name --profile**
|
|
128
|
+
AWS configuration profile override. Similar to AWS CLI.
|
|
129
|
+
Alias: **-p**
|
|
130
|
+
|
|
131
|
+
- **contrast lambda --function-name --json**
|
|
132
|
+
Return response in JSON (versus default human-readable format).
|
|
133
|
+
Alias: **-j**
|
|
134
|
+
|
|
135
|
+
- **contrast lambda -–function-name -–verbose**
|
|
136
|
+
Returns extended information to the terminal.
|
|
137
|
+
Alias: **-v**
|
|
173
138
|
|
|
174
|
-
|
|
139
|
+
- **contrast lambda --function-name -–help**
|
|
140
|
+
Displays usage guide.
|
|
141
|
+
Alias: **-h**
|
|
175
142
|
|
|
176
|
-
###
|
|
143
|
+
### help
|
|
177
144
|
|
|
178
|
-
|
|
145
|
+
Displays usage guide. To list detailed help for any CLI command, add the -h or --help flag to the command.
|
|
146
|
+
**Usage:** `contrast scan --help`
|
|
147
|
+
Alias: **-h**
|
|
179
148
|
|
|
180
|
-
###
|
|
149
|
+
### version
|
|
181
150
|
|
|
182
|
-
|
|
151
|
+
Displays version of Contrast CLI.
|
|
152
|
+
**Usage:** `contrast version` Alias: **-v**, **--version**
|
package/dist/audit/languageAnalysisEngine/{langugageAnalysisFactory.js → languageAnalysisFactory.js}
RENAMED
|
@@ -9,9 +9,11 @@ const pythonAE = require('../pythonAnalysisEngine');
|
|
|
9
9
|
const phpAE = require('../phpAnalysisEngine');
|
|
10
10
|
const goAE = require('../goAnalysisEngine');
|
|
11
11
|
const { vulnerabilityReport } = require('./report/reportingFeature');
|
|
12
|
-
const { vulnReportWithoutDevDep } = require('./report/newReportingFeature');
|
|
13
|
-
const { checkDevDeps } = require('./report/checkIgnoreDevDep');
|
|
14
12
|
const { newSendSnapShot } = require('../languageAnalysisEngine/sendSnapshot');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const chalk = require('chalk');
|
|
15
|
+
const saveFile = require('../../commands/audit/saveFile').default;
|
|
16
|
+
const generateSbom = require('../../sbom/generateSbom').default;
|
|
15
17
|
module.exports = exports = (err, analysis) => {
|
|
16
18
|
const { identifiedLanguageInfo } = analysis.languageAnalysis;
|
|
17
19
|
const catalogueAppId = analysis.languageAnalysis.appId;
|
|
@@ -35,15 +37,8 @@ module.exports = exports = (err, analysis) => {
|
|
|
35
37
|
}
|
|
36
38
|
console.log('\n **************CONTRAST OSS ANALYSIS BEGINS**************');
|
|
37
39
|
const snapshotResponse = await newSendSnapShot(analysis, catalogueAppId);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if (ignoreDevUrl) {
|
|
41
|
-
await vulnReportWithoutDevDep(analysis, catalogueAppId, snapshotResponse.id, config);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
await vulnerabilityReport(analysis, catalogueAppId, config);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
40
|
+
await vulnerabilityReport(analysis, catalogueAppId, snapshotResponse.id);
|
|
41
|
+
await auditSave(config);
|
|
47
42
|
console.log('\n ***************CONTRAST OSS ANALYSIS COMPLETE************** \n');
|
|
48
43
|
};
|
|
49
44
|
if (identifiedLanguageInfo.language === DOTNET) {
|
|
@@ -68,3 +63,23 @@ module.exports = exports = (err, analysis) => {
|
|
|
68
63
|
goAE(identifiedLanguageInfo, analysis.config, langCallback);
|
|
69
64
|
}
|
|
70
65
|
};
|
|
66
|
+
async function auditSave(config) {
|
|
67
|
+
if (config.save) {
|
|
68
|
+
if (config.save.toLowerCase() === 'sbom') {
|
|
69
|
+
saveFile(config, await generateSbom(config));
|
|
70
|
+
const filename = `${config.applicationId}-sbom-cyclonedx.json`;
|
|
71
|
+
if (fs.existsSync(filename)) {
|
|
72
|
+
console.log(i18n.__('auditSBOMSaveSuccess') + ` - ${filename}`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log(chalk.yellow.bold(`\n Unable to save ${filename} Software Bill of Materials (SBOM)`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(i18n.__('auditBadFiletypeSpecifiedForSave'));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (config.save === null) {
|
|
83
|
+
console.log(i18n.__('auditNoFiletypeSpecifiedForSave'));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -1,257 +1,85 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function displaySuccessMessageReport() {
|
|
5
|
-
console.log('\n' + i18n.__('reportSuccessMessage'));
|
|
6
|
-
}
|
|
7
|
-
function getAllDependenciesArray(packageJson) {
|
|
8
|
-
const { dependencies, optionalDependencies, devDependencies, peerDependencies } = packageJson;
|
|
9
|
-
const allDep = {
|
|
10
|
-
...dependencies,
|
|
11
|
-
...devDependencies,
|
|
12
|
-
...optionalDependencies,
|
|
13
|
-
...peerDependencies
|
|
14
|
-
};
|
|
15
|
-
return Object.entries(allDep);
|
|
16
|
-
}
|
|
17
|
-
function checkIfDepIsScoped(arrDep) {
|
|
18
|
-
let count = 0;
|
|
19
|
-
arrDep.forEach(([key, value]) => {
|
|
20
|
-
if (!key.startsWith('@')) {
|
|
21
|
-
console.log(` WARNING not scoped: ${key}:${value}`);
|
|
22
|
-
count++;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
return count;
|
|
26
|
-
}
|
|
27
|
-
const dependencyRiskReport = async (packageJson, config) => {
|
|
28
|
-
const arrDep = getAllDependenciesArray(packageJson);
|
|
29
|
-
const unRegisteredDeps = await checkIfDepIsRegisteredOnNPM(arrDep, config);
|
|
30
|
-
let scopedCount = checkIfDepIsScoped(unRegisteredDeps);
|
|
31
|
-
return {
|
|
32
|
-
scopedCount: scopedCount,
|
|
33
|
-
unRegisteredCount: unRegisteredDeps.length
|
|
34
|
-
};
|
|
35
|
-
};
|
|
36
|
-
const checkIfDepIsRegisteredOnNPM = async (arrDep, config) => {
|
|
37
|
-
let promises = [];
|
|
38
|
-
let unRegisteredDeps = [];
|
|
39
|
-
const client = getHttpClient(config);
|
|
40
|
-
for (const [index, element] of arrDep) {
|
|
41
|
-
const query = `query artifactByGAV($name: String!, $language: String!, $groupName: String, $version: String!, $nameCheck: Boolean) {
|
|
42
|
-
artifact: exactVersion(name: $name, language: $language, groupName: $groupName, version: $version, nameCheck: $nameCheck) {
|
|
43
|
-
version
|
|
44
|
-
cves {
|
|
45
|
-
baseScore
|
|
46
|
-
}}}`;
|
|
47
|
-
const data = {
|
|
48
|
-
query: query,
|
|
49
|
-
variables: {
|
|
50
|
-
name: index,
|
|
51
|
-
version: element,
|
|
52
|
-
language: 'node',
|
|
53
|
-
nameCheck: true
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
promises.push(client.checkLibrary(data));
|
|
57
|
-
}
|
|
58
|
-
await Promise.all(promises).then(response => {
|
|
59
|
-
response.forEach(res => {
|
|
60
|
-
const libName = JSON.parse(res.request.body);
|
|
61
|
-
if (res.statusCode === 200) {
|
|
62
|
-
if (res.body.data.artifact == null) {
|
|
63
|
-
unRegisteredDeps.push([
|
|
64
|
-
libName.variables.name,
|
|
65
|
-
libName.variables.version
|
|
66
|
-
]);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
if (unRegisteredDeps.length !== 0) {
|
|
72
|
-
console.log('\n Dependencies Risk Report', '\n\n Private libraries that are not scoped. We recommend these libraries are reviewed and the scope claimed to prevent dependency confusion breaches');
|
|
73
|
-
}
|
|
74
|
-
return unRegisteredDeps;
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
75
4
|
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.printFormattedOutput = exports.printVulnerabilityResponse = exports.getReport = exports.createLibraryHeader = void 0;
|
|
7
|
+
const i18n_1 = __importDefault(require("i18n"));
|
|
8
|
+
const commonApi_1 = require("../../../utils/commonApi");
|
|
9
|
+
const reportListModel_1 = require("./models/reportListModel");
|
|
10
|
+
const lodash_1 = require("lodash");
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const reportUtils_1 = require("./utils/reportUtils");
|
|
13
|
+
const oraWrapper_1 = require("../../../utils/oraWrapper");
|
|
76
14
|
const createLibraryHeader = (id, numberOfVulnerableLibraries, numberOfCves, name) => {
|
|
77
15
|
name
|
|
78
|
-
? console.log(
|
|
16
|
+
? console.log(`\n Application Name: ${name} | Application ID: ${id}`)
|
|
79
17
|
: console.log(` Application ID: ${id}`);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const inputtedCLIOptions = cliOptions.getCommandLineArgs();
|
|
88
|
-
let cveSeverityOption = inputtedCLIOptions['cve_severity'];
|
|
89
|
-
let fail = inputtedCLIOptions['fail'];
|
|
90
|
-
let cve_threshold = inputtedCLIOptions['cve_threshold'];
|
|
91
|
-
let expr;
|
|
92
|
-
if (cveSeverityOption && fail && cve_threshold) {
|
|
93
|
-
expr = 'SeverityAndThreshold';
|
|
94
|
-
}
|
|
95
|
-
else if (!cveSeverityOption && fail && cve_threshold) {
|
|
96
|
-
expr = 'ThresholdOnly';
|
|
97
|
-
}
|
|
98
|
-
else if (!cve_threshold && fail && hasSomeVulnerabilitiesReported[0]) {
|
|
99
|
-
expr = 'FailOnly';
|
|
100
|
-
}
|
|
101
|
-
return expr;
|
|
102
|
-
};
|
|
103
|
-
const analyseReportOptions = hasSomeVulnerabilitiesReported => {
|
|
104
|
-
const inputtedCLIOptions = cliOptions.getCommandLineArgs();
|
|
105
|
-
let cve_threshold = inputtedCLIOptions['cve_threshold'];
|
|
106
|
-
let cveSeverity;
|
|
107
|
-
let criticalSeverity;
|
|
108
|
-
let highSeverity;
|
|
109
|
-
let mediumSeverity;
|
|
110
|
-
let lowSeverity;
|
|
111
|
-
switch (parameterOptions(hasSomeVulnerabilitiesReported)) {
|
|
112
|
-
case 'SeverityAndThreshold':
|
|
113
|
-
cveSeverity = inputtedCLIOptions['cve_severity'].severity;
|
|
114
|
-
criticalSeverity = hasSomeVulnerabilitiesReported[2].critical;
|
|
115
|
-
highSeverity = hasSomeVulnerabilitiesReported[2].high;
|
|
116
|
-
mediumSeverity = hasSomeVulnerabilitiesReported[2].medium;
|
|
117
|
-
lowSeverity = hasSomeVulnerabilitiesReported[2].low;
|
|
118
|
-
if (cveSeverity === 'HIGH') {
|
|
119
|
-
if (cve_threshold < highSeverity + criticalSeverity) {
|
|
120
|
-
breakPipeline();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (cveSeverity === 'MEDIUM') {
|
|
124
|
-
if (cve_threshold < mediumSeverity + highSeverity) {
|
|
125
|
-
breakPipeline();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (cveSeverity === 'LOW') {
|
|
129
|
-
if (cve_threshold < lowSeverity + mediumSeverity + highSeverity) {
|
|
130
|
-
breakPipeline();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
break;
|
|
134
|
-
case 'ThresholdOnly':
|
|
135
|
-
if (cve_threshold < hasSomeVulnerabilitiesReported[1]) {
|
|
136
|
-
breakPipeline();
|
|
137
|
-
}
|
|
138
|
-
break;
|
|
139
|
-
case 'FailOnly':
|
|
140
|
-
breakPipeline();
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
18
|
+
numberOfVulnerableLibraries === 1
|
|
19
|
+
? console.log('\n **************************' +
|
|
20
|
+
` Found 1 vulnerable library containing ${numberOfCves} CVE's` +
|
|
21
|
+
'************************** ')
|
|
22
|
+
: console.log('\n **************************' +
|
|
23
|
+
` Found ${numberOfVulnerableLibraries} vulnerable libraries containing ${numberOfCves} CVE's ` +
|
|
24
|
+
'************************** ');
|
|
143
25
|
};
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
|
|
26
|
+
exports.createLibraryHeader = createLibraryHeader;
|
|
27
|
+
const getReport = async (config, reportId) => {
|
|
28
|
+
const client = (0, commonApi_1.getHttpClient)(config);
|
|
29
|
+
const reportSpinner = (0, oraWrapper_1.returnOra)(i18n_1.default.__('auditReportWaiting'));
|
|
30
|
+
reportSpinner.indent = 1;
|
|
31
|
+
(0, oraWrapper_1.startSpinner)(reportSpinner);
|
|
149
32
|
return client
|
|
150
|
-
.
|
|
151
|
-
.then(res => {
|
|
33
|
+
.getReportById(config, reportId)
|
|
34
|
+
.then((res) => {
|
|
152
35
|
if (res.statusCode === 200) {
|
|
153
|
-
|
|
36
|
+
(0, oraWrapper_1.succeedSpinner)(reportSpinner, i18n_1.default.__('auditReportSuccessMessage'));
|
|
154
37
|
return res.body;
|
|
155
38
|
}
|
|
156
39
|
else {
|
|
157
|
-
|
|
40
|
+
(0, oraWrapper_1.failSpinner)(reportSpinner, i18n_1.default.__('auditReportFail'));
|
|
41
|
+
console.log('config-------------------');
|
|
42
|
+
console.log(config);
|
|
43
|
+
console.log('reportId----------------');
|
|
44
|
+
console.log(reportId);
|
|
45
|
+
console.log(JSON.stringify(res));
|
|
46
|
+
(0, commonApi_1.handleResponseErrors)(res, 'report');
|
|
158
47
|
}
|
|
159
48
|
})
|
|
160
|
-
.catch(err => {
|
|
49
|
+
.catch((err) => {
|
|
161
50
|
console.log(err);
|
|
162
51
|
});
|
|
163
52
|
};
|
|
164
|
-
|
|
53
|
+
exports.getReport = getReport;
|
|
54
|
+
const printVulnerabilityResponse = (vulnerabilities, config) => {
|
|
165
55
|
let hasSomeVulnerabilitiesReported = false;
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
hasSomeVulnerabilitiesReported = true;
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
returnCveData(vulnerabilities);
|
|
173
|
-
if (Object.keys(vulnerabilities).length > 0)
|
|
174
|
-
hasSomeVulnerabilitiesReported = true;
|
|
56
|
+
(0, exports.printFormattedOutput)(vulnerabilities, config);
|
|
57
|
+
if (Object.keys(vulnerabilities).length > 0) {
|
|
58
|
+
hasSomeVulnerabilitiesReported = true;
|
|
175
59
|
}
|
|
176
60
|
return hasSomeVulnerabilitiesReported;
|
|
177
61
|
};
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
const version = nameVersion[1];
|
|
186
|
-
const libName = group !== 'null'
|
|
187
|
-
? `${group}/${name}/${version} is vulnerable`
|
|
188
|
-
: `${name}/${version} is vulnerable`;
|
|
189
|
-
console.log('\n\n ' + libName);
|
|
190
|
-
value.forEach(vuln => {
|
|
191
|
-
let sevCode = vuln.severityCode || vuln.severity_code;
|
|
192
|
-
console.log('\n ' + vuln.name + ' ' + sevCode + '\n ' + vuln.description);
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
function searchHighCVEs(vuln) {
|
|
197
|
-
let sevCode = vuln.severityCode || vuln.severity_code;
|
|
198
|
-
if (sevCode === 'HIGH') {
|
|
199
|
-
return vuln;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
function searchMediumCVEs(vuln) {
|
|
203
|
-
let sevCode = vuln.severityCode || vuln.severity_code;
|
|
204
|
-
if (sevCode === 'HIGH' || sevCode === 'MEDIUM') {
|
|
205
|
-
return vuln;
|
|
62
|
+
exports.printVulnerabilityResponse = printVulnerabilityResponse;
|
|
63
|
+
const printFormattedOutput = (libraries, config) => {
|
|
64
|
+
const report = new reportListModel_1.ReportList();
|
|
65
|
+
for (const library of libraries) {
|
|
66
|
+
const { name, version } = (0, reportUtils_1.findNameAndVersion)(library, config);
|
|
67
|
+
const newOutputModel = new reportListModel_1.ReportModelStructure(new reportListModel_1.ReportCompositeKey(name, version, (0, reportUtils_1.findHighestSeverityCVE)(library.cveArray)), library.cveArray);
|
|
68
|
+
report.reportOutputList.push(newOutputModel);
|
|
206
69
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
70
|
+
const orderedOutputList = (0, lodash_1.orderBy)(report.reportOutputList, reportListItem => reportListItem.compositeKey.highestSeverity.priority);
|
|
71
|
+
for (const reportModel of orderedOutputList) {
|
|
72
|
+
const name = reportModel.compositeKey.libraryName;
|
|
73
|
+
const version = reportModel.compositeKey.libraryVersion;
|
|
74
|
+
const highestSeverity = reportModel.compositeKey.highestSeverity.severity;
|
|
75
|
+
const numOfCVEs = reportModel.cveArray.length;
|
|
76
|
+
const cveNames = [];
|
|
77
|
+
reportModel.cveArray.forEach(cve => cveNames.push(cve.name));
|
|
78
|
+
const boldHeader = chalk_1.default.bold(`${highestSeverity} | Vulnerable Library`);
|
|
79
|
+
const cvePluralised = numOfCVEs > 1 ? 'CVEs' : 'CVE';
|
|
80
|
+
console.log(`\n ${boldHeader} ${name} (${version}) has ${numOfCVEs} known ${cvePluralised}`);
|
|
81
|
+
console.log(` ${cveNames.join(', ')}`);
|
|
82
|
+
console.log(chalk_1.default.bold(' How to fix: Update to latest version'));
|
|
212
83
|
}
|
|
213
|
-
}
|
|
214
|
-
const filterVulnerabilitiesBySeverity = (severity, vulnerabilities) => {
|
|
215
|
-
let filteredVulns = [];
|
|
216
|
-
if (severity) {
|
|
217
|
-
for (let x in vulnerabilities) {
|
|
218
|
-
if (severity.severity === 'HIGH') {
|
|
219
|
-
let highVulnerability = vulnerabilities[x].filter(searchHighCVEs);
|
|
220
|
-
if (highVulnerability.length > 0) {
|
|
221
|
-
filteredVulns[x] = highVulnerability;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
else if (severity.severity === 'MEDIUM') {
|
|
225
|
-
let mediumVulnerability = vulnerabilities[x].filter(searchMediumCVEs);
|
|
226
|
-
if (mediumVulnerability.length > 0) {
|
|
227
|
-
filteredVulns[x] = mediumVulnerability;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
else if (severity.severity === 'LOW') {
|
|
231
|
-
let lowVulnerability = vulnerabilities[x].filter(searchLowCVEs);
|
|
232
|
-
if (lowVulnerability.length > 0) {
|
|
233
|
-
filteredVulns[x] = lowVulnerability;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return filteredVulns;
|
|
239
|
-
};
|
|
240
|
-
module.exports = {
|
|
241
|
-
displaySuccessMessageReport: displaySuccessMessageReport,
|
|
242
|
-
getAllDependenciesArray: getAllDependenciesArray,
|
|
243
|
-
dependencyRiskReport: dependencyRiskReport,
|
|
244
|
-
createLibraryHeader: createLibraryHeader,
|
|
245
|
-
breakPipeline: breakPipeline,
|
|
246
|
-
parameterOptions: parameterOptions,
|
|
247
|
-
analyseReportOptions: analyseReportOptions,
|
|
248
|
-
getReport: getReport,
|
|
249
|
-
checkIfDepIsScoped: checkIfDepIsScoped,
|
|
250
|
-
checkIfDepIsRegisteredOnNPM: checkIfDepIsRegisteredOnNPM,
|
|
251
|
-
filterVulnerabilitiesBySeverity: filterVulnerabilitiesBySeverity,
|
|
252
|
-
searchLowCVEs: searchLowCVEs,
|
|
253
|
-
searchMediumCVEs: searchMediumCVEs,
|
|
254
|
-
searchHighCVEs: searchHighCVEs,
|
|
255
|
-
returnCveData: returnCveData,
|
|
256
|
-
printVulnerabilityResponse: printVulnerabilityResponse
|
|
257
84
|
};
|
|
85
|
+
exports.printFormattedOutput = printFormattedOutput;
|