@contrast/contrast 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -0
- package/bin/contrast.js +2 -0
- package/dist/commands/auth/auth.js +61 -0
- package/dist/commands/config/config.js +24 -0
- package/dist/commands/scan/processScan.js +23 -0
- package/dist/common/HTTPClient.js +192 -0
- package/dist/common/errorHandling.js +60 -0
- package/dist/constants/constants.js +30 -0
- package/dist/constants/lambda.js +31 -0
- package/dist/constants/locales.js +259 -0
- package/dist/constants.js +150 -0
- package/dist/index.js +56 -0
- package/dist/lambda/__mocks__/aws.js +21 -0
- package/dist/lambda/__mocks__/lambdaConfig.json +42 -0
- package/dist/lambda/arn.js +21 -0
- package/dist/lambda/aws.js +169 -0
- package/dist/lambda/cliError.js +76 -0
- package/dist/lambda/constants.js +11 -0
- package/dist/lambda/help.js +75 -0
- package/dist/lambda/lambda.js +130 -0
- package/dist/lambda/logUtils.js +36 -0
- package/dist/lambda/scanDetail.js +30 -0
- package/dist/lambda/scanDetailCompletion.js +56 -0
- package/dist/lambda/scanRequest.js +93 -0
- package/dist/lambda/scanResults.js +16 -0
- package/dist/lambda/utils.js +90 -0
- package/dist/scan/autoDetection.js +76 -0
- package/dist/scan/fileFinder.js +15 -0
- package/dist/scan/fileUtils.js +31 -0
- package/dist/scan/help.js +68 -0
- package/dist/scan/populateProjectIdAndProjectName.js +55 -0
- package/dist/scan/scan.js +96 -0
- package/dist/scan/scanController.js +54 -0
- package/dist/scan/scanResults.js +85 -0
- package/dist/utils/commonApi.js +45 -0
- package/dist/utils/filterProjectPath.js +20 -0
- package/dist/utils/getConfig.js +30 -0
- package/dist/utils/oraWrapper.js +20 -0
- package/dist/utils/paramsUtil/commandlineParams.js +31 -0
- package/dist/utils/paramsUtil/configStoreParams.js +18 -0
- package/dist/utils/paramsUtil/envVariableParams.js +10 -0
- package/dist/utils/paramsUtil/paramHandler.js +28 -0
- package/dist/utils/paramsUtil/yamlParams.js +6 -0
- package/dist/utils/parsedCLIOptions.js +13 -0
- package/dist/utils/requestUtils.js +18 -0
- package/dist/utils/validationCheck.js +26 -0
- package/package.json +123 -0
- package/src/commands/auth/auth.js +73 -0
- package/src/commands/config/config.js +25 -0
- package/src/commands/scan/processScan.js +29 -0
- package/src/common/HTTPClient.js +269 -0
- package/src/common/errorHandling.ts +79 -0
- package/src/constants/constants.js +34 -0
- package/src/constants/lambda.js +41 -0
- package/src/constants/locales.js +381 -0
- package/src/constants.js +168 -0
- package/src/index.ts +69 -0
- package/src/lambda/__mocks__/aws.ts +32 -0
- package/src/lambda/__mocks__/lambdaConfig.json +42 -0
- package/src/lambda/arn.ts +32 -0
- package/src/lambda/aws.ts +247 -0
- package/src/lambda/cliError.ts +72 -0
- package/src/lambda/constants.ts +11 -0
- package/src/lambda/help.ts +76 -0
- package/src/lambda/lambda.ts +174 -0
- package/src/lambda/logUtils.ts +46 -0
- package/src/lambda/scanDetailCompletion.ts +78 -0
- package/src/lambda/scanRequest.ts +142 -0
- package/src/lambda/scanResults.ts +29 -0
- package/src/lambda/utils.ts +125 -0
- package/src/scan/autoDetection.js +77 -0
- package/src/scan/fileUtils.js +33 -0
- package/src/scan/help.js +74 -0
- package/src/scan/populateProjectIdAndProjectName.js +62 -0
- package/src/scan/scan.js +126 -0
- package/src/scan/scanController.js +69 -0
- package/src/scan/scanResults.js +96 -0
- package/src/utils/commonApi.js +54 -0
- package/src/utils/filterProjectPath.js +21 -0
- package/src/utils/getConfig.ts +42 -0
- package/src/utils/oraWrapper.js +24 -0
- package/src/utils/paramsUtil/commandlineParams.js +37 -0
- package/src/utils/paramsUtil/configStoreParams.js +19 -0
- package/src/utils/paramsUtil/envVariableParams.js +10 -0
- package/src/utils/paramsUtil/paramHandler.js +28 -0
- package/src/utils/parsedCLIOptions.js +17 -0
- package/src/utils/requestUtils.js +22 -0
- package/src/utils/validationCheck.js +34 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.throwAwsError = exports.getLambdaPolicies = exports.getLayersLinks = exports.getLambdaFunctionConfiguration = exports.getLambdaClient = exports.getIAMClient = exports.getRolePolicyNames = exports.getAttachedPolicyNames = void 0;
|
|
4
|
+
const client_lambda_1 = require("@aws-sdk/client-lambda");
|
|
5
|
+
const client_iam_1 = require("@aws-sdk/client-iam");
|
|
6
|
+
const credential_provider_ini_1 = require("@aws-sdk/credential-provider-ini");
|
|
7
|
+
const cliError_1 = require("./cliError");
|
|
8
|
+
const constants_1 = require("./constants");
|
|
9
|
+
const getAwsClientOptions = ({ region, endpointUrl, profile }) => {
|
|
10
|
+
const credentials = profile ? (0, credential_provider_ini_1.fromIni)({ profile }) : undefined;
|
|
11
|
+
return {
|
|
12
|
+
region: region || process.env.AWS_DEFAULT_REGION,
|
|
13
|
+
endpoint: endpointUrl,
|
|
14
|
+
credentials
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const getLambdaClient = (lambdaOptions) => {
|
|
18
|
+
try {
|
|
19
|
+
const clientOptions = getAwsClientOptions(lambdaOptions);
|
|
20
|
+
return new client_lambda_1.Lambda(clientOptions);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const errorObj = error;
|
|
24
|
+
if (errorObj?.code === 'ERR_INVALID_URL') {
|
|
25
|
+
throw new cliError_1.CliError(constants_1.ERRORS.AWS_ERROR, { description: errorObj.message });
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
exports.getLambdaClient = getLambdaClient;
|
|
31
|
+
const getIAMClient = (lambdaOptions) => {
|
|
32
|
+
const clientOptions = getAwsClientOptions(lambdaOptions);
|
|
33
|
+
return new client_iam_1.IAMClient(clientOptions);
|
|
34
|
+
};
|
|
35
|
+
exports.getIAMClient = getIAMClient;
|
|
36
|
+
const getLambdaFunctionConfiguration = async (client, lambdaOptions) => {
|
|
37
|
+
const { functionName: FunctionName } = lambdaOptions;
|
|
38
|
+
const getFunctionCommand = new client_lambda_1.GetFunctionCommand({ FunctionName });
|
|
39
|
+
try {
|
|
40
|
+
return await client.send(getFunctionCommand);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throwAwsError(error);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
exports.getLambdaFunctionConfiguration = getLambdaFunctionConfiguration;
|
|
47
|
+
const getLayersLinks = async (client, functionConfiguration) => {
|
|
48
|
+
const { Layers: layers = [] } = functionConfiguration;
|
|
49
|
+
const resultPromises = layers.map(async (layerDict) => {
|
|
50
|
+
try {
|
|
51
|
+
const layerArn = layerDict.Arn;
|
|
52
|
+
const getLayerVersionByArnCommand = new client_lambda_1.GetLayerVersionByArnCommand({
|
|
53
|
+
Arn: layerArn
|
|
54
|
+
});
|
|
55
|
+
const layer = await client.send(getLayerVersionByArnCommand);
|
|
56
|
+
return {
|
|
57
|
+
Arn: layerArn,
|
|
58
|
+
Location: layer.Content?.Location
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
if (e instanceof client_lambda_1.ResourceNotFoundException) {
|
|
63
|
+
e.message = `The layer ${layerDict.Arn} could not be found. We will continue the scan without it.`;
|
|
64
|
+
throw e;
|
|
65
|
+
}
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const results = await Promise.allSettled(resultPromises);
|
|
70
|
+
const validLayers = results.filter(layerResult => {
|
|
71
|
+
if (layerResult.status === 'rejected') {
|
|
72
|
+
console.warn(layerResult.reason.message);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
return validLayers.map(layer => layer.value);
|
|
78
|
+
};
|
|
79
|
+
exports.getLayersLinks = getLayersLinks;
|
|
80
|
+
const getLambdaPolicies = async (functionConfiguration, lambdaOptions) => {
|
|
81
|
+
const { Role: roleArn } = functionConfiguration;
|
|
82
|
+
const roleSplitList = roleArn?.split('/');
|
|
83
|
+
if (roleSplitList) {
|
|
84
|
+
const roleName = roleSplitList[roleSplitList.length - 1];
|
|
85
|
+
const client = exports.getIAMClient(lambdaOptions);
|
|
86
|
+
const rolePolicies = await getRolePolicies(roleName, client);
|
|
87
|
+
const attachedPolicies = await getAttachedPolicies(roleName, client);
|
|
88
|
+
return [...rolePolicies, ...attachedPolicies];
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
exports.getLambdaPolicies = getLambdaPolicies;
|
|
92
|
+
const getRolePolicyNames = async (roleName, client) => {
|
|
93
|
+
const listRolePolicyNames = [];
|
|
94
|
+
try {
|
|
95
|
+
for await (const page of (0, client_iam_1.paginateListRolePolicies)({ client }, { RoleName: roleName })) {
|
|
96
|
+
if (page.PolicyNames) {
|
|
97
|
+
listRolePolicyNames.push(...page.PolicyNames);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
throwAwsError(error);
|
|
103
|
+
}
|
|
104
|
+
return listRolePolicyNames;
|
|
105
|
+
};
|
|
106
|
+
exports.getRolePolicyNames = getRolePolicyNames;
|
|
107
|
+
const getAttachedPolicyNames = async (roleName, client) => {
|
|
108
|
+
const listAttachedPolicies = [];
|
|
109
|
+
for await (const page of (0, client_iam_1.paginateListAttachedRolePolicies)({ client }, { RoleName: roleName })) {
|
|
110
|
+
if (page.AttachedPolicies) {
|
|
111
|
+
listAttachedPolicies.push(...page.AttachedPolicies);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return listAttachedPolicies;
|
|
115
|
+
};
|
|
116
|
+
exports.getAttachedPolicyNames = getAttachedPolicyNames;
|
|
117
|
+
const getRolePolicies = async (roleName, client) => {
|
|
118
|
+
const listRolePolicyNames = await exports.getRolePolicyNames(roleName, client);
|
|
119
|
+
if (listRolePolicyNames) {
|
|
120
|
+
const rolePoliciesPromises = listRolePolicyNames.map(async (policyName) => {
|
|
121
|
+
const getRolePolicyCommand = new client_iam_1.GetRolePolicyCommand({
|
|
122
|
+
PolicyName: policyName,
|
|
123
|
+
RoleName: roleName
|
|
124
|
+
});
|
|
125
|
+
const rolePolicy = await client.send(getRolePolicyCommand);
|
|
126
|
+
const policyDoc = JSON.parse(decodeURIComponent(rolePolicy?.PolicyDocument || '{}'));
|
|
127
|
+
policyDoc.PolicyName = policyName;
|
|
128
|
+
return policyDoc;
|
|
129
|
+
});
|
|
130
|
+
const rolePolicies = await Promise.all(rolePoliciesPromises);
|
|
131
|
+
return rolePolicies;
|
|
132
|
+
}
|
|
133
|
+
return [];
|
|
134
|
+
};
|
|
135
|
+
const getAttachedPolicies = async (roleName, client) => {
|
|
136
|
+
const listAttachedPolicies = await exports.getAttachedPolicyNames(roleName, client);
|
|
137
|
+
const attachedPoliciesPromises = listAttachedPolicies.map(async (policyDict) => {
|
|
138
|
+
const getPolicyCommand = new client_iam_1.GetPolicyCommand({
|
|
139
|
+
PolicyArn: policyDict.PolicyArn
|
|
140
|
+
});
|
|
141
|
+
const policy = await client.send(getPolicyCommand);
|
|
142
|
+
if (policy.Policy) {
|
|
143
|
+
const getPolicyVersionCommand = new client_iam_1.GetPolicyVersionCommand({
|
|
144
|
+
PolicyArn: policy.Policy.Arn,
|
|
145
|
+
VersionId: policy.Policy.DefaultVersionId
|
|
146
|
+
});
|
|
147
|
+
const policyVersion = await client.send(getPolicyVersionCommand);
|
|
148
|
+
const policyDoc = JSON.parse(decodeURIComponent(policyVersion?.PolicyVersion?.Document || '{}'));
|
|
149
|
+
policyDoc['PolicyName'] = policyDict.PolicyName;
|
|
150
|
+
policyDoc['PolicyArn'] = policyDict.PolicyArn;
|
|
151
|
+
return policyDoc;
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const attachedPolicies = await Promise.all(attachedPoliciesPromises);
|
|
155
|
+
return attachedPolicies;
|
|
156
|
+
};
|
|
157
|
+
const throwAwsError = (error) => {
|
|
158
|
+
const serviceError = error;
|
|
159
|
+
if (error instanceof Error && serviceError.$metadata) {
|
|
160
|
+
const { httpStatusCode } = serviceError.$metadata;
|
|
161
|
+
const { message } = error;
|
|
162
|
+
throw new cliError_1.CliError(constants_1.ERRORS.AWS_ERROR, {
|
|
163
|
+
statusCode: httpStatusCode,
|
|
164
|
+
description: message
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
};
|
|
169
|
+
exports.throwAwsError = throwAwsError;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.CliError = void 0;
|
|
30
|
+
const i18n_1 = __importDefault(require("i18n"));
|
|
31
|
+
const errorHandling = __importStar(require("../common/errorHandling"));
|
|
32
|
+
class CliError extends Error {
|
|
33
|
+
constructor(headerMessage, details) {
|
|
34
|
+
const message = i18n_1.default.__(headerMessage || '');
|
|
35
|
+
super(message);
|
|
36
|
+
const { statusCode, errorCode, data, description } = details || {};
|
|
37
|
+
this.statusCode = statusCode;
|
|
38
|
+
this.errorCode = errorCode;
|
|
39
|
+
this.data = data;
|
|
40
|
+
this.description = description;
|
|
41
|
+
if (errorCode) {
|
|
42
|
+
this.errorCodeDescription = i18n_1.default.__(errorCode || '');
|
|
43
|
+
}
|
|
44
|
+
if (statusCode) {
|
|
45
|
+
this.statusCodeDescription = this.getMessageByStatusCode(statusCode);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
getErrorMessage() {
|
|
49
|
+
let finalDesc = this.errorCodeDescription || this.statusCodeDescription || '';
|
|
50
|
+
if (this.description) {
|
|
51
|
+
finalDesc += finalDesc ? `\n${this.description}` : this.description;
|
|
52
|
+
}
|
|
53
|
+
return errorHandling.getErrorMessage(this.message, finalDesc);
|
|
54
|
+
}
|
|
55
|
+
getMessageByStatusCode(statusCode) {
|
|
56
|
+
switch (statusCode) {
|
|
57
|
+
case 200:
|
|
58
|
+
return '';
|
|
59
|
+
case 400:
|
|
60
|
+
return i18n_1.default.__('badRequestErrorHeader');
|
|
61
|
+
case 401:
|
|
62
|
+
return i18n_1.default.__('unauthenticatedErrorHeader');
|
|
63
|
+
case 403:
|
|
64
|
+
return i18n_1.default.__('forbiddenRequestErrorHeader');
|
|
65
|
+
case 404:
|
|
66
|
+
return i18n_1.default.__('not_found_404');
|
|
67
|
+
case 423:
|
|
68
|
+
return i18n_1.default.__('resourceLockedErrorHeader');
|
|
69
|
+
case 500:
|
|
70
|
+
return i18n_1.default.__('internalServerErrorHeader');
|
|
71
|
+
default:
|
|
72
|
+
return i18n_1.default.__('something_went_wrong');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.CliError = CliError;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ERRORS = void 0;
|
|
4
|
+
const ERRORS = Object.freeze({
|
|
5
|
+
AWS_ERROR: 'awsError',
|
|
6
|
+
FAILED_TO_START_SCAN: 'failedToStartScan',
|
|
7
|
+
FAILED_TO_GET_SCAN: 'failedToGetScan',
|
|
8
|
+
FAILED_TO_GET_RESULTS: 'failedToGetResults',
|
|
9
|
+
VALIDATION_FAILED: 'validationFailed'
|
|
10
|
+
});
|
|
11
|
+
exports.ERRORS = ERRORS;
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
exports.lambdaUsageGuide = void 0;
|
|
7
|
+
const command_line_usage_1 = __importDefault(require("command-line-usage"));
|
|
8
|
+
const i18n_1 = __importDefault(require("i18n"));
|
|
9
|
+
const lambdaUsageGuide = (0, command_line_usage_1.default)([
|
|
10
|
+
{
|
|
11
|
+
header: i18n_1.default.__('lambdaHeader'),
|
|
12
|
+
content: [i18n_1.default.__('lambdaSummary')]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
header: i18n_1.default.__('constantsPrerequisitesHeader'),
|
|
16
|
+
content: [i18n_1.default.__('lambdaPrerequisitesContent')]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
header: i18n_1.default.__('constantsUsage'),
|
|
20
|
+
content: [i18n_1.default.__('lambdaUsage')]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
header: i18n_1.default.__('constantsOptions'),
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
name: i18n_1.default.__('lambdaFunctionNameOption'),
|
|
27
|
+
summary: i18n_1.default.__('lambdaFunctionNameSummery')
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: i18n_1.default.__('lambdaEndpointOption'),
|
|
31
|
+
summary: '{italic ' +
|
|
32
|
+
i18n_1.default.__('constantsOptional') +
|
|
33
|
+
'}: ' +
|
|
34
|
+
i18n_1.default.__('lambdaEndpointSummery')
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: i18n_1.default.__('lambdaRegionOption'),
|
|
38
|
+
summary: '{italic ' +
|
|
39
|
+
i18n_1.default.__('constantsOptional') +
|
|
40
|
+
'}: ' +
|
|
41
|
+
i18n_1.default.__('lambdaRegionSummery')
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: i18n_1.default.__('lambdaProfileOption'),
|
|
45
|
+
summary: '{italic ' +
|
|
46
|
+
i18n_1.default.__('constantsOptional') +
|
|
47
|
+
'}: ' +
|
|
48
|
+
i18n_1.default.__('lambdaProfileSummery')
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: i18n_1.default.__('lambdaJsonOption'),
|
|
52
|
+
summary: '{italic ' +
|
|
53
|
+
i18n_1.default.__('constantsOptional') +
|
|
54
|
+
'}: ' +
|
|
55
|
+
i18n_1.default.__('lambdaJsonSummery')
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: i18n_1.default.__('lambdaVerboseOption'),
|
|
59
|
+
summary: '{italic ' +
|
|
60
|
+
i18n_1.default.__('constantsOptional') +
|
|
61
|
+
'}: ' +
|
|
62
|
+
i18n_1.default.__('lambdaVerbosSummery')
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
content: [
|
|
68
|
+
{ name: i18n_1.default.__('lambdaHelpOption'), summary: i18n_1.default.__('helpSummary') }
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
content: '{underline https://www.contrastsecurity.com}'
|
|
73
|
+
}
|
|
74
|
+
]);
|
|
75
|
+
exports.lambdaUsageGuide = lambdaUsageGuide;
|
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
exports.processLambda = void 0;
|
|
7
|
+
const command_line_args_1 = __importDefault(require("command-line-args"));
|
|
8
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
9
|
+
const lodash_1 = require("lodash");
|
|
10
|
+
const i18n_1 = __importDefault(require("i18n"));
|
|
11
|
+
const paramHandler_1 = require("../utils/paramsUtil/paramHandler");
|
|
12
|
+
const cliError_1 = require("./cliError");
|
|
13
|
+
const constants_1 = require("./constants");
|
|
14
|
+
const help_1 = require("./help");
|
|
15
|
+
const logUtils_1 = require("./logUtils");
|
|
16
|
+
const scanDetailCompletion_1 = require("./scanDetailCompletion");
|
|
17
|
+
const scanRequest_1 = require("./scanRequest");
|
|
18
|
+
const scanResults_1 = require("./scanResults");
|
|
19
|
+
const utils_1 = require("./utils");
|
|
20
|
+
const failedStates = [
|
|
21
|
+
'UNSUPPORTED',
|
|
22
|
+
'EXCLUDED',
|
|
23
|
+
'CANCELED',
|
|
24
|
+
'FAILED',
|
|
25
|
+
'DISMISSED'
|
|
26
|
+
];
|
|
27
|
+
const printHelpMessage = () => {
|
|
28
|
+
(0, logUtils_1.log)(help_1.lambdaUsageGuide);
|
|
29
|
+
};
|
|
30
|
+
const getLambdaOptions = (argv) => {
|
|
31
|
+
const lambdaDefinitions = [
|
|
32
|
+
{ name: 'function-name', alias: 'f', type: String },
|
|
33
|
+
{ name: 'region', alias: 'r', type: String },
|
|
34
|
+
{ name: 'endpoint-url', alias: 'e', type: String },
|
|
35
|
+
{ name: 'profile', alias: 'p', type: String },
|
|
36
|
+
{ name: 'help', alias: 'h', type: Boolean },
|
|
37
|
+
{ name: 'verbose', alias: 'v', type: Boolean },
|
|
38
|
+
{ name: 'json-output', alias: 'j', type: Boolean }
|
|
39
|
+
];
|
|
40
|
+
const lambdaOptions = (0, command_line_args_1.default)(lambdaDefinitions, {
|
|
41
|
+
argv,
|
|
42
|
+
partial: true,
|
|
43
|
+
camelCase: true,
|
|
44
|
+
caseInsensitive: true
|
|
45
|
+
});
|
|
46
|
+
return lambdaOptions;
|
|
47
|
+
};
|
|
48
|
+
const processLambda = async (argv) => {
|
|
49
|
+
const lambdaOptions = getLambdaOptions(argv);
|
|
50
|
+
const { help } = lambdaOptions;
|
|
51
|
+
if (help) {
|
|
52
|
+
return handleLambdaHelp();
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
validateRequiredLambdaParams(lambdaOptions);
|
|
56
|
+
await actualProcessLambda(lambdaOptions);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof cliError_1.CliError) {
|
|
60
|
+
console.error(error.getErrorMessage());
|
|
61
|
+
}
|
|
62
|
+
else if (error instanceof Error) {
|
|
63
|
+
console.error(error.message);
|
|
64
|
+
}
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
exports.processLambda = processLambda;
|
|
69
|
+
const actualProcessLambda = async (lambdaOptions) => {
|
|
70
|
+
const auth = (0, paramHandler_1.getAuth)();
|
|
71
|
+
const startTime = perf_hooks_1.performance.now();
|
|
72
|
+
const { jsonOutput } = lambdaOptions;
|
|
73
|
+
const { scanId, params, functionArn } = await (0, scanRequest_1.requestScanFunctionPost)(auth, lambdaOptions);
|
|
74
|
+
const scans = await (0, scanDetailCompletion_1.pollScanUntilCompletion)(auth, 10, params, scanId);
|
|
75
|
+
const failedScan = scans
|
|
76
|
+
?.filter((s) => s.scanType === 2)
|
|
77
|
+
.find((s) => failedStates.includes(s.state));
|
|
78
|
+
if (failedScan) {
|
|
79
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_GET_SCAN, {
|
|
80
|
+
statusCode: 200,
|
|
81
|
+
errorCode: failedScan.state,
|
|
82
|
+
description: failedScan.stateReasonText
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const resultsResponse = await (0, scanResults_1.getScanResults)(auth, params, scanId, functionArn);
|
|
86
|
+
if (jsonOutput) {
|
|
87
|
+
console.log(JSON.stringify(resultsResponse?.data?.results, null, 2));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const results = resultsResponse?.data?.results;
|
|
91
|
+
if (!results) {
|
|
92
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_GET_RESULTS, {
|
|
93
|
+
errorCode: 'missingResults'
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (!results.length) {
|
|
97
|
+
(0, logUtils_1.log)('👏 No vulnerabilities found');
|
|
98
|
+
}
|
|
99
|
+
const endTime = perf_hooks_1.performance.now();
|
|
100
|
+
const scanDurationMs = endTime - startTime;
|
|
101
|
+
(0, logUtils_1.log)(`----- Scan completed ${(scanDurationMs / 1000).toFixed(2)}s -----`);
|
|
102
|
+
if (results?.length) {
|
|
103
|
+
(0, utils_1.prettyPrintResults)(results);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const validateRequiredLambdaParams = (options) => {
|
|
107
|
+
if (options._unknown?.length) {
|
|
108
|
+
throw new cliError_1.CliError(constants_1.ERRORS.VALIDATION_FAILED, {
|
|
109
|
+
description: i18n_1.default.__('notSupportedFlags', options._unknown.join('\n'))
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (!options?.functionName) {
|
|
113
|
+
throw new cliError_1.CliError(constants_1.ERRORS.VALIDATION_FAILED, {
|
|
114
|
+
errorCode: 'missingFunctionName'
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const flagsWithoutValues = Object.entries(options)
|
|
118
|
+
.filter(([, value]) => !value)
|
|
119
|
+
.map(([key]) => key)
|
|
120
|
+
.map(p => `--${(0, lodash_1.kebabCase)(p)}`);
|
|
121
|
+
if (flagsWithoutValues.length) {
|
|
122
|
+
throw new cliError_1.CliError(constants_1.ERRORS.VALIDATION_FAILED, {
|
|
123
|
+
description: i18n_1.default.__('missingFlagArguments', flagsWithoutValues.join('\n'))
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const handleLambdaHelp = () => {
|
|
128
|
+
printHelpMessage();
|
|
129
|
+
process.exit(0);
|
|
130
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
exports.prettyPrintJson = exports.log = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const util_1 = __importDefault(require("util"));
|
|
9
|
+
const log = (message, styles) => {
|
|
10
|
+
let chalkFunction = chalk_1.default.reset;
|
|
11
|
+
if (styles?.bold) {
|
|
12
|
+
chalkFunction = chalk_1.default.bold;
|
|
13
|
+
}
|
|
14
|
+
else if (styles?.italic) {
|
|
15
|
+
chalkFunction = chalk_1.default.italic;
|
|
16
|
+
}
|
|
17
|
+
else if (styles?.underline) {
|
|
18
|
+
chalkFunction = chalk_1.default.underline;
|
|
19
|
+
}
|
|
20
|
+
else if (styles?.strikethrough) {
|
|
21
|
+
chalkFunction = chalk_1.default.strikethrough;
|
|
22
|
+
}
|
|
23
|
+
console.log(styles ? chalkFunction(message) : message);
|
|
24
|
+
};
|
|
25
|
+
exports.log = log;
|
|
26
|
+
const prettyPrintJson = (obj, depth = null) => {
|
|
27
|
+
if (!obj) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let objToPrint = obj;
|
|
31
|
+
if (typeof obj === 'string') {
|
|
32
|
+
objToPrint = JSON.parse(obj);
|
|
33
|
+
}
|
|
34
|
+
console.log(util_1.default.inspect(objToPrint, { colors: true, depth }));
|
|
35
|
+
};
|
|
36
|
+
exports.prettyPrintJson = prettyPrintJson;
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
exports.getScanResources = exports.pollScanUntilCompletion = void 0;
|
|
7
|
+
const requestUtils_1 = require("../utils/requestUtils");
|
|
8
|
+
const commonApi_1 = require("../utils/commonApi");
|
|
9
|
+
const oraWrapper_1 = __importDefault(require("../utils/oraWrapper"));
|
|
10
|
+
const cliError_1 = require("./cliError");
|
|
11
|
+
const constants_1 = require("./constants");
|
|
12
|
+
const MS_IN_MINUTE = 1000 * 60;
|
|
13
|
+
const getScanResources = async (config, params, scanId, httpClient) => {
|
|
14
|
+
const res = await httpClient.getScanResources(config, params, scanId);
|
|
15
|
+
const { statusCode, body } = res;
|
|
16
|
+
if (statusCode === 200) {
|
|
17
|
+
return res;
|
|
18
|
+
}
|
|
19
|
+
const { errorCode } = body || {};
|
|
20
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_GET_SCAN, { statusCode, errorCode });
|
|
21
|
+
};
|
|
22
|
+
exports.getScanResources = getScanResources;
|
|
23
|
+
const pollScanUntilCompletion = async (config, timeoutInMinutes, params, scanId) => {
|
|
24
|
+
const client = (0, commonApi_1.getHttpClient)(config);
|
|
25
|
+
const activeStatuses = ['PENDING', 'SCANNING', 'QUEUED'];
|
|
26
|
+
const startedText = 'Scan started';
|
|
27
|
+
const maxEndTime = new Date().getTime() + timeoutInMinutes * MS_IN_MINUTE;
|
|
28
|
+
const startScanSpinner = oraWrapper_1.default.returnOra(startedText);
|
|
29
|
+
oraWrapper_1.default.startSpinner(startScanSpinner);
|
|
30
|
+
await (0, requestUtils_1.sleep)(5000);
|
|
31
|
+
let complete = false;
|
|
32
|
+
while (!complete) {
|
|
33
|
+
try {
|
|
34
|
+
const result = await exports.getScanResources(config, params, scanId, client);
|
|
35
|
+
const { resources: scans } = result.body.data;
|
|
36
|
+
const staticScans = scans?.filter((s) => s.scanType === 2);
|
|
37
|
+
complete = staticScans.some((s) => !activeStatuses.includes(s.state));
|
|
38
|
+
if (complete) {
|
|
39
|
+
oraWrapper_1.default.succeedSpinner(startScanSpinner, 'Scan Finished');
|
|
40
|
+
return scans;
|
|
41
|
+
}
|
|
42
|
+
await (0, requestUtils_1.sleep)(2 * 1000);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
oraWrapper_1.default.failSpinner(startScanSpinner, 'Scan Failed');
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
if (Date.now() >= maxEndTime) {
|
|
49
|
+
oraWrapper_1.default.failSpinner(startScanSpinner, 'Scan timed out');
|
|
50
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_GET_SCAN, {
|
|
51
|
+
errorCode: 'waitingTimedOut'
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
exports.pollScanUntilCompletion = pollScanUntilCompletion;
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
exports.createFunctionEvent = exports.requestScanFunctionPost = exports.sendScanPostRequest = void 0;
|
|
7
|
+
const i18n_1 = __importDefault(require("i18n"));
|
|
8
|
+
const log_symbols_1 = __importDefault(require("log-symbols"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const arn_1 = require("./arn");
|
|
11
|
+
const aws_1 = require("./aws");
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const commonApi_1 = require("../utils/commonApi");
|
|
14
|
+
const logUtils_1 = require("./logUtils");
|
|
15
|
+
const cliError_1 = require("./cliError");
|
|
16
|
+
const constants_1 = require("./constants");
|
|
17
|
+
const sendScanPostRequest = async (config, params, functionsEvent, showProgress = false) => {
|
|
18
|
+
const client = (0, commonApi_1.getHttpClient)(config);
|
|
19
|
+
if (showProgress) {
|
|
20
|
+
(0, logUtils_1.log)(`${log_symbols_1.default.success} Sending Lambda Function scan request to Contrast`);
|
|
21
|
+
}
|
|
22
|
+
const res = await client.postFunctionScan(config, params, functionsEvent);
|
|
23
|
+
const { statusCode, body } = res;
|
|
24
|
+
if (statusCode === 201) {
|
|
25
|
+
if (showProgress) {
|
|
26
|
+
(0, logUtils_1.log)(`${log_symbols_1.default.success} Scan requested successfully`);
|
|
27
|
+
}
|
|
28
|
+
return body?.data?.scanId;
|
|
29
|
+
}
|
|
30
|
+
let { errorCode } = body?.data || {};
|
|
31
|
+
const { data } = body?.data || {};
|
|
32
|
+
let description = '';
|
|
33
|
+
switch (errorCode) {
|
|
34
|
+
case 'not_supported_runtime':
|
|
35
|
+
description = i18n_1.default.__(errorCode, data?.runtime, data?.supportedRuntimes.sort().join(' | '));
|
|
36
|
+
errorCode = false;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_START_SCAN, {
|
|
40
|
+
statusCode,
|
|
41
|
+
errorCode,
|
|
42
|
+
data,
|
|
43
|
+
description
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
exports.sendScanPostRequest = sendScanPostRequest;
|
|
47
|
+
const createFunctionEvent = (lambdaConfig, layersLinks, lambdaPolicies) => {
|
|
48
|
+
delete lambdaConfig.$metadata;
|
|
49
|
+
const functionEvent = (0, utils_1.toLowerKeys)(lambdaConfig.Configuration);
|
|
50
|
+
functionEvent['code'] = lambdaConfig.Code;
|
|
51
|
+
functionEvent['rolePolicies'] = lambdaPolicies;
|
|
52
|
+
if (layersLinks) {
|
|
53
|
+
functionEvent['layers'] = layersLinks;
|
|
54
|
+
}
|
|
55
|
+
return { function: functionEvent };
|
|
56
|
+
};
|
|
57
|
+
exports.createFunctionEvent = createFunctionEvent;
|
|
58
|
+
const requestScanFunctionPost = async (config, lambdaOptions) => {
|
|
59
|
+
const { verbose, jsonOutput, functionName } = lambdaOptions;
|
|
60
|
+
const lambdaClient = (0, aws_1.getLambdaClient)(lambdaOptions);
|
|
61
|
+
if (!jsonOutput) {
|
|
62
|
+
(0, logUtils_1.log)(`${log_symbols_1.default.success} Fetching configuration and policies for Lambda Function ${chalk_1.default.bold(functionName)}`);
|
|
63
|
+
}
|
|
64
|
+
const lambdaConfig = await (0, aws_1.getLambdaFunctionConfiguration)(lambdaClient, lambdaOptions);
|
|
65
|
+
if (!lambdaConfig?.Configuration) {
|
|
66
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_START_SCAN, {
|
|
67
|
+
errorCode: 'missingLambdaConfig'
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
const { Configuration } = lambdaConfig;
|
|
71
|
+
const layersLinks = await (0, aws_1.getLayersLinks)(lambdaClient, Configuration);
|
|
72
|
+
const lambdaPolicies = await (0, aws_1.getLambdaPolicies)(Configuration, lambdaOptions);
|
|
73
|
+
const functionEvent = createFunctionEvent(lambdaConfig, layersLinks, lambdaPolicies);
|
|
74
|
+
const { FunctionArn: functionArn } = Configuration;
|
|
75
|
+
if (!functionArn) {
|
|
76
|
+
throw new cliError_1.CliError(constants_1.ERRORS.FAILED_TO_START_SCAN, {
|
|
77
|
+
errorCode: 'missingLambdaArn'
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const parsedARN = (0, arn_1.parseARN)(functionArn);
|
|
81
|
+
const params = {
|
|
82
|
+
organizationId: config.organizationId,
|
|
83
|
+
provider: 'aws',
|
|
84
|
+
accountId: parsedARN.accountId
|
|
85
|
+
};
|
|
86
|
+
if (verbose) {
|
|
87
|
+
(0, logUtils_1.log)(`${log_symbols_1.default.success} Fetched configuration from AWS:`);
|
|
88
|
+
(0, logUtils_1.prettyPrintJson)(functionEvent);
|
|
89
|
+
}
|
|
90
|
+
const scanId = await sendScanPostRequest(config, params, functionEvent, !jsonOutput);
|
|
91
|
+
return { scanId, params, functionArn };
|
|
92
|
+
};
|
|
93
|
+
exports.requestScanFunctionPost = requestScanFunctionPost;
|