7613 lines
297 KiB
JavaScript
7613 lines
297 KiB
JavaScript
exports.id = 784;
|
|
exports.ids = [784];
|
|
exports.modules = {
|
|
|
|
/***/ 61452:
|
|
/***/ ((module) => {
|
|
|
|
function webpackEmptyContext(req) {
|
|
var e = new Error("Cannot find module '" + req + "'");
|
|
e.code = 'MODULE_NOT_FOUND';
|
|
throw e;
|
|
}
|
|
webpackEmptyContext.keys = () => ([]);
|
|
webpackEmptyContext.resolve = webpackEmptyContext;
|
|
webpackEmptyContext.id = 61452;
|
|
module.exports = webpackEmptyContext;
|
|
|
|
/***/ }),
|
|
|
|
/***/ 3196:
|
|
/***/ ((module) => {
|
|
|
|
function webpackEmptyContext(req) {
|
|
var e = new Error("Cannot find module '" + req + "'");
|
|
e.code = 'MODULE_NOT_FOUND';
|
|
throw e;
|
|
}
|
|
webpackEmptyContext.keys = () => ([]);
|
|
webpackEmptyContext.resolve = webpackEmptyContext;
|
|
webpackEmptyContext.id = 3196;
|
|
module.exports = webpackEmptyContext;
|
|
|
|
/***/ }),
|
|
|
|
/***/ 3708:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.validateTags = exports.generateTags = exports.generateProjectAttributes = exports.validateProjectAttributes = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const fs = __webpack_require__(35747);
|
|
const Debug = __webpack_require__(15158);
|
|
const pathUtil = __webpack_require__(85622);
|
|
const cli_interface_1 = __webpack_require__(65266);
|
|
const options_validator_1 = __webpack_require__(1570);
|
|
const check_paths_1 = __webpack_require__(94501);
|
|
const types_1 = __webpack_require__(94055);
|
|
const config_1 = __webpack_require__(25425);
|
|
const detect = __webpack_require__(45318);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const analytics = __webpack_require__(82744);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const print_deps_1 = __webpack_require__(79792);
|
|
const monitor_1 = __webpack_require__(3959);
|
|
const process_json_monitor_1 = __webpack_require__(21506);
|
|
const snyk = __webpack_require__(9146); // TODO(kyegupov): fix import
|
|
const formatters_1 = __webpack_require__(81329);
|
|
const get_deps_from_plugin_1 = __webpack_require__(4842);
|
|
const get_extra_project_count_1 = __webpack_require__(34355);
|
|
const extract_package_manager_1 = __webpack_require__(22805);
|
|
const convert_multi_plugin_res_to_multi_custom_1 = __webpack_require__(23110);
|
|
const convert_single_splugin_res_to_multi_custom_1 = __webpack_require__(99695);
|
|
const dev_count_analysis_1 = __webpack_require__(73898);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const is_multi_project_scan_1 = __webpack_require__(62435);
|
|
const ecosystems_1 = __webpack_require__(5168);
|
|
const monitor_2 = __webpack_require__(62406);
|
|
const process_command_args_1 = __webpack_require__(52369);
|
|
const SEPARATOR = '\n-------------------------------------------------------\n';
|
|
const debug = Debug('snyk');
|
|
// This is used instead of `let x; try { x = await ... } catch { cleanup }` to avoid
|
|
// declaring the type of x as possibly undefined.
|
|
async function promiseOrCleanup(p, cleanup) {
|
|
return p.catch((error) => {
|
|
cleanup();
|
|
throw error;
|
|
});
|
|
}
|
|
// Returns an array of Registry responses (one per every sub-project scanned), a single response,
|
|
// or an error message.
|
|
async function monitor(...args0) {
|
|
var _a;
|
|
const { options, paths } = process_command_args_1.processCommandArgs(...args0);
|
|
const results = [];
|
|
if (options.id) {
|
|
snyk.id = options.id;
|
|
}
|
|
if (options.allSubProjects && options['project-name']) {
|
|
throw new Error('`--all-sub-projects` is currently not compatible with `--project-name`');
|
|
}
|
|
if (!options.docker) {
|
|
check_paths_1.checkOSSPaths(paths, options);
|
|
}
|
|
if (options.docker && options['remote-repo-url']) {
|
|
throw new Error('`--remote-repo-url` is not supported for container scans');
|
|
}
|
|
// TODO remove once https://github.com/snyk/cli/pull/3433 is merged
|
|
if (options.docker && !options['app-vulns']) {
|
|
options['exclude-app-vulns'] = true;
|
|
}
|
|
// Handles no image arg provided to the container command until
|
|
// a validation interface is implemented in the docker plugin.
|
|
if (options.docker && paths.length === 0) {
|
|
throw new errors_1.MissingArgError();
|
|
}
|
|
api_token_1.apiOrOAuthTokenExists();
|
|
let contributors = [];
|
|
if (!options.docker && analytics.allowAnalytics()) {
|
|
try {
|
|
contributors = await dev_count_analysis_1.getContributors();
|
|
}
|
|
catch (err) {
|
|
debug('error getting repo contributors', err);
|
|
}
|
|
}
|
|
const ecosystem = ecosystems_1.getEcosystem(options);
|
|
if (ecosystem) {
|
|
const commandResult = await ecosystems_1.monitorEcosystem(ecosystem, paths, options, contributors);
|
|
const [monitorResults, monitorErrors] = commandResult;
|
|
return await monitor_2.getFormattedMonitorOutput(results, monitorResults, monitorErrors, options);
|
|
}
|
|
// Part 1: every argument is a scan target; process them sequentially
|
|
for (const path of paths) {
|
|
debug(`Processing ${path}...`);
|
|
try {
|
|
validateMonitorPath(path, options.docker);
|
|
let analysisType = 'all';
|
|
let packageManager;
|
|
if (is_multi_project_scan_1.isMultiProjectScan(options)) {
|
|
analysisType = 'all';
|
|
}
|
|
else if (options.docker) {
|
|
analysisType = 'docker';
|
|
}
|
|
else {
|
|
packageManager = detect.detectPackageManager(path, options);
|
|
}
|
|
await options_validator_1.validateOptions(options, packageManager);
|
|
const targetFile = !options.scanAllUnmanaged && options.docker && !options.file // snyk monitor --docker (without --file)
|
|
? undefined
|
|
: options.file || detect.detectPackageFile(path);
|
|
const displayPath = pathUtil.relative('.', pathUtil.join(path, targetFile || ''));
|
|
const analyzingDepsSpinnerLabel = 'Analyzing ' +
|
|
(packageManager ? packageManager : analysisType) +
|
|
' dependencies for ' +
|
|
displayPath;
|
|
await spinner_1.spinner(analyzingDepsSpinnerLabel);
|
|
// Scan the project dependencies via a plugin
|
|
debug('getDepsFromPlugin ...');
|
|
// each plugin will be asked to scan once per path
|
|
// some return single InspectResult & newer ones return Multi
|
|
const inspectResult = await promiseOrCleanup(get_deps_from_plugin_1.getDepsFromPlugin(path, {
|
|
...options,
|
|
path,
|
|
packageManager,
|
|
}), spinner_1.spinner.clear(analyzingDepsSpinnerLabel));
|
|
analytics.add('pluginName', inspectResult.plugin.name);
|
|
// We send results from "all-sub-projects" scanning as different Monitor objects
|
|
// multi result will become default, so start migrating code to always work with it
|
|
let perProjectResult;
|
|
if (!cli_interface_1.legacyPlugin.isMultiResult(inspectResult)) {
|
|
perProjectResult = convert_single_splugin_res_to_multi_custom_1.convertSingleResultToMultiCustom(inspectResult);
|
|
}
|
|
else {
|
|
perProjectResult = convert_multi_plugin_res_to_multi_custom_1.convertMultiResultToMultiCustom(inspectResult);
|
|
}
|
|
const failedResults = inspectResult
|
|
.failedResults;
|
|
if (failedResults === null || failedResults === void 0 ? void 0 : failedResults.length) {
|
|
failedResults.forEach((result) => {
|
|
results.push({
|
|
ok: false,
|
|
data: new errors_1.MonitorError(500, result.errMessage),
|
|
path: result.targetFile || '',
|
|
});
|
|
});
|
|
}
|
|
const postingMonitorSpinnerLabel = 'Posting monitor snapshot for ' + displayPath + ' ...';
|
|
await spinner_1.spinner(postingMonitorSpinnerLabel);
|
|
// Post the project dependencies to the Registry
|
|
for (const projectDeps of perProjectResult.scannedProjects) {
|
|
try {
|
|
if (!projectDeps.depGraph && !projectDeps.depTree) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError('Your monitor request could not be completed. Please email support@snyk.io');
|
|
}
|
|
const extractedPackageManager = extract_package_manager_1.extractPackageManager(projectDeps, perProjectResult, options);
|
|
analytics.add('packageManager', extractedPackageManager);
|
|
const projectName = getProjectName(projectDeps);
|
|
if (projectDeps.depGraph) {
|
|
debug(`Processing ${(_a = projectDeps.depGraph.rootPkg) === null || _a === void 0 ? void 0 : _a.name}...`);
|
|
print_deps_1.maybePrintDepGraph(options, projectDeps.depGraph);
|
|
}
|
|
if (projectDeps.depTree) {
|
|
debug(`Processing ${projectDeps.depTree.name}...`);
|
|
print_deps_1.maybePrintDepTree(options, projectDeps.depTree);
|
|
}
|
|
const tFile = projectDeps.targetFile || targetFile;
|
|
const targetFileRelativePath = projectDeps.plugin.targetFile ||
|
|
(tFile && pathUtil.join(pathUtil.resolve(path), tFile)) ||
|
|
'';
|
|
const res = await promiseOrCleanup(monitor_1.monitor(path, generateMonitorMeta(options, extractedPackageManager), projectDeps, options, projectDeps.plugin, targetFileRelativePath, contributors, generateProjectAttributes(options), generateTags(options)), spinner_1.spinner.clear(postingMonitorSpinnerLabel));
|
|
res.path = path;
|
|
const monOutput = formatters_1.formatMonitorOutput(extractedPackageManager, res, options, projectName, await get_extra_project_count_1.getExtraProjectCount(path, options, inspectResult));
|
|
// push a good result
|
|
results.push({ ok: true, data: monOutput, path, projectName });
|
|
}
|
|
catch (err) {
|
|
// pushing this error allow this inner loop to keep scanning the projects
|
|
// even if 1 in 100 fails
|
|
results.push({ ok: false, data: err, path });
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
// push this error, the loop continues
|
|
results.push({ ok: false, data: err, path });
|
|
}
|
|
finally {
|
|
spinner_1.spinner.clearAll();
|
|
}
|
|
}
|
|
// Part 2: process the output from the Registry
|
|
if (options.json) {
|
|
return process_json_monitor_1.processJsonMonitorResponse(results);
|
|
}
|
|
const output = results
|
|
.map((res) => {
|
|
if (res.ok) {
|
|
return res.data;
|
|
}
|
|
const errorMessage = res.data && res.data.userMessage
|
|
? chalk_1.default.bold.red(res.data.userMessage)
|
|
: res.data
|
|
? res.data.message
|
|
: 'Unknown error occurred.';
|
|
return (chalk_1.default.bold.white('\nMonitoring ' + res.path + '...\n\n') + errorMessage);
|
|
})
|
|
.join('\n' + SEPARATOR);
|
|
if (results.every((res) => res.ok)) {
|
|
return output;
|
|
}
|
|
throw new Error(output);
|
|
}
|
|
exports.default = monitor;
|
|
function generateMonitorMeta(options, packageManager) {
|
|
return {
|
|
method: 'cli',
|
|
packageManager,
|
|
'policy-path': options['policy-path'],
|
|
'project-name': options['project-name'] || config_1.default.PROJECT_NAME,
|
|
isDocker: !!options.docker,
|
|
prune: !!options.pruneRepeatedSubdependencies,
|
|
'remote-repo-url': options['remote-repo-url'],
|
|
targetReference: options['target-reference'],
|
|
};
|
|
}
|
|
/**
|
|
* Parse an attribute from the CLI into the relevant enum type.
|
|
*
|
|
* @param attribute The project attribute (e.g. environment)
|
|
* @param permitted Permitted options
|
|
* @param options CLI options provided
|
|
* @returns An array of attributes to set on the project or undefined to mean "do not touch".
|
|
*/
|
|
function getProjectAttribute(attribute, permitted, options) {
|
|
const permittedValues = Object.values(permitted);
|
|
if (options[attribute] === undefined) {
|
|
return undefined;
|
|
}
|
|
// Explicit flag to clear the existing values for this attribute already set on the project
|
|
// e.g. if you specify --environment=
|
|
// then this means you want to remove existing environment values on the project.
|
|
if (options[attribute] === '') {
|
|
return [];
|
|
}
|
|
// When it's specified without the =, we raise an explicit error to avoid
|
|
// accidentally clearing the existing values.
|
|
if (options[attribute] === true) {
|
|
throw new errors_1.ValidationError(`--${attribute} must contain an '=' with a comma-separated list of values. To clear all existing values, pass no values i.e. --${attribute}=`);
|
|
}
|
|
const values = options[attribute].split(',');
|
|
const extra = values.filter((value) => !permittedValues.includes(value));
|
|
if (extra.length > 0) {
|
|
throw new errors_1.ValidationError(`${extra.length} invalid ${attribute}: ${extra.join(', ')}. ` +
|
|
`Possible values are: ${permittedValues.join(', ')}`);
|
|
}
|
|
return values;
|
|
}
|
|
function validateProjectAttributes(options) {
|
|
// The validation is deep within the parsing, so call the generate but throw away the return for simplicity.
|
|
// Using this method makes it much clearer what the intent is of the caller.
|
|
generateProjectAttributes(options);
|
|
}
|
|
exports.validateProjectAttributes = validateProjectAttributes;
|
|
function generateProjectAttributes(options) {
|
|
return {
|
|
criticality: getProjectAttribute('project-business-criticality', types_1.PROJECT_CRITICALITY, options),
|
|
environment: getProjectAttribute('project-environment', types_1.PROJECT_ENVIRONMENT, options),
|
|
lifecycle: getProjectAttribute('project-lifecycle', types_1.PROJECT_LIFECYCLE, options),
|
|
};
|
|
}
|
|
exports.generateProjectAttributes = generateProjectAttributes;
|
|
/**
|
|
* Parse CLI --tags options into an internal data structure.
|
|
*
|
|
* If this returns undefined, it means "do not touch the existing tags on the project".
|
|
*
|
|
* Anything else means "replace existing tags on the project with this list" even if empty.
|
|
*
|
|
* @param options CLI options
|
|
* @returns List of parsed tags or undefined if they are to be left untouched.
|
|
*/
|
|
function generateTags(options) {
|
|
if (options['project-tags'] === undefined && options['tags'] === undefined) {
|
|
return undefined;
|
|
}
|
|
if (options['project-tags'] !== undefined && options['tags'] !== undefined) {
|
|
throw new errors_1.ValidationError('Only one of --tags or --project-tags may be specified, not both');
|
|
}
|
|
const rawTags = options['tags'] === undefined ? options['project-tags'] : options['tags'];
|
|
if (rawTags === '') {
|
|
return [];
|
|
}
|
|
// When it's specified without the =, we raise an explicit error to avoid
|
|
// accidentally clearing the existing tags;
|
|
if (rawTags === true) {
|
|
throw new errors_1.ValidationError(`--project-tags must contain an '=' with a comma-separated list of pairs (also separated with an '='). To clear all existing values, pass no values i.e. --project-tags=`);
|
|
}
|
|
const keyEqualsValuePairs = rawTags.split(',');
|
|
const tags = [];
|
|
for (const keyEqualsValue of keyEqualsValuePairs) {
|
|
const parts = keyEqualsValue.split('=');
|
|
if (parts.length !== 2) {
|
|
throw new errors_1.ValidationError(`The tag "${keyEqualsValue}" does not have an "=" separating the key and value. For example: --project-tag=KEY=VALUE`);
|
|
}
|
|
tags.push({
|
|
key: parts[0],
|
|
value: parts[1],
|
|
});
|
|
}
|
|
return tags;
|
|
}
|
|
exports.generateTags = generateTags;
|
|
function validateTags(options) {
|
|
// The validation is deep within the parsing, so call the generate but throw away the return for simplicity.
|
|
// Using this method makes it much clearer what the intent is of the caller.
|
|
generateTags(options);
|
|
}
|
|
exports.validateTags = validateTags;
|
|
function validateMonitorPath(path, isDocker) {
|
|
const exists = fs.existsSync(path);
|
|
if (!exists && !isDocker) {
|
|
throw new Error('"' + path + '" is not a valid path for "snyk monitor"');
|
|
}
|
|
}
|
|
function getProjectName(projectDeps) {
|
|
var _a, _b, _c, _d;
|
|
return (((_a = projectDeps.meta) === null || _a === void 0 ? void 0 : _a.gradleProjectName) || ((_c = (_b = projectDeps.depGraph) === null || _b === void 0 ? void 0 : _b.rootPkg) === null || _c === void 0 ? void 0 : _c.name) || ((_d = projectDeps.depTree) === null || _d === void 0 ? void 0 : _d.name));
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 21506:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.processJsonMonitorResponse = void 0;
|
|
function processJsonMonitorResponse(results) {
|
|
let dataToSend = results.map((result) => {
|
|
if (result.ok) {
|
|
const jsonData = JSON.parse(result.data);
|
|
if (result.projectName) {
|
|
jsonData.projectName = result.projectName;
|
|
}
|
|
return jsonData;
|
|
}
|
|
return { ok: false, error: result.data.message, path: result.path };
|
|
});
|
|
// backwards compat - strip array if only one result
|
|
dataToSend = dataToSend.length === 1 ? dataToSend[0] : dataToSend;
|
|
const stringifiedData = JSON.stringify(dataToSend, null, 2);
|
|
if (results.every((res) => res.ok)) {
|
|
return stringifiedData;
|
|
}
|
|
const err = new Error(stringifiedData);
|
|
err.json = stringifiedData;
|
|
throw err;
|
|
}
|
|
exports.processJsonMonitorResponse = processJsonMonitorResponse;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 52369:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.processCommandArgs = void 0;
|
|
function processCommandArgs(...args) {
|
|
let options = {};
|
|
if (typeof args[args.length - 1] === 'object') {
|
|
options = args.pop();
|
|
}
|
|
args = args.filter(Boolean);
|
|
// For repository scanning, populate with default path (cwd) if no path given
|
|
if (args.length === 0 && !options.docker) {
|
|
args.unshift(process.cwd());
|
|
}
|
|
return { options, paths: args };
|
|
}
|
|
exports.processCommandArgs = processCommandArgs;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 55246:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.TestCommandResult = exports.CommandResult = void 0;
|
|
class CommandResult {
|
|
constructor(result) {
|
|
this.result = result;
|
|
}
|
|
toString() {
|
|
return this.result;
|
|
}
|
|
getDisplayResults() {
|
|
return this.result;
|
|
}
|
|
}
|
|
exports.CommandResult = CommandResult;
|
|
class TestCommandResult extends CommandResult {
|
|
constructor() {
|
|
super(...arguments);
|
|
this.jsonResult = '';
|
|
this.sarifResult = '';
|
|
}
|
|
getJsonResult() {
|
|
return this.jsonResult;
|
|
}
|
|
getSarifResult() {
|
|
return this.sarifResult;
|
|
}
|
|
static createHumanReadableTestCommandResult(humanReadableResult, jsonResult, sarifResult) {
|
|
return new HumanReadableTestCommandResult(humanReadableResult, jsonResult, sarifResult);
|
|
}
|
|
static createJsonTestCommandResult(stdout, jsonResult, sarifResult) {
|
|
return new JsonTestCommandResult(stdout, jsonResult, sarifResult);
|
|
}
|
|
}
|
|
exports.TestCommandResult = TestCommandResult;
|
|
class HumanReadableTestCommandResult extends TestCommandResult {
|
|
constructor(humanReadableResult, jsonResult, sarifResult) {
|
|
super(humanReadableResult);
|
|
this.jsonResult = '';
|
|
this.sarifResult = '';
|
|
this.jsonResult = jsonResult;
|
|
if (sarifResult) {
|
|
this.sarifResult = sarifResult;
|
|
}
|
|
}
|
|
getJsonResult() {
|
|
return this.jsonResult;
|
|
}
|
|
getSarifResult() {
|
|
return this.sarifResult;
|
|
}
|
|
}
|
|
class JsonTestCommandResult extends TestCommandResult {
|
|
constructor(stdout, jsonResult, sarifResult) {
|
|
super(stdout);
|
|
if (jsonResult) {
|
|
this.jsonResult = jsonResult;
|
|
}
|
|
if (sarifResult) {
|
|
this.sarifResult = sarifResult;
|
|
}
|
|
else {
|
|
this.jsonResult = stdout;
|
|
}
|
|
}
|
|
getJsonResult() {
|
|
return this.jsonResult;
|
|
}
|
|
getSarifResult() {
|
|
return this.sarifResult;
|
|
}
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 94501:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.checkOSSPaths = void 0;
|
|
const errors_1 = __webpack_require__(55191);
|
|
const detect_1 = __webpack_require__(45318);
|
|
// Throw error if user specifies package file name as part of path,
|
|
// and if user specifies multiple paths and used project-name option.
|
|
function checkOSSPaths(paths, options) {
|
|
let count = 0;
|
|
for (const path of paths) {
|
|
if (typeof path === 'string' && detect_1.isPathToPackageFile(path)) {
|
|
throw errors_1.MissingTargetFileError(path);
|
|
}
|
|
else if (typeof path === 'string') {
|
|
if (++count > 1 && options['project-name']) {
|
|
throw new errors_1.UnsupportedOptionCombinationError([
|
|
'multiple paths',
|
|
'project-name',
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.checkOSSPaths = checkOSSPaths;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 55203:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getBase64Encoding = exports.getCodeClientProxyUrl = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const user_config_1 = __webpack_require__(28137);
|
|
function getCodeClientProxyUrl() {
|
|
const url = new URL(config_1.default.API);
|
|
const domain = url.origin;
|
|
return (config_1.default.CODE_CLIENT_PROXY_URL ||
|
|
domain.replace(/\/\/(ap[pi]\.)?/, '//deeproxy.'));
|
|
}
|
|
exports.getCodeClientProxyUrl = getCodeClientProxyUrl;
|
|
function getBase64Encoding(enabled = user_config_1.config.get('use-base64-encoding')) {
|
|
if (enabled) {
|
|
return enabled.toLowerCase() === 'true';
|
|
}
|
|
return false;
|
|
}
|
|
exports.getBase64Encoding = getBase64Encoding;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 65623:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.CALL_PATH_TRAILING_ELEMENTS = exports.CALL_PATH_LEADING_ELEMENTS = exports.PATH_HIDDEN_ELEMENTS = exports.PATH_SEPARATOR = void 0;
|
|
// Separator used while displaying various paths (e.g. package paths, call
|
|
// paths) to the user
|
|
exports.PATH_SEPARATOR = ' > ';
|
|
// String used to signify hidden path elements e.g. for abbreviated paths
|
|
exports.PATH_HIDDEN_ELEMENTS = '...';
|
|
// Number of function names to show in the beginning of an abbreviated call path
|
|
exports.CALL_PATH_LEADING_ELEMENTS = 2;
|
|
// Number of function names to show in the end of an abbreviated call path
|
|
exports.CALL_PATH_TRAILING_ELEMENTS = 2;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 69813:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.isUnmanagedEcosystem = void 0;
|
|
function isUnmanagedEcosystem(ecosystem) {
|
|
return ecosystem === 'cpp';
|
|
}
|
|
exports.isUnmanagedEcosystem = isUnmanagedEcosystem;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 5168:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getEcosystem = exports.getEcosystemForTest = void 0;
|
|
var test_1 = __webpack_require__(60937);
|
|
Object.defineProperty(exports, "testEcosystem", ({ enumerable: true, get: function () { return test_1.testEcosystem; } }));
|
|
var monitor_1 = __webpack_require__(62406);
|
|
Object.defineProperty(exports, "monitorEcosystem", ({ enumerable: true, get: function () { return monitor_1.monitorEcosystem; } }));
|
|
var plugins_1 = __webpack_require__(78053);
|
|
Object.defineProperty(exports, "getPlugin", ({ enumerable: true, get: function () { return plugins_1.getPlugin; } }));
|
|
/**
|
|
* Ecosystems are listed here if you opt in to the new plugin test flow.
|
|
* This is a breaking change to the old plugin formats, so only a select few
|
|
* plugins currently work with it.
|
|
*
|
|
* Currently container scanning is not yet ready to work with this flow,
|
|
* hence this is in a separate function from getEcosystem().
|
|
*/
|
|
function getEcosystemForTest(options) {
|
|
if (options.unmanaged) {
|
|
return 'cpp';
|
|
}
|
|
if (options.code) {
|
|
return 'code';
|
|
}
|
|
return null;
|
|
}
|
|
exports.getEcosystemForTest = getEcosystemForTest;
|
|
function getEcosystem(options) {
|
|
if (options.unmanaged) {
|
|
return 'cpp';
|
|
}
|
|
if (options.docker) {
|
|
return 'docker';
|
|
}
|
|
return null;
|
|
}
|
|
exports.getEcosystem = getEcosystem;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 62406:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getFormattedMonitorOutput = exports.generateMonitorDependenciesRequest = exports.monitorEcosystem = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const config_1 = __webpack_require__(25425);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const promise_1 = __webpack_require__(90430);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const plugins_1 = __webpack_require__(78053);
|
|
const formatters_1 = __webpack_require__(81329);
|
|
const get_extra_project_count_1 = __webpack_require__(34355);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const policy_1 = __webpack_require__(4669);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const resolve_monitor_facts_1 = __webpack_require__(47630);
|
|
const monitor_1 = __webpack_require__(3708);
|
|
const common_1 = __webpack_require__(69813);
|
|
const policy_2 = __webpack_require__(32615);
|
|
const SEPARATOR = '\n-------------------------------------------------------\n';
|
|
async function monitorEcosystem(ecosystem, paths, options, contributors) {
|
|
const plugin = plugins_1.getPlugin(ecosystem);
|
|
monitor_1.validateTags(options);
|
|
monitor_1.validateProjectAttributes(options);
|
|
const scanResultsByPath = {};
|
|
for (const path of paths) {
|
|
try {
|
|
await spinner_1.spinner(`Analyzing dependencies in ${path}`);
|
|
options.path = path;
|
|
const pluginResponse = await plugin.scan(options);
|
|
scanResultsByPath[path] = pluginResponse.scanResults;
|
|
const policy = await policy_2.findAndLoadPolicy(path, 'cpp', options);
|
|
if (policy) {
|
|
scanResultsByPath[path].forEach((scanResult) => (scanResult.policy = policy.toString()));
|
|
}
|
|
}
|
|
catch (error) {
|
|
if (ecosystem === 'docker' &&
|
|
error.statusCode === 401 &&
|
|
error.message === 'authentication required') {
|
|
throw new errors_1.DockerImageNotFoundError(path);
|
|
}
|
|
if (ecosystem === 'docker' && error.message === 'invalid image format') {
|
|
throw new errors_1.DockerImageNotFoundError(path);
|
|
}
|
|
throw error;
|
|
}
|
|
finally {
|
|
spinner_1.spinner.clearAll();
|
|
}
|
|
}
|
|
const [monitorResults, errors] = await selectAndExecuteMonitorStrategy(ecosystem, scanResultsByPath, options, contributors);
|
|
return [monitorResults, errors];
|
|
}
|
|
exports.monitorEcosystem = monitorEcosystem;
|
|
async function selectAndExecuteMonitorStrategy(ecosystem, scanResultsByPath, options, contributors) {
|
|
return common_1.isUnmanagedEcosystem(ecosystem)
|
|
? await resolve_monitor_facts_1.resolveAndMonitorFacts(scanResultsByPath, options, contributors)
|
|
: await monitorDependencies(scanResultsByPath, options);
|
|
}
|
|
async function generateMonitorDependenciesRequest(scanResult, options) {
|
|
// WARNING! This mutates the payload. The project name logic should be handled in the plugin.
|
|
scanResult.name =
|
|
options['project-name'] || config_1.default.PROJECT_NAME || scanResult.name;
|
|
// WARNING! This mutates the payload. Policy logic should be in the plugin.
|
|
const policy = await policy_1.findAndLoadPolicyForScanResult(scanResult, options);
|
|
if (policy !== undefined) {
|
|
scanResult.policy = policy.toString();
|
|
}
|
|
return {
|
|
scanResult,
|
|
method: 'cli',
|
|
projectName: options['project-name'] || config_1.default.PROJECT_NAME || undefined,
|
|
tags: monitor_1.generateTags(options),
|
|
attributes: monitor_1.generateProjectAttributes(options),
|
|
};
|
|
}
|
|
exports.generateMonitorDependenciesRequest = generateMonitorDependenciesRequest;
|
|
async function monitorDependencies(scans, options) {
|
|
const results = [];
|
|
const errors = [];
|
|
for (const [path, scanResults] of Object.entries(scans)) {
|
|
await spinner_1.spinner(`Monitoring dependencies in ${path}`);
|
|
for (const scanResult of scanResults) {
|
|
const monitorDependenciesRequest = await generateMonitorDependenciesRequest(scanResult, options);
|
|
const configOrg = config_1.default.org ? decodeURIComponent(config_1.default.org) : undefined;
|
|
const payload = {
|
|
method: 'PUT',
|
|
url: `${config_1.default.API}/monitor-dependencies`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
body: monitorDependenciesRequest,
|
|
qs: {
|
|
org: options.org || configOrg,
|
|
},
|
|
};
|
|
try {
|
|
const response = await promise_1.makeRequest(payload);
|
|
results.push({
|
|
...response,
|
|
path,
|
|
scanResult,
|
|
});
|
|
}
|
|
catch (error) {
|
|
if (error.code === 401) {
|
|
throw errors_1.AuthFailedError();
|
|
}
|
|
if (error.code >= 400 && error.code < 500) {
|
|
throw new errors_1.MonitorError(error.code, error.message);
|
|
}
|
|
errors.push({
|
|
error: 'Could not monitor dependencies in ' + path,
|
|
path,
|
|
scanResult,
|
|
});
|
|
}
|
|
}
|
|
spinner_1.spinner.clearAll();
|
|
}
|
|
return [results, errors];
|
|
}
|
|
async function getFormattedMonitorOutput(results, monitorResults, errors, options) {
|
|
for (const monitorResult of monitorResults) {
|
|
let monOutput = '';
|
|
if (monitorResult.ok) {
|
|
monOutput = formatters_1.formatMonitorOutput(monitorResult.scanResult.identity.type, monitorResult, options, monitorResult.projectName, await get_extra_project_count_1.getExtraProjectCount(monitorResult.path, options,
|
|
// TODO: Fix to pass the old "inspectResult.plugin.meta.allSubProjectNames", which ecosystem uses this?
|
|
// "allSubProjectNames" can become a Fact returned by a plugin.
|
|
{}));
|
|
}
|
|
else {
|
|
monOutput = formatters_1.formatErrorMonitorOutput(monitorResult.scanResult.identity.type, monitorResult, options);
|
|
}
|
|
results.push({
|
|
ok: true,
|
|
data: monOutput,
|
|
path: monitorResult.path,
|
|
projectName: monitorResult.id,
|
|
});
|
|
}
|
|
for (const monitorError of errors) {
|
|
results.push({
|
|
ok: false,
|
|
data: new errors_1.MonitorError(500, monitorError.error),
|
|
path: monitorError.path,
|
|
});
|
|
}
|
|
const outputString = results
|
|
.map((res) => {
|
|
if (res.ok) {
|
|
return res.data;
|
|
}
|
|
const errorMessage = res.data && res.data.userMessage
|
|
? chalk_1.default.bold.red(res.data.userMessage)
|
|
: res.data
|
|
? res.data.message
|
|
: 'Unknown error occurred.';
|
|
return (chalk_1.default.bold.white('\nMonitoring ' + res.path + '...\n\n') + errorMessage);
|
|
})
|
|
.join('\n' + SEPARATOR);
|
|
if (results.every((res) => res.ok)) {
|
|
return outputString;
|
|
}
|
|
throw new Error(outputString);
|
|
}
|
|
exports.getFormattedMonitorOutput = getFormattedMonitorOutput;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 33077:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.extractAndApplyPluginAnalytics = void 0;
|
|
const analytics = __webpack_require__(82744);
|
|
function extractAndApplyPluginAnalytics(pluginAnalytics, asyncRequestToken) {
|
|
if (asyncRequestToken) {
|
|
analytics.add('asyncRequestToken', asyncRequestToken);
|
|
}
|
|
for (const { name, data } of pluginAnalytics) {
|
|
analytics.add(name, data);
|
|
}
|
|
}
|
|
exports.extractAndApplyPluginAnalytics = extractAndApplyPluginAnalytics;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 78053:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getPlugin = void 0;
|
|
const cppPlugin = __webpack_require__(96957);
|
|
const dockerPlugin = __webpack_require__(61165);
|
|
const sast_1 = __webpack_require__(93221);
|
|
const EcosystemPlugins = {
|
|
cpp: cppPlugin,
|
|
// TODO: not any
|
|
docker: dockerPlugin,
|
|
code: sast_1.codePlugin,
|
|
};
|
|
function getPlugin(ecosystem) {
|
|
return EcosystemPlugins[ecosystem];
|
|
}
|
|
exports.getPlugin = getPlugin;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 4669:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.filterIgnoredIssues = exports.findAndLoadPolicyForScanResult = void 0;
|
|
const path = __webpack_require__(85622);
|
|
const policy_1 = __webpack_require__(32615);
|
|
async function findAndLoadPolicyForScanResult(scanResult, options) {
|
|
const targetFileRelativePath = scanResult.identity.targetFile
|
|
? path.join(path.resolve(`${options.path}`), scanResult.identity.targetFile)
|
|
: undefined;
|
|
const targetFileDir = targetFileRelativePath
|
|
? path.parse(targetFileRelativePath).dir
|
|
: undefined;
|
|
const scanType = options.docker
|
|
? 'docker'
|
|
: scanResult.identity.type;
|
|
// TODO: fix this and send only send when we used resolve-deps for node
|
|
// it should be a ExpandedPkgTree type instead
|
|
const packageExpanded = undefined;
|
|
const policy = (await policy_1.findAndLoadPolicy(options.path, scanType, options, packageExpanded, targetFileDir)); // TODO: findAndLoadPolicy() does not return a string!
|
|
return policy;
|
|
}
|
|
exports.findAndLoadPolicyForScanResult = findAndLoadPolicyForScanResult;
|
|
function filterIgnoredIssues(issues, issuesData, policy) {
|
|
if (!(policy === null || policy === void 0 ? void 0 : policy.ignore)) {
|
|
return [issues, issuesData];
|
|
}
|
|
const filteredIssuesData = { ...issuesData };
|
|
const filteredIssues = issues.filter((issue) => {
|
|
const ignoredIssue = policy.ignore[issue.issueId];
|
|
if (!ignoredIssue) {
|
|
return true;
|
|
}
|
|
const allResourcesRule = ignoredIssue.find((element) => '*' in element);
|
|
if (!allResourcesRule) {
|
|
return true;
|
|
}
|
|
const expiredIgnoreRule = new Date(allResourcesRule['*'].expires) < new Date();
|
|
if (!expiredIgnoreRule) {
|
|
delete filteredIssuesData[issue.issueId];
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return [filteredIssues, filteredIssuesData];
|
|
}
|
|
exports.filterIgnoredIssues = filterIgnoredIssues;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 47630:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.resolveAndMonitorFacts = void 0;
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const polling_monitor_1 = __webpack_require__(59354);
|
|
const plugin_analytics_1 = __webpack_require__(33077);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const common_1 = __webpack_require__(74434);
|
|
async function resolveAndMonitorFacts(scans, options, contributors) {
|
|
const results = [];
|
|
const errors = [];
|
|
for (const [path, scanResults] of Object.entries(scans)) {
|
|
await spinner_1.spinner(`Resolving and Monitoring fileSignatures in ${path}`);
|
|
for (const scanResult of scanResults) {
|
|
try {
|
|
const res = await polling_monitor_1.requestMonitorPollingToken(options, true, scanResult);
|
|
if (scanResult.analytics) {
|
|
plugin_analytics_1.extractAndApplyPluginAnalytics(scanResult.analytics, res.token);
|
|
}
|
|
const resolutionMeta = common_1.extractResolutionMetaFromScanResult(scanResult);
|
|
const { maxAttempts, pollInterval } = res.pollingTask;
|
|
const attemptsCount = 0;
|
|
const response = await polling_monitor_1.pollingMonitorWithTokenUntilDone(res.token, true, options, pollInterval, attemptsCount, maxAttempts, resolutionMeta, contributors);
|
|
const ecosystemMonitorResult = {
|
|
...response,
|
|
path,
|
|
scanResult,
|
|
};
|
|
results.push(ecosystemMonitorResult);
|
|
}
|
|
catch (error) {
|
|
if (error.code === 401) {
|
|
throw errors_1.AuthFailedError();
|
|
}
|
|
if (error.code >= 400 && error.code < 500) {
|
|
throw new errors_1.MonitorError(error.code, error.message);
|
|
}
|
|
errors.push({
|
|
error: 'Could not monitor dependencies in ' + path,
|
|
path,
|
|
scanResult,
|
|
});
|
|
}
|
|
}
|
|
spinner_1.spinner.clearAll();
|
|
}
|
|
return [results, errors];
|
|
}
|
|
exports.resolveAndMonitorFacts = resolveAndMonitorFacts;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 85164:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.resolveAndTestFacts = void 0;
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const polling_test_1 = __webpack_require__(77584);
|
|
const plugin_analytics_1 = __webpack_require__(33077);
|
|
const policy_1 = __webpack_require__(32615);
|
|
const policy_2 = __webpack_require__(4669);
|
|
async function resolveAndTestFacts(ecosystem, scans, options) {
|
|
var _a, _b, _c, _d, _e;
|
|
const results = [];
|
|
const errors = [];
|
|
const packageManager = 'Unmanaged (C/C++)';
|
|
for (const [path, scanResults] of Object.entries(scans)) {
|
|
await spinner_1.spinner(`Resolving and Testing fileSignatures in ${path}`);
|
|
for (const scanResult of scanResults) {
|
|
try {
|
|
const res = await polling_test_1.requestTestPollingToken(options, true, scanResult);
|
|
if (scanResult.analytics) {
|
|
plugin_analytics_1.extractAndApplyPluginAnalytics(scanResult.analytics, res.token);
|
|
}
|
|
const { maxAttempts, pollInterval } = res.pollingTask;
|
|
const attemptsCount = 0;
|
|
const response = await polling_test_1.pollingTestWithTokenUntilDone(res.token, ecosystem, options, pollInterval, attemptsCount, maxAttempts);
|
|
const policy = await policy_1.findAndLoadPolicy(path, 'cpp', options);
|
|
const [issues, issuesData] = policy_2.filterIgnoredIssues(response.issues, response.issuesData, policy);
|
|
const issuesMap = new Map();
|
|
response.issues.forEach((i) => {
|
|
issuesMap[i.issueId] = i;
|
|
});
|
|
const vulnerabilities = [];
|
|
for (const issuesDataKey in response.issuesData) {
|
|
const issueData = response.issuesData[issuesDataKey];
|
|
const pkgCoordinate = `${issuesMap[issuesDataKey].pkgName}@${issuesMap[issuesDataKey].pkgVersion}`;
|
|
issueData.from = [pkgCoordinate];
|
|
issueData.name = pkgCoordinate;
|
|
issueData.packageManager = packageManager;
|
|
vulnerabilities.push(issueData);
|
|
}
|
|
const dependencyCount = (_e = (_d = (_c = (_b = (_a = response === null || response === void 0 ? void 0 : response.depGraphData) === null || _a === void 0 ? void 0 : _a.graph) === null || _b === void 0 ? void 0 : _b.nodes) === null || _c === void 0 ? void 0 : _c.find((graphNode) => {
|
|
return graphNode.nodeId === 'root-node';
|
|
})) === null || _d === void 0 ? void 0 : _d.deps) === null || _e === void 0 ? void 0 : _e.length;
|
|
results.push({
|
|
issues,
|
|
issuesData,
|
|
depGraphData: response === null || response === void 0 ? void 0 : response.depGraphData,
|
|
depsFilePaths: response === null || response === void 0 ? void 0 : response.depsFilePaths,
|
|
fileSignaturesDetails: response === null || response === void 0 ? void 0 : response.fileSignaturesDetails,
|
|
vulnerabilities,
|
|
path,
|
|
dependencyCount,
|
|
packageManager,
|
|
});
|
|
}
|
|
catch (error) {
|
|
const hasStatusCodeError = error.code >= 400 && error.code <= 500;
|
|
if (hasStatusCodeError) {
|
|
errors.push(error.message);
|
|
continue;
|
|
}
|
|
const failedPath = path ? `in ${path}` : '.';
|
|
errors.push(`Could not test dependencies ${failedPath}`);
|
|
}
|
|
}
|
|
}
|
|
spinner_1.spinner.clearAll();
|
|
return [results, errors];
|
|
}
|
|
exports.resolveAndTestFacts = resolveAndTestFacts;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 60937:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.selectAndExecuteTestStrategy = exports.testEcosystem = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const promise_1 = __webpack_require__(90430);
|
|
const types_1 = __webpack_require__(55246);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const plugins_1 = __webpack_require__(78053);
|
|
const common_1 = __webpack_require__(53110);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const resolve_test_facts_1 = __webpack_require__(85164);
|
|
const common_2 = __webpack_require__(69813);
|
|
async function testEcosystem(ecosystem, paths, options) {
|
|
const plugin = plugins_1.getPlugin(ecosystem);
|
|
// TODO: this is an intermediate step before consolidating ecosystem plugins
|
|
// to accept flows that act differently in the testDependencies step
|
|
if (plugin.test) {
|
|
const { readableResult: res, sarifResult: sarifRes } = await plugin.test(paths, options);
|
|
return types_1.TestCommandResult.createHumanReadableTestCommandResult(res, '', sarifRes);
|
|
}
|
|
const scanResultsByPath = {};
|
|
for (const path of paths) {
|
|
await spinner_1.spinner(`Scanning dependencies in ${path}`);
|
|
options.path = path;
|
|
const pluginResponse = await plugin.scan(options);
|
|
scanResultsByPath[path] = pluginResponse.scanResults;
|
|
}
|
|
spinner_1.spinner.clearAll();
|
|
const [testResults, errors] = await selectAndExecuteTestStrategy(ecosystem, scanResultsByPath, options);
|
|
const stringifiedData = JSON.stringify(testResults, null, 2);
|
|
if (options.json) {
|
|
return types_1.TestCommandResult.createJsonTestCommandResult(stringifiedData);
|
|
}
|
|
const emptyResults = [];
|
|
const scanResults = emptyResults.concat(...Object.values(scanResultsByPath));
|
|
const readableResult = await plugin.display(scanResults, testResults, errors, options);
|
|
return types_1.TestCommandResult.createHumanReadableTestCommandResult(readableResult, stringifiedData);
|
|
}
|
|
exports.testEcosystem = testEcosystem;
|
|
async function selectAndExecuteTestStrategy(ecosystem, scanResultsByPath, options) {
|
|
return common_2.isUnmanagedEcosystem(ecosystem)
|
|
? await resolve_test_facts_1.resolveAndTestFacts(ecosystem, scanResultsByPath, options)
|
|
: await testDependencies(scanResultsByPath, options);
|
|
}
|
|
exports.selectAndExecuteTestStrategy = selectAndExecuteTestStrategy;
|
|
async function testDependencies(scans, options) {
|
|
const results = [];
|
|
const errors = [];
|
|
for (const [path, scanResults] of Object.entries(scans)) {
|
|
await spinner_1.spinner(`Testing dependencies in ${path}`);
|
|
for (const scanResult of scanResults) {
|
|
const payload = {
|
|
method: 'POST',
|
|
url: `${config_1.default.API}/test-dependencies`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
body: {
|
|
scanResult,
|
|
},
|
|
qs: common_1.assembleQueryString(options),
|
|
};
|
|
try {
|
|
const response = await promise_1.makeRequest(payload);
|
|
results.push({
|
|
issues: response.result.issues,
|
|
issuesData: response.result.issuesData,
|
|
depGraphData: response.result.depGraphData,
|
|
});
|
|
}
|
|
catch (error) {
|
|
if (error.code >= 400 && error.code < 500) {
|
|
throw new Error(error.message);
|
|
}
|
|
errors.push('Could not test dependencies in ' + path);
|
|
}
|
|
}
|
|
}
|
|
spinner_1.spinner.clearAll();
|
|
return [results, errors];
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 59369:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.abridgeErrorMessage = void 0;
|
|
function abridgeErrorMessage(msg, maxLen, ellipsis = ' ... ') {
|
|
if (msg.length <= maxLen) {
|
|
return msg;
|
|
}
|
|
const toKeep = Math.floor((maxLen - ellipsis.length) / 2);
|
|
return (msg.slice(0, toKeep) + ellipsis + msg.slice(msg.length - toKeep, msg.length));
|
|
}
|
|
exports.abridgeErrorMessage = abridgeErrorMessage;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 86033:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.InvalidRemoteUrlError = void 0;
|
|
const custom_error_1 = __webpack_require__(17188);
|
|
class InvalidRemoteUrlError extends custom_error_1.CustomError {
|
|
constructor() {
|
|
super(InvalidRemoteUrlError.ERROR_MESSAGE);
|
|
}
|
|
}
|
|
exports.InvalidRemoteUrlError = InvalidRemoteUrlError;
|
|
InvalidRemoteUrlError.ERROR_MESSAGE = 'Invalid argument provided for --remote-repo-url. Value must be a string.';
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 63011:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.hasFeatureFlag = exports.isFeatureFlagSupportedForOrg = void 0;
|
|
const request_1 = __webpack_require__(52050);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const config_1 = __webpack_require__(25425);
|
|
const common_1 = __webpack_require__(53110);
|
|
const errors_1 = __webpack_require__(55191);
|
|
async function isFeatureFlagSupportedForOrg(featureFlag, org) {
|
|
const response = await request_1.makeRequest({
|
|
method: 'GET',
|
|
headers: {
|
|
Authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
qs: common_1.assembleQueryString({ org }),
|
|
url: `${config_1.default.API}/cli-config/feature-flags/${featureFlag}`,
|
|
gzip: true,
|
|
json: true,
|
|
});
|
|
return response.body;
|
|
}
|
|
exports.isFeatureFlagSupportedForOrg = isFeatureFlagSupportedForOrg;
|
|
async function hasFeatureFlag(featureFlag, options) {
|
|
const { code, error, ok } = await isFeatureFlagSupportedForOrg(featureFlag, options.org);
|
|
if (code === 401 || code === 403) {
|
|
throw errors_1.AuthFailedError(error, code);
|
|
}
|
|
return ok;
|
|
}
|
|
exports.hasFeatureFlag = hasFeatureFlag;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 46123:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.find = exports.getStats = exports.readDirectory = void 0;
|
|
const fs = __webpack_require__(35747);
|
|
const pathLib = __webpack_require__(85622);
|
|
const sortBy = __webpack_require__(58254);
|
|
const groupBy = __webpack_require__(20276);
|
|
const detect_1 = __webpack_require__(45318);
|
|
const debugModule = __webpack_require__(15158);
|
|
const debug = debugModule('snyk:find-files');
|
|
// TODO: use util.promisify once we move to node 8
|
|
/**
|
|
* Returns files inside given file path.
|
|
*
|
|
* @param path file path.
|
|
*/
|
|
async function readDirectory(path) {
|
|
return await new Promise((resolve, reject) => {
|
|
fs.readdir(path, (err, files) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
resolve(files);
|
|
});
|
|
});
|
|
}
|
|
exports.readDirectory = readDirectory;
|
|
/**
|
|
* Returns file stats object for given file path.
|
|
*
|
|
* @param path path to file or directory.
|
|
*/
|
|
async function getStats(path) {
|
|
return await new Promise((resolve, reject) => {
|
|
fs.stat(path, (err, stats) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
resolve(stats);
|
|
});
|
|
});
|
|
}
|
|
exports.getStats = getStats;
|
|
/**
|
|
* Find all files in given search path. Returns paths to files found.
|
|
*
|
|
* @param path file path to search.
|
|
* @param ignore (optional) files to ignore. Will always ignore node_modules.
|
|
* @param filter (optional) file names to find. If not provided all files are returned.
|
|
* @param levelsDeep (optional) how many levels deep to search, defaults to two, this path and one sub directory.
|
|
*/
|
|
async function find(path, ignore = [], filter = [], levelsDeep = 4) {
|
|
const found = [];
|
|
const foundAll = [];
|
|
// ensure we ignore find against node_modules path.
|
|
if (path.endsWith('node_modules')) {
|
|
return { files: found, allFilesFound: foundAll };
|
|
}
|
|
// ensure node_modules is always ignored
|
|
if (!ignore.includes('node_modules')) {
|
|
ignore.push('node_modules');
|
|
}
|
|
try {
|
|
if (levelsDeep < 0) {
|
|
return { files: found, allFilesFound: foundAll };
|
|
}
|
|
else {
|
|
levelsDeep--;
|
|
}
|
|
const fileStats = await getStats(path);
|
|
if (fileStats.isDirectory()) {
|
|
const { files, allFilesFound } = await findInDirectory(path, ignore, filter, levelsDeep);
|
|
found.push(...files);
|
|
foundAll.push(...allFilesFound);
|
|
}
|
|
else if (fileStats.isFile()) {
|
|
const fileFound = findFile(path, filter);
|
|
if (fileFound) {
|
|
found.push(fileFound);
|
|
foundAll.push(fileFound);
|
|
}
|
|
}
|
|
const filteredOutFiles = foundAll.filter((f) => !found.includes(f));
|
|
if (filteredOutFiles.length) {
|
|
debug(`Filtered out ${filteredOutFiles.length}/${foundAll.length} files: ${filteredOutFiles.join(', ')}`);
|
|
}
|
|
return { files: filterForDefaultManifests(found), allFilesFound: foundAll };
|
|
}
|
|
catch (err) {
|
|
throw new Error(`Error finding files in path '${path}'.\n${err.message}`);
|
|
}
|
|
}
|
|
exports.find = find;
|
|
function findFile(path, filter = []) {
|
|
if (filter.length > 0) {
|
|
const filename = pathLib.basename(path);
|
|
if (filter.includes(filename)) {
|
|
return path;
|
|
}
|
|
}
|
|
else {
|
|
return path;
|
|
}
|
|
return null;
|
|
}
|
|
async function findInDirectory(path, ignore = [], filter = [], levelsDeep = 4) {
|
|
const files = await readDirectory(path);
|
|
const toFind = files
|
|
.filter((file) => !ignore.includes(file))
|
|
.map((file) => {
|
|
const resolvedPath = pathLib.resolve(path, file);
|
|
if (!fs.existsSync(resolvedPath)) {
|
|
debug('File does not seem to exist, skipping: ', file);
|
|
return { files: [], allFilesFound: [] };
|
|
}
|
|
return find(resolvedPath, ignore, filter, levelsDeep);
|
|
});
|
|
const found = await Promise.all(toFind);
|
|
return {
|
|
files: Array.prototype.concat.apply([], found.map((f) => f.files)),
|
|
allFilesFound: Array.prototype.concat.apply([], found.map((f) => f.allFilesFound)),
|
|
};
|
|
}
|
|
function filterForDefaultManifests(files) {
|
|
// take all the files in the same dir & filter out
|
|
// based on package Manager
|
|
if (files.length <= 1) {
|
|
return files;
|
|
}
|
|
const filteredFiles = [];
|
|
const beforeSort = files
|
|
.filter(Boolean)
|
|
.filter((p) => fs.existsSync(p))
|
|
.map((p) => ({
|
|
path: p,
|
|
...pathLib.parse(p),
|
|
packageManager: detectProjectTypeFromFile(p),
|
|
}));
|
|
const sorted = sortBy(beforeSort, 'dir');
|
|
const foundFiles = groupBy(sorted, 'dir');
|
|
for (const directory of Object.keys(foundFiles)) {
|
|
const filesInDirectory = foundFiles[directory];
|
|
const beforeGroup = filesInDirectory.filter((p) => !!p.packageManager);
|
|
const groupedFiles = groupBy(beforeGroup, 'packageManager');
|
|
for (const packageManager of Object.keys(groupedFiles)) {
|
|
const filesPerPackageManager = groupedFiles[packageManager];
|
|
if (filesPerPackageManager.length <= 1) {
|
|
const shouldSkip = shouldSkipAddingFile(packageManager, filesPerPackageManager[0].path, filteredFiles);
|
|
if (shouldSkip) {
|
|
continue;
|
|
}
|
|
filteredFiles.push(filesPerPackageManager[0].path);
|
|
continue;
|
|
}
|
|
const defaultManifestFileName = chooseBestManifest(filesPerPackageManager, packageManager);
|
|
if (defaultManifestFileName) {
|
|
const shouldSkip = shouldSkipAddingFile(packageManager, filesPerPackageManager[0].path, filteredFiles);
|
|
if (shouldSkip) {
|
|
continue;
|
|
}
|
|
filteredFiles.push(defaultManifestFileName);
|
|
}
|
|
}
|
|
}
|
|
return filteredFiles;
|
|
}
|
|
function detectProjectTypeFromFile(file) {
|
|
try {
|
|
const packageManager = detect_1.detectPackageManagerFromFile(file);
|
|
if (['yarn', 'npm'].includes(packageManager)) {
|
|
return 'node';
|
|
}
|
|
return packageManager;
|
|
}
|
|
catch (error) {
|
|
return null;
|
|
}
|
|
}
|
|
function shouldSkipAddingFile(packageManager, filePath, filteredFiles) {
|
|
if (['gradle'].includes(packageManager) && filePath) {
|
|
const rootGradleFile = filteredFiles
|
|
.filter((targetFile) => targetFile.endsWith('build.gradle') ||
|
|
targetFile.endsWith('build.gradle.kts'))
|
|
.filter((targetFile) => {
|
|
const parsedPath = pathLib.parse(targetFile);
|
|
const relativePath = pathLib.relative(parsedPath.dir, filePath);
|
|
return !relativePath.startsWith(`..${pathLib.sep}`);
|
|
});
|
|
return !!rootGradleFile.length;
|
|
}
|
|
return false;
|
|
}
|
|
function chooseBestManifest(files, projectType) {
|
|
switch (projectType) {
|
|
case 'node': {
|
|
const lockFile = files.filter((path) => ['package-lock.json', 'yarn.lock'].includes(path.base))[0];
|
|
debug(`Encountered multiple node lockfiles files, defaulting to ${lockFile.path}`);
|
|
if (lockFile) {
|
|
return lockFile.path;
|
|
}
|
|
const packageJson = files.filter((path) => ['package.json'].includes(path.base))[0];
|
|
debug(`Encountered multiple npm manifest files, defaulting to ${packageJson.path}`);
|
|
return packageJson.path;
|
|
}
|
|
case 'rubygems': {
|
|
const defaultManifest = files.filter((path) => ['Gemfile.lock'].includes(path.base))[0];
|
|
debug(`Encountered multiple gem manifest files, defaulting to ${defaultManifest.path}`);
|
|
return defaultManifest.path;
|
|
}
|
|
case 'cocoapods': {
|
|
const defaultManifest = files.filter((path) => ['Podfile'].includes(path.base))[0];
|
|
debug(`Encountered multiple cocoapods manifest files, defaulting to ${defaultManifest.path}`);
|
|
return defaultManifest.path;
|
|
}
|
|
case 'pip': {
|
|
const defaultManifest = files.filter((path) => ['Pipfile'].includes(path.base))[0];
|
|
debug(`Encountered multiple pip manifest files, defaulting to ${defaultManifest.path}`);
|
|
return defaultManifest.path;
|
|
}
|
|
case 'gradle': {
|
|
const defaultManifest = files.filter((path) => ['build.gradle'].includes(path.base))[0];
|
|
debug(`Encountered multiple gradle manifest files, defaulting to ${defaultManifest.path}`);
|
|
return defaultManifest.path;
|
|
}
|
|
case 'poetry': {
|
|
const defaultManifest = files.filter((path) => ['pyproject.toml'].includes(path.base))[0];
|
|
debug(`Encountered multiple poetry manifest files, defaulting to ${defaultManifest.path}`);
|
|
return defaultManifest.path;
|
|
}
|
|
case 'hex': {
|
|
const defaultManifest = files.filter((path) => ['mix.exs'].includes(path.base))[0];
|
|
debug(`Encountered multiple hex manifest files, defaulting to ${defaultManifest.path}`);
|
|
return defaultManifest.path;
|
|
}
|
|
default: {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 18362:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.dockerRemediationForDisplay = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
function dockerRemediationForDisplay(res) {
|
|
if (!res.docker || !res.docker.baseImageRemediation) {
|
|
return '';
|
|
}
|
|
const { advice, message } = res.docker.baseImageRemediation;
|
|
const out = [];
|
|
if (advice) {
|
|
for (const item of advice) {
|
|
out.push(getTerminalStringFormatter(item)(item.message));
|
|
}
|
|
}
|
|
else if (message) {
|
|
out.push(message);
|
|
}
|
|
else {
|
|
return '';
|
|
}
|
|
return `\n\n${out.join('\n')}`;
|
|
}
|
|
exports.dockerRemediationForDisplay = dockerRemediationForDisplay;
|
|
function getTerminalStringFormatter({ color, bold, }) {
|
|
let formatter = chalk_1.default;
|
|
if (color && formatter[color]) {
|
|
formatter = formatter[color];
|
|
}
|
|
if (bold) {
|
|
formatter = formatter.bold;
|
|
}
|
|
return formatter;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 4928:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.createDockerBinaryHeading = void 0;
|
|
const values = __webpack_require__(17720);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
function createDockerBinaryHeading(pkgInfo) {
|
|
const binaryName = pkgInfo.pkg.name;
|
|
const binaryVersion = pkgInfo.pkg.version;
|
|
const numOfVulns = values(pkgInfo.issues).length;
|
|
const vulnCountText = numOfVulns > 1 ? 'vulnerabilities' : 'vulnerability';
|
|
return numOfVulns
|
|
? chalk_1.default.bold.white(`------------ Detected ${numOfVulns} ${vulnCountText}` +
|
|
` for ${binaryName}@${binaryVersion} ------------`, '\n')
|
|
: '';
|
|
}
|
|
exports.createDockerBinaryHeading = createDockerBinaryHeading;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 80576:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatDockerBinariesIssues = void 0;
|
|
const values = __webpack_require__(17720);
|
|
const format_docker_binary_heading_1 = __webpack_require__(4928);
|
|
const legacy_format_issue_1 = __webpack_require__(63540);
|
|
function formatDockerBinariesIssues(dockerBinariesSortedGroupedVulns, binariesVulns, options) {
|
|
const binariesIssuesOutput = [];
|
|
for (const pkgInfo of values(binariesVulns.affectedPkgs)) {
|
|
binariesIssuesOutput.push(format_docker_binary_heading_1.createDockerBinaryHeading(pkgInfo));
|
|
const binaryIssues = dockerBinariesSortedGroupedVulns.filter((vuln) => vuln.metadata.name === pkgInfo.pkg.name);
|
|
const formattedBinaryIssues = binaryIssues.map((vuln) => legacy_format_issue_1.formatIssues(vuln, options));
|
|
binariesIssuesOutput.push(formattedBinaryIssues.join('\n\n'));
|
|
}
|
|
return binariesIssuesOutput;
|
|
}
|
|
exports.formatDockerBinariesIssues = formatDockerBinariesIssues;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 41287:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var format_docker_advice_1 = __webpack_require__(18362);
|
|
Object.defineProperty(exports, "dockerRemediationForDisplay", ({ enumerable: true, get: function () { return format_docker_advice_1.dockerRemediationForDisplay; } }));
|
|
var format_docker_binary_issues_1 = __webpack_require__(80576);
|
|
Object.defineProperty(exports, "formatDockerBinariesIssues", ({ enumerable: true, get: function () { return format_docker_binary_issues_1.formatDockerBinariesIssues; } }));
|
|
var format_docker_binary_heading_1 = __webpack_require__(4928);
|
|
Object.defineProperty(exports, "createDockerBinaryHeading", ({ enumerable: true, get: function () { return format_docker_binary_heading_1.createDockerBinaryHeading; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 13232:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.summariseErrorResults = void 0;
|
|
const errors_1 = __webpack_require__(55191);
|
|
function summariseErrorResults(errorResultsLength) {
|
|
const projects = errorResultsLength > 1 ? 'projects' : 'project';
|
|
if (errorResultsLength > 0) {
|
|
return errors_1.errorMessageWithRetry(` Failed to test ${errorResultsLength} ${projects}.`);
|
|
}
|
|
return '';
|
|
}
|
|
exports.summariseErrorResults = summariseErrorResults;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 4040:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatMonitorOutput = exports.formatErrorMonitorOutput = void 0;
|
|
const assign = __webpack_require__(31730);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const url = __webpack_require__(78835);
|
|
const config_1 = __webpack_require__(25425);
|
|
const show_multi_scan_tip_1 = __webpack_require__(95100);
|
|
function formatErrorMonitorOutput(packageManager, res, options, projectName) {
|
|
const humanReadableName = projectName
|
|
? `${res.path} (${projectName})`
|
|
: res.path;
|
|
const strOutput = chalk_1.default.bold.white('\nMonitoring ' + humanReadableName + '...\n\n') +
|
|
'\n\n' +
|
|
(packageManager === 'maven'
|
|
? chalk_1.default.yellow('Detected 0 dependencies (no project created)')
|
|
: '');
|
|
return options.json
|
|
? JSON.stringify(assign({}, res, {
|
|
packageManager,
|
|
}))
|
|
: strOutput;
|
|
}
|
|
exports.formatErrorMonitorOutput = formatErrorMonitorOutput;
|
|
function formatMonitorOutput(packageManager, res, options, projectName, foundProjectCount) {
|
|
const manageUrl = buildManageUrl(res.id, res.org);
|
|
const multiScanTip = show_multi_scan_tip_1.showMultiScanTip(packageManager, options, foundProjectCount);
|
|
const issues = res.licensesPolicy ? 'issues' : 'vulnerabilities';
|
|
const humanReadableName = projectName
|
|
? `${res.path} (${projectName})`
|
|
: res.path;
|
|
const strOutput = chalk_1.default.bold.white('\nMonitoring ' + humanReadableName + '...\n\n') +
|
|
'Explore this snapshot at ' +
|
|
res.uri +
|
|
'\n\n' +
|
|
(multiScanTip ? `${multiScanTip}\n\n` : '') +
|
|
(res.isMonitored
|
|
? 'Notifications about newly disclosed ' +
|
|
issues +
|
|
' related ' +
|
|
'to these dependencies will be emailed to you.\n'
|
|
: chalk_1.default.bold.red('Project is inactive, so notifications are turned ' +
|
|
'off.\nActivate this project here: ' +
|
|
manageUrl +
|
|
'\n\n')) +
|
|
(res.trialStarted
|
|
? chalk_1.default.yellow("You're over the free plan usage limit, \n" +
|
|
'and are now on a free 14-day premium trial.\n' +
|
|
'View plans here: ' +
|
|
manageUrl +
|
|
'\n\n')
|
|
: '');
|
|
return options.json
|
|
? JSON.stringify(assign({}, res, {
|
|
manageUrl,
|
|
packageManager,
|
|
}))
|
|
: strOutput;
|
|
}
|
|
exports.formatMonitorOutput = formatMonitorOutput;
|
|
function buildManageUrl(resId, org) {
|
|
const endpoint = url.parse(config_1.default.API);
|
|
let leader = '';
|
|
if (org) {
|
|
leader = '/org/' + org;
|
|
}
|
|
endpoint.pathname = leader + '/manage';
|
|
const manageUrl = url.format(endpoint);
|
|
// TODO: what was this meant to do?
|
|
endpoint.pathname = leader + '/monitor/' + resId;
|
|
return manageUrl;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 13331:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatReachablePath = exports.formatReachablePaths = exports.summariseReachableVulns = exports.getReachabilityJson = exports.getReachabilityText = exports.formatReachability = void 0;
|
|
const wrap = __webpack_require__(88152);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const legacy_1 = __webpack_require__(34013);
|
|
const constants_1 = __webpack_require__(65623);
|
|
const reachabilityLevels = {
|
|
[legacy_1.REACHABILITY.FUNCTION]: {
|
|
color: chalk_1.default.redBright,
|
|
text: 'Reachable',
|
|
json: 'reachable',
|
|
},
|
|
[legacy_1.REACHABILITY.PACKAGE]: {
|
|
color: chalk_1.default.yellow,
|
|
text: 'Potentially reachable',
|
|
json: 'potentially-reachable',
|
|
},
|
|
[legacy_1.REACHABILITY.NOT_REACHABLE]: {
|
|
color: chalk_1.default.blueBright,
|
|
text: 'Not reachable',
|
|
json: 'not-reachable',
|
|
},
|
|
[legacy_1.REACHABILITY.NO_INFO]: {
|
|
color: (str) => str,
|
|
text: '',
|
|
json: 'no-path-found',
|
|
},
|
|
};
|
|
function formatReachability(reachability) {
|
|
if (!reachability) {
|
|
return '';
|
|
}
|
|
const reachableInfo = reachabilityLevels[reachability];
|
|
const textFunc = reachableInfo ? reachableInfo.color : (str) => str;
|
|
const text = reachableInfo && reachableInfo.text ? `[${reachableInfo.text}]` : '';
|
|
return wrap(textFunc(text), 100);
|
|
}
|
|
exports.formatReachability = formatReachability;
|
|
function getReachabilityText(reachability) {
|
|
if (!reachability) {
|
|
return '';
|
|
}
|
|
const reachableInfo = reachabilityLevels[reachability];
|
|
return reachableInfo ? reachableInfo.text : '';
|
|
}
|
|
exports.getReachabilityText = getReachabilityText;
|
|
function getReachabilityJson(reachability) {
|
|
if (!reachability) {
|
|
return '';
|
|
}
|
|
const reachableInfo = reachabilityLevels[reachability];
|
|
return reachableInfo ? reachableInfo.json : '';
|
|
}
|
|
exports.getReachabilityJson = getReachabilityJson;
|
|
function summariseReachableVulns(vulnerabilities) {
|
|
const reachableVulnsCount = vulnerabilities.filter((v) => v.reachability === legacy_1.REACHABILITY.FUNCTION).length;
|
|
if (reachableVulnsCount > 0) {
|
|
const vulnText = reachableVulnsCount === 1 ? 'vulnerability' : 'vulnerabilities';
|
|
return `In addition, found ${reachableVulnsCount} ${vulnText} with a reachable path.`;
|
|
}
|
|
return '';
|
|
}
|
|
exports.summariseReachableVulns = summariseReachableVulns;
|
|
function getDistinctReachablePaths(reachablePaths, maxPathCount) {
|
|
const uniquePaths = new Set();
|
|
for (const path of reachablePaths) {
|
|
if (uniquePaths.size >= maxPathCount) {
|
|
break;
|
|
}
|
|
uniquePaths.add(formatReachablePath(path));
|
|
}
|
|
return Array.from(uniquePaths.values());
|
|
}
|
|
function formatReachablePaths(sampleReachablePaths, maxPathCount, template) {
|
|
const paths = (sampleReachablePaths === null || sampleReachablePaths === void 0 ? void 0 : sampleReachablePaths.paths) || [];
|
|
const pathCount = (sampleReachablePaths === null || sampleReachablePaths === void 0 ? void 0 : sampleReachablePaths.pathCount) || 0;
|
|
const distinctPaths = getDistinctReachablePaths(paths, maxPathCount);
|
|
const extraPaths = pathCount - distinctPaths.length;
|
|
return template(distinctPaths, extraPaths);
|
|
}
|
|
exports.formatReachablePaths = formatReachablePaths;
|
|
function formatReachablePath(path) {
|
|
const head = path.slice(0, constants_1.CALL_PATH_LEADING_ELEMENTS).join(constants_1.PATH_SEPARATOR);
|
|
const tail = path
|
|
.slice(path.length - constants_1.CALL_PATH_TRAILING_ELEMENTS, path.length)
|
|
.join(constants_1.PATH_SEPARATOR);
|
|
return `${head}${constants_1.PATH_SEPARATOR}${constants_1.PATH_HIDDEN_ELEMENTS}${constants_1.PATH_SEPARATOR}${tail}`;
|
|
}
|
|
exports.formatReachablePath = formatReachablePath;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 28001:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatTestMeta = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const right_pad_1 = __webpack_require__(80627);
|
|
const iac_output_1 = __webpack_require__(68145);
|
|
function formatTestMeta(res, options) {
|
|
const padToLength = 19; // chars to align
|
|
const packageManager = res.packageManager || options.packageManager;
|
|
const targetFile = res.targetFile || res.displayTargetFile || options.file;
|
|
const openSource = res.isPrivate ? 'no' : 'yes';
|
|
const meta = res.org
|
|
? [chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Organization: ', padToLength)) + res.org]
|
|
: [];
|
|
if (options.iac) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Type: ', padToLength)) +
|
|
iac_output_1.capitalizePackageManager(packageManager));
|
|
}
|
|
else {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Package manager: ', padToLength)) +
|
|
packageManager);
|
|
}
|
|
if (targetFile) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Target file: ', padToLength)) + targetFile);
|
|
}
|
|
if (res.projectName) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Project name: ', padToLength)) +
|
|
res.projectName);
|
|
}
|
|
if (options.docker) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Docker image: ', padToLength)) +
|
|
options.path);
|
|
if (res.platform) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Platform: ', padToLength)) +
|
|
res.platform);
|
|
}
|
|
}
|
|
else {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Open source: ', padToLength)) + openSource);
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Project path: ', padToLength)) +
|
|
options.path);
|
|
}
|
|
if (res.payloadType !== 'k8sconfig') {
|
|
const legacyRes = res;
|
|
if (legacyRes.docker && legacyRes.docker.baseImage) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Base image: ', padToLength)) +
|
|
legacyRes.docker.baseImage);
|
|
}
|
|
if (legacyRes.filesystemPolicy) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Local Snyk policy: ', padToLength)) +
|
|
chalk_1.default.green('found'));
|
|
if (legacyRes.ignoreSettings &&
|
|
legacyRes.ignoreSettings.disregardFilesystemIgnores) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Local Snyk policy ignored: ', padToLength)) + chalk_1.default.red('yes'));
|
|
}
|
|
}
|
|
if (legacyRes.licensesPolicy) {
|
|
meta.push(chalk_1.default.bold(right_pad_1.rightPadWithSpaces('Licenses: ', padToLength)) +
|
|
chalk_1.default.green('enabled'));
|
|
}
|
|
}
|
|
return meta.join('\n');
|
|
}
|
|
exports.formatTestMeta = formatTestMeta;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 27495:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.summariseVulnerableResults = void 0;
|
|
function summariseVulnerableResults(vulnerableResults, options) {
|
|
const vulnsLength = vulnerableResults.length;
|
|
if (vulnsLength) {
|
|
if (options.showVulnPaths) {
|
|
return `, ${vulnsLength} contained ${options.iac ? 'issues' : 'vulnerable paths'}.`;
|
|
}
|
|
return `, ${vulnsLength} had issues.`;
|
|
}
|
|
if (options.showVulnPaths) {
|
|
return ', no vulnerable paths were found.';
|
|
}
|
|
return ', no issues were found.';
|
|
}
|
|
exports.summariseVulnerableResults = summariseVulnerableResults;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 24898:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getSeverityValue = void 0;
|
|
const common_1 = __webpack_require__(53110);
|
|
function getSeverityValue(severity) {
|
|
return common_1.SEVERITIES.find((s) => s.verboseName === severity).value;
|
|
}
|
|
exports.getSeverityValue = getSeverityValue;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 68145:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var v1_1 = __webpack_require__(7666);
|
|
Object.defineProperty(exports, "capitalizePackageManager", ({ enumerable: true, get: function () { return v1_1.capitalizePackageManager; } }));
|
|
Object.defineProperty(exports, "createSarifOutputForIac", ({ enumerable: true, get: function () { return v1_1.createSarifOutputForIac; } }));
|
|
Object.defineProperty(exports, "shareResultsOutput", ({ enumerable: true, get: function () { return v1_1.shareResultsOutput; } }));
|
|
Object.defineProperty(exports, "getIacDisplayedOutput", ({ enumerable: true, get: function () { return v1_1.getIacDisplayedOutput; } }));
|
|
Object.defineProperty(exports, "getIacDisplayErrorFileOutput", ({ enumerable: true, get: function () { return v1_1.getIacDisplayErrorFileOutput; } }));
|
|
var v2_1 = __webpack_require__(49041);
|
|
Object.defineProperty(exports, "formatIacTestSummary", ({ enumerable: true, get: function () { return v2_1.formatIacTestSummary; } }));
|
|
Object.defineProperty(exports, "getIacDisplayedIssues", ({ enumerable: true, get: function () { return v2_1.getIacDisplayedIssues; } }));
|
|
Object.defineProperty(exports, "formatIacTestFailures", ({ enumerable: true, get: function () { return v2_1.formatIacTestFailures; } }));
|
|
Object.defineProperty(exports, "formatFailuresList", ({ enumerable: true, get: function () { return v2_1.formatFailuresList; } }));
|
|
Object.defineProperty(exports, "iacTestTitle", ({ enumerable: true, get: function () { return v2_1.iacTestTitle; } }));
|
|
Object.defineProperty(exports, "spinnerMessage", ({ enumerable: true, get: function () { return v2_1.spinnerMessage; } }));
|
|
Object.defineProperty(exports, "spinnerSuccessMessage", ({ enumerable: true, get: function () { return v2_1.spinnerSuccessMessage; } }));
|
|
Object.defineProperty(exports, "customRulesMessage", ({ enumerable: true, get: function () { return v2_1.customRulesMessage; } }));
|
|
Object.defineProperty(exports, "customRulesReportMessage", ({ enumerable: true, get: function () { return v2_1.customRulesReportMessage; } }));
|
|
Object.defineProperty(exports, "shouldLogUserMessages", ({ enumerable: true, get: function () { return v2_1.shouldLogUserMessages; } }));
|
|
Object.defineProperty(exports, "formatShareResultsOutput", ({ enumerable: true, get: function () { return v2_1.formatShareResultsOutput; } }));
|
|
Object.defineProperty(exports, "failuresTipOutput", ({ enumerable: true, get: function () { return v2_1.failuresTipOutput; } }));
|
|
Object.defineProperty(exports, "formatSnykIacTestTestData", ({ enumerable: true, get: function () { return v2_1.formatSnykIacTestTestData; } }));
|
|
Object.defineProperty(exports, "formatTestData", ({ enumerable: true, get: function () { return v2_1.formatTestData; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 7666:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.shareResultsOutput = exports.mapIacTestResponseToSarifResults = exports.extractReportingDescriptor = exports.createSarifOutputForIac = exports.capitalizePackageManager = exports.getIacDisplayErrorFileOutput = exports.getIacDisplayedOutput = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const Debug = __webpack_require__(15158);
|
|
const pathLib = __webpack_require__(85622);
|
|
const url_1 = __webpack_require__(78835);
|
|
const upperFirst = __webpack_require__(90039);
|
|
const camelCase = __webpack_require__(76884);
|
|
const remediation_based_format_issues_1 = __webpack_require__(57995);
|
|
const legacy_format_issue_1 = __webpack_require__(63540);
|
|
const common_1 = __webpack_require__(53110);
|
|
const detect_1 = __webpack_require__(45318);
|
|
const get_severity_value_1 = __webpack_require__(24898);
|
|
const sarif_output_1 = __webpack_require__(5034);
|
|
const version_1 = __webpack_require__(38217);
|
|
const config_1 = __webpack_require__(25425);
|
|
const git_1 = __webpack_require__(82421);
|
|
const debug = Debug('iac-output');
|
|
function formatIacIssue(issue, isNew, path) {
|
|
const newBadge = isNew ? ' (new)' : '';
|
|
const name = issue.subType ? ` in ${chalk_1.default.bold(issue.subType)}` : '';
|
|
let introducedBy = '';
|
|
if (path) {
|
|
// In this mode, we show only one path by default, for compactness
|
|
const pathStr = remediation_based_format_issues_1.printPath(path, 0);
|
|
introducedBy = `\n introduced by ${pathStr}`;
|
|
}
|
|
return (common_1.colorTextBySeverity(issue.severity, ` ${theme_1.icon.ISSUE} ${chalk_1.default.bold(issue.title)}${newBadge} [${legacy_format_issue_1.titleCaseText(issue.severity)} Severity]`) +
|
|
` [${issue.id}]` +
|
|
name +
|
|
introducedBy +
|
|
'\n');
|
|
}
|
|
function getIacDisplayedOutput(iacTest, testedInfoText, meta, prefix) {
|
|
const issuesTextArray = [
|
|
chalk_1.default.bold.white('\nInfrastructure as code issues:'),
|
|
];
|
|
const NotNew = false;
|
|
const issues = iacTest.result.cloudConfigResults;
|
|
debug(`iac display output - ${issues.length} issues`);
|
|
issues
|
|
.sort((a, b) => get_severity_value_1.getSeverityValue(b.severity) - get_severity_value_1.getSeverityValue(a.severity))
|
|
.forEach((issue) => {
|
|
issuesTextArray.push(formatIacIssue(issue, NotNew, issue.cloudConfigPath));
|
|
});
|
|
const issuesInfoOutput = [];
|
|
debug(`Iac display output - ${issuesTextArray.length} issues text`);
|
|
if (issuesTextArray.length > 0) {
|
|
issuesInfoOutput.push(issuesTextArray.join('\n'));
|
|
}
|
|
let body = issuesInfoOutput.join('\n\n') + '\n\n' + meta;
|
|
const vulnCountText = `found ${issues.length} issues`;
|
|
const summary = testedInfoText + ', ' + chalk_1.default.red.bold(vulnCountText);
|
|
body = body + '\n\n' + summary;
|
|
return prefix + body;
|
|
}
|
|
exports.getIacDisplayedOutput = getIacDisplayedOutput;
|
|
function getIacDisplayErrorFileOutput(iacFileResult) {
|
|
const fileName = pathLib.basename(iacFileResult.filePath);
|
|
return `
|
|
|
|
-------------------------------------------------------
|
|
|
|
Testing ${fileName}...
|
|
|
|
${iacFileResult.failureReason}`;
|
|
}
|
|
exports.getIacDisplayErrorFileOutput = getIacDisplayErrorFileOutput;
|
|
function capitalizePackageManager(type) {
|
|
switch (type) {
|
|
case 'k8sconfig': {
|
|
return 'Kubernetes';
|
|
}
|
|
case 'helmconfig': {
|
|
return 'Helm';
|
|
}
|
|
case 'terraformconfig': {
|
|
return 'Terraform';
|
|
}
|
|
case 'cloudformationconfig': {
|
|
return 'CloudFormation';
|
|
}
|
|
case 'armconfig': {
|
|
return 'ARM';
|
|
}
|
|
default: {
|
|
return 'Infrastructure as Code';
|
|
}
|
|
}
|
|
}
|
|
exports.capitalizePackageManager = capitalizePackageManager;
|
|
// Used to reference the base path in results.
|
|
const PROJECT_ROOT_KEY = 'PROJECTROOT';
|
|
function createSarifOutputForIac(iacTestResponses) {
|
|
// If the CLI scans a singular file, then the base path is the current working directory
|
|
// Otherwise it's the computed path
|
|
const basePath = detect_1.isLocalFolder(iacTestResponses[0].path)
|
|
? pathLib.resolve('.', iacTestResponses[0].path)
|
|
: pathLib.resolve('.');
|
|
let repoRoot;
|
|
try {
|
|
repoRoot = git_1.getRepositoryRoot() + '/';
|
|
}
|
|
catch {
|
|
repoRoot = pathLib.join(basePath, '/'); // the slash at the end is required, otherwise the artifactLocation.uri starts with a slash
|
|
}
|
|
const issues = iacTestResponses.reduce((collect, res) => {
|
|
if (res.result) {
|
|
// targetFile is the computed relative path of the scanned file
|
|
// so needs to be cleaned up before assigning to the URI
|
|
const targetPath = getPathRelativeToRepoRoot(repoRoot, basePath, res.targetFile);
|
|
const mapped = res.result.cloudConfigResults.map((issue) => ({
|
|
issue,
|
|
targetPath,
|
|
}));
|
|
collect.push(...mapped);
|
|
}
|
|
return collect;
|
|
}, []);
|
|
const tool = {
|
|
driver: {
|
|
name: 'Snyk IaC',
|
|
fullName: 'Snyk Infrastructure as Code',
|
|
version: version_1.getVersion(),
|
|
informationUri: 'https://docs.snyk.io/products/snyk-infrastructure-as-code',
|
|
rules: extractReportingDescriptor(issues),
|
|
},
|
|
};
|
|
return {
|
|
$schema: 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json',
|
|
version: '2.1.0',
|
|
runs: [
|
|
{
|
|
// https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html#_Toc34317498
|
|
originalUriBaseIds: {
|
|
[PROJECT_ROOT_KEY]: {
|
|
uri: url_1.pathToFileURL(repoRoot).href,
|
|
description: {
|
|
text: 'The root directory for all project files.',
|
|
},
|
|
},
|
|
},
|
|
tool,
|
|
automationDetails: {
|
|
id: 'snyk-iac',
|
|
},
|
|
results: mapIacTestResponseToSarifResults(issues),
|
|
},
|
|
],
|
|
};
|
|
}
|
|
exports.createSarifOutputForIac = createSarifOutputForIac;
|
|
function extractReportingDescriptor(results) {
|
|
const tool = {};
|
|
results.forEach(({ issue }) => {
|
|
if (tool[issue.id]) {
|
|
return;
|
|
}
|
|
// custom rules may not have some of these fields so we check them first
|
|
const fullDescriptionText = issue.subType
|
|
? `${upperFirst(issue.severity)} severity - ${issue.subType}`
|
|
: `${upperFirst(issue.severity)} severity`;
|
|
const issueText = issue.iacDescription.issue
|
|
? `The issue is... \n${issue.iacDescription.issue}\n\n`
|
|
: '';
|
|
const issueMarkdown = issue.iacDescription.issue
|
|
? `**The issue is...** \n${issue.iacDescription.issue}\n\n`
|
|
: '';
|
|
const impactText = issue.iacDescription.impact
|
|
? ` The impact of this is... \n ${issue.iacDescription.impact}\n\n`
|
|
: '';
|
|
const impactMarkdown = issue.iacDescription.impact
|
|
? ` **The impact of this is...** \n ${issue.iacDescription.impact}\n\n`
|
|
: '';
|
|
const resolveText = issue.iacDescription.resolve
|
|
? ` You can resolve this by... \n${issue.iacDescription.resolve}`
|
|
: '';
|
|
const resolveMarkdown = issue.iacDescription.resolve
|
|
? ` **You can resolve this by...** \n${issue.iacDescription.resolve}`
|
|
: '';
|
|
const tags = ['security'];
|
|
if (issue.subType) {
|
|
tags.push(issue.subType);
|
|
}
|
|
tool[issue.id] = {
|
|
id: issue.id,
|
|
name: upperFirst(camelCase(issue.title)).replace(/ /g, ''),
|
|
shortDescription: {
|
|
text: `${upperFirst(issue.severity)} severity - ${issue.title}`,
|
|
},
|
|
fullDescription: {
|
|
text: fullDescriptionText,
|
|
},
|
|
help: {
|
|
text: `${issueText}${impactText}${resolveText}`.replace(/^\s+/g, ''),
|
|
markdown: `${issueMarkdown}${impactMarkdown}${resolveMarkdown}`.replace(/^\s+/g, ''),
|
|
},
|
|
defaultConfiguration: {
|
|
level: sarif_output_1.getIssueLevel(issue.severity),
|
|
},
|
|
properties: {
|
|
tags,
|
|
problem: {
|
|
severity: issue.severity,
|
|
},
|
|
},
|
|
helpUri: issue.documentation,
|
|
};
|
|
});
|
|
return Object.values(tool);
|
|
}
|
|
exports.extractReportingDescriptor = extractReportingDescriptor;
|
|
function mapIacTestResponseToSarifResults(issues) {
|
|
return issues.map(({ targetPath, issue }) => {
|
|
const hasLineNumber = issue.lineNumber && issue.lineNumber >= 0;
|
|
// custom rules may not have some of these fields so we check them first
|
|
const affectingText = issue.subType
|
|
? ` affecting the ${issue.subType}`
|
|
: '';
|
|
return {
|
|
ruleId: issue.id,
|
|
message: {
|
|
text: `This line contains a potential ${issue.severity} severity misconfiguration${affectingText}`,
|
|
},
|
|
locations: [
|
|
{
|
|
physicalLocation: {
|
|
artifactLocation: {
|
|
uri: targetPath,
|
|
uriBaseId: PROJECT_ROOT_KEY,
|
|
},
|
|
// We exclude the `region` key when the line number is missing or -1.
|
|
// https://docs.oasis-open.org/sarif/sarif/v2.0/csprd02/sarif-v2.0-csprd02.html#_Toc10127873
|
|
...(hasLineNumber && {
|
|
region: {
|
|
startLine: issue.lineNumber,
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
],
|
|
};
|
|
});
|
|
}
|
|
exports.mapIacTestResponseToSarifResults = mapIacTestResponseToSarifResults;
|
|
function getPathRelativeToRepoRoot(repoRoot, basePath, filePath) {
|
|
const fullPath = pathLib.resolve(basePath, filePath).replace(/\\/g, '/');
|
|
return fullPath.replace(repoRoot, '');
|
|
}
|
|
function shareResultsOutput(iacOutputMeta) {
|
|
let projectName = iacOutputMeta.projectName;
|
|
if (iacOutputMeta === null || iacOutputMeta === void 0 ? void 0 : iacOutputMeta.gitRemoteUrl) {
|
|
// from "http://github.com/snyk/cli.git" to "snyk/cli"
|
|
projectName = iacOutputMeta.gitRemoteUrl.replace(/^https?:\/\/github.com\/(.*)\.git$/, '$1');
|
|
}
|
|
return `Your test results are available at: ${config_1.default.ROOT}/org/${iacOutputMeta.orgName}/projects under the name ${projectName}`;
|
|
}
|
|
exports.shareResultsOutput = shareResultsOutput;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 39567:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var list_1 = __webpack_require__(38889);
|
|
Object.defineProperty(exports, "formatIacTestFailures", ({ enumerable: true, get: function () { return list_1.formatIacTestFailures; } }));
|
|
Object.defineProperty(exports, "formatFailuresList", ({ enumerable: true, get: function () { return list_1.formatFailuresList; } }));
|
|
var tip_1 = __webpack_require__(82245);
|
|
Object.defineProperty(exports, "failuresTipOutput", ({ enumerable: true, get: function () { return tip_1.failuresTipOutput; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 38889:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatFailuresList = exports.formatIacTestFailures = void 0;
|
|
const os_1 = __webpack_require__(12087);
|
|
const utils_1 = __webpack_require__(67533);
|
|
function formatIacTestFailures(testFailures) {
|
|
const sectionComponents = [];
|
|
const titleOutput = utils_1.colors.title(`Test Failures`);
|
|
sectionComponents.push(titleOutput);
|
|
const testFailuresListOutput = formatFailuresList(testFailures);
|
|
sectionComponents.push(testFailuresListOutput);
|
|
return sectionComponents.join(os_1.EOL.repeat(2));
|
|
}
|
|
exports.formatIacTestFailures = formatIacTestFailures;
|
|
function groupTestFailuresByFailureReason(testFailures) {
|
|
return testFailures.reduce((groupedFailures, failure) => {
|
|
const reason = failure.failureReason;
|
|
if (reason) {
|
|
if (!groupedFailures[reason]) {
|
|
groupedFailures[reason] = [];
|
|
}
|
|
groupedFailures[reason].push(failure);
|
|
}
|
|
return groupedFailures;
|
|
}, {});
|
|
}
|
|
function formatFailuresList(testFailures) {
|
|
const testFailuresByReason = groupTestFailuresByFailureReason(testFailures);
|
|
return Object.entries(testFailuresByReason)
|
|
.map(([failureReason, testFailures]) => formatFailure(failureReason, testFailures))
|
|
.join(os_1.EOL.repeat(2));
|
|
}
|
|
exports.formatFailuresList = formatFailuresList;
|
|
function formatFailure(failureReason, testFailures) {
|
|
const pathPrefix = utils_1.contentPadding + 'Path: ';
|
|
const pathLeftPadding = ' '.repeat(pathPrefix.length);
|
|
return (utils_1.contentPadding +
|
|
utils_1.colors.failure.bold(failureReason) +
|
|
os_1.EOL +
|
|
pathPrefix +
|
|
testFailures
|
|
.map((testFailure) => testFailure.filePath)
|
|
.join(os_1.EOL + pathLeftPadding));
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 82245:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.failuresTipOutput = void 0;
|
|
const os_1 = __webpack_require__(12087);
|
|
const common_1 = __webpack_require__(70527);
|
|
const utils_1 = __webpack_require__(67533);
|
|
exports.failuresTipOutput = utils_1.colors.info.bold(common_1.reTryMessage + os_1.EOL + common_1.contactSupportMessage);
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 62089:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatSnykIacTestTestData = exports.formatTestData = void 0;
|
|
const legacy_1 = __webpack_require__(34013);
|
|
function formatTestData({ oldFormattedResults, iacOutputMeta: iacTestMeta, ignoresCount, }) {
|
|
const resultsBySeverity = formatScanResultsNewOutput(oldFormattedResults);
|
|
return {
|
|
resultsBySeverity,
|
|
metadata: iacTestMeta,
|
|
counts: formatTestCounts(resultsBySeverity, {
|
|
oldFormattedResults,
|
|
ignoresCount,
|
|
}),
|
|
};
|
|
}
|
|
exports.formatTestData = formatTestData;
|
|
function formatTestCounts(resultsBySeverity, { oldFormattedResults, ignoresCount, }) {
|
|
const filesWithIssues = oldFormattedResults.filter((result) => result.result.cloudConfigResults.length).length;
|
|
const filesWithoutIssues = oldFormattedResults.length - filesWithIssues;
|
|
let totalIssues = 0;
|
|
const issuesCountBySeverity = {};
|
|
Object.values(legacy_1.SEVERITY).forEach((severity) => {
|
|
var _a;
|
|
issuesCountBySeverity[severity] = ((_a = resultsBySeverity[severity]) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
totalIssues += issuesCountBySeverity[severity];
|
|
});
|
|
return {
|
|
ignores: ignoresCount,
|
|
filesWithIssues,
|
|
filesWithoutIssues,
|
|
issuesBySeverity: issuesCountBySeverity,
|
|
issues: totalIssues,
|
|
};
|
|
}
|
|
function formatScanResultsNewOutput(oldFormattedResults) {
|
|
const newFormattedResults = {};
|
|
oldFormattedResults.forEach((oldFormattedResult) => {
|
|
oldFormattedResult.result.cloudConfigResults.forEach((issue) => {
|
|
if (!newFormattedResults[issue.severity]) {
|
|
newFormattedResults[issue.severity] = [];
|
|
}
|
|
newFormattedResults[issue.severity].push({
|
|
issue,
|
|
targetFile: oldFormattedResult.targetFile,
|
|
projectType: oldFormattedResult.result.projectType,
|
|
});
|
|
});
|
|
});
|
|
return newFormattedResults;
|
|
}
|
|
function formatSnykIacTestTestData(snykIacTestScanResult, projectName, orgName) {
|
|
const resultsBySeverity = formatSnykIacTestScanResultNewOutput(snykIacTestScanResult);
|
|
let totalIssues = 0;
|
|
const issuesCountBySeverity = {};
|
|
Object.values(legacy_1.SEVERITY).forEach((severity) => {
|
|
var _a;
|
|
issuesCountBySeverity[severity] = ((_a = resultsBySeverity[severity]) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
totalIssues += issuesCountBySeverity[severity];
|
|
});
|
|
const allFilesCount = countFiles(snykIacTestScanResult);
|
|
const filesWithIssuesCount = countFilesWithIssues(snykIacTestScanResult);
|
|
const filesWithoutIssuesCount = allFilesCount - filesWithIssuesCount;
|
|
return {
|
|
resultsBySeverity,
|
|
metadata: { projectName, orgName },
|
|
counts: {
|
|
ignores: 0,
|
|
filesWithIssues: filesWithIssuesCount,
|
|
filesWithoutIssues: filesWithoutIssuesCount,
|
|
issues: totalIssues,
|
|
issuesBySeverity: issuesCountBySeverity,
|
|
},
|
|
};
|
|
}
|
|
exports.formatSnykIacTestTestData = formatSnykIacTestTestData;
|
|
function countFilesWithIssues(results) {
|
|
if (results && results.vulnerabilities) {
|
|
const files = new Set();
|
|
for (const vulnerability of results.vulnerabilities) {
|
|
if (vulnerability.resource.file) {
|
|
files.add(vulnerability.resource.file);
|
|
}
|
|
}
|
|
return files.size;
|
|
}
|
|
return 0;
|
|
}
|
|
function countFiles(results) {
|
|
if (results && (results === null || results === void 0 ? void 0 : results.resources)) {
|
|
const files = new Set();
|
|
for (const resource of results.resources) {
|
|
if (resource.file) {
|
|
files.add(resource.file);
|
|
}
|
|
}
|
|
return files.size;
|
|
}
|
|
return 0;
|
|
}
|
|
function formatSnykIacTestScanResultNewOutput(snykIacTestScanResult) {
|
|
const resultsBySeverity = {};
|
|
if (snykIacTestScanResult === null || snykIacTestScanResult === void 0 ? void 0 : snykIacTestScanResult.vulnerabilities) {
|
|
snykIacTestScanResult.vulnerabilities.forEach((vulnerability) => {
|
|
if (!resultsBySeverity[vulnerability.severity]) {
|
|
resultsBySeverity[vulnerability.severity] = [];
|
|
}
|
|
resultsBySeverity[vulnerability.severity].push({
|
|
issue: formatSnykIacTestScanVulnerability(vulnerability),
|
|
targetFile: vulnerability.resource.file,
|
|
projectType: vulnerability.resource.type,
|
|
});
|
|
});
|
|
}
|
|
return resultsBySeverity;
|
|
}
|
|
function formatSnykIacTestScanVulnerability(vulnerability) {
|
|
return {
|
|
id: vulnerability.rule.id,
|
|
publicId: vulnerability.rule.id,
|
|
severity: vulnerability.severity,
|
|
title: vulnerability.rule.title,
|
|
isIgnored: vulnerability.ignored,
|
|
lineNumber: vulnerability.resource.line,
|
|
cloudConfigPath: formatCloudConfigPath(vulnerability),
|
|
subType: '',
|
|
iacDescription: {
|
|
issue: '',
|
|
impact: '',
|
|
resolve: '',
|
|
},
|
|
issue: '',
|
|
impact: '',
|
|
resolve: '',
|
|
msg: '',
|
|
references: [],
|
|
};
|
|
}
|
|
function formatCloudConfigPath(vulnerability) {
|
|
const cloudConfigPath = vulnerability.resource.id.split('.');
|
|
if (vulnerability.resource.path) {
|
|
cloudConfigPath.push(...vulnerability.resource.path);
|
|
}
|
|
return cloudConfigPath;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 49041:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var issues_list_1 = __webpack_require__(97695);
|
|
Object.defineProperty(exports, "getIacDisplayedIssues", ({ enumerable: true, get: function () { return issues_list_1.getIacDisplayedIssues; } }));
|
|
var test_summary_1 = __webpack_require__(42242);
|
|
Object.defineProperty(exports, "formatIacTestSummary", ({ enumerable: true, get: function () { return test_summary_1.formatIacTestSummary; } }));
|
|
var user_messages_1 = __webpack_require__(80564);
|
|
Object.defineProperty(exports, "iacTestTitle", ({ enumerable: true, get: function () { return user_messages_1.iacTestTitle; } }));
|
|
Object.defineProperty(exports, "spinnerMessage", ({ enumerable: true, get: function () { return user_messages_1.spinnerMessage; } }));
|
|
Object.defineProperty(exports, "spinnerSuccessMessage", ({ enumerable: true, get: function () { return user_messages_1.spinnerSuccessMessage; } }));
|
|
Object.defineProperty(exports, "shouldLogUserMessages", ({ enumerable: true, get: function () { return user_messages_1.shouldLogUserMessages; } }));
|
|
Object.defineProperty(exports, "customRulesMessage", ({ enumerable: true, get: function () { return user_messages_1.customRulesMessage; } }));
|
|
Object.defineProperty(exports, "customRulesReportMessage", ({ enumerable: true, get: function () { return user_messages_1.customRulesReportMessage; } }));
|
|
var share_results_1 = __webpack_require__(5400);
|
|
Object.defineProperty(exports, "formatShareResultsOutput", ({ enumerable: true, get: function () { return share_results_1.formatShareResultsOutput; } }));
|
|
Object.defineProperty(exports, "shareResultsTip", ({ enumerable: true, get: function () { return share_results_1.shareResultsTip; } }));
|
|
Object.defineProperty(exports, "shareCustomRulesDisclaimer", ({ enumerable: true, get: function () { return share_results_1.shareCustomRulesDisclaimer; } }));
|
|
var failures_1 = __webpack_require__(39567);
|
|
Object.defineProperty(exports, "formatIacTestFailures", ({ enumerable: true, get: function () { return failures_1.formatIacTestFailures; } }));
|
|
Object.defineProperty(exports, "formatFailuresList", ({ enumerable: true, get: function () { return failures_1.formatFailuresList; } }));
|
|
Object.defineProperty(exports, "failuresTipOutput", ({ enumerable: true, get: function () { return failures_1.failuresTipOutput; } }));
|
|
var formatters_1 = __webpack_require__(62089);
|
|
Object.defineProperty(exports, "formatSnykIacTestTestData", ({ enumerable: true, get: function () { return formatters_1.formatSnykIacTestTestData; } }));
|
|
Object.defineProperty(exports, "formatTestData", ({ enumerable: true, get: function () { return formatters_1.formatTestData; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 97695:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getIacDisplayedIssues = void 0;
|
|
const os_1 = __webpack_require__(12087);
|
|
const capitalize = __webpack_require__(51633);
|
|
const isEmpty = __webpack_require__(99245);
|
|
const debug = __webpack_require__(15158);
|
|
const utils_1 = __webpack_require__(67533);
|
|
const issue_1 = __webpack_require__(58863);
|
|
const common_1 = __webpack_require__(53110);
|
|
function getIacDisplayedIssues(resultsBySeverity, options) {
|
|
const titleOutput = utils_1.colors.title('Issues');
|
|
if (isEmpty(resultsBySeverity)) {
|
|
return (titleOutput +
|
|
os_1.EOL +
|
|
utils_1.contentPadding +
|
|
utils_1.colors.success.bold('No vulnerable paths were found!'));
|
|
}
|
|
const severitySectionsOutput = Object.values(common_1.SEVERITY)
|
|
.filter((severity) => !!resultsBySeverity[severity])
|
|
.map((severity) => {
|
|
const severityResults = resultsBySeverity[severity];
|
|
const titleOutput = utils_1.colors.title(`${capitalize(severity)} Severity Issues: ${severityResults.length}`);
|
|
const issuesOutput = severityResults
|
|
.sort((severityResult1, severityResult2) => severityResult1.targetFile.localeCompare(severityResult2.targetFile) ||
|
|
severityResult1.issue.id.localeCompare(severityResult2.issue.id))
|
|
.map((result) => issue_1.formatIssue(result, options))
|
|
.join(os_1.EOL.repeat(2));
|
|
debug(`iac display output - ${severity} severity ${severityResults.length} issues`);
|
|
return titleOutput + os_1.EOL.repeat(2) + issuesOutput;
|
|
})
|
|
.join(os_1.EOL.repeat(2));
|
|
return titleOutput + os_1.EOL.repeat(2) + severitySectionsOutput;
|
|
}
|
|
exports.getIacDisplayedIssues = getIacDisplayedIssues;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 58863:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatIssue = void 0;
|
|
const capitalize = __webpack_require__(51633);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const os_1 = __webpack_require__(12087);
|
|
const constants_1 = __webpack_require__(68620);
|
|
const remediation_based_format_issues_1 = __webpack_require__(57995);
|
|
const utils_1 = __webpack_require__(67533);
|
|
function formatIssue(result, options) {
|
|
const titleOutput = formatTitle(result.issue);
|
|
const propertiesOutput = formatProperties(result, options);
|
|
return (utils_1.contentPadding +
|
|
titleOutput +
|
|
os_1.EOL +
|
|
utils_1.contentPadding +
|
|
propertiesOutput.join(os_1.EOL + utils_1.contentPadding));
|
|
}
|
|
exports.formatIssue = formatIssue;
|
|
function formatTitle(issue) {
|
|
const severity = issue.severity;
|
|
const titleOutput = utils_1.colors.severities[severity](`[${capitalize([issue.severity])}] ${chalk_1.default.bold(issue.title)}`);
|
|
return titleOutput;
|
|
}
|
|
function formatInfo(issue) {
|
|
const issueDesc = issue.iacDescription.issue;
|
|
const issueImpact = issue.iacDescription.impact;
|
|
if (!issueDesc) {
|
|
return issueImpact;
|
|
}
|
|
if (!issueImpact) {
|
|
return issueDesc;
|
|
}
|
|
return `${issueDesc}${!issueDesc.endsWith('.') ? '.' : ''} ${issueImpact}`;
|
|
}
|
|
function formatProperties(result, options) {
|
|
var _a;
|
|
const remediationKey = constants_1.iacRemediationTypes === null || constants_1.iacRemediationTypes === void 0 ? void 0 : constants_1.iacRemediationTypes[result.projectType];
|
|
const properties = [
|
|
['Info', formatInfo(result.issue)],
|
|
[
|
|
'Rule',
|
|
result.issue.isGeneratedByCustomRule
|
|
? `custom rule ${result.issue.id}`
|
|
: chalk_1.default.underline(result.issue.documentation || ''),
|
|
],
|
|
['Path', remediation_based_format_issues_1.printPath(result.issue.cloudConfigPath, 0)],
|
|
[
|
|
'File',
|
|
`${result.targetFile}${(options === null || options === void 0 ? void 0 : options.shouldShowLineNumbers) &&
|
|
isValidLineNumber(result.issue.lineNumber)
|
|
? `:${result.issue.lineNumber}`
|
|
: ''}`,
|
|
],
|
|
[
|
|
'Resolve',
|
|
remediationKey && ((_a = result.issue.remediation) === null || _a === void 0 ? void 0 : _a[remediationKey])
|
|
? result.issue.remediation[remediationKey]
|
|
: result.issue.iacDescription.resolve,
|
|
],
|
|
].filter(([, val]) => !!val);
|
|
const maxPropertyNameLength = Math.max(...properties.map(([key]) => key.length));
|
|
return properties.map(([key, value]) => `${key}: ${' '.repeat(maxPropertyNameLength - key.length)}${value}`);
|
|
}
|
|
function isValidLineNumber(lineNumber) {
|
|
return (typeof lineNumber === 'number' && lineNumber > 0 && lineNumber % 1 === 0);
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 5400:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.shareCustomRulesDisclaimer = exports.shareResultsTip = exports.formatShareResultsOutput = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const os_1 = __webpack_require__(12087);
|
|
const utils_1 = __webpack_require__(67533);
|
|
function formatShareResultsOutput(iacOutputMeta) {
|
|
return (utils_1.colors.title('Report Complete') +
|
|
os_1.EOL +
|
|
os_1.EOL +
|
|
utils_1.contentPadding +
|
|
'Your test results are available at: ' +
|
|
utils_1.colors.title(`${config_1.default.ROOT}/org/${iacOutputMeta.orgName}/projects`) +
|
|
os_1.EOL +
|
|
utils_1.contentPadding +
|
|
'under the name: ' +
|
|
utils_1.colors.title(iacOutputMeta.projectName));
|
|
}
|
|
exports.formatShareResultsOutput = formatShareResultsOutput;
|
|
exports.shareResultsTip = utils_1.colors.title('Tip') +
|
|
os_1.EOL +
|
|
os_1.EOL +
|
|
utils_1.contentPadding +
|
|
'New: Share your test results in the Snyk Web UI with the option ' +
|
|
utils_1.colors.title('--report');
|
|
exports.shareCustomRulesDisclaimer = utils_1.contentPadding +
|
|
utils_1.colors.suggestion('Please note that your custom rules will not be sent to the platform, and will not be available on the projects page.');
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 42242:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatIacTestSummary = void 0;
|
|
const os_1 = __webpack_require__(12087);
|
|
const right_pad_1 = __webpack_require__(80627);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const utils_1 = __webpack_require__(67533);
|
|
const PAD_LENGTH = 19; // chars to align
|
|
const INDENT = ' ';
|
|
function formatIacTestSummary(testData) {
|
|
const title = utils_1.colors.title('Test Summary');
|
|
const summarySections = [title];
|
|
if (testData.metadata) {
|
|
summarySections.push(formatTestMetaSection(testData.metadata));
|
|
}
|
|
summarySections.push(formatCountsSection(testData.counts));
|
|
return summarySections.join(os_1.EOL.repeat(2));
|
|
}
|
|
exports.formatIacTestSummary = formatIacTestSummary;
|
|
function formatTestMetaSection(iacMeta) {
|
|
const metaSectionProperties = [];
|
|
if (iacMeta.orgName) {
|
|
metaSectionProperties.push(['Organization', iacMeta.orgName]);
|
|
}
|
|
if (iacMeta.projectName) {
|
|
metaSectionProperties.push(['Project name', iacMeta.projectName]);
|
|
}
|
|
const metaSection = metaSectionProperties
|
|
.map(([key, value]) => right_pad_1.rightPadWithSpaces(`${INDENT}${key}: ${value}`, PAD_LENGTH))
|
|
.join(os_1.EOL);
|
|
return metaSection;
|
|
}
|
|
function formatCountsSection(testCounts) {
|
|
const countsSectionProperties = [];
|
|
countsSectionProperties.push(`${utils_1.colors.success.bold(theme_1.icon.VALID)} Files without issues: ${utils_1.colors.info.bold(`${testCounts.filesWithoutIssues}`)}`);
|
|
countsSectionProperties.push(`${utils_1.colors.failure.bold(theme_1.icon.ISSUE)} Files with issues: ${utils_1.colors.info.bold(`${testCounts.filesWithIssues}`)}`);
|
|
countsSectionProperties.push(`${INDENT}Ignored issues: ${utils_1.colors.info.bold(`${testCounts.ignores}`)}`);
|
|
countsSectionProperties.push(`${INDENT}Total issues: ${utils_1.colors.info.bold(`${testCounts.issues}`)} [ ${utils_1.colors.severities.critical(`${testCounts.issuesBySeverity.critical} critical`)}, ${utils_1.colors.severities.high(`${testCounts.issuesBySeverity.high} high`)}, ${utils_1.colors.severities.medium(`${testCounts.issuesBySeverity.medium} medium`)}, ${utils_1.colors.severities.low(`${testCounts.issuesBySeverity.low} low`)} ]`);
|
|
return countsSectionProperties.join(os_1.EOL);
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 80564:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.shouldLogUserMessages = exports.customRulesReportMessage = exports.customRulesMessage = exports.spinnerSuccessMessage = exports.spinnerMessage = exports.iacTestTitle = void 0;
|
|
const utils_1 = __webpack_require__(67533);
|
|
/**
|
|
* Displayed as the title of the test output.
|
|
*/
|
|
exports.iacTestTitle = utils_1.colors.info.bold('Snyk Infrastructure as Code');
|
|
/**
|
|
* Progress indication message while files are tested.
|
|
*/
|
|
exports.spinnerMessage = utils_1.colors.info('Snyk testing Infrastructure as Code configuration issues.');
|
|
/**
|
|
* Displayed when a test resolves successfully.
|
|
*/
|
|
exports.spinnerSuccessMessage = utils_1.colors.info('Test completed.');
|
|
/**
|
|
* Message for using custom rules.
|
|
*/
|
|
exports.customRulesMessage = utils_1.colors.info('Using custom rules to generate misconfigurations.');
|
|
/**
|
|
* Message for using custom rules.
|
|
*/
|
|
exports.customRulesReportMessage = utils_1.colors.info("Please note that your custom rules will not be sent to the Snyk platform, and will not be available on the project's page.");
|
|
/**
|
|
* @returns whether or not to include user messages in the output.
|
|
*/
|
|
function shouldLogUserMessages(options, iacCliOutputFeatureFlag) {
|
|
return !!(!options.json &&
|
|
!options.sarif &&
|
|
!options.quiet &&
|
|
iacCliOutputFeatureFlag);
|
|
}
|
|
exports.shouldLogUserMessages = shouldLogUserMessages;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 67533:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.contentPadding = exports.colors = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
exports.colors = {
|
|
severities: {
|
|
critical: chalk_1.default.magenta,
|
|
high: chalk_1.default.red,
|
|
medium: chalk_1.default.yellow,
|
|
low: chalk_1.default.reset,
|
|
},
|
|
failure: chalk_1.default.red,
|
|
success: chalk_1.default.green,
|
|
info: chalk_1.default.reset,
|
|
title: chalk_1.default.reset.bold,
|
|
suggestion: chalk_1.default.gray,
|
|
};
|
|
exports.contentPadding = ' '.repeat(2);
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 81329:
|
|
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
|
|
"use strict";
|
|
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
|
|
};
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var format_test_meta_1 = __webpack_require__(28001);
|
|
Object.defineProperty(exports, "formatTestMeta", ({ enumerable: true, get: function () { return format_test_meta_1.formatTestMeta; } }));
|
|
var format_vulnerable_result_summary_1 = __webpack_require__(27495);
|
|
Object.defineProperty(exports, "summariseVulnerableResults", ({ enumerable: true, get: function () { return format_vulnerable_result_summary_1.summariseVulnerableResults; } }));
|
|
var format_error_result_summary_1 = __webpack_require__(13232);
|
|
Object.defineProperty(exports, "summariseErrorResults", ({ enumerable: true, get: function () { return format_error_result_summary_1.summariseErrorResults; } }));
|
|
var legacy_format_issue_1 = __webpack_require__(63540);
|
|
Object.defineProperty(exports, "formatIssues", ({ enumerable: true, get: function () { return legacy_format_issue_1.formatIssues; } }));
|
|
var legal_license_instructions_1 = __webpack_require__(48049);
|
|
Object.defineProperty(exports, "formatLegalInstructions", ({ enumerable: true, get: function () { return legal_license_instructions_1.formatLegalInstructions; } }));
|
|
var remediation_based_format_issues_1 = __webpack_require__(57995);
|
|
Object.defineProperty(exports, "formatIssuesWithRemediation", ({ enumerable: true, get: function () { return remediation_based_format_issues_1.formatIssuesWithRemediation; } }));
|
|
var format_reachability_1 = __webpack_require__(13331);
|
|
Object.defineProperty(exports, "summariseReachableVulns", ({ enumerable: true, get: function () { return format_reachability_1.summariseReachableVulns; } }));
|
|
var format_monitor_response_1 = __webpack_require__(4040);
|
|
Object.defineProperty(exports, "formatErrorMonitorOutput", ({ enumerable: true, get: function () { return format_monitor_response_1.formatErrorMonitorOutput; } }));
|
|
Object.defineProperty(exports, "formatMonitorOutput", ({ enumerable: true, get: function () { return format_monitor_response_1.formatMonitorOutput; } }));
|
|
__exportStar(__webpack_require__(41287), exports);
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 63540:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.titleCaseText = exports.formatIssues = void 0;
|
|
const uniq = __webpack_require__(97644);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const config_1 = __webpack_require__(25425);
|
|
const detect_1 = __webpack_require__(45318);
|
|
const snyk_module_1 = __webpack_require__(60390);
|
|
const package_managers_1 = __webpack_require__(53847);
|
|
const legal_license_instructions_1 = __webpack_require__(48049);
|
|
const format_reachability_1 = __webpack_require__(13331);
|
|
const common_1 = __webpack_require__(53110);
|
|
const constants_1 = __webpack_require__(65623);
|
|
function formatIssues(vuln, options) {
|
|
const vulnID = vuln.list[0].id;
|
|
const packageManager = options.packageManager;
|
|
const localPackageTest = detect_1.isLocalFolder(options.path);
|
|
const uniquePackages = uniq(vuln.list.map((i) => {
|
|
if (i.from[1]) {
|
|
return i.from && i.from[1];
|
|
}
|
|
return i.from;
|
|
})).join(', ');
|
|
const vulnOutput = {
|
|
issueHeading: createSeverityBasedIssueHeading({
|
|
severity: vuln.metadata.severity,
|
|
originalSeverity: vuln.originalSeverity,
|
|
type: vuln.metadata.type,
|
|
packageName: vuln.metadata.name,
|
|
isNew: false,
|
|
}),
|
|
introducedThrough: ' Introduced through: ' + uniquePackages,
|
|
description: ' Description: ' + vuln.title,
|
|
info: ' Info: ' +
|
|
chalk_1.default.underline(config_1.default.PUBLIC_VULN_DB_URL + '/vuln/' + vulnID),
|
|
fromPaths: createTruncatedVulnsPathsText(vuln.list, options.showVulnPaths),
|
|
extraInfo: vuln.note ? chalk_1.default.bold('\n Note: ' + vuln.note) : '',
|
|
remediationInfo: vuln.metadata.type !== 'license' && localPackageTest
|
|
? createRemediationText(vuln, packageManager)
|
|
: '',
|
|
fixedIn: options.docker ? createFixedInText(vuln) : '',
|
|
dockerfilePackage: options.docker ? dockerfileInstructionText(vuln) : '',
|
|
legalInstructions: vuln.legalInstructionsArray
|
|
? chalk_1.default.bold('\n Legal instructions:\n') +
|
|
' '.repeat(2) +
|
|
legal_license_instructions_1.formatLegalInstructions(vuln.legalInstructionsArray, 2)
|
|
: '',
|
|
reachability: vuln.reachability ? createReachabilityInText(vuln) : '',
|
|
};
|
|
return (`${vulnOutput.issueHeading}\n` +
|
|
`${vulnOutput.description}\n` +
|
|
`${vulnOutput.info}\n` +
|
|
`${vulnOutput.introducedThrough}\n` +
|
|
vulnOutput.fromPaths +
|
|
// Optional - not always there
|
|
vulnOutput.reachability +
|
|
vulnOutput.remediationInfo +
|
|
vulnOutput.dockerfilePackage +
|
|
vulnOutput.fixedIn +
|
|
vulnOutput.extraInfo +
|
|
vulnOutput.legalInstructions);
|
|
}
|
|
exports.formatIssues = formatIssues;
|
|
function createSeverityBasedIssueHeading({ severity, originalSeverity, type, packageName, isNew, }) {
|
|
// Example: ✗ Medium severity vulnerability found in xmldom
|
|
const vulnTypeText = type === 'license' ? 'issue' : 'vulnerability';
|
|
let originalSeverityStr = '';
|
|
if (originalSeverity && originalSeverity !== severity) {
|
|
originalSeverityStr = ` (originally ${titleCaseText(originalSeverity)})`;
|
|
}
|
|
return (common_1.colorTextBySeverity(severity, '✗ ' +
|
|
titleCaseText(severity) +
|
|
` severity${originalSeverityStr} ` +
|
|
vulnTypeText +
|
|
' found in ' +
|
|
chalk_1.default.underline(packageName)) + chalk_1.default.bold.magenta(isNew ? ' (new)' : ''));
|
|
}
|
|
function titleCaseText(text) {
|
|
return text[0].toUpperCase() + text.slice(1);
|
|
}
|
|
exports.titleCaseText = titleCaseText;
|
|
function dockerfileInstructionText(vuln) {
|
|
if (vuln.dockerfileInstruction) {
|
|
JSON.stringify(vuln.dockerfileInstruction);
|
|
return `\n Image layer: '${vuln.dockerfileInstruction}'`;
|
|
}
|
|
if (vuln.dockerBaseImage) {
|
|
return `\n Image layer: Introduced by your base image (${vuln.dockerBaseImage})`;
|
|
}
|
|
return '';
|
|
}
|
|
function createTruncatedVulnsPathsText(vulnList, show) {
|
|
if (show === 'none') {
|
|
return '';
|
|
}
|
|
const numberOfPathsToDisplay = show === 'all' ? 1000 : 3;
|
|
const fromPathsArray = vulnList.map((i) => i.from);
|
|
const formatedFromPathsArray = fromPathsArray.map((i) => {
|
|
const fromWithoutBaseProject = i.slice(1);
|
|
// If more than one From path
|
|
if (fromWithoutBaseProject.length) {
|
|
return i.slice(1).join(constants_1.PATH_SEPARATOR);
|
|
}
|
|
// Else issue is in the core package
|
|
return i;
|
|
});
|
|
const notShownPathsNumber = fromPathsArray.length - numberOfPathsToDisplay;
|
|
const shouldTruncatePaths = fromPathsArray.length > numberOfPathsToDisplay;
|
|
const truncatedText = `\n and ${notShownPathsNumber} more...`;
|
|
const formattedPathsText = formatedFromPathsArray
|
|
.slice(0, numberOfPathsToDisplay)
|
|
.join('\n From: ');
|
|
if (fromPathsArray.length > 0) {
|
|
return (' From: ' +
|
|
formattedPathsText +
|
|
(shouldTruncatePaths ? truncatedText : ''));
|
|
}
|
|
}
|
|
function createFixedInText(vuln) {
|
|
if (vuln.nearestFixedInVersion) {
|
|
return chalk_1.default.bold('\n Fixed in: ' + vuln.nearestFixedInVersion);
|
|
}
|
|
else if (vuln.fixedIn && vuln.fixedIn.length > 0) {
|
|
return chalk_1.default.bold('\n Fixed in: ' + vuln.fixedIn.join(', '));
|
|
}
|
|
return '';
|
|
}
|
|
function createReachabilityInText(vuln) {
|
|
if (!vuln.reachability) {
|
|
return '';
|
|
}
|
|
const reachabilityText = format_reachability_1.getReachabilityText(vuln.reachability);
|
|
if (!reachabilityText) {
|
|
return '';
|
|
}
|
|
return `\n Reachability: ${reachabilityText}`;
|
|
}
|
|
function createRemediationText(vuln, packageManager) {
|
|
if (vuln.fixedIn &&
|
|
package_managers_1.PINNING_SUPPORTED_PACKAGE_MANAGERS.includes(packageManager)) {
|
|
const toVersion = vuln.fixedIn.join(' or ');
|
|
const transitive = vuln.list.every((i) => i.from.length > 2);
|
|
const fromVersionArray = vuln.list.map((v) => v.from[1]);
|
|
const fromVersion = fromVersionArray[0];
|
|
if (transitive) {
|
|
return chalk_1.default.bold(`\n Remediation:\n Pin the transitive dependency ${vuln.name} to version ${toVersion}`);
|
|
}
|
|
else {
|
|
return chalk_1.default.bold(`\n Remediation:\n Upgrade direct dependency ${fromVersion} to ${vuln.name}@${toVersion}`);
|
|
}
|
|
}
|
|
if (vuln.isFixable === true) {
|
|
const upgradePathsArray = uniq(vuln.list.map((v) => {
|
|
const shouldUpgradeItself = !!v.upgradePath[0];
|
|
const shouldUpgradeDirectDep = !!v.upgradePath[1];
|
|
if (shouldUpgradeItself) {
|
|
// If we are testing a library/package like express
|
|
// Then we can suggest they get the latest version
|
|
// Example command: snyk test express@3
|
|
const selfUpgradeInfo = v.upgradePath.length > 0
|
|
? ` (triggers upgrades to ${v.upgradePath.join(constants_1.PATH_SEPARATOR)})`
|
|
: '';
|
|
const testedPackageName = snyk_module_1.parsePackageString(v.upgradePath[0]);
|
|
return (`You've tested an outdated version of ${testedPackageName[0]}.` +
|
|
+` Upgrade to ${v.upgradePath[0]}${selfUpgradeInfo}`);
|
|
}
|
|
if (shouldUpgradeDirectDep) {
|
|
const formattedUpgradePath = v.upgradePath
|
|
.slice(1)
|
|
.join(constants_1.PATH_SEPARATOR);
|
|
const upgradeTextInfo = v.upgradePath.length
|
|
? ` (triggers upgrades to ${formattedUpgradePath})`
|
|
: '';
|
|
return `Upgrade direct dependency ${v.from[1]} to ${v.upgradePath[1]}${upgradeTextInfo}`;
|
|
}
|
|
return 'Some paths have no direct dependency upgrade that can address this issue.';
|
|
}));
|
|
return chalk_1.default.bold(`\n Remediation:\n ${upgradePathsArray.join('\n ')}`);
|
|
}
|
|
if (vuln.fixedIn && vuln.fixedIn.length > 0) {
|
|
return createFixedInText(vuln);
|
|
}
|
|
return '';
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 48049:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatLegalInstructions = void 0;
|
|
const wrap = __webpack_require__(88152);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
function formatLegalInstructions(legalInstructions, paddingLength = 4) {
|
|
const legalContent = legalInstructions.map((legalData) => wrap(chalk_1.default.bold(`○ for ${legalData.licenseName}: `) + legalData.legalContent, 100)
|
|
.split('\n')
|
|
.join('\n' + ' '.repeat(paddingLength)));
|
|
return legalContent.join('\n' + ' '.repeat(paddingLength));
|
|
}
|
|
exports.formatLegalInstructions = formatLegalInstructions;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 57995:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.formatIssue = exports.printPath = exports.formatIssuesWithRemediation = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const config_1 = __webpack_require__(25425);
|
|
const common_1 = __webpack_require__(53110);
|
|
const legal_license_instructions_1 = __webpack_require__(48049);
|
|
const format_reachability_1 = __webpack_require__(13331);
|
|
const constants_1 = __webpack_require__(65623);
|
|
const get_severity_value_1 = __webpack_require__(24898);
|
|
// How many reachable paths to show in the output
|
|
const MAX_REACHABLE_PATHS = 2;
|
|
function formatIssuesWithRemediation(vulns, remediationInfo, options) {
|
|
var _a;
|
|
const basicVulnInfo = {};
|
|
const basicLicenseInfo = {};
|
|
for (const vuln of vulns) {
|
|
const allReachablePaths = { pathCount: 0, paths: [] };
|
|
for (const issue of vuln.list) {
|
|
const issueReachablePaths = ((_a = issue.reachablePaths) === null || _a === void 0 ? void 0 : _a.paths) || [];
|
|
for (const functionReachablePaths of issueReachablePaths) {
|
|
allReachablePaths.paths = allReachablePaths.paths.concat(functionReachablePaths.callPaths);
|
|
allReachablePaths.pathCount += functionReachablePaths.callPaths.length;
|
|
}
|
|
}
|
|
const vulnData = {
|
|
title: vuln.title,
|
|
severity: vuln.severity,
|
|
originalSeverity: vuln.originalSeverity,
|
|
isNew: vuln.isNew,
|
|
name: vuln.name,
|
|
type: vuln.metadata.type,
|
|
version: vuln.version,
|
|
fixedIn: vuln.fixedIn,
|
|
note: vuln.note,
|
|
legalInstructions: vuln.legalInstructionsArray,
|
|
paths: vuln.list.map((v) => v.from),
|
|
reachability: vuln.reachability,
|
|
sampleReachablePaths: allReachablePaths,
|
|
};
|
|
if (vulnData.type === 'license') {
|
|
basicLicenseInfo[vuln.metadata.id] = vulnData;
|
|
}
|
|
else {
|
|
basicVulnInfo[vuln.metadata.id] = vulnData;
|
|
}
|
|
}
|
|
const results = [''];
|
|
let upgradeTextArray;
|
|
if (remediationInfo.pin && Object.keys(remediationInfo.pin).length) {
|
|
const upgradesByAffected = {};
|
|
for (const topLevelPkg of Object.keys(remediationInfo.upgrade)) {
|
|
for (const targetPkgStr of remediationInfo.upgrade[topLevelPkg]
|
|
.upgrades) {
|
|
if (!upgradesByAffected[targetPkgStr]) {
|
|
upgradesByAffected[targetPkgStr] = [];
|
|
}
|
|
upgradesByAffected[targetPkgStr].push({
|
|
name: topLevelPkg,
|
|
version: remediationInfo.upgrade[topLevelPkg].upgradeTo,
|
|
});
|
|
}
|
|
}
|
|
upgradeTextArray = constructPinText(remediationInfo.pin, upgradesByAffected, basicVulnInfo, options);
|
|
const allVulnIds = new Set();
|
|
Object.keys(remediationInfo.pin).forEach((name) => remediationInfo.pin[name].vulns.forEach((vid) => allVulnIds.add(vid)));
|
|
remediationInfo.unresolved = remediationInfo.unresolved.filter((issue) => !allVulnIds.has(issue.id));
|
|
}
|
|
else {
|
|
upgradeTextArray = constructUpgradesText(remediationInfo.upgrade, basicVulnInfo, options);
|
|
}
|
|
if (upgradeTextArray.length > 0) {
|
|
results.push(upgradeTextArray.join('\n'));
|
|
}
|
|
const patchedTextArray = constructPatchesText(remediationInfo.patch, basicVulnInfo, options);
|
|
if (patchedTextArray.length > 0) {
|
|
results.push(patchedTextArray.join('\n'));
|
|
}
|
|
const unfixableIssuesTextArray = constructUnfixableText(remediationInfo.unresolved, basicVulnInfo, options);
|
|
if (unfixableIssuesTextArray.length > 0) {
|
|
results.push(unfixableIssuesTextArray.join('\n'));
|
|
}
|
|
const licenseIssuesTextArray = constructLicenseText(basicLicenseInfo, options);
|
|
if (licenseIssuesTextArray.length > 0) {
|
|
results.push(licenseIssuesTextArray.join('\n'));
|
|
}
|
|
return results;
|
|
}
|
|
exports.formatIssuesWithRemediation = formatIssuesWithRemediation;
|
|
function constructLicenseText(basicLicenseInfo, testOptions) {
|
|
if (!(Object.keys(basicLicenseInfo).length > 0)) {
|
|
return [];
|
|
}
|
|
const licenseTextArray = [chalk_1.default.bold.green('\nLicense issues:')];
|
|
for (const id of Object.keys(basicLicenseInfo)) {
|
|
const licenseText = formatIssue(id, basicLicenseInfo[id].title, basicLicenseInfo[id].severity, basicLicenseInfo[id].isNew, `${basicLicenseInfo[id].name}@${basicLicenseInfo[id].version}`, basicLicenseInfo[id].paths, testOptions, basicLicenseInfo[id].note, undefined, // We can never override license rules, so no originalSeverity here
|
|
basicLicenseInfo[id].legalInstructions);
|
|
licenseTextArray.push('\n' + licenseText);
|
|
}
|
|
return licenseTextArray;
|
|
}
|
|
function constructPatchesText(patches, basicVulnInfo, testOptions) {
|
|
if (!(Object.keys(patches).length > 0)) {
|
|
return [];
|
|
}
|
|
const patchedTextArray = [chalk_1.default.bold.green('\nPatchable issues:')];
|
|
for (const id of Object.keys(patches)) {
|
|
if (!basicVulnInfo[id]) {
|
|
continue;
|
|
}
|
|
if (basicVulnInfo[id].type === 'license') {
|
|
continue;
|
|
}
|
|
// todo: add vulnToPatch package name
|
|
const packageAtVersion = `${basicVulnInfo[id].name}@${basicVulnInfo[id].version}`;
|
|
const patchedText = `\n Patch available for ${chalk_1.default.bold.whiteBright(packageAtVersion)}\n`;
|
|
const thisPatchFixes = formatIssue(id, basicVulnInfo[id].title, basicVulnInfo[id].severity, basicVulnInfo[id].isNew, `${basicVulnInfo[id].name}@${basicVulnInfo[id].version}`, basicVulnInfo[id].paths, testOptions, basicVulnInfo[id].note, basicVulnInfo[id].originalSeverity);
|
|
patchedTextArray.push(patchedText + thisPatchFixes);
|
|
}
|
|
return patchedTextArray;
|
|
}
|
|
function thisUpgradeFixes(vulnIds, basicVulnInfo, testOptions) {
|
|
return vulnIds
|
|
.filter((id) => basicVulnInfo[id]) // basicVulnInfo only contains issues with the specified severity levels
|
|
.sort((a, b) => get_severity_value_1.getSeverityValue(basicVulnInfo[a].severity) -
|
|
get_severity_value_1.getSeverityValue(basicVulnInfo[b].severity))
|
|
.filter((id) => basicVulnInfo[id].type !== 'license')
|
|
.map((id) => formatIssue(id, basicVulnInfo[id].title, basicVulnInfo[id].severity, basicVulnInfo[id].isNew, `${basicVulnInfo[id].name}@${basicVulnInfo[id].version}`, basicVulnInfo[id].paths, testOptions, basicVulnInfo[id].note, basicVulnInfo[id].originalSeverity, [], basicVulnInfo[id].reachability, basicVulnInfo[id].sampleReachablePaths))
|
|
.join('\n');
|
|
}
|
|
function processUpgrades(sink, upgradesByDep, deps, basicVulnInfo, testOptions) {
|
|
for (const dep of deps) {
|
|
const data = upgradesByDep[dep];
|
|
const upgradeDepTo = data.upgradeTo;
|
|
const vulnIds = data.vulns || data.vulns;
|
|
const upgradeText = `\n Upgrade ${chalk_1.default.bold.whiteBright(dep)} to ${chalk_1.default.bold.whiteBright(upgradeDepTo)} to fix\n`;
|
|
sink.push(upgradeText + thisUpgradeFixes(vulnIds, basicVulnInfo, testOptions));
|
|
}
|
|
}
|
|
function constructUpgradesText(upgrades, basicVulnInfo, testOptions) {
|
|
if (!(Object.keys(upgrades).length > 0)) {
|
|
return [];
|
|
}
|
|
const upgradeTextArray = [chalk_1.default.bold.green('\nIssues to fix by upgrading:')];
|
|
processUpgrades(upgradeTextArray, upgrades, Object.keys(upgrades), basicVulnInfo, testOptions);
|
|
return upgradeTextArray;
|
|
}
|
|
function constructPinText(pins, upgradesByAffected, // classical "remediation via top-level dep" upgrades
|
|
basicVulnInfo, testOptions) {
|
|
if (!Object.keys(pins).length) {
|
|
return [];
|
|
}
|
|
const upgradeTextArray = [];
|
|
upgradeTextArray.push(chalk_1.default.bold.green('\nIssues to fix by upgrading dependencies:'));
|
|
// First, direct upgrades
|
|
const upgradeables = Object.keys(pins).filter((name) => !pins[name].isTransitive);
|
|
if (upgradeables.length) {
|
|
processUpgrades(upgradeTextArray, pins, upgradeables, basicVulnInfo, testOptions);
|
|
}
|
|
// Second, pins
|
|
const pinables = Object.keys(pins).filter((name) => pins[name].isTransitive);
|
|
if (pinables.length) {
|
|
for (const pkgName of pinables) {
|
|
const data = pins[pkgName];
|
|
const vulnIds = data.vulns;
|
|
const upgradeDepTo = data.upgradeTo;
|
|
const upgradeText = `\n Pin ${chalk_1.default.bold.whiteBright(pkgName)} to ${chalk_1.default.bold.whiteBright(upgradeDepTo)} to fix`;
|
|
upgradeTextArray.push(upgradeText);
|
|
upgradeTextArray.push(thisUpgradeFixes(vulnIds, basicVulnInfo, testOptions));
|
|
// Finally, if we have some upgrade paths that fix the same issues, suggest them as well.
|
|
const topLevelUpgradesAlreadySuggested = new Set();
|
|
for (const vid of vulnIds) {
|
|
for (const topLevelPkg of upgradesByAffected[pkgName + '@' + basicVulnInfo[vid].version] || []) {
|
|
const setKey = `${topLevelPkg.name}\n${topLevelPkg.version}`;
|
|
if (!topLevelUpgradesAlreadySuggested.has(setKey)) {
|
|
topLevelUpgradesAlreadySuggested.add(setKey);
|
|
upgradeTextArray.push(' The issues above can also be fixed by upgrading top-level dependency ' +
|
|
`${topLevelPkg.name} to ${topLevelPkg.version}`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return upgradeTextArray;
|
|
}
|
|
function constructUnfixableText(unresolved, basicVulnInfo, testOptions) {
|
|
if (!(unresolved.length > 0)) {
|
|
return [];
|
|
}
|
|
const unfixableIssuesTextArray = [
|
|
chalk_1.default.bold.white('\nIssues with no direct upgrade or patch:'),
|
|
];
|
|
for (const issue of unresolved) {
|
|
const issueInfo = basicVulnInfo[issue.id];
|
|
if (!issueInfo) {
|
|
// basicVulnInfo only contains issues with the specified severity levels
|
|
continue;
|
|
}
|
|
const extraInfo = issue.fixedIn && issue.fixedIn.length
|
|
? `\n This issue was fixed in versions: ${chalk_1.default.bold(issue.fixedIn.join(', '))}`
|
|
: '\n No upgrade or patch available';
|
|
unfixableIssuesTextArray.push(formatIssue(issue.id, issue.title, issue.severity, issue.isNew, `${issue.packageName}@${issue.version}`, issueInfo.paths, testOptions, issueInfo.note, issueInfo.originalSeverity, [], issue.reachability) + `${extraInfo}`);
|
|
}
|
|
if (unfixableIssuesTextArray.length === 1) {
|
|
// seems we still only have
|
|
// the initial section title, so nothing to return
|
|
return [];
|
|
}
|
|
return unfixableIssuesTextArray;
|
|
}
|
|
function printPath(path, slice = 1) {
|
|
return path.slice(slice).join(constants_1.PATH_SEPARATOR);
|
|
}
|
|
exports.printPath = printPath;
|
|
function formatIssue(id, title, severity, isNew, vulnerableModule, paths, testOptions, note, originalSeverity, legalInstructions, reachability, sampleReachablePaths) {
|
|
const newBadge = isNew ? ' (new)' : '';
|
|
const name = vulnerableModule ? ` in ${chalk_1.default.bold(vulnerableModule)}` : '';
|
|
let legalLicenseInstructionsText;
|
|
if (legalInstructions) {
|
|
legalLicenseInstructionsText = legal_license_instructions_1.formatLegalInstructions(legalInstructions);
|
|
}
|
|
let reachabilityText = '';
|
|
if (reachability) {
|
|
reachabilityText = format_reachability_1.formatReachability(reachability);
|
|
}
|
|
let introducedBy = '';
|
|
if (testOptions.showVulnPaths === 'some' &&
|
|
paths &&
|
|
paths.find((p) => p.length > 1)) {
|
|
// In this mode, we show only one path by default, for compactness
|
|
const pathStr = printPath(paths[0]);
|
|
introducedBy =
|
|
paths.length === 1
|
|
? `\n introduced by ${pathStr}`
|
|
: `\n introduced by ${pathStr} and ${chalk_1.default.cyanBright('' + (paths.length - 1))} other path(s)`;
|
|
}
|
|
else if (testOptions.showVulnPaths === 'all' && paths) {
|
|
introducedBy =
|
|
'\n introduced by:' +
|
|
paths
|
|
.slice(0, 1000)
|
|
.map((p) => '\n ' + printPath(p))
|
|
.join('');
|
|
if (paths.length > 1000) {
|
|
introducedBy += `\n and ${chalk_1.default.cyanBright('' + (paths.length - 1))} other path(s)`;
|
|
}
|
|
}
|
|
const reachableVia = format_reachability_1.formatReachablePaths(sampleReachablePaths, MAX_REACHABLE_PATHS, reachablePathsTemplate);
|
|
let originalSeverityStr = '';
|
|
if (originalSeverity && originalSeverity !== severity) {
|
|
originalSeverityStr = ` (originally ${titleCaseText(originalSeverity)})`;
|
|
}
|
|
return (common_1.colorTextBySeverity(severity, ` ${theme_1.icon.ISSUE} ${chalk_1.default.bold(title)}${newBadge} [${titleCaseText(severity)} Severity${originalSeverityStr}]`) +
|
|
reachabilityText +
|
|
`[${config_1.default.PUBLIC_VULN_DB_URL}/vuln/${id}]` +
|
|
name +
|
|
reachableVia +
|
|
introducedBy +
|
|
(legalLicenseInstructionsText
|
|
? `${chalk_1.default.bold('\n Legal instructions')}:\n ${legalLicenseInstructionsText}`
|
|
: '') +
|
|
(note ? `${chalk_1.default.bold('\n Note')}:\n ${note}` : ''));
|
|
}
|
|
exports.formatIssue = formatIssue;
|
|
function titleCaseText(text) {
|
|
return text[0].toUpperCase() + text.slice(1);
|
|
}
|
|
function reachablePathsTemplate(samplePaths, extraPathsCount) {
|
|
if (samplePaths.length === 0 && extraPathsCount === 0) {
|
|
return '';
|
|
}
|
|
if (samplePaths.length === 0) {
|
|
return `\n reachable via at least ${extraPathsCount} paths`;
|
|
}
|
|
let reachableVia = '\n reachable via:\n';
|
|
for (const p of samplePaths) {
|
|
reachableVia += ` ${p}\n`;
|
|
}
|
|
if (extraPathsCount > 0) {
|
|
reachableVia += ` and at least ${chalk_1.default.cyanBright('' + extraPathsCount)} other path(s)`;
|
|
}
|
|
return reachableVia;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 5034:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getResults = exports.getTool = exports.getIssueLevel = exports.createSarifOutputForContainers = void 0;
|
|
const legacy_1 = __webpack_require__(34013);
|
|
const upperFirst = __webpack_require__(90039);
|
|
function createSarifOutputForContainers(testResults) {
|
|
const sarifRes = {
|
|
version: '2.1.0',
|
|
runs: [],
|
|
};
|
|
testResults.forEach((testResult) => {
|
|
sarifRes.runs.push({
|
|
tool: getTool(testResult),
|
|
results: getResults(testResult),
|
|
});
|
|
});
|
|
return sarifRes;
|
|
}
|
|
exports.createSarifOutputForContainers = createSarifOutputForContainers;
|
|
function getIssueLevel(severity) {
|
|
return severity === legacy_1.SEVERITY.HIGH || severity === legacy_1.SEVERITY.CRITICAL
|
|
? 'error'
|
|
: 'warning';
|
|
}
|
|
exports.getIssueLevel = getIssueLevel;
|
|
function getTool(testResult) {
|
|
const tool = {
|
|
driver: {
|
|
name: 'Snyk Container',
|
|
rules: [],
|
|
},
|
|
};
|
|
if (!testResult.vulnerabilities) {
|
|
return tool;
|
|
}
|
|
const pushedIds = {};
|
|
tool.driver.rules = testResult.vulnerabilities
|
|
.map((vuln) => {
|
|
if (pushedIds[vuln.id]) {
|
|
return;
|
|
}
|
|
const level = getIssueLevel(vuln.severity);
|
|
const cve = vuln['identifiers']['CVE'][0];
|
|
pushedIds[vuln.id] = true;
|
|
return {
|
|
id: vuln.id,
|
|
shortDescription: {
|
|
text: `${upperFirst(vuln.severity)} severity - ${vuln.title} vulnerability in ${vuln.packageName}`,
|
|
},
|
|
fullDescription: {
|
|
text: cve
|
|
? `(${cve}) ${vuln.name}@${vuln.version}`
|
|
: `${vuln.name}@${vuln.version}`,
|
|
},
|
|
help: {
|
|
text: '',
|
|
markdown: vuln.description,
|
|
},
|
|
defaultConfiguration: {
|
|
level: level,
|
|
},
|
|
properties: {
|
|
tags: ['security', ...vuln.identifiers.CWE],
|
|
},
|
|
};
|
|
})
|
|
.filter(Boolean);
|
|
return tool;
|
|
}
|
|
exports.getTool = getTool;
|
|
function getResults(testResult) {
|
|
const results = [];
|
|
if (!testResult.vulnerabilities) {
|
|
return results;
|
|
}
|
|
testResult.vulnerabilities.forEach((vuln) => {
|
|
results.push({
|
|
ruleId: vuln.id,
|
|
message: {
|
|
text: `This file introduces a vulnerable ${vuln.packageName} package with a ${vuln.severity} severity vulnerability.`,
|
|
},
|
|
locations: [
|
|
{
|
|
physicalLocation: {
|
|
artifactLocation: {
|
|
uri: testResult.displayTargetFile,
|
|
},
|
|
region: {
|
|
startLine: vuln.lineNumber || 1,
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
});
|
|
return results;
|
|
}
|
|
exports.getResults = getResults;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 98202:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.showAllProjectsTip = void 0;
|
|
const is_multi_project_scan_1 = __webpack_require__(62435);
|
|
function showAllProjectsTip(packageManager, options, foundProjectCount) {
|
|
if (packageManager === 'gradle' ||
|
|
!foundProjectCount ||
|
|
is_multi_project_scan_1.isMultiProjectScan(options)) {
|
|
return '';
|
|
}
|
|
return (`Tip: Detected multiple supported manifests (${foundProjectCount}), ` +
|
|
'use --all-projects to scan all of them at once.');
|
|
}
|
|
exports.showAllProjectsTip = showAllProjectsTip;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 9658:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.showGradleSubProjectsTip = void 0;
|
|
const is_multi_project_scan_1 = __webpack_require__(62435);
|
|
function showGradleSubProjectsTip(packageManager, options, foundProjectCount) {
|
|
if (packageManager !== 'gradle' ||
|
|
!foundProjectCount ||
|
|
is_multi_project_scan_1.isMultiProjectScan(options) ||
|
|
options.allSubProjects) {
|
|
return '';
|
|
}
|
|
return (`Tip: This project has multiple sub-projects (${foundProjectCount}), ` +
|
|
'use --all-sub-projects flag to scan all sub-projects.');
|
|
}
|
|
exports.showGradleSubProjectsTip = showGradleSubProjectsTip;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 95100:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.showMultiScanTip = void 0;
|
|
const show_all_projects_tip_1 = __webpack_require__(98202);
|
|
const show_all_sub_projects_tip_1 = __webpack_require__(9658);
|
|
function showMultiScanTip(projectType, options, foundProjectCount) {
|
|
const gradleSubProjectsTip = show_all_sub_projects_tip_1.showGradleSubProjectsTip(projectType, options, foundProjectCount);
|
|
if (gradleSubProjectsTip) {
|
|
return gradleSubProjectsTip;
|
|
}
|
|
const allProjectsTip = show_all_projects_tip_1.showAllProjectsTip(projectType, options, foundProjectCount);
|
|
if (allProjectsTip) {
|
|
return allProjectsTip;
|
|
}
|
|
return '';
|
|
}
|
|
exports.showMultiScanTip = showMultiScanTip;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 84210:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getFileContents = void 0;
|
|
const fs = __webpack_require__(35747);
|
|
const path = __webpack_require__(85622);
|
|
function getFileContents(root, fileName) {
|
|
const fullPath = path.resolve(root, fileName);
|
|
if (!fs.existsSync(fullPath)) {
|
|
throw new Error('Manifest ' + fileName + ' not found at location: ' + fileName);
|
|
}
|
|
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
return {
|
|
content,
|
|
fileName,
|
|
};
|
|
}
|
|
exports.getFileContents = getFileContents;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 68620:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.iacRemediationTypes = exports.TEST_SUPPORTED_IAC_PROJECTS = exports.IacProjectType = void 0;
|
|
var IacProjectType;
|
|
(function (IacProjectType) {
|
|
IacProjectType["K8S"] = "k8sconfig";
|
|
IacProjectType["TERRAFORM"] = "terraformconfig";
|
|
IacProjectType["CLOUDFORMATION"] = "cloudformationconfig";
|
|
IacProjectType["ARM"] = "armconfig";
|
|
IacProjectType["CUSTOM"] = "customconfig";
|
|
IacProjectType["MULTI_IAC"] = "multiiacconfig";
|
|
})(IacProjectType = exports.IacProjectType || (exports.IacProjectType = {}));
|
|
exports.TEST_SUPPORTED_IAC_PROJECTS = [
|
|
IacProjectType.K8S,
|
|
IacProjectType.TERRAFORM,
|
|
IacProjectType.CLOUDFORMATION,
|
|
IacProjectType.ARM,
|
|
IacProjectType.MULTI_IAC,
|
|
IacProjectType.CUSTOM,
|
|
];
|
|
exports.iacRemediationTypes = {
|
|
armconfig: 'arm',
|
|
cloudformationconfig: 'cloudformation',
|
|
k8sconfig: 'kubernetes',
|
|
terraformconfig: 'terraform',
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 82421:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getWorkingDirectoryForPath = exports.getRepositoryRoot = exports.getRepositoryRootForPath = void 0;
|
|
const fs_1 = __webpack_require__(35747);
|
|
const path_1 = __webpack_require__(85622);
|
|
const childProcess = __webpack_require__(63129);
|
|
function getRepositoryRootForPath(p) {
|
|
return getRepositoryRoot(getWorkingDirectoryForPath(p));
|
|
}
|
|
exports.getRepositoryRootForPath = getRepositoryRootForPath;
|
|
function getRepositoryRoot(cwd) {
|
|
const proc = childProcess.spawnSync('git', ['rev-parse', '--show-toplevel'], {
|
|
cwd,
|
|
});
|
|
if (proc.status !== 0) {
|
|
throw new Error();
|
|
}
|
|
return proc.stdout.toString().trim();
|
|
}
|
|
exports.getRepositoryRoot = getRepositoryRoot;
|
|
function getWorkingDirectoryForPath(p) {
|
|
if (fs_1.statSync(p).isDirectory()) {
|
|
return p;
|
|
}
|
|
return path_1.dirname(p);
|
|
}
|
|
exports.getWorkingDirectoryForPath = getWorkingDirectoryForPath;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 62435:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.isMultiProjectScan = void 0;
|
|
function isMultiProjectScan(options) {
|
|
return !!(options.allProjects || options.yarnWorkspaces);
|
|
}
|
|
exports.isMultiProjectScan = isMultiProjectScan;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 27019:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.jsonStringifyLargeObject = void 0;
|
|
const debug = __webpack_require__(15158)('snyk-json');
|
|
/**
|
|
* Attempt to json-stringify an object which is potentially very large and might exceed the string limit.
|
|
* If it does exceed the string limit, try again without pretty-print to hopefully come out below the string limit.
|
|
* @param obj the object from which you want to get a JSON string
|
|
*/
|
|
function jsonStringifyLargeObject(obj) {
|
|
let res = '';
|
|
try {
|
|
// first try pretty-print
|
|
res = JSON.stringify(obj, null, 2);
|
|
return res;
|
|
}
|
|
catch (err) {
|
|
// if that doesn't work, try non-pretty print
|
|
debug('JSON.stringify failed - trying again without pretty print', err);
|
|
res = JSON.stringify(obj);
|
|
return res;
|
|
}
|
|
}
|
|
exports.jsonStringifyLargeObject = jsonStringifyLargeObject;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 80777:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.ModuleInfo = void 0;
|
|
const merge = __webpack_require__(72378);
|
|
const Debug = __webpack_require__(15158);
|
|
const debug = Debug('snyk-module-info');
|
|
function ModuleInfo(plugin, policy) {
|
|
return {
|
|
async inspect(root, targetFile, options) {
|
|
const pluginOptions = merge({
|
|
args: options._doubleDashArgs,
|
|
}, options);
|
|
debug('calling plugin inspect()', { root, targetFile, pluginOptions });
|
|
const info = await plugin.inspect(root, targetFile, pluginOptions);
|
|
debug('plugin inspect() done');
|
|
// attach policy if not provided by plugin
|
|
if (policy && !info.package.policy) {
|
|
info.package.policy = policy.toString();
|
|
}
|
|
return info;
|
|
},
|
|
};
|
|
}
|
|
exports.ModuleInfo = ModuleInfo;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 61900:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.countTotalDependenciesInTree = void 0;
|
|
function countTotalDependenciesInTree(depTree) {
|
|
let count = 0;
|
|
if (depTree.dependencies) {
|
|
for (const name of Object.keys(depTree.dependencies)) {
|
|
const dep = depTree.dependencies[name];
|
|
if (dep) {
|
|
count += 1 + countTotalDependenciesInTree(dep);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
exports.countTotalDependenciesInTree = countTotalDependenciesInTree;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 73898:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.ShellOutError = exports.execShell = exports.separateLines = exports.runGitLog = exports.getTimestampStartOfContributingDevTimeframe = exports.parseGitLog = exports.parseGitLogLine = exports.GitRepoCommitStats = exports.GitCommitInfo = exports.getContributors = exports.MAX_COMMITS_IN_GIT_LOG = exports.CONTRIBUTING_DEVELOPER_PERIOD_DAYS = exports.SERIOUS_DELIMITER = void 0;
|
|
/**
|
|
* This is to count the number of "contributing" developers using Snyk on a given repo.
|
|
* "Contributing" is defined as having contributed a commit in the last 90 days.
|
|
* This is use only on the `snyk monitor` command as that is used to monitor a project's dependencies in an
|
|
* on-going manner.
|
|
* It collects the email of a git user and the most recent commit timestamp (both per the `git log`
|
|
* output) and can be disabled by config (see https://snyk.io/policies/tracking-and-analytics/).
|
|
*/
|
|
const child_process_1 = __webpack_require__(63129);
|
|
exports.SERIOUS_DELIMITER = '_SNYK_SEPARATOR_';
|
|
exports.CONTRIBUTING_DEVELOPER_PERIOD_DAYS = 90;
|
|
// Limit the number of commits returned from `git log` command to stay within maxBuffer limit
|
|
exports.MAX_COMMITS_IN_GIT_LOG = 500;
|
|
async function getContributors({ endDate, periodDays, repoPath } = {
|
|
endDate: new Date(),
|
|
periodDays: exports.CONTRIBUTING_DEVELOPER_PERIOD_DAYS,
|
|
repoPath: process.cwd(),
|
|
}) {
|
|
const timestampStartOfContributingDeveloperPeriod = getTimestampStartOfContributingDevTimeframe(endDate, periodDays);
|
|
const gitLogResults = await runGitLog(timestampStartOfContributingDeveloperPeriod, Math.floor(endDate.getTime() / 1000), repoPath, execShell);
|
|
const stats = parseGitLog(gitLogResults);
|
|
return stats.getRepoContributors();
|
|
}
|
|
exports.getContributors = getContributors;
|
|
class GitCommitInfo {
|
|
constructor(authorEmail, commitTimestamp) {
|
|
this.authorEmail = authorEmail;
|
|
this.commitTimestamp = commitTimestamp;
|
|
}
|
|
}
|
|
exports.GitCommitInfo = GitCommitInfo;
|
|
class GitRepoCommitStats {
|
|
constructor(commitInfos) {
|
|
this.commitInfos = commitInfos;
|
|
}
|
|
static empty() {
|
|
return new GitRepoCommitStats([]);
|
|
}
|
|
addCommitInfo(info) {
|
|
this.commitInfos.push(info);
|
|
}
|
|
getUniqueAuthorsCount() {
|
|
const uniqueAuthorEmails = this.getUniqueAuthorEmails();
|
|
return uniqueAuthorEmails.size;
|
|
}
|
|
getCommitsCount() {
|
|
return this.commitInfos.length;
|
|
}
|
|
getUniqueAuthorEmails() {
|
|
const allCommitAuthorHashedEmails = this.commitInfos.map((c) => c.authorEmail);
|
|
const uniqueAuthorEmails = new Set(allCommitAuthorHashedEmails);
|
|
return uniqueAuthorEmails;
|
|
}
|
|
getRepoContributors() {
|
|
const uniqueAuthorEmails = this.getUniqueAuthorEmails();
|
|
const contributors = [];
|
|
for (const nextUniqueAuthorEmail of uniqueAuthorEmails) {
|
|
const latestCommitTimestamp = this.getMostRecentCommitTimestamp(nextUniqueAuthorEmail);
|
|
contributors.push({
|
|
email: nextUniqueAuthorEmail,
|
|
lastCommitDate: latestCommitTimestamp,
|
|
});
|
|
}
|
|
return contributors;
|
|
}
|
|
getMostRecentCommitTimestamp(authorHashedEmail) {
|
|
for (const nextGI of this.commitInfos) {
|
|
if (nextGI.authorEmail === authorHashedEmail) {
|
|
return nextGI.commitTimestamp;
|
|
}
|
|
}
|
|
return '';
|
|
}
|
|
}
|
|
exports.GitRepoCommitStats = GitRepoCommitStats;
|
|
function parseGitLogLine(logLine) {
|
|
const lineComponents = logLine.split(exports.SERIOUS_DELIMITER);
|
|
const authorEmail = lineComponents[2];
|
|
const commitTimestamp = lineComponents[3];
|
|
const commitInfo = new GitCommitInfo(authorEmail, commitTimestamp);
|
|
return commitInfo;
|
|
}
|
|
exports.parseGitLogLine = parseGitLogLine;
|
|
function parseGitLog(gitLog) {
|
|
if (gitLog.trim() === '') {
|
|
return GitRepoCommitStats.empty();
|
|
}
|
|
const logLines = separateLines(gitLog);
|
|
const logLineInfos = logLines.map(parseGitLogLine);
|
|
const stats = new GitRepoCommitStats(logLineInfos);
|
|
return stats;
|
|
}
|
|
exports.parseGitLog = parseGitLog;
|
|
/**
|
|
* @returns time stamp in seconds-since-epoch of 90 days ago since 90 days is the "contributing devs" timeframe
|
|
*/
|
|
function getTimestampStartOfContributingDevTimeframe(dNow, timespanInDays = exports.CONTRIBUTING_DEVELOPER_PERIOD_DAYS) {
|
|
const nowUtcEpocMS = dNow.getTime();
|
|
const nowUtcEpocS = Math.floor(nowUtcEpocMS / 1000);
|
|
const ONE_DAY_IN_SECONDS = 86400;
|
|
const lookbackTimespanSeconds = timespanInDays * ONE_DAY_IN_SECONDS;
|
|
const startOfPeriodEpochSeconds = nowUtcEpocS - lookbackTimespanSeconds;
|
|
return startOfPeriodEpochSeconds;
|
|
}
|
|
exports.getTimestampStartOfContributingDevTimeframe = getTimestampStartOfContributingDevTimeframe;
|
|
async function runGitLog(timestampEpochSecondsStartOfPeriod, timestampEpochSecondsEndOfPeriod, repoPath, fnShellout) {
|
|
try {
|
|
const gitLogCommand = `git --no-pager log --pretty=tformat:"%H${exports.SERIOUS_DELIMITER}%an${exports.SERIOUS_DELIMITER}%ae${exports.SERIOUS_DELIMITER}%aI" --after="${timestampEpochSecondsStartOfPeriod}" --until="${timestampEpochSecondsEndOfPeriod}" --max-count=${exports.MAX_COMMITS_IN_GIT_LOG}`;
|
|
const gitLogStdout = await fnShellout(gitLogCommand, repoPath);
|
|
return gitLogStdout;
|
|
}
|
|
catch {
|
|
return '';
|
|
}
|
|
}
|
|
exports.runGitLog = runGitLog;
|
|
function separateLines(inputText) {
|
|
const linuxStyleNewLine = '\n';
|
|
const windowsStyleNewLine = '\r\n';
|
|
const reg = new RegExp(`${linuxStyleNewLine}|${windowsStyleNewLine}`);
|
|
const lines = inputText.trim().split(reg);
|
|
return lines;
|
|
}
|
|
exports.separateLines = separateLines;
|
|
function execShell(cmd, workingDirectory) {
|
|
const options = {
|
|
cwd: workingDirectory,
|
|
};
|
|
return new Promise((resolve, reject) => {
|
|
child_process_1.exec(cmd, options, (error, stdout, stderr) => {
|
|
if (error) {
|
|
const exitCode = error.code;
|
|
const e = new ShellOutError(error.message, exitCode, stdout, stderr, error);
|
|
reject(e);
|
|
}
|
|
else {
|
|
resolve(stdout ? stdout : stderr);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
exports.execShell = execShell;
|
|
class ShellOutError extends Error {
|
|
constructor(message, exitCode, stdout, stderr, innerError) {
|
|
super(message);
|
|
this.exitCode = exitCode;
|
|
this.stdout = stdout;
|
|
this.stderr = stderr;
|
|
this.innerError = innerError;
|
|
}
|
|
}
|
|
exports.ShellOutError = ShellOutError;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 55916:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.dropEmptyDeps = void 0;
|
|
function dropEmptyDeps(depTree) {
|
|
if (depTree.dependencies) {
|
|
const keys = Object.keys(depTree.dependencies);
|
|
if (keys.length === 0) {
|
|
delete depTree.dependencies;
|
|
}
|
|
else {
|
|
for (const k of keys) {
|
|
dropEmptyDeps(depTree.dependencies[k]);
|
|
}
|
|
}
|
|
}
|
|
return depTree;
|
|
}
|
|
exports.dropEmptyDeps = dropEmptyDeps;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 85768:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.filterOutMissingDeps = void 0;
|
|
function filterOutMissingDeps(depTree) {
|
|
const filteredDeps = {};
|
|
const missingDeps = [];
|
|
if (!depTree.dependencies) {
|
|
return {
|
|
filteredDepTree: depTree,
|
|
missingDeps,
|
|
};
|
|
}
|
|
for (const depKey of Object.keys(depTree.dependencies)) {
|
|
const dep = depTree.dependencies[depKey];
|
|
if (dep.missingLockFileEntry ||
|
|
(dep.labels && dep.labels.missingLockFileEntry)) {
|
|
// TODO(kyegupov): add field to the type
|
|
missingDeps.push(`${dep.name}@${dep.version}`);
|
|
}
|
|
else {
|
|
filteredDeps[depKey] = dep;
|
|
}
|
|
}
|
|
const filteredDepTree = {
|
|
...depTree,
|
|
dependencies: filteredDeps,
|
|
};
|
|
return {
|
|
filteredDepTree,
|
|
missingDeps,
|
|
};
|
|
}
|
|
exports.filterOutMissingDeps = filterOutMissingDeps;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 3959:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.monitorDepGraph = exports.monitor = void 0;
|
|
const Debug = __webpack_require__(15158);
|
|
const path = __webpack_require__(85622);
|
|
const depGraphLib = __webpack_require__(71479);
|
|
const snyk = __webpack_require__(9146);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const request_1 = __webpack_require__(52050);
|
|
const config_1 = __webpack_require__(25425);
|
|
const os = __webpack_require__(12087);
|
|
const get = __webpack_require__(29208);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const analytics = __webpack_require__(82744);
|
|
const projectMetadata = __webpack_require__(3594);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const prune_1 = __webpack_require__(87725);
|
|
const package_managers_1 = __webpack_require__(53847);
|
|
const count_total_deps_in_tree_1 = __webpack_require__(61900);
|
|
const filter_out_missing_deps_1 = __webpack_require__(85768);
|
|
const drop_empty_deps_1 = __webpack_require__(55916);
|
|
const prune_dep_tree_1 = __webpack_require__(35797);
|
|
const policy_1 = __webpack_require__(32615);
|
|
const types_1 = __webpack_require__(39409);
|
|
const reachable_vulns_1 = __webpack_require__(86978);
|
|
const utils_1 = __webpack_require__(49530);
|
|
const utils_2 = __webpack_require__(61721);
|
|
const alerts = __webpack_require__(21696);
|
|
const error_format_1 = __webpack_require__(59369);
|
|
const debug = Debug('snyk');
|
|
const ANALYTICS_PAYLOAD_MAX_LENGTH = 1024;
|
|
async function monitor(root, meta, scannedProject, options, pluginMeta, targetFileRelativePath, contributors, projectAttributes, tags) {
|
|
api_token_1.apiOrOAuthTokenExists();
|
|
const packageManager = meta.packageManager;
|
|
analytics.add('packageManager', packageManager);
|
|
analytics.add('isDocker', !!meta.isDocker);
|
|
if (scannedProject.depGraph) {
|
|
return await monitorDepGraph(root, meta, scannedProject, pluginMeta, options, targetFileRelativePath, contributors, projectAttributes, tags);
|
|
}
|
|
if (package_managers_1.GRAPH_SUPPORTED_PACKAGE_MANAGERS.includes(packageManager)) {
|
|
return await monitorDepGraphFromDepTree(root, meta, scannedProject, pluginMeta, options, targetFileRelativePath, contributors, projectAttributes, tags);
|
|
}
|
|
return await monitorDepTree(root, meta, scannedProject, pluginMeta, options, targetFileRelativePath, contributors, projectAttributes, tags);
|
|
}
|
|
exports.monitor = monitor;
|
|
async function monitorDepTree(root, meta, scannedProject, pluginMeta, options, targetFileRelativePath, contributors, projectAttributes, tags) {
|
|
var _a, _b, _c, _d;
|
|
let treeMissingDeps = [];
|
|
const packageManager = meta.packageManager;
|
|
let depTree = scannedProject.depTree;
|
|
if (!depTree) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your monitor request could not be completed.'));
|
|
}
|
|
let prePruneDepCount;
|
|
if (meta.prune) {
|
|
debug('prune used, counting total dependencies');
|
|
prePruneDepCount = count_total_deps_in_tree_1.countTotalDependenciesInTree(depTree);
|
|
analytics.add('prePruneDepCount', prePruneDepCount);
|
|
debug('total dependencies: %d', prePruneDepCount);
|
|
debug('pruning dep tree');
|
|
depTree = await prune_dep_tree_1.pruneTree(depTree, meta.packageManager);
|
|
debug('finished pruning dep tree');
|
|
}
|
|
if (['npm', 'yarn'].includes(meta.packageManager)) {
|
|
const { filteredDepTree, missingDeps } = filter_out_missing_deps_1.filterOutMissingDeps(depTree);
|
|
depTree = filteredDepTree;
|
|
treeMissingDeps = missingDeps;
|
|
}
|
|
let targetFileDir;
|
|
if (targetFileRelativePath) {
|
|
const { dir } = path.parse(targetFileRelativePath);
|
|
targetFileDir = dir;
|
|
}
|
|
const policy = await policy_1.findAndLoadPolicy(root, meta.isDocker ? 'docker' : packageManager, options, depTree, targetFileDir);
|
|
const target = await projectMetadata.getInfo(scannedProject, meta, depTree);
|
|
if (types_1.isGitTarget(target) && target.branch) {
|
|
analytics.add('targetBranch', target.branch);
|
|
}
|
|
depTree = drop_empty_deps_1.dropEmptyDeps(depTree);
|
|
let callGraphPayload;
|
|
if (options.reachableVulns && ((_a = scannedProject.callGraph) === null || _a === void 0 ? void 0 : _a.innerError)) {
|
|
const err = scannedProject.callGraph;
|
|
analytics.add('callGraphError', error_format_1.abridgeErrorMessage(err.innerError.toString(), ANALYTICS_PAYLOAD_MAX_LENGTH));
|
|
alerts.registerAlerts([
|
|
{
|
|
type: 'error',
|
|
name: 'missing-call-graph',
|
|
msg: err.message,
|
|
},
|
|
]);
|
|
}
|
|
else if (scannedProject.callGraph) {
|
|
const { callGraph, nodeCount, edgeCount } = reachable_vulns_1.serializeCallGraphWithMetrics(scannedProject.callGraph);
|
|
debug(`Adding call graph to payload, node count: ${nodeCount}, edge count: ${edgeCount}`);
|
|
const callGraphMetrics = get(pluginMeta, 'meta.callGraphMetrics', {});
|
|
analytics.add('callGraphMetrics', {
|
|
callGraphEdgeCount: edgeCount,
|
|
callGraphNodeCount: nodeCount,
|
|
...callGraphMetrics,
|
|
});
|
|
callGraphPayload = callGraph;
|
|
}
|
|
if (!depTree) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your monitor request could not be completed.'));
|
|
}
|
|
const { res, body } = await request_1.makeRequest({
|
|
body: {
|
|
meta: {
|
|
method: meta.method,
|
|
hostname: os.hostname(),
|
|
id: snyk.id || depTree.name,
|
|
ci: is_ci_1.isCI(),
|
|
pid: process.pid,
|
|
node: process.version,
|
|
master: snyk.config.isMaster,
|
|
name: utils_1.getNameDepTree(scannedProject, depTree, meta),
|
|
version: depTree.version,
|
|
org: config_1.default.org ? decodeURIComponent(config_1.default.org) : undefined,
|
|
pluginName: pluginMeta.name,
|
|
pluginRuntime: pluginMeta.runtime,
|
|
missingDeps: treeMissingDeps,
|
|
dockerImageId: pluginMeta.dockerImageId,
|
|
dockerBaseImage: depTree.docker ? depTree.docker.baseImage : undefined,
|
|
dockerfileLayers: depTree.docker
|
|
? depTree.docker.dockerfileLayers
|
|
: undefined,
|
|
projectName: utils_1.getProjectName(scannedProject, meta),
|
|
prePruneDepCount,
|
|
monitorGraph: false,
|
|
versionBuildInfo: JSON.stringify((_b = scannedProject.meta) === null || _b === void 0 ? void 0 : _b.versionBuildInfo),
|
|
gradleProjectName: (_c = scannedProject.meta) === null || _c === void 0 ? void 0 : _c.gradleProjectName,
|
|
platform: (_d = scannedProject.meta) === null || _d === void 0 ? void 0 : _d.platform,
|
|
},
|
|
policy: policy ? policy.toString() : undefined,
|
|
package: depTree,
|
|
callGraph: callGraphPayload,
|
|
// we take the targetFile from the plugin,
|
|
// because we want to send it only for specific package-managers
|
|
target,
|
|
// WARNING: be careful changing this as it affects project uniqueness
|
|
targetFile: utils_1.getTargetFile(scannedProject, pluginMeta),
|
|
targetFileRelativePath,
|
|
targetReference: meta.targetReference,
|
|
contributors,
|
|
projectAttributes,
|
|
tags,
|
|
},
|
|
gzip: true,
|
|
method: 'PUT',
|
|
headers: {
|
|
authorization: api_token_1.getAuthHeader(),
|
|
'content-encoding': 'gzip',
|
|
},
|
|
url: config_1.default.API + '/monitor/' + packageManager,
|
|
json: true,
|
|
});
|
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode <= 299) {
|
|
return body;
|
|
}
|
|
else {
|
|
const userMessage = body && body.userMessage;
|
|
if (!userMessage && res.statusCode === 504) {
|
|
throw new errors_1.ConnectionTimeoutError();
|
|
}
|
|
else {
|
|
throw new errors_1.MonitorError(res.statusCode, userMessage);
|
|
}
|
|
}
|
|
}
|
|
async function monitorDepGraph(root, meta, scannedProject, pluginMeta, options, targetFileRelativePath, contributors, projectAttributes, tags) {
|
|
var _a, _b, _c;
|
|
const packageManager = meta.packageManager;
|
|
analytics.add('monitorDepGraph', true);
|
|
let depGraph = scannedProject.depGraph;
|
|
if (!depGraph) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError('Your monitor request could not be completed. ');
|
|
}
|
|
let targetFileDir;
|
|
if (targetFileRelativePath) {
|
|
const { dir } = path.parse(targetFileRelativePath);
|
|
targetFileDir = dir;
|
|
}
|
|
const policy = await policy_1.findAndLoadPolicy(root, meta.isDocker ? 'docker' : packageManager, options, undefined, targetFileDir);
|
|
const target = await projectMetadata.getInfo(scannedProject, meta);
|
|
if (types_1.isGitTarget(target) && target.branch) {
|
|
analytics.add('targetBranch', target.branch);
|
|
}
|
|
const pruneIsRequired = options.pruneRepeatedSubdependencies;
|
|
depGraph = await prune_1.pruneGraph(depGraph, packageManager, pruneIsRequired);
|
|
let callGraphPayload;
|
|
if (options.reachableVulns && ((_a = scannedProject.callGraph) === null || _a === void 0 ? void 0 : _a.innerError)) {
|
|
const err = scannedProject.callGraph;
|
|
analytics.add('callGraphError', error_format_1.abridgeErrorMessage(err.innerError.toString(), ANALYTICS_PAYLOAD_MAX_LENGTH));
|
|
alerts.registerAlerts([
|
|
{
|
|
type: 'error',
|
|
name: 'missing-call-graph',
|
|
msg: err.message,
|
|
},
|
|
]);
|
|
}
|
|
else if (scannedProject.callGraph) {
|
|
const { callGraph, nodeCount, edgeCount } = reachable_vulns_1.serializeCallGraphWithMetrics(scannedProject.callGraph);
|
|
debug(`Adding call graph to payload, node count: ${nodeCount}, edge count: ${edgeCount}`);
|
|
const callGraphMetrics = get(pluginMeta, 'meta.callGraphMetrics', {});
|
|
analytics.add('callGraphMetrics', {
|
|
callGraphEdgeCount: edgeCount,
|
|
callGraphNodeCount: nodeCount,
|
|
...callGraphMetrics,
|
|
});
|
|
callGraphPayload = callGraph;
|
|
}
|
|
if (!depGraph) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your monitor request could not be completed.'));
|
|
}
|
|
const { res, body } = await request_1.makeRequest({
|
|
body: {
|
|
meta: {
|
|
method: meta.method,
|
|
hostname: os.hostname(),
|
|
id: snyk.id || depGraph.rootPkg.name,
|
|
ci: is_ci_1.isCI(),
|
|
pid: process.pid,
|
|
node: process.version,
|
|
master: snyk.config.isMaster,
|
|
name: utils_1.getNameDepGraph(scannedProject, depGraph, meta),
|
|
version: depGraph.rootPkg.version,
|
|
org: config_1.default.org ? decodeURIComponent(config_1.default.org) : undefined,
|
|
pluginName: pluginMeta.name,
|
|
pluginRuntime: pluginMeta.runtime,
|
|
projectName: utils_1.getProjectName(scannedProject, meta),
|
|
monitorGraph: true,
|
|
versionBuildInfo: JSON.stringify((_b = scannedProject.meta) === null || _b === void 0 ? void 0 : _b.versionBuildInfo),
|
|
gradleProjectName: (_c = scannedProject.meta) === null || _c === void 0 ? void 0 : _c.gradleProjectName,
|
|
},
|
|
policy: policy ? policy.toString() : undefined,
|
|
depGraphJSON: depGraph,
|
|
// we take the targetFile from the plugin,
|
|
// because we want to send it only for specific package-managers
|
|
target,
|
|
targetFile: utils_1.getTargetFile(scannedProject, pluginMeta),
|
|
targetFileRelativePath,
|
|
targetReference: meta.targetReference,
|
|
contributors,
|
|
callGraph: callGraphPayload,
|
|
projectAttributes,
|
|
tags,
|
|
},
|
|
gzip: true,
|
|
method: 'PUT',
|
|
headers: {
|
|
authorization: api_token_1.getAuthHeader(),
|
|
'content-encoding': 'gzip',
|
|
},
|
|
url: `${config_1.default.API}/monitor/${packageManager}/graph`,
|
|
json: true,
|
|
});
|
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode <= 299) {
|
|
return body;
|
|
}
|
|
else {
|
|
const userMessage = body && body.userMessage;
|
|
if (!userMessage && res.statusCode === 504) {
|
|
throw new errors_1.ConnectionTimeoutError();
|
|
}
|
|
else {
|
|
throw new errors_1.MonitorError(res.statusCode, userMessage);
|
|
}
|
|
}
|
|
}
|
|
exports.monitorDepGraph = monitorDepGraph;
|
|
async function monitorDepGraphFromDepTree(root, meta, scannedProject, pluginMeta, options, targetFileRelativePath, contributors, projectAttributes, tags) {
|
|
const packageManager = meta.packageManager;
|
|
let treeMissingDeps;
|
|
let depTree = scannedProject.depTree;
|
|
if (!depTree) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your monitor request could not be completed'));
|
|
}
|
|
let targetFileDir;
|
|
if (targetFileRelativePath) {
|
|
const { dir } = path.parse(targetFileRelativePath);
|
|
targetFileDir = dir;
|
|
}
|
|
const policy = await policy_1.findAndLoadPolicy(root, meta.isDocker ? 'docker' : packageManager, options,
|
|
// TODO: fix this and send only send when we used resolve-deps for node
|
|
// it should be a ExpandedPkgTree type instead
|
|
depTree, targetFileDir);
|
|
if (['npm', 'yarn'].includes(meta.packageManager)) {
|
|
const { filteredDepTree, missingDeps } = filter_out_missing_deps_1.filterOutMissingDeps(depTree);
|
|
depTree = filteredDepTree;
|
|
treeMissingDeps = missingDeps;
|
|
}
|
|
const depGraph = await depGraphLib.legacy.depTreeToGraph(depTree, packageManager);
|
|
const target = await projectMetadata.getInfo(scannedProject, meta, depTree);
|
|
if (types_1.isGitTarget(target) && target.branch) {
|
|
analytics.add('targetBranch', target.branch);
|
|
}
|
|
let prunedGraph = depGraph;
|
|
let prePruneDepCount;
|
|
if (meta.prune) {
|
|
debug('Trying to prune the graph');
|
|
prePruneDepCount = utils_2.countPathsToGraphRoot(depGraph);
|
|
debug('pre prunedPathsCount: ' + prePruneDepCount);
|
|
prunedGraph = await prune_1.pruneGraph(depGraph, packageManager, meta.prune);
|
|
}
|
|
if (!depTree) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your monitor request could not be completed.'));
|
|
}
|
|
const { res, body } = await request_1.makeRequest({
|
|
body: {
|
|
meta: {
|
|
method: meta.method,
|
|
hostname: os.hostname(),
|
|
id: snyk.id || depTree.name,
|
|
ci: is_ci_1.isCI(),
|
|
pid: process.pid,
|
|
node: process.version,
|
|
master: snyk.config.isMaster,
|
|
name: utils_1.getNameDepGraph(scannedProject, depGraph, meta),
|
|
version: depGraph.rootPkg.version,
|
|
org: config_1.default.org ? decodeURIComponent(config_1.default.org) : undefined,
|
|
pluginName: pluginMeta.name,
|
|
pluginRuntime: pluginMeta.runtime,
|
|
dockerImageId: pluginMeta.dockerImageId,
|
|
dockerBaseImage: depTree.docker ? depTree.docker.baseImage : undefined,
|
|
dockerfileLayers: depTree.docker
|
|
? depTree.docker.dockerfileLayers
|
|
: undefined,
|
|
projectName: utils_1.getProjectName(scannedProject, meta),
|
|
prePruneDepCount,
|
|
missingDeps: treeMissingDeps,
|
|
monitorGraph: true,
|
|
},
|
|
policy: policy ? policy.toString() : undefined,
|
|
depGraphJSON: prunedGraph,
|
|
// we take the targetFile from the plugin,
|
|
// because we want to send it only for specific package-managers
|
|
target,
|
|
targetFile: utils_1.getTargetFile(scannedProject, pluginMeta),
|
|
targetFileRelativePath,
|
|
targetReference: meta.targetReference,
|
|
contributors,
|
|
projectAttributes,
|
|
tags,
|
|
},
|
|
gzip: true,
|
|
method: 'PUT',
|
|
headers: {
|
|
authorization: api_token_1.getAuthHeader(),
|
|
'content-encoding': 'gzip',
|
|
},
|
|
url: `${config_1.default.API}/monitor/${packageManager}/graph`,
|
|
json: true,
|
|
});
|
|
if (res.statusCode && res.statusCode >= 200 && res.statusCode <= 299) {
|
|
return body;
|
|
}
|
|
else {
|
|
const userMessage = body && body.userMessage;
|
|
if (!userMessage && res.statusCode === 504) {
|
|
throw new errors_1.ConnectionTimeoutError();
|
|
}
|
|
else {
|
|
throw new errors_1.MonitorError(res.statusCode, userMessage);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 35797:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.pruneTree = void 0;
|
|
const depGraphLib = __webpack_require__(71479);
|
|
async function pruneTree(tree, packageManagerName) {
|
|
// Pruning requires conversion to the graph first.
|
|
// This is slow.
|
|
const graph = await depGraphLib.legacy.depTreeToGraph(tree, packageManagerName);
|
|
const prunedTree = (await depGraphLib.legacy.graphToDepTree(graph, packageManagerName, { deduplicateWithinTopLevelDeps: true }));
|
|
// Transplant pruned dependencies in the original tree (we want to keep all other fields):
|
|
tree.dependencies = prunedTree.dependencies;
|
|
return tree;
|
|
}
|
|
exports.pruneTree = pruneTree;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 49530:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getTargetFile = exports.getProjectName = exports.getNameDepGraph = exports.getNameDepTree = void 0;
|
|
const container_1 = __webpack_require__(51804);
|
|
function getNameDepTree(scannedProject, depTree, meta) {
|
|
if (container_1.isContainer(scannedProject)) {
|
|
return container_1.getContainerName(scannedProject, meta);
|
|
}
|
|
return depTree.name;
|
|
}
|
|
exports.getNameDepTree = getNameDepTree;
|
|
function getNameDepGraph(scannedProject, depGraph, meta) {
|
|
var _a;
|
|
if (container_1.isContainer(scannedProject)) {
|
|
return container_1.getContainerName(scannedProject, meta);
|
|
}
|
|
return (_a = depGraph.rootPkg) === null || _a === void 0 ? void 0 : _a.name;
|
|
}
|
|
exports.getNameDepGraph = getNameDepGraph;
|
|
function getProjectName(scannedProject, meta) {
|
|
var _a;
|
|
if (container_1.isContainer(scannedProject)) {
|
|
return container_1.getContainerProjectName(scannedProject, meta);
|
|
}
|
|
if (meta['project-name'] && ((_a = scannedProject.meta) === null || _a === void 0 ? void 0 : _a.projectName)) {
|
|
return scannedProject.meta.projectName;
|
|
}
|
|
return meta['project-name'];
|
|
}
|
|
exports.getProjectName = getProjectName;
|
|
function getTargetFile(scannedProject, pluginMeta) {
|
|
if (container_1.isContainer(scannedProject)) {
|
|
return container_1.getContainerTargetFile(scannedProject);
|
|
}
|
|
return pluginMeta.targetFile;
|
|
}
|
|
exports.getTargetFile = getTargetFile;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 1570:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.validateOptions = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const reachableVulns = __webpack_require__(86978);
|
|
const is_multi_project_scan_1 = __webpack_require__(62435);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const alerts = __webpack_require__(21696);
|
|
async function validateOptions(options, packageManager) {
|
|
if (options.reachableVulns) {
|
|
alerts.registerAlerts([
|
|
{
|
|
type: 'warning',
|
|
name: 'reachable deprecation',
|
|
msg: reachableVulns.reachableVulnsRemovalMessage,
|
|
},
|
|
]);
|
|
}
|
|
if (options.reachableVulns) {
|
|
// Throwing error only in case when both packageManager and allProjects not defined
|
|
if (!packageManager && !is_multi_project_scan_1.isMultiProjectScan(options)) {
|
|
throw new Error('Could not determine package manager');
|
|
}
|
|
const org = options.org || config_1.default.org;
|
|
try {
|
|
await reachableVulns.validatePayload(org, options, packageManager);
|
|
}
|
|
catch (err) {
|
|
if (err instanceof errors_1.FeatureNotSupportedByPackageManagerError &&
|
|
err.feature === 'Reachable vulns' &&
|
|
err.userMessage) {
|
|
alerts.registerAlerts([
|
|
{
|
|
type: 'error',
|
|
name: 'pkgman-not-supported',
|
|
msg: err.userMessage,
|
|
},
|
|
]);
|
|
}
|
|
else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.validateOptions = validateOptions;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 23110:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.convertMultiResultToMultiCustom = void 0;
|
|
const convert_scanned_projects_to_custom_1 = __webpack_require__(92909);
|
|
function convertMultiResultToMultiCustom(inspectRes, packageManager, targetFile) {
|
|
// convert all results from the same plugin to MultiProjectResultCustom
|
|
// and annotate each scannedProject with packageManager
|
|
return {
|
|
plugin: inspectRes.plugin,
|
|
scannedProjects: convert_scanned_projects_to_custom_1.convertScannedProjectsToCustom(inspectRes.scannedProjects, inspectRes.plugin, inspectRes.plugin.packageManager ||
|
|
packageManager, targetFile),
|
|
};
|
|
}
|
|
exports.convertMultiResultToMultiCustom = convertMultiResultToMultiCustom;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 92909:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.convertScannedProjectsToCustom = void 0;
|
|
function convertScannedProjectsToCustom(scannedProjects, pluginMeta, packageManager, targetFile) {
|
|
// annotate the package manager & targetFile to be used
|
|
// for test & monitor
|
|
return scannedProjects.map((a) => {
|
|
a.plugin =
|
|
a.plugin || pluginMeta;
|
|
a.targetFile = a.targetFile || targetFile;
|
|
a.packageManager = a
|
|
.packageManager
|
|
? a.packageManager
|
|
: packageManager;
|
|
a.meta = a.meta;
|
|
return a;
|
|
});
|
|
}
|
|
exports.convertScannedProjectsToCustom = convertScannedProjectsToCustom;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 99695:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.convertSingleResultToMultiCustom = void 0;
|
|
function convertSingleResultToMultiCustom(inspectRes, packageManager) {
|
|
if (!packageManager) {
|
|
packageManager = inspectRes.plugin
|
|
.packageManager;
|
|
}
|
|
if (inspectRes.dependencyGraph) {
|
|
return convertDepGraphResult(inspectRes, packageManager);
|
|
}
|
|
else {
|
|
return convertDepTreeResult(inspectRes, packageManager);
|
|
}
|
|
}
|
|
exports.convertSingleResultToMultiCustom = convertSingleResultToMultiCustom;
|
|
function convertDepGraphResult(inspectRes, packageManager) {
|
|
const { plugin, meta, dependencyGraph: depGraph, callGraph } = inspectRes;
|
|
return {
|
|
plugin,
|
|
scannedProjects: [
|
|
{
|
|
plugin: plugin,
|
|
depGraph,
|
|
callGraph: callGraph,
|
|
meta,
|
|
targetFile: plugin.targetFile,
|
|
packageManager,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
/**
|
|
* @deprecated @boost: delete me when all languages uses depGraph
|
|
*/
|
|
function convertDepTreeResult(inspectRes, packageManager) {
|
|
if (inspectRes.package &&
|
|
!inspectRes.package.targetFile &&
|
|
inspectRes.plugin) {
|
|
inspectRes.package.targetFile = inspectRes.plugin.targetFile;
|
|
}
|
|
const { plugin, meta, package: depTree, callGraph } = inspectRes;
|
|
if (depTree && !depTree.targetFile && plugin) {
|
|
depTree.targetFile = plugin.targetFile;
|
|
}
|
|
return {
|
|
plugin,
|
|
scannedProjects: [
|
|
{
|
|
plugin: plugin,
|
|
depTree,
|
|
callGraph: callGraph,
|
|
meta,
|
|
targetFile: plugin.targetFile,
|
|
packageManager,
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 22805:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.extractPackageManager = void 0;
|
|
function extractPackageManager(scannedProject, pluginRes, options) {
|
|
// try and use the package Manager from the plugin
|
|
// result if present
|
|
const packageManager = scannedProject.packageManager ||
|
|
(pluginRes.plugin && pluginRes.plugin.packageManager);
|
|
if (packageManager) {
|
|
return packageManager;
|
|
}
|
|
if (!packageManager && options.packageManager) {
|
|
// fallback to Options packageManager
|
|
return options.packageManager;
|
|
}
|
|
// for example: docker
|
|
return undefined;
|
|
}
|
|
exports.extractPackageManager = extractPackageManager;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 4842:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.warnSomeGradleManifestsNotScanned = exports.getDepsFromPlugin = void 0;
|
|
const debugModule = __webpack_require__(15158);
|
|
const pathLib = __webpack_require__(85622);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const cli_interface_1 = __webpack_require__(65266);
|
|
const find_files_1 = __webpack_require__(46123);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const get_multi_plugin_result_1 = __webpack_require__(66058);
|
|
const get_single_plugin_result_1 = __webpack_require__(8598);
|
|
const detect_1 = __webpack_require__(45318);
|
|
const analytics = __webpack_require__(82744);
|
|
const convert_single_splugin_res_to_multi_custom_1 = __webpack_require__(99695);
|
|
const convert_multi_plugin_res_to_multi_custom_1 = __webpack_require__(23110);
|
|
const yarn_workspaces_parser_1 = __webpack_require__(27326);
|
|
const debug = debugModule('snyk-test');
|
|
const multiProjectProcessors = {
|
|
yarnWorkspaces: {
|
|
handler: yarn_workspaces_parser_1.processYarnWorkspaces,
|
|
files: ['package.json'],
|
|
},
|
|
allProjects: {
|
|
handler: get_multi_plugin_result_1.getMultiPluginResult,
|
|
files: detect_1.AUTO_DETECTABLE_FILES,
|
|
},
|
|
};
|
|
// Force getDepsFromPlugin to return scannedProjects for processing
|
|
async function getDepsFromPlugin(root, options) {
|
|
let inspectRes;
|
|
if (Object.keys(multiProjectProcessors).some((key) => options[key])) {
|
|
const scanType = options.yarnWorkspaces ? 'yarnWorkspaces' : 'allProjects';
|
|
const levelsDeep = options.detectionDepth;
|
|
const ignore = options.exclude ? options.exclude.split(',') : [];
|
|
const { files: targetFiles, allFilesFound } = await find_files_1.find(root, ignore, multiProjectProcessors[scanType].files, levelsDeep);
|
|
debug(`auto detect manifest files, found ${targetFiles.length}`, targetFiles);
|
|
if (targetFiles.length === 0) {
|
|
throw errors_1.NoSupportedManifestsFoundError([root]);
|
|
}
|
|
// enable full sub-project scan for gradle
|
|
options.allSubProjects = true;
|
|
inspectRes = await multiProjectProcessors[scanType].handler(root, options, targetFiles);
|
|
const scannedProjects = inspectRes.scannedProjects;
|
|
const analyticData = {
|
|
scannedProjects: scannedProjects.length,
|
|
targetFiles,
|
|
packageManagers: targetFiles.map((file) => detect_1.detectPackageManagerFromFile(file)),
|
|
levelsDeep,
|
|
ignore,
|
|
};
|
|
analytics.add(scanType, analyticData);
|
|
debug(`Found ${scannedProjects.length} projects from ${allFilesFound.length} detected manifests`);
|
|
const userWarningMessage = warnSomeGradleManifestsNotScanned(scannedProjects, allFilesFound, root);
|
|
if (!options.json && !options.quiet && userWarningMessage) {
|
|
console.warn(chalk_1.default.bold.red(userWarningMessage));
|
|
}
|
|
return inspectRes;
|
|
}
|
|
// TODO: is this needed for the auto detect handling above?
|
|
// don't override options.file if scanning multiple files at once
|
|
if (!options.scanAllUnmanaged) {
|
|
options.file = options.file || detect_1.detectPackageFile(root);
|
|
}
|
|
if (!options.docker && !(options.file || options.packageManager)) {
|
|
throw errors_1.NoSupportedManifestsFoundError([...root]);
|
|
}
|
|
inspectRes = await get_single_plugin_result_1.getSinglePluginResult(root, options);
|
|
if (!cli_interface_1.legacyPlugin.isMultiResult(inspectRes)) {
|
|
if (!inspectRes.package && !inspectRes.dependencyGraph) {
|
|
// something went wrong if both are not present...
|
|
throw Error(`error getting dependencies from ${options.docker ? 'docker' : options.packageManager} ` + "plugin: neither 'package' nor 'scannedProjects' were found");
|
|
}
|
|
return convert_single_splugin_res_to_multi_custom_1.convertSingleResultToMultiCustom(inspectRes, options.packageManager);
|
|
}
|
|
// We are using "options" to store some information returned from plugin that we need to use later,
|
|
// but don't want to send to Registry in the Payload.
|
|
// TODO(kyegupov): decouple inspect and payload so that we don't need this hack
|
|
options.projectNames = inspectRes.scannedProjects.map((scannedProject) => { var _a; return (_a = scannedProject === null || scannedProject === void 0 ? void 0 : scannedProject.depTree) === null || _a === void 0 ? void 0 : _a.name; });
|
|
return convert_multi_plugin_res_to_multi_custom_1.convertMultiResultToMultiCustom(inspectRes, options.packageManager);
|
|
}
|
|
exports.getDepsFromPlugin = getDepsFromPlugin;
|
|
function warnSomeGradleManifestsNotScanned(scannedProjects, allFilesFound, root) {
|
|
const gradleTargetFilesFilter = (targetFile) => targetFile &&
|
|
(targetFile.endsWith('build.gradle') ||
|
|
targetFile.endsWith('build.gradle.kts'));
|
|
const scannedGradleFiles = scannedProjects
|
|
.map((p) => {
|
|
var _a;
|
|
const targetFile = ((_a = p.meta) === null || _a === void 0 ? void 0 : _a.targetFile) || p.targetFile;
|
|
return targetFile ? pathLib.resolve(root, targetFile) : null;
|
|
})
|
|
.filter(gradleTargetFilesFilter);
|
|
const detectedGradleFiles = allFilesFound.filter(gradleTargetFilesFilter);
|
|
const diff = detectedGradleFiles.filter((file) => !scannedGradleFiles.includes(file));
|
|
if (diff.length > 0) {
|
|
debug(`These Gradle manifests did not return any dependency results:\n${diff.join(',\n')}`);
|
|
return `${theme_1.icon.ISSUE} ${diff.length}/${detectedGradleFiles.length} detected Gradle manifests did not return dependencies. They may have errored or were not included as part of a multi-project build. You may need to scan them individually with --file=path/to/file. Run with \`-d\` for more info.`;
|
|
}
|
|
return null;
|
|
}
|
|
exports.warnSomeGradleManifestsNotScanned = warnSomeGradleManifestsNotScanned;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 34355:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getExtraProjectCount = void 0;
|
|
const find_files_1 = __webpack_require__(46123);
|
|
const detect_1 = __webpack_require__(45318);
|
|
async function getExtraProjectCount(root, options, inspectResult) {
|
|
if (options.docker || options.unmanaged) {
|
|
return undefined;
|
|
}
|
|
if (inspectResult.plugin.meta &&
|
|
inspectResult.plugin.meta.allSubProjectNames &&
|
|
inspectResult.plugin.meta.allSubProjectNames.length > 0) {
|
|
return inspectResult.plugin.meta.allSubProjectNames.length;
|
|
}
|
|
try {
|
|
const { files: extraTargetFiles } = await find_files_1.find(root, [], detect_1.AUTO_DETECTABLE_FILES);
|
|
const foundProjectsCount = extraTargetFiles.length > 1 ? extraTargetFiles.length - 1 : undefined;
|
|
return foundProjectsCount;
|
|
}
|
|
catch (e) {
|
|
return undefined;
|
|
}
|
|
}
|
|
exports.getExtraProjectCount = getExtraProjectCount;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 66058:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.filterOutProcessedWorkspaces = exports.getMultiPluginResult = void 0;
|
|
const cloneDeep = __webpack_require__(83465);
|
|
const pathLib = __webpack_require__(85622);
|
|
const cliInterface = __webpack_require__(65266);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const debugModule = __webpack_require__(15158);
|
|
const detect_1 = __webpack_require__(45318);
|
|
const get_single_plugin_result_1 = __webpack_require__(8598);
|
|
const convert_single_splugin_res_to_multi_custom_1 = __webpack_require__(99695);
|
|
const convert_multi_plugin_res_to_multi_custom_1 = __webpack_require__(23110);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const yarn_workspaces_parser_1 = __webpack_require__(27326);
|
|
const debug = debugModule('snyk-test');
|
|
async function getMultiPluginResult(root, options, targetFiles) {
|
|
var _a;
|
|
const allResults = [];
|
|
const failedResults = [];
|
|
// process any yarn workspaces first
|
|
// the files need to be proceeded together as they provide context to each other
|
|
const { scannedProjects, unprocessedFiles, } = await processYarnWorkspacesProjects(root, options, targetFiles);
|
|
allResults.push(...scannedProjects);
|
|
debug(`Not part of a workspace: ${unprocessedFiles.join(', ')}}`);
|
|
// process the rest 1 by 1 sent to relevant plugins
|
|
for (const targetFile of unprocessedFiles) {
|
|
const optionsClone = cloneDeep(options);
|
|
optionsClone.file = pathLib.relative(root, targetFile);
|
|
optionsClone.packageManager = detect_1.detectPackageManagerFromFile(pathLib.basename(targetFile));
|
|
try {
|
|
const inspectRes = await get_single_plugin_result_1.getSinglePluginResult(root, optionsClone, optionsClone.file);
|
|
let resultWithScannedProjects;
|
|
if (!cliInterface.legacyPlugin.isMultiResult(inspectRes)) {
|
|
resultWithScannedProjects = convert_single_splugin_res_to_multi_custom_1.convertSingleResultToMultiCustom(inspectRes, optionsClone.packageManager);
|
|
}
|
|
else {
|
|
resultWithScannedProjects = inspectRes;
|
|
}
|
|
const pluginResultWithCustomScannedProjects = convert_multi_plugin_res_to_multi_custom_1.convertMultiResultToMultiCustom(resultWithScannedProjects, optionsClone.packageManager, optionsClone.file);
|
|
// annotate the package manager, project name & targetFile to be used
|
|
// for test & monitor
|
|
// TODO: refactor how we display meta to not have to do this
|
|
options.projectNames = resultWithScannedProjects.scannedProjects.map((scannedProject) => { var _a; return (_a = scannedProject === null || scannedProject === void 0 ? void 0 : scannedProject.depTree) === null || _a === void 0 ? void 0 : _a.name; });
|
|
allResults.push(...pluginResultWithCustomScannedProjects.scannedProjects);
|
|
}
|
|
catch (error) {
|
|
const errMessage = (_a = error.message) !== null && _a !== void 0 ? _a : 'Something went wrong getting dependencies';
|
|
// TODO: propagate this all the way back and include in --json output
|
|
failedResults.push({
|
|
targetFile,
|
|
error,
|
|
errMessage: errMessage,
|
|
});
|
|
debug(chalk_1.default.bold.red(`\n${theme_1.icon.ISSUE} Failed to get dependencies for ${targetFile}\nERROR: ${errMessage}\n`));
|
|
}
|
|
}
|
|
if (!allResults.length) {
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry(`Failed to get dependencies for all ${targetFiles.length} potential projects.`));
|
|
}
|
|
return {
|
|
plugin: {
|
|
name: 'custom-auto-detect',
|
|
},
|
|
scannedProjects: allResults,
|
|
failedResults,
|
|
};
|
|
}
|
|
exports.getMultiPluginResult = getMultiPluginResult;
|
|
async function processYarnWorkspacesProjects(root, options, targetFiles) {
|
|
try {
|
|
const { scannedProjects } = await yarn_workspaces_parser_1.processYarnWorkspaces(root, {
|
|
strictOutOfSync: options.strictOutOfSync,
|
|
dev: options.dev,
|
|
}, targetFiles);
|
|
const unprocessedFiles = filterOutProcessedWorkspaces(root, scannedProjects, targetFiles);
|
|
return { scannedProjects, unprocessedFiles };
|
|
}
|
|
catch (e) {
|
|
debug('Error during detecting or processing Yarn Workspaces: ', e);
|
|
return { scannedProjects: [], unprocessedFiles: targetFiles };
|
|
}
|
|
}
|
|
function filterOutProcessedWorkspaces(root, scannedProjects, allTargetFiles) {
|
|
const targetFiles = [];
|
|
const scanned = scannedProjects
|
|
.map((p) => p.targetFile)
|
|
.map((p) => pathLib.resolve(process.cwd(), root, p));
|
|
const all = allTargetFiles.map((p) => ({
|
|
path: pathLib.resolve(process.cwd(), root, p),
|
|
original: p,
|
|
}));
|
|
for (const entry of all) {
|
|
const { path, original } = entry;
|
|
const { base } = pathLib.parse(path);
|
|
if (!['package.json', 'yarn.lock'].includes(base)) {
|
|
targetFiles.push(original);
|
|
continue;
|
|
}
|
|
// standardise to package.json
|
|
// we discover the lockfiles but targetFile is package.json
|
|
if (!scanned.includes(path.replace('yarn.lock', 'package.json'))) {
|
|
targetFiles.push(original);
|
|
continue;
|
|
}
|
|
}
|
|
return targetFiles;
|
|
}
|
|
exports.filterOutProcessedWorkspaces = filterOutProcessedWorkspaces;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 8598:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getSinglePluginResult = void 0;
|
|
const plugins = __webpack_require__(45632);
|
|
const module_info_1 = __webpack_require__(80777);
|
|
async function getSinglePluginResult(root, options, targetFile) {
|
|
const plugin = plugins.loadPlugin(options.packageManager);
|
|
const moduleInfo = module_info_1.ModuleInfo(plugin, options.policy);
|
|
const inspectRes = await moduleInfo.inspect(root, targetFile || options.file, { ...options });
|
|
return inspectRes;
|
|
}
|
|
exports.getSinglePluginResult = getSinglePluginResult;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 45632:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.loadPlugin = void 0;
|
|
const rubygemsPlugin = __webpack_require__(92632);
|
|
const mvnPlugin = __webpack_require__(29615);
|
|
const gradlePlugin = __webpack_require__(71673);
|
|
const sbtPlugin = __webpack_require__(1444);
|
|
const pythonPlugin = __webpack_require__(85054);
|
|
const goPlugin = __webpack_require__(29376);
|
|
const nugetPlugin = __webpack_require__(82843);
|
|
const phpPlugin = __webpack_require__(18630);
|
|
const nodejsPlugin = __webpack_require__(59947);
|
|
const cocoapodsPlugin = __webpack_require__(49556);
|
|
const hexPlugin = __webpack_require__(1649);
|
|
const errors_1 = __webpack_require__(55191);
|
|
function loadPlugin(packageManager) {
|
|
switch (packageManager) {
|
|
case 'npm': {
|
|
return nodejsPlugin;
|
|
}
|
|
case 'rubygems': {
|
|
return rubygemsPlugin;
|
|
}
|
|
case 'maven': {
|
|
return mvnPlugin;
|
|
}
|
|
case 'gradle': {
|
|
return gradlePlugin;
|
|
}
|
|
case 'sbt': {
|
|
return sbtPlugin;
|
|
}
|
|
case 'yarn': {
|
|
return nodejsPlugin;
|
|
}
|
|
case 'pip':
|
|
case 'poetry': {
|
|
return pythonPlugin;
|
|
}
|
|
case 'golangdep':
|
|
case 'gomodules':
|
|
case 'govendor': {
|
|
return goPlugin;
|
|
}
|
|
case 'nuget': {
|
|
return nugetPlugin;
|
|
}
|
|
case 'paket': {
|
|
return nugetPlugin;
|
|
}
|
|
case 'composer': {
|
|
return phpPlugin;
|
|
}
|
|
case 'cocoapods': {
|
|
return cocoapodsPlugin;
|
|
}
|
|
case 'hex': {
|
|
return hexPlugin;
|
|
}
|
|
default: {
|
|
throw new errors_1.UnsupportedPackageManagerError(packageManager);
|
|
}
|
|
}
|
|
}
|
|
exports.loadPlugin = loadPlugin;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 59947:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.inspect = void 0;
|
|
const modulesParser = __webpack_require__(85994);
|
|
const lockParser = __webpack_require__(82791);
|
|
const analytics = __webpack_require__(82744);
|
|
const missing_targetfile_error_1 = __webpack_require__(56775);
|
|
async function inspect(root, targetFile, options = {}) {
|
|
var _a;
|
|
if (!targetFile) {
|
|
throw missing_targetfile_error_1.MissingTargetFileError(root);
|
|
}
|
|
const isLockFileBased = targetFile.endsWith('package-lock.json') ||
|
|
targetFile.endsWith('yarn.lock');
|
|
const getLockFileDeps = isLockFileBased && !options.traverseNodeModules;
|
|
const depTree = getLockFileDeps
|
|
? await lockParser.parse(root, targetFile, options)
|
|
: await modulesParser.parse(root, targetFile, options);
|
|
if ((_a = depTree === null || depTree === void 0 ? void 0 : depTree.meta) === null || _a === void 0 ? void 0 : _a.lockfileVersion) {
|
|
analytics.add('lockfileVersion', depTree.meta.lockfileVersion);
|
|
}
|
|
return {
|
|
plugin: {
|
|
name: 'snyk-nodejs-lockfile-parser',
|
|
runtime: process.version,
|
|
},
|
|
scannedProjects: [{ depTree }],
|
|
};
|
|
}
|
|
exports.inspect = inspect;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 82791:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.parse = void 0;
|
|
const baseDebug = __webpack_require__(15158);
|
|
const debug = baseDebug('snyk-test');
|
|
const path = __webpack_require__(85622);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const analytics = __webpack_require__(82744);
|
|
const fs = __webpack_require__(35747);
|
|
const lockFileParser = __webpack_require__(423);
|
|
async function parse(root, targetFile, options) {
|
|
const lockFileFullPath = path.resolve(root, targetFile);
|
|
if (!fs.existsSync(lockFileFullPath)) {
|
|
throw new Error('Lockfile ' + targetFile + ' not found at location: ' + lockFileFullPath);
|
|
}
|
|
const fullPath = path.parse(lockFileFullPath);
|
|
const manifestFileFullPath = path.resolve(fullPath.dir, 'package.json');
|
|
const shrinkwrapFullPath = path.resolve(fullPath.dir, 'npm-shrinkwrap.json');
|
|
if (!fs.existsSync(manifestFileFullPath)) {
|
|
throw new Error(`Could not find package.json at ${manifestFileFullPath} ` +
|
|
`(lockfile found at ${targetFile})`);
|
|
}
|
|
if (fs.existsSync(shrinkwrapFullPath)) {
|
|
throw new Error('Both `npm-shrinkwrap.json` and `package-lock.json` were found in ' +
|
|
fullPath.dir +
|
|
'.\n' +
|
|
'Please run your command again specifying `--file=package.json` flag.');
|
|
}
|
|
analytics.add('local', true);
|
|
analytics.add('generating-node-dependency-tree', {
|
|
lockFile: true,
|
|
targetFile,
|
|
});
|
|
const resolveModuleSpinnerLabel = `Analyzing npm dependencies for ${lockFileFullPath}`;
|
|
debug(resolveModuleSpinnerLabel);
|
|
try {
|
|
await spinner_1.spinner(resolveModuleSpinnerLabel);
|
|
const strictOutOfSync = options.strictOutOfSync !== false;
|
|
return lockFileParser.buildDepTreeFromFiles(root, manifestFileFullPath, lockFileFullPath, options.dev, strictOutOfSync);
|
|
}
|
|
finally {
|
|
await spinner_1.spinner.clear(resolveModuleSpinnerLabel)();
|
|
}
|
|
}
|
|
exports.parse = parse;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 85994:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.parse = void 0;
|
|
const path = __webpack_require__(85622);
|
|
const fs = __webpack_require__(35747);
|
|
const resolveNodeDeps = __webpack_require__(40068);
|
|
const baseDebug = __webpack_require__(15158);
|
|
const isEmpty = __webpack_require__(99245);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const analytics = __webpack_require__(82744);
|
|
const get_file_contents_1 = __webpack_require__(84210);
|
|
const debug = baseDebug('snyk-nodejs-plugin');
|
|
async function parse(root, targetFile, options) {
|
|
if (targetFile.endsWith('yarn.lock')) {
|
|
options.file =
|
|
options.file && options.file.replace('yarn.lock', 'package.json');
|
|
}
|
|
// package-lock.json falls back to package.json (used in wizard code)
|
|
if (targetFile.endsWith('package-lock.json')) {
|
|
options.file =
|
|
options.file && options.file.replace('package-lock.json', 'package.json');
|
|
}
|
|
// check if there any dependencies
|
|
const packageJsonFileName = path.resolve(root, options.file);
|
|
const packageManager = options.packageManager || 'npm';
|
|
try {
|
|
const packageJson = JSON.parse(get_file_contents_1.getFileContents(root, packageJsonFileName).content);
|
|
let dependencies = packageJson.dependencies;
|
|
if (options.dev) {
|
|
dependencies = { ...dependencies, ...packageJson.devDependencies };
|
|
}
|
|
if (isEmpty(dependencies)) {
|
|
return new Promise((resolve) => resolve({
|
|
name: packageJson.name || 'package.json',
|
|
dependencies: {},
|
|
version: packageJson.version,
|
|
}));
|
|
}
|
|
}
|
|
catch (e) {
|
|
debug(`Failed to read ${packageJsonFileName}: Error: ${e}`);
|
|
throw new Error(`Failed to read ${packageJsonFileName}. Error: ${e.message}`);
|
|
}
|
|
const nodeModulesPath = path.join(path.dirname(path.resolve(root, targetFile)), 'node_modules');
|
|
if (!fs.existsSync(nodeModulesPath)) {
|
|
// throw a custom error
|
|
throw new Error("Missing node_modules folder: we can't test " +
|
|
`without dependencies.\nPlease run '${packageManager} install' first.`);
|
|
}
|
|
analytics.add('local', true);
|
|
analytics.add('generating-node-dependency-tree', {
|
|
lockFile: false,
|
|
targetFile,
|
|
});
|
|
const resolveModuleSpinnerLabel = 'Analyzing npm dependencies for ' +
|
|
path.dirname(path.resolve(root, targetFile));
|
|
try {
|
|
await spinner_1.spinner.clear(resolveModuleSpinnerLabel)();
|
|
await spinner_1.spinner(resolveModuleSpinnerLabel);
|
|
return resolveNodeDeps(root, Object.assign({}, options, { noFromArrays: true }));
|
|
}
|
|
finally {
|
|
await spinner_1.spinner.clear(resolveModuleSpinnerLabel)();
|
|
}
|
|
}
|
|
exports.parse = parse;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 27326:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.packageJsonBelongsToWorkspace = exports.getWorkspacesMap = exports.processYarnWorkspaces = void 0;
|
|
const baseDebug = __webpack_require__(15158);
|
|
const pathUtil = __webpack_require__(85622);
|
|
const sortBy = __webpack_require__(58254);
|
|
const groupBy = __webpack_require__(20276);
|
|
const micromatch = __webpack_require__(70850);
|
|
const debug = baseDebug('snyk-yarn-workspaces');
|
|
const lockFileParser = __webpack_require__(423);
|
|
const get_file_contents_1 = __webpack_require__(84210);
|
|
const errors_1 = __webpack_require__(55191);
|
|
async function processYarnWorkspaces(root, settings, targetFiles) {
|
|
// the order of yarnTargetFiles folders is important
|
|
// must have the root level most folders at the top
|
|
const mappedAndFiltered = targetFiles
|
|
.map((p) => ({ path: p, ...pathUtil.parse(p) }))
|
|
.filter((res) => ['package.json', 'yarn.lock'].includes(res.base));
|
|
const sorted = sortBy(mappedAndFiltered, 'dir');
|
|
const grouped = groupBy(sorted, 'dir');
|
|
const yarnTargetFiles = grouped;
|
|
debug(`Processing potential Yarn workspaces (${targetFiles.length})`);
|
|
if (settings.yarnWorkspaces && Object.keys(yarnTargetFiles).length === 0) {
|
|
throw errors_1.NoSupportedManifestsFoundError([root]);
|
|
}
|
|
let yarnWorkspacesMap = {};
|
|
const yarnWorkspacesFilesMap = {};
|
|
const result = {
|
|
plugin: {
|
|
name: 'snyk-nodejs-yarn-workspaces',
|
|
runtime: process.version,
|
|
},
|
|
scannedProjects: [],
|
|
};
|
|
let rootWorkspaceManifestContent = {};
|
|
// the folders must be ordered highest first
|
|
for (const directory of Object.keys(yarnTargetFiles)) {
|
|
debug(`Processing ${directory} as a potential Yarn workspace`);
|
|
let isYarnWorkspacePackage = false;
|
|
let isRootPackageJson = false;
|
|
const packageJsonFileName = pathUtil.join(directory, 'package.json');
|
|
const packageJson = get_file_contents_1.getFileContents(root, packageJsonFileName);
|
|
yarnWorkspacesMap = {
|
|
...yarnWorkspacesMap,
|
|
...getWorkspacesMap(packageJson),
|
|
};
|
|
for (const workspaceRoot of Object.keys(yarnWorkspacesMap)) {
|
|
const match = packageJsonBelongsToWorkspace(packageJsonFileName, yarnWorkspacesMap, workspaceRoot);
|
|
if (match) {
|
|
debug(`${packageJsonFileName} matches an existing workspace pattern`);
|
|
yarnWorkspacesFilesMap[packageJsonFileName] = {
|
|
root: workspaceRoot,
|
|
};
|
|
isYarnWorkspacePackage = true;
|
|
}
|
|
if (packageJsonFileName === workspaceRoot) {
|
|
isRootPackageJson = true;
|
|
rootWorkspaceManifestContent = JSON.parse(packageJson.content);
|
|
}
|
|
}
|
|
if (!(isYarnWorkspacePackage || isRootPackageJson)) {
|
|
debug(`${packageJsonFileName} is not part of any detected workspace, skipping`);
|
|
continue;
|
|
}
|
|
try {
|
|
const rootDir = isYarnWorkspacePackage
|
|
? pathUtil.dirname(yarnWorkspacesFilesMap[packageJsonFileName].root)
|
|
: pathUtil.dirname(packageJsonFileName);
|
|
const rootYarnLockfileName = pathUtil.join(rootDir, 'yarn.lock');
|
|
const yarnLock = await get_file_contents_1.getFileContents(root, rootYarnLockfileName);
|
|
if (rootWorkspaceManifestContent.hasOwnProperty('resolutions') &&
|
|
lockFileParser.getYarnLockfileType(yarnLock.content) ===
|
|
lockFileParser.LockfileType.yarn2) {
|
|
const parsedManifestContent = JSON.parse(packageJson.content);
|
|
packageJson.content = JSON.stringify({
|
|
...parsedManifestContent,
|
|
resolutions: rootWorkspaceManifestContent['resolutions'],
|
|
});
|
|
}
|
|
const res = await lockFileParser.buildDepTree(packageJson.content, yarnLock.content, settings.dev, lockFileParser.LockfileType.yarn, settings.strictOutOfSync !== false);
|
|
const project = {
|
|
packageManager: 'yarn',
|
|
targetFile: pathUtil.relative(root, packageJson.fileName),
|
|
depTree: res,
|
|
plugin: {
|
|
name: 'snyk-nodejs-lockfile-parser',
|
|
runtime: process.version,
|
|
},
|
|
};
|
|
result.scannedProjects.push(project);
|
|
}
|
|
catch (e) {
|
|
if (settings.yarnWorkspaces) {
|
|
throw e;
|
|
}
|
|
debug(`Error process workspace: ${packageJsonFileName}. ERROR: ${e}`);
|
|
}
|
|
}
|
|
if (!result.scannedProjects.length) {
|
|
debug(`No yarn workspaces detected in any of the ${targetFiles.length} target files.`);
|
|
}
|
|
return result;
|
|
}
|
|
exports.processYarnWorkspaces = processYarnWorkspaces;
|
|
function getWorkspacesMap(file) {
|
|
const yarnWorkspacesMap = {};
|
|
if (!file) {
|
|
return yarnWorkspacesMap;
|
|
}
|
|
try {
|
|
const rootFileWorkspacesDefinitions = lockFileParser.getYarnWorkspaces(file.content);
|
|
if (rootFileWorkspacesDefinitions && rootFileWorkspacesDefinitions.length) {
|
|
yarnWorkspacesMap[file.fileName] = {
|
|
workspaces: rootFileWorkspacesDefinitions,
|
|
};
|
|
}
|
|
}
|
|
catch (e) {
|
|
debug('Failed to process a workspace', e.message);
|
|
}
|
|
return yarnWorkspacesMap;
|
|
}
|
|
exports.getWorkspacesMap = getWorkspacesMap;
|
|
function packageJsonBelongsToWorkspace(packageJsonFileName, yarnWorkspacesMap, workspaceRoot) {
|
|
const workspaceRootFolder = pathUtil.dirname(workspaceRoot.replace(/\\/g, '/'));
|
|
const workspacesGlobs = (yarnWorkspacesMap[workspaceRoot].workspaces || []).map((workspace) => pathUtil.join(workspaceRootFolder, workspace));
|
|
const match = micromatch.isMatch(packageJsonFileName.replace(/\\/g, '/'), workspacesGlobs.map((p) => pathUtil.normalize(pathUtil.join(p, '**')).replace(/\\/g, '/')));
|
|
return match;
|
|
}
|
|
exports.packageJsonBelongsToWorkspace = packageJsonBelongsToWorkspace;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 92632:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.inspect = void 0;
|
|
const inspectors_1 = __webpack_require__(9438);
|
|
const missing_targetfile_error_1 = __webpack_require__(56775);
|
|
const gemfileLockToDependencies = __webpack_require__(97467);
|
|
const get = __webpack_require__(29208);
|
|
async function inspect(root, targetFile, options = {}) {
|
|
if (!targetFile) {
|
|
throw missing_targetfile_error_1.MissingTargetFileError(root);
|
|
}
|
|
const specs = await gatherSpecs(root, targetFile, options);
|
|
return {
|
|
plugin: {
|
|
name: 'bundled:rubygems',
|
|
runtime: 'unknown',
|
|
},
|
|
scannedProjects: [
|
|
{
|
|
depTree: {
|
|
name: specs.packageName,
|
|
targetFile: specs.targetFile,
|
|
dependencies: getDependenciesFromSpecs(specs),
|
|
},
|
|
},
|
|
],
|
|
};
|
|
}
|
|
exports.inspect = inspect;
|
|
function getDependenciesFromSpecs(specs) {
|
|
const gemfileLockBase64 = get(specs, 'files.gemfileLock.contents');
|
|
const gemspecBase64 = get(specs, 'files.gemspec.contents');
|
|
const contents = Buffer.from(gemfileLockBase64 || gemspecBase64, 'base64').toString();
|
|
const dependencies = gemfileLockToDependencies(contents);
|
|
return dependencies;
|
|
}
|
|
async function gatherSpecs(root, targetFile, options) {
|
|
for (const inspector of inspectors_1.inspectors) {
|
|
if (inspector.canHandle(targetFile)) {
|
|
return await inspector.gatherSpecs(root, targetFile, options);
|
|
}
|
|
}
|
|
throw new Error(`Could not handle rubygems file: ${targetFile}`);
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 9162:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.gatherSpecs = exports.canHandle = void 0;
|
|
const path = __webpack_require__(85622);
|
|
const try_get_spec_1 = __webpack_require__(50552);
|
|
/* Supported example patterns:
|
|
* Gemfile
|
|
* Gemfile.lock
|
|
* rails.2.4.5.gemfile
|
|
* rails.2.4.5.gemfile.lock
|
|
* gemfiles/Gemfile.rails-2.4.5.lock
|
|
* gemfiles/Gemfile.lock.rails-2.4.5
|
|
*/
|
|
const gemfileOrLockfilePattern = /.*[gG]emfile.*(\.lock)?.*$/;
|
|
const gemfileLockPattern = /.*[gG]emfile.*(\.lock).*$/;
|
|
function canHandle(file) {
|
|
return !!file && gemfileOrLockfilePattern.test(path.basename(file));
|
|
}
|
|
exports.canHandle = canHandle;
|
|
async function gatherSpecs(root, target, options) {
|
|
const { dir, name } = path.parse(target);
|
|
const isGemfileLock = gemfileLockPattern.test(target);
|
|
// if the target is a Gemfile we treat is as the lockfile
|
|
const gemfileLock = await try_get_spec_1.tryGetSpec(root, isGemfileLock ? target : path.join(target + '.lock'));
|
|
if (gemfileLock) {
|
|
const basePackageName = path.basename(root);
|
|
return {
|
|
packageName: options.allSubProjects
|
|
? path.join(basePackageName, dir)
|
|
: basePackageName,
|
|
targetFile: path.join(dir, name),
|
|
files: { gemfileLock },
|
|
};
|
|
}
|
|
else {
|
|
throw new Error(`Could not read ${target || 'Gemfile.lock'} lockfile: can't test ` +
|
|
'without dependencies.\nPlease run `bundle install` first or' +
|
|
' if this is a custom file name re-run with --file=path/to/custom.gemfile.lock --package-manager=rubygems');
|
|
}
|
|
}
|
|
exports.gatherSpecs = gatherSpecs;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 31810:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.gatherSpecs = exports.canHandle = void 0;
|
|
const path = __webpack_require__(85622);
|
|
const try_get_spec_1 = __webpack_require__(50552);
|
|
const pattern = /\.gemspec$/;
|
|
function canHandle(file) {
|
|
return !!file && pattern.test(file);
|
|
}
|
|
exports.canHandle = canHandle;
|
|
async function gatherSpecs(root, target) {
|
|
const targetName = path.basename(target);
|
|
const targetDir = path.dirname(target);
|
|
const files = {};
|
|
const gemspec = await try_get_spec_1.tryGetSpec(root, path.join(targetDir, targetName));
|
|
if (gemspec) {
|
|
files.gemspec = gemspec;
|
|
}
|
|
else {
|
|
throw new Error(`File not found: ${target}`);
|
|
}
|
|
const gemfileLock = await try_get_spec_1.tryGetSpec(root, path.join(targetDir, 'Gemfile.lock'));
|
|
if (gemfileLock) {
|
|
files.gemfileLock = gemfileLock;
|
|
}
|
|
return {
|
|
packageName: path.basename(root),
|
|
targetFile: path.join(targetDir, targetName),
|
|
files,
|
|
};
|
|
}
|
|
exports.gatherSpecs = gatherSpecs;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 9438:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.inspectors = void 0;
|
|
const gemfile = __webpack_require__(9162);
|
|
const gemspec = __webpack_require__(31810);
|
|
exports.inspectors = [gemfile, gemspec];
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 50552:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.tryGetSpec = void 0;
|
|
const path = __webpack_require__(85622);
|
|
const fs = __webpack_require__(35747);
|
|
async function tryGetSpec(dir, name) {
|
|
const filePath = path.resolve(dir, name);
|
|
if (fs.existsSync(filePath)) {
|
|
return {
|
|
name,
|
|
contents: Buffer.from(fs.readFileSync(filePath)).toString('base64'),
|
|
};
|
|
}
|
|
return null;
|
|
}
|
|
exports.tryGetSpec = tryGetSpec;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 2806:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getCodeAnalysisAndParseResults = void 0;
|
|
const code_client_1 = __webpack_require__(95951);
|
|
const legacy_1 = __webpack_require__(34013);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const config_1 = __webpack_require__(25425);
|
|
const code_config_1 = __webpack_require__(55203);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const utils_1 = __webpack_require__(33113);
|
|
const errors_1 = __webpack_require__(61315);
|
|
const proxy_from_env_1 = __webpack_require__(21394);
|
|
const global_agent_1 = __webpack_require__(97959);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const debugLib = __webpack_require__(15158);
|
|
const code_config_2 = __webpack_require__(55203);
|
|
const debug = debugLib('snyk-code');
|
|
async function getCodeAnalysisAndParseResults(root, options, sastSettings, requestId) {
|
|
await spinner_1.spinner.clearAll();
|
|
utils_1.analysisProgressUpdate();
|
|
const codeAnalysis = await getCodeAnalysis(root, options, sastSettings, requestId);
|
|
spinner_1.spinner.clearAll();
|
|
return parseSecurityResults(codeAnalysis);
|
|
}
|
|
exports.getCodeAnalysisAndParseResults = getCodeAnalysisAndParseResults;
|
|
async function getCodeAnalysis(root, options, sastSettings, requestId) {
|
|
var _a;
|
|
const isLocalCodeEngineEnabled = isLocalCodeEngine(sastSettings);
|
|
if (isLocalCodeEngineEnabled) {
|
|
validateLocalCodeEngineUrl(sastSettings.localCodeEngine.url);
|
|
}
|
|
const source = 'snyk-cli';
|
|
const baseURL = isLocalCodeEngineEnabled
|
|
? sastSettings.localCodeEngine.url
|
|
: code_config_2.getCodeClientProxyUrl();
|
|
const base64Encoding = code_config_1.getBase64Encoding();
|
|
debug(`base64 encoding enabled: ${base64Encoding}`);
|
|
// TODO(james) This mirrors the implementation in request.ts and we need to use this for deeproxy calls
|
|
// This ensures we support lowercase http(s)_proxy values as well
|
|
// The weird IF around it ensures we don't create an envvar with
|
|
// a value of undefined, which throws error when trying to use it as a proxy
|
|
if (process.env.HTTP_PROXY || process.env.http_proxy) {
|
|
process.env.HTTP_PROXY = process.env.HTTP_PROXY || process.env.http_proxy;
|
|
}
|
|
if (process.env.HTTPS_PROXY || process.env.https_proxy) {
|
|
process.env.HTTPS_PROXY =
|
|
process.env.HTTPS_PROXY || process.env.https_proxy;
|
|
}
|
|
const proxyUrl = proxy_from_env_1.getProxyForUrl(baseURL);
|
|
if (proxyUrl) {
|
|
global_agent_1.bootstrap({
|
|
environmentVariableNamespace: '',
|
|
});
|
|
}
|
|
const sessionToken = api_token_1.api() || '';
|
|
const severity = options.severityThreshold
|
|
? severityToAnalysisSeverity(options.severityThreshold)
|
|
: code_client_1.AnalysisSeverity.info;
|
|
const result = await code_client_1.analyzeFolders({
|
|
connection: { baseURL, sessionToken, source, requestId, base64Encoding },
|
|
analysisOptions: { severity },
|
|
fileOptions: { paths: [root] },
|
|
analysisContext: {
|
|
initiator: 'CLI',
|
|
flow: source,
|
|
orgDisplayName: sastSettings.org,
|
|
projectName: config_1.default.PROJECT_NAME,
|
|
org: {
|
|
name: sastSettings.org || 'unknown',
|
|
displayName: 'unknown',
|
|
publicId: 'unknown',
|
|
flags: {},
|
|
},
|
|
},
|
|
languages: sastSettings.supportedLanguages,
|
|
});
|
|
if ((_a = result === null || result === void 0 ? void 0 : result.fileBundle.skippedOversizedFiles) === null || _a === void 0 ? void 0 : _a.length) {
|
|
debug('\n', chalk_1.default.yellow(`Warning!\nFiles were skipped in the analysis due to their size being greater than ${code_client_1.MAX_FILE_SIZE}B. Skipped files: ${[
|
|
...result.fileBundle.skippedOversizedFiles,
|
|
].join(', ')}`));
|
|
}
|
|
if ((result === null || result === void 0 ? void 0 : result.analysisResults.type) === 'sarif') {
|
|
return result.analysisResults.sarif;
|
|
}
|
|
return null;
|
|
}
|
|
function severityToAnalysisSeverity(severity) {
|
|
if (severity === legacy_1.SEVERITY.CRITICAL) {
|
|
throw new errors_1.FeatureNotSupportedBySnykCodeError(legacy_1.SEVERITY.CRITICAL);
|
|
}
|
|
const severityLevel = {
|
|
low: 1,
|
|
medium: 2,
|
|
high: 3,
|
|
};
|
|
return severityLevel[severity];
|
|
}
|
|
function parseSecurityResults(codeAnalysis) {
|
|
let securityRulesMap;
|
|
if (!codeAnalysis) {
|
|
return codeAnalysis;
|
|
}
|
|
const rules = codeAnalysis.runs[0].tool.driver.rules;
|
|
const results = codeAnalysis.runs[0].results;
|
|
if (rules) {
|
|
securityRulesMap = getSecurityRulesMap(rules);
|
|
codeAnalysis.runs[0].tool.driver.rules = Object.values(securityRulesMap);
|
|
}
|
|
if (results && securityRulesMap) {
|
|
codeAnalysis.runs[0].results = getSecurityResultsOnly(results, Object.keys(securityRulesMap));
|
|
}
|
|
return codeAnalysis;
|
|
}
|
|
function getSecurityRulesMap(rules) {
|
|
const securityRulesMap = rules.reduce((acc, rule) => {
|
|
var _a;
|
|
const { id: ruleId, properties } = rule;
|
|
const isSecurityRule = (_a = properties === null || properties === void 0 ? void 0 : properties.categories) === null || _a === void 0 ? void 0 : _a.some((category) => category.toLowerCase() === 'security');
|
|
if (isSecurityRule) {
|
|
acc[ruleId] = rule;
|
|
}
|
|
return acc;
|
|
}, {});
|
|
return securityRulesMap;
|
|
}
|
|
function getSecurityResultsOnly(results, securityRules) {
|
|
const securityResults = results.reduce((acc, result) => {
|
|
const securityRule = securityRules.find((sr) => sr === (result === null || result === void 0 ? void 0 : result.ruleId));
|
|
if (securityRule) {
|
|
result.ruleIndex = securityRules.indexOf(securityRule);
|
|
acc.push(result);
|
|
}
|
|
return acc;
|
|
}, []);
|
|
return securityResults;
|
|
}
|
|
function isLocalCodeEngine(sastSettings) {
|
|
const { sastEnabled, localCodeEngine } = sastSettings;
|
|
return sastEnabled && localCodeEngine.enabled;
|
|
}
|
|
function validateLocalCodeEngineUrl(localCodeEngineUrl) {
|
|
if (localCodeEngineUrl.length === 0) {
|
|
throw new errors_1.MissingConfigurationError('Snyk Code Local Engine. Refer to our docs on https://docs.snyk.io/products/snyk-code/deployment-options/snyk-code-local-engine/cli-and-ide to learn more');
|
|
}
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 15976:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.trackUsage = exports.getSastSettingsForOrg = void 0;
|
|
const request_1 = __webpack_require__(52050);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const config_1 = __webpack_require__(25425);
|
|
const common_1 = __webpack_require__(53110);
|
|
async function getSastSettingsForOrg(org) {
|
|
const response = await request_1.makeRequest({
|
|
method: 'GET',
|
|
headers: {
|
|
Authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
qs: common_1.assembleQueryString({ org }),
|
|
url: `${config_1.default.API}/cli-config/settings/sast`,
|
|
gzip: true,
|
|
json: true,
|
|
});
|
|
return response.body;
|
|
}
|
|
exports.getSastSettingsForOrg = getSastSettingsForOrg;
|
|
async function trackUsage(org) {
|
|
const response = await request_1.makeRequest({
|
|
method: 'POST',
|
|
headers: {
|
|
Authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
qs: common_1.assembleQueryString({ org }),
|
|
url: `${config_1.default.API}/track-sast-usage/cli`,
|
|
gzip: true,
|
|
json: true,
|
|
});
|
|
return response.body;
|
|
}
|
|
exports.trackUsage = trackUsage;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 61315:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var missing_configuration_error_1 = __webpack_require__(73016);
|
|
Object.defineProperty(exports, "MissingConfigurationError", ({ enumerable: true, get: function () { return missing_configuration_error_1.MissingConfigurationError; } }));
|
|
var unsupported_feature_snyk_code_error_1 = __webpack_require__(90551);
|
|
Object.defineProperty(exports, "FeatureNotSupportedBySnykCodeError", ({ enumerable: true, get: function () { return unsupported_feature_snyk_code_error_1.FeatureNotSupportedBySnykCodeError; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 73016:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.MissingConfigurationError = void 0;
|
|
const custom_error_1 = __webpack_require__(17188);
|
|
class MissingConfigurationError extends custom_error_1.CustomError {
|
|
constructor(action, additionalUserHelp = '') {
|
|
super(`Missing configuration for ${action}.`);
|
|
this.code = 422;
|
|
this.action = action;
|
|
this.userMessage = `'Configuration is missing or wrong for ${action}'. ${additionalUserHelp}`;
|
|
}
|
|
}
|
|
exports.MissingConfigurationError = MissingConfigurationError;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 90551:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.FeatureNotSupportedBySnykCodeError = void 0;
|
|
const custom_error_1 = __webpack_require__(17188);
|
|
class FeatureNotSupportedBySnykCodeError extends custom_error_1.CustomError {
|
|
constructor(feature, additionalUserHelp = '') {
|
|
super(`Unsupported action for ${feature}.`);
|
|
this.code = 422;
|
|
this.feature = feature;
|
|
this.userMessage = `'${feature}' is not supported for snyk code. ${additionalUserHelp}`;
|
|
}
|
|
}
|
|
exports.FeatureNotSupportedBySnykCodeError = FeatureNotSupportedBySnykCodeError;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 75802:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getPrefix = exports.getMeta = exports.getCodeDisplayedOutput = void 0;
|
|
const Debug = __webpack_require__(15158);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const common_1 = __webpack_require__(53110);
|
|
const right_pad_1 = __webpack_require__(80627);
|
|
const debug = Debug('code-output');
|
|
function getCodeDisplayedOutput(codeTest, meta, prefix) {
|
|
let issues = {};
|
|
if (codeTest.runs[0].results) {
|
|
const results = codeTest.runs[0].results;
|
|
const rulesMap = getRulesMap(codeTest.runs[0].tool.driver.rules || []);
|
|
issues = getIssues(results, rulesMap);
|
|
}
|
|
const issuesText = issues.low.join('') + issues.medium.join('') + issues.high.join('');
|
|
const summaryOKText = theme_1.color.status.success(`${theme_1.icon.VALID} Test completed`);
|
|
const codeIssueSummary = getCodeIssuesSummary(issues);
|
|
return (prefix +
|
|
issuesText +
|
|
'\n' +
|
|
summaryOKText +
|
|
'\n\n' +
|
|
meta +
|
|
'\n\n' +
|
|
chalk_1.default.bold('Summary:') +
|
|
'\n\n' +
|
|
codeIssueSummary +
|
|
'\n\n');
|
|
}
|
|
exports.getCodeDisplayedOutput = getCodeDisplayedOutput;
|
|
function getCodeIssuesSummary(issues) {
|
|
const lowSeverityText = issues.low.length
|
|
? common_1.colorTextBySeverity(common_1.SEVERITY.LOW, ` ${issues.low.length} [Low] `)
|
|
: '';
|
|
const mediumSeverityText = issues.medium.length
|
|
? common_1.colorTextBySeverity(common_1.SEVERITY.MEDIUM, ` ${issues.medium.length} [Medium] `)
|
|
: '';
|
|
const highSeverityText = issues.high.length
|
|
? common_1.colorTextBySeverity(common_1.SEVERITY.HIGH, ` ${issues.high.length} [High] `)
|
|
: '';
|
|
const codeIssueCount = issues.low.length + issues.medium.length + issues.high.length;
|
|
const codeIssueFound = ` ${codeIssueCount} Code issue${codeIssueCount > 0 ? 's' : ''} found`;
|
|
const issuesBySeverityText = highSeverityText + mediumSeverityText + lowSeverityText;
|
|
const vulnPathsText = theme_1.color.status.success(`${theme_1.icon.VALID} Awesome! No issues were found.`);
|
|
return codeIssueCount > 0
|
|
? codeIssueFound + '\n' + issuesBySeverityText
|
|
: vulnPathsText;
|
|
}
|
|
function getIssues(results, rulesMap) {
|
|
const issuesInit = {
|
|
low: [],
|
|
medium: [],
|
|
high: [],
|
|
};
|
|
const issues = results.reduce((acc, res) => {
|
|
var _a, _b;
|
|
if ((_a = res.locations) === null || _a === void 0 ? void 0 : _a.length) {
|
|
const location = res.locations[0].physicalLocation;
|
|
if (res.level && (location === null || location === void 0 ? void 0 : location.artifactLocation) && (location === null || location === void 0 ? void 0 : location.region)) {
|
|
const severity = sarifToSeverityLevel(res.level);
|
|
const ruleId = res.ruleId;
|
|
if (!(ruleId in rulesMap)) {
|
|
debug('Rule ID does not exist in the rules list');
|
|
}
|
|
const ruleName = ((_b = rulesMap[ruleId].shortDescription) === null || _b === void 0 ? void 0 : _b.text) ||
|
|
rulesMap[ruleId].name ||
|
|
'';
|
|
const ruleIdSeverityText = common_1.colorTextBySeverity(severity, ` ${theme_1.icon.ISSUE} [${severity}] ${chalk_1.default.bold(ruleName)}`);
|
|
const artifactLocationUri = location.artifactLocation.uri;
|
|
const startLine = location.region.startLine;
|
|
const text = res.message.text;
|
|
const title = ruleIdSeverityText;
|
|
const path = ` Path: ${artifactLocationUri}, line ${startLine}`;
|
|
const info = ` Info: ${text}`;
|
|
acc[severity.toLowerCase()].push(`${title} \n ${path} \n ${info}\n\n`);
|
|
}
|
|
}
|
|
return acc;
|
|
}, issuesInit);
|
|
return issues;
|
|
}
|
|
function getRulesMap(rules) {
|
|
const rulesMapByID = rules.reduce((acc, rule) => {
|
|
acc[rule.id] = rule;
|
|
return acc;
|
|
}, {});
|
|
return rulesMapByID;
|
|
}
|
|
function sarifToSeverityLevel(sarifConfigurationLevel) {
|
|
const severityLevel = {
|
|
note: 'Low',
|
|
warning: 'Medium',
|
|
error: 'High',
|
|
};
|
|
return severityLevel[sarifConfigurationLevel];
|
|
}
|
|
function getMeta(options, path) {
|
|
const padToLength = 19; // chars to align
|
|
const orgName = options.org || '';
|
|
const projectPath = options.path || path;
|
|
const meta = [
|
|
right_pad_1.rightPadWithSpaces('Organization: ', padToLength) + chalk_1.default.bold(orgName),
|
|
];
|
|
meta.push(right_pad_1.rightPadWithSpaces('Test type: ', padToLength) +
|
|
chalk_1.default.bold('Static code analysis'));
|
|
meta.push(right_pad_1.rightPadWithSpaces('Project path: ', padToLength) + chalk_1.default.bold(projectPath));
|
|
return meta.join('\n');
|
|
}
|
|
exports.getMeta = getMeta;
|
|
function getPrefix(path) {
|
|
return chalk_1.default.bold.white('\nTesting ' + path + ' ...\n\n');
|
|
}
|
|
exports.getPrefix = getPrefix;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 93221:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.codePlugin = void 0;
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const debugLib = __webpack_require__(15158);
|
|
const uuid_1 = __webpack_require__(42277);
|
|
const analysis_1 = __webpack_require__(2806);
|
|
const settings_1 = __webpack_require__(24930);
|
|
const output_format_1 = __webpack_require__(75802);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const json_1 = __webpack_require__(27019);
|
|
const analytics = __webpack_require__(82744);
|
|
const debug = debugLib('snyk-code');
|
|
exports.codePlugin = {
|
|
// We currently don't use scan/display. we will need to consolidate ecosystem plugins
|
|
// to accept flows that act differently in the `testDependencies` step, as we have here
|
|
async scan() {
|
|
return null;
|
|
},
|
|
async display() {
|
|
return '';
|
|
},
|
|
async test(paths, options) {
|
|
var _a, _b, _c, _d;
|
|
const requestId = uuid_1.v4();
|
|
debug(`Request ID: ${requestId}`);
|
|
try {
|
|
analytics.add('sast-scan', true);
|
|
const sastSettings = await settings_1.getSastSettings(options);
|
|
// Currently code supports only one path
|
|
const path = paths[0];
|
|
const sarifTypedResult = await analysis_1.getCodeAnalysisAndParseResults(path, options, sastSettings, requestId);
|
|
if (!sarifTypedResult) {
|
|
throw new errors_1.NoSupportedSastFiles();
|
|
}
|
|
const numOfIssues = ((_b = (_a = sarifTypedResult.runs) === null || _a === void 0 ? void 0 : _a[0].results) === null || _b === void 0 ? void 0 : _b.length) || 0;
|
|
analytics.add('sast-issues-found', numOfIssues);
|
|
let newOrg = options.org;
|
|
if (!newOrg && sastSettings.org) {
|
|
newOrg = sastSettings.org;
|
|
}
|
|
const meta = output_format_1.getMeta({ ...options, org: newOrg }, path);
|
|
const prefix = output_format_1.getPrefix(path);
|
|
let readableResult = output_format_1.getCodeDisplayedOutput(sarifTypedResult, meta, prefix);
|
|
if (numOfIssues > 0 && options['no-markdown']) {
|
|
(_d = (_c = sarifTypedResult.runs) === null || _c === void 0 ? void 0 : _c[0].results) === null || _d === void 0 ? void 0 : _d.forEach(({ message }) => {
|
|
delete message.markdown;
|
|
});
|
|
}
|
|
let sarifResult;
|
|
if (options['sarif-file-output']) {
|
|
sarifResult = json_1.jsonStringifyLargeObject(sarifTypedResult);
|
|
}
|
|
let jsonResult;
|
|
if (options['json-file-output']) {
|
|
jsonResult = json_1.jsonStringifyLargeObject(sarifTypedResult);
|
|
}
|
|
if (options.sarif || options.json) {
|
|
readableResult = json_1.jsonStringifyLargeObject(sarifTypedResult);
|
|
}
|
|
if (numOfIssues > 0) {
|
|
throwIssuesError({ readableResult, sarifResult, jsonResult });
|
|
}
|
|
return sarifResult ? { readableResult, sarifResult } : { readableResult };
|
|
}
|
|
catch (error) {
|
|
let err;
|
|
if (isCodeClientError(error)) {
|
|
const isUnauthorized = isUnauthorizedError(error)
|
|
? 'Unauthorized: '
|
|
: '';
|
|
err = new errors_1.FailedToRunTestError(`${isUnauthorized}Failed to run 'code test'`, error.statusCode);
|
|
}
|
|
else if (error instanceof Error) {
|
|
err = error;
|
|
}
|
|
else if (isUnauthorizedError(error)) {
|
|
err = new errors_1.FailedToRunTestError(error.message, error.code);
|
|
}
|
|
else {
|
|
err = new Error(error);
|
|
}
|
|
debug(chalk_1.default.bold.red(`requestId: ${requestId} statusCode:${error.code ||
|
|
error.statusCode}, message: ${error.statusText || error.message}`));
|
|
throw err;
|
|
}
|
|
},
|
|
};
|
|
function isCodeClientError(error) {
|
|
return (error.hasOwnProperty('statusCode') &&
|
|
error.hasOwnProperty('statusText') &&
|
|
error.hasOwnProperty('apiName'));
|
|
}
|
|
function isUnauthorizedError(error) {
|
|
return (error.statusCode === 401 ||
|
|
error.statusCode === 403 ||
|
|
error.code === 403 ||
|
|
error.code === 401);
|
|
}
|
|
function throwIssuesError(args) {
|
|
const err = new Error(args.readableResult);
|
|
err.code = 'VULNS';
|
|
if (args.sarifResult !== undefined) {
|
|
err.sarifStringifiedResults = args.sarifResult;
|
|
}
|
|
if (args.jsonResult !== undefined) {
|
|
err.jsonStringifiedResults = args.jsonResult;
|
|
}
|
|
throw err;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 24930:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getSastSettings = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const checks_1 = __webpack_require__(15976);
|
|
const errors_1 = __webpack_require__(55191);
|
|
async function getSastSettings(options) {
|
|
const org = options.org || config_1.default.org;
|
|
// This is an unexpected path, code plugin executed for non-code command.
|
|
if (!options.code) {
|
|
throw new errors_1.FeatureNotSupportedForOrgError(org);
|
|
}
|
|
const sastSettingsResponse = await checks_1.getSastSettingsForOrg(org);
|
|
if ((sastSettingsResponse === null || sastSettingsResponse === void 0 ? void 0 : sastSettingsResponse.code) === 401 ||
|
|
(sastSettingsResponse === null || sastSettingsResponse === void 0 ? void 0 : sastSettingsResponse.code) === 403) {
|
|
throw errors_1.AuthFailedError(sastSettingsResponse.error, sastSettingsResponse.code);
|
|
}
|
|
if ((sastSettingsResponse === null || sastSettingsResponse === void 0 ? void 0 : sastSettingsResponse.code) === 404) {
|
|
throw new errors_1.NotFoundError(sastSettingsResponse === null || sastSettingsResponse === void 0 ? void 0 : sastSettingsResponse.userMessage);
|
|
}
|
|
if (!sastSettingsResponse.sastEnabled) {
|
|
throw new errors_1.FeatureNotSupportedForOrgError(org, 'Snyk Code', 'enable in Settings > Snyk Code');
|
|
}
|
|
const trackUsageResponse = await checks_1.trackUsage(org);
|
|
if (trackUsageResponse.code === 429) {
|
|
throw new errors_1.FailedToRunTestError(trackUsageResponse.userMessage, trackUsageResponse.code);
|
|
}
|
|
return sastSettingsResponse;
|
|
}
|
|
exports.getSastSettings = getSastSettings;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 33113:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var testEmitter_1 = __webpack_require__(83901);
|
|
Object.defineProperty(exports, "analysisProgressUpdate", ({ enumerable: true, get: function () { return testEmitter_1.analysisProgressUpdate; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 83901:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.analysisProgressUpdate = void 0;
|
|
const code_client_1 = __webpack_require__(95951);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const debugLib = __webpack_require__(15158);
|
|
function analysisProgressUpdate() {
|
|
let currentMessage = '';
|
|
const showSpinner = (message) => {
|
|
if (currentMessage === message)
|
|
return;
|
|
spinner_1.spinner.clear(currentMessage)();
|
|
currentMessage = message;
|
|
return spinner_1.spinner(message);
|
|
};
|
|
code_client_1.emitter.on('supportedFilesLoaded', () => showSpinner(`Supported extensions loaded`));
|
|
code_client_1.emitter.on('scanFilesProgress', (processed) => showSpinner(`Scanning files: ${Math.round(processed / 100)}00`));
|
|
code_client_1.emitter.on('createBundleProgress', (processed, total) => showSpinner(`Batching file upload: ${processed} / ${total}`));
|
|
code_client_1.emitter.on('uploadBundleProgress', (processed, total) => showSpinner(`Upload progress: ${processed} / ${total}`));
|
|
code_client_1.emitter.on('analyseProgress', (data) => showSpinner(`${data.status}: ${Math.round(data.progress * 100)}%`));
|
|
code_client_1.emitter.on('sendError', (error) => {
|
|
throw error;
|
|
});
|
|
code_client_1.emitter.on('apiRequestLog', (data) => {
|
|
const debug = debugLib('snyk-code');
|
|
debug('---> API request log ', data);
|
|
});
|
|
}
|
|
exports.analysisProgressUpdate = analysisProgressUpdate;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 8820:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.findAndLoadPolicy = void 0;
|
|
const snykPolicyLib = __webpack_require__(70535);
|
|
const debugModule = __webpack_require__(15158);
|
|
const _1 = __webpack_require__(32615);
|
|
const analytics = __webpack_require__(82744);
|
|
const debug = debugModule('snyk');
|
|
async function findAndLoadPolicy(root, scanType, options, pkg, scannedProjectFolder) {
|
|
const isDocker = scanType === 'docker';
|
|
const isNodeProject = ['npm', 'yarn'].includes(scanType);
|
|
// monitor
|
|
let policyLocations = [
|
|
options['policy-path'] || scannedProjectFolder || root,
|
|
];
|
|
if (isDocker) {
|
|
policyLocations = policyLocations.filter((loc) => loc !== root);
|
|
}
|
|
else if (isNodeProject) {
|
|
// TODO: pluckPolicies expects a package.json object to
|
|
// find and apply policies in node_modules
|
|
policyLocations = policyLocations.concat(_1.pluckPolicies(pkg));
|
|
}
|
|
debug('Potential policy locations found:', policyLocations);
|
|
analytics.add('policies', policyLocations.length);
|
|
analytics.add('policyLocations', policyLocations);
|
|
if (policyLocations.length === 0) {
|
|
return;
|
|
}
|
|
let policy;
|
|
try {
|
|
policy = await snykPolicyLib.load(policyLocations, options);
|
|
}
|
|
catch (err) {
|
|
// note: inline catch, to handle error from .load
|
|
// if the .snyk file wasn't found, it is fine
|
|
if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
|
|
throw err;
|
|
}
|
|
}
|
|
return policy;
|
|
}
|
|
exports.findAndLoadPolicy = findAndLoadPolicy;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 32615:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
var pluck_policies_1 = __webpack_require__(68247);
|
|
Object.defineProperty(exports, "pluckPolicies", ({ enumerable: true, get: function () { return pluck_policies_1.pluckPolicies; } }));
|
|
var find_and_load_policy_1 = __webpack_require__(8820);
|
|
Object.defineProperty(exports, "findAndLoadPolicy", ({ enumerable: true, get: function () { return find_and_load_policy_1.findAndLoadPolicy; } }));
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 68247:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.pluckPolicies = void 0;
|
|
const flatten = __webpack_require__(5800);
|
|
function pluckPolicies(pkg) {
|
|
if (!pkg) {
|
|
return [];
|
|
}
|
|
if (pkg.snyk) {
|
|
return pkg.snyk;
|
|
}
|
|
if (!pkg.dependencies) {
|
|
return [];
|
|
}
|
|
return flatten(Object.keys(pkg.dependencies)
|
|
.map((name) => pluckPolicies(pkg.dependencies[name]))
|
|
.filter(Boolean));
|
|
}
|
|
exports.pluckPolicies = pluckPolicies;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 74434:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.handleProcessingStatus = exports.extractResolutionMetaFromScanResult = exports.delayNextStep = void 0;
|
|
const common_1 = __webpack_require__(70527);
|
|
const errors_1 = __webpack_require__(55191);
|
|
async function delayNextStep(attemptsCount, maxAttempts, pollInterval) {
|
|
attemptsCount++;
|
|
checkPollingAttempts(maxAttempts)(attemptsCount);
|
|
await common_1.sleep(pollInterval);
|
|
}
|
|
exports.delayNextStep = delayNextStep;
|
|
function checkPollingAttempts(maxAttempts) {
|
|
return (attemptsCount) => {
|
|
if (attemptsCount > maxAttempts) {
|
|
throw new Error('Exceeded Polling maxAttempts');
|
|
}
|
|
};
|
|
}
|
|
function extractResolutionMetaFromScanResult({ name, target, identity, policy, targetReference, }) {
|
|
return {
|
|
name,
|
|
target,
|
|
identity,
|
|
policy,
|
|
targetReference,
|
|
};
|
|
}
|
|
exports.extractResolutionMetaFromScanResult = extractResolutionMetaFromScanResult;
|
|
function handleProcessingStatus(response) {
|
|
if ((response === null || response === void 0 ? void 0 : response.status) === 'CANCELLED' || (response === null || response === void 0 ? void 0 : response.status) === 'ERROR') {
|
|
throw new errors_1.FailedToRunTestError('Failed to process the project. Please run the command again with the `-d` flag and contact support@snyk.io with the contents of the output');
|
|
}
|
|
}
|
|
exports.handleProcessingStatus = handleProcessingStatus;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 59354:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.pollingMonitorWithTokenUntilDone = exports.requestMonitorPollingToken = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const promise_1 = __webpack_require__(90430);
|
|
const common_1 = __webpack_require__(53110);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const common_2 = __webpack_require__(74434);
|
|
const monitor_1 = __webpack_require__(3708);
|
|
async function requestMonitorPollingToken(options, isAsync, scanResult) {
|
|
if ((scanResult === null || scanResult === void 0 ? void 0 : scanResult.target) && scanResult.target['remoteUrl'] === '') {
|
|
scanResult.target['remoteUrl'] = scanResult.name;
|
|
}
|
|
const payload = {
|
|
method: 'PUT',
|
|
url: `${config_1.default.API}/monitor-dependencies`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
body: {
|
|
isAsync,
|
|
scanResult,
|
|
method: 'cli',
|
|
},
|
|
qs: { ...common_1.assembleQueryString(options) },
|
|
};
|
|
return await promise_1.makeRequest(payload);
|
|
}
|
|
exports.requestMonitorPollingToken = requestMonitorPollingToken;
|
|
async function pollingMonitorWithTokenUntilDone(token, isAsync, options, pollInterval, attemptsCount, maxAttempts = Infinity, resolutionMeta, contributors = []) {
|
|
const payload = {
|
|
method: 'PUT',
|
|
url: `${config_1.default.API}/monitor-dependencies/${token}`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
qs: { ...common_1.assembleQueryString(options) },
|
|
body: {
|
|
isAsync,
|
|
resolutionMeta,
|
|
contributors,
|
|
method: 'cli',
|
|
tags: monitor_1.generateTags(options),
|
|
attributes: monitor_1.generateProjectAttributes(options),
|
|
projectName: (resolutionMeta === null || resolutionMeta === void 0 ? void 0 : resolutionMeta.name) || options['project-name'] || config_1.default.PROJECT_NAME,
|
|
},
|
|
};
|
|
const response = await promise_1.makeRequest(payload);
|
|
common_2.handleProcessingStatus(response);
|
|
if (response.ok && response.isMonitored) {
|
|
return response;
|
|
}
|
|
await common_2.delayNextStep(attemptsCount, maxAttempts, pollInterval);
|
|
return await pollingMonitorWithTokenUntilDone(token, isAsync, options, pollInterval, attemptsCount, maxAttempts, resolutionMeta, contributors);
|
|
}
|
|
exports.pollingMonitorWithTokenUntilDone = pollingMonitorWithTokenUntilDone;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 77584:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.pollingTestWithTokenUntilDone = exports.requestTestPollingToken = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const promise_1 = __webpack_require__(90430);
|
|
const common_1 = __webpack_require__(53110);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const common_2 = __webpack_require__(74434);
|
|
async function requestTestPollingToken(options, isAsync, scanResult) {
|
|
const payload = {
|
|
method: 'POST',
|
|
url: `${config_1.default.API}/test-dependencies`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
body: {
|
|
isAsync,
|
|
scanResult,
|
|
},
|
|
qs: common_1.assembleQueryString(options),
|
|
};
|
|
return await promise_1.makeRequest(payload);
|
|
}
|
|
exports.requestTestPollingToken = requestTestPollingToken;
|
|
async function pollingTestWithTokenUntilDone(token, type, options, pollInterval, attemptsCount, maxAttempts = Infinity) {
|
|
const payload = {
|
|
method: 'GET',
|
|
url: `${config_1.default.API}/test-dependencies/${token}`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
qs: { ...common_1.assembleQueryString(options), type },
|
|
};
|
|
const response = await promise_1.makeRequest(payload);
|
|
common_2.handleProcessingStatus(response);
|
|
if (response.result) {
|
|
const { issues, issuesData, depGraphData, depsFilePaths, fileSignaturesDetails, vulnerabilities, path, dependencyCount, packageManager, } = response.result;
|
|
return {
|
|
issues,
|
|
issuesData,
|
|
depGraphData,
|
|
depsFilePaths,
|
|
fileSignaturesDetails,
|
|
vulnerabilities,
|
|
path,
|
|
dependencyCount,
|
|
packageManager,
|
|
};
|
|
}
|
|
await common_2.delayNextStep(attemptsCount, maxAttempts, pollInterval);
|
|
return await pollingTestWithTokenUntilDone(token, type, options, pollInterval, attemptsCount, maxAttempts);
|
|
}
|
|
exports.pollingTestWithTokenUntilDone = pollingTestWithTokenUntilDone;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 79792:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.maybePrintDepTree = exports.maybePrintDepGraph = void 0;
|
|
const config_1 = __webpack_require__(25425);
|
|
const depGraphLib = __webpack_require__(71479);
|
|
const utils_1 = __webpack_require__(61721);
|
|
const json_1 = __webpack_require__(27019);
|
|
async function maybePrintDepGraph(options, depGraph) {
|
|
// TODO @boost: remove this logic once we get a valid depGraph print format
|
|
const graphPathsCount = utils_1.countPathsToGraphRoot(depGraph);
|
|
const hasTooManyPaths = graphPathsCount > config_1.default.PRUNE_DEPS_THRESHOLD;
|
|
if (!hasTooManyPaths) {
|
|
const depTree = (await depGraphLib.legacy.graphToDepTree(depGraph, depGraph.pkgManager.name));
|
|
maybePrintDepTree(options, depTree);
|
|
}
|
|
else {
|
|
if (options['print-deps']) {
|
|
if (options.json) {
|
|
console.warn('--print-deps --json option not yet supported for large projects. Displaying graph json output instead');
|
|
// TODO @boost: add as output graphviz 'dot' file to visualize?
|
|
console.log(json_1.jsonStringifyLargeObject(depGraph.toJSON()));
|
|
}
|
|
else {
|
|
console.warn('--print-deps option not yet supported for large projects. Try with --json.');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.maybePrintDepGraph = maybePrintDepGraph;
|
|
// This option is still experimental and might be deprecated.
|
|
// It might be a better idea to convert it to a command (i.e. do not perform test/monitor).
|
|
function maybePrintDepTree(options, rootPackage) {
|
|
if (options['print-deps']) {
|
|
if (options.json) {
|
|
// Will produce 2 JSON outputs, one for the deps, one for the vuln scan.
|
|
console.log(json_1.jsonStringifyLargeObject(rootPackage));
|
|
}
|
|
else {
|
|
printDepsForTree({ [rootPackage.name]: rootPackage });
|
|
}
|
|
}
|
|
}
|
|
exports.maybePrintDepTree = maybePrintDepTree;
|
|
function printDepsForTree(depDict, prefix = '') {
|
|
let counter = 0;
|
|
const keys = Object.keys(depDict);
|
|
for (const name of keys) {
|
|
const dep = depDict[name];
|
|
let branch = '├─ ';
|
|
const last = counter === keys.length - 1;
|
|
if (last) {
|
|
branch = '└─ ';
|
|
}
|
|
console.log(prefix +
|
|
(prefix ? branch : '') +
|
|
dep.name +
|
|
' @ ' +
|
|
(dep.version ? dep.version : ''));
|
|
if (dep.dependencies) {
|
|
printDepsForTree(dep.dependencies, prefix + (last ? ' ' : '│ '));
|
|
}
|
|
counter++;
|
|
}
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 3594:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getInfo = void 0;
|
|
const gitTargetBuilder = __webpack_require__(24850);
|
|
const containerTargetBuilder = __webpack_require__(57493);
|
|
const invalid_remote_url_error_1 = __webpack_require__(86033);
|
|
const TARGET_BUILDERS = [containerTargetBuilder, gitTargetBuilder];
|
|
async function getInfo(scannedProject, options, packageInfo) {
|
|
const isFromContainer = options.docker || options.isDocker || false;
|
|
for (const builder of TARGET_BUILDERS) {
|
|
const target = await builder.getInfo({
|
|
isFromContainer,
|
|
scannedProject,
|
|
packageInfo,
|
|
});
|
|
if (target) {
|
|
const remoteUrl = options['remote-repo-url'];
|
|
if (!remoteUrl) {
|
|
return target;
|
|
}
|
|
if (typeof remoteUrl !== 'string') {
|
|
throw new invalid_remote_url_error_1.InvalidRemoteUrlError();
|
|
}
|
|
return { ...target, remoteUrl };
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
exports.getInfo = getInfo;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 57493:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getInfo = void 0;
|
|
async function getInfo({ isFromContainer, scannedProject, packageInfo, }) {
|
|
var _a;
|
|
// safety check
|
|
if (!isFromContainer) {
|
|
return null;
|
|
}
|
|
const imageNameOnProjectMeta = scannedProject.meta && scannedProject.meta.imageName;
|
|
return {
|
|
image: imageNameOnProjectMeta || ((_a = packageInfo) === null || _a === void 0 ? void 0 : _a.image) || (packageInfo === null || packageInfo === void 0 ? void 0 : packageInfo.name),
|
|
};
|
|
}
|
|
exports.getInfo = getInfo;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 24850:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.getInfo = void 0;
|
|
const url = __webpack_require__(78835);
|
|
const subProcess = __webpack_require__(66487);
|
|
// for scp-like syntax [user@]server:project.git
|
|
const originRegex = /(.+@)?(.+):(.+$)/;
|
|
async function getInfo({ isFromContainer, cwd, }) {
|
|
// safety check
|
|
if (isFromContainer) {
|
|
return null;
|
|
}
|
|
const target = {};
|
|
try {
|
|
const origin = (await subProcess.execute('git', ['remote', 'get-url', 'origin'], { cwd })).trim();
|
|
if (origin) {
|
|
const { protocol, host, pathname = '' } = url.parse(origin);
|
|
// Not handling git:// as it has no connection options
|
|
if (host && protocol && ['ssh:', 'http:', 'https:'].includes(protocol)) {
|
|
// same format for parseable URLs
|
|
target.remoteUrl = `http://${host}${pathname}`;
|
|
}
|
|
else {
|
|
const originRes = originRegex.exec(origin);
|
|
if (originRes && originRes[2] && originRes[3]) {
|
|
target.remoteUrl = `http://${originRes[2]}/${originRes[3]}`;
|
|
}
|
|
else {
|
|
// else keep the original
|
|
target.remoteUrl = origin;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (err) {
|
|
// Swallowing exception since we don't want to break the monitor if there is a problem
|
|
// executing git commands.
|
|
}
|
|
try {
|
|
target.branch = (await subProcess.execute('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
cwd,
|
|
})).trim();
|
|
}
|
|
catch (err) {
|
|
// Swallowing exception since we don't want to break the monitor if there is a problem
|
|
// executing git commands.
|
|
}
|
|
return target;
|
|
}
|
|
exports.getInfo = getInfo;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 39409:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.isGitTarget = void 0;
|
|
function isGitTarget(target) {
|
|
return target && (target.branch || target.remoteUrl);
|
|
}
|
|
exports.isGitTarget = isGitTarget;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 87725:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.pruneGraph = void 0;
|
|
const _debug = __webpack_require__(15158);
|
|
const dep_graph_1 = __webpack_require__(71479);
|
|
const config_1 = __webpack_require__(25425);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const analytics = __webpack_require__(82744);
|
|
const utils_1 = __webpack_require__(61721);
|
|
const debug = _debug('snyk:prune');
|
|
const { depTreeToGraph, graphToDepTree } = dep_graph_1.legacy;
|
|
async function pruneGraph(depGraph, packageManager, pruneIsRequired = false) {
|
|
const prePrunePathsCount = utils_1.countPathsToGraphRoot(depGraph);
|
|
const isDenseGraph = prePrunePathsCount > config_1.default.PRUNE_DEPS_THRESHOLD;
|
|
debug('rootPkg', depGraph.rootPkg);
|
|
debug('prePrunePathsCount: ' + prePrunePathsCount);
|
|
debug('isDenseGraph', isDenseGraph);
|
|
analytics.add('prePrunedPathsCount', prePrunePathsCount);
|
|
if (isDenseGraph || pruneIsRequired) {
|
|
debug('Trying to prune the graph');
|
|
const pruneStartTime = Date.now();
|
|
const prunedTree = (await graphToDepTree(depGraph, packageManager, {
|
|
deduplicateWithinTopLevelDeps: true,
|
|
}));
|
|
const graphToTreeEndTime = Date.now();
|
|
analytics.add('prune.graphToTreeDuration', graphToTreeEndTime - pruneStartTime);
|
|
const prunedGraph = await depTreeToGraph(prunedTree, packageManager);
|
|
analytics.add('prune.treeToGraphDuration', Date.now() - graphToTreeEndTime);
|
|
const postPrunePathsCount = utils_1.countPathsToGraphRoot(prunedGraph);
|
|
analytics.add('postPrunedPathsCount', postPrunePathsCount);
|
|
debug('postPrunePathsCount' + postPrunePathsCount);
|
|
if (postPrunePathsCount > config_1.default.MAX_PATH_COUNT) {
|
|
debug('Too many paths to process the project');
|
|
//TODO replace the throw below with TooManyPaths we do not calculate vuln paths there
|
|
throw new errors_1.TooManyVulnPaths();
|
|
}
|
|
return prunedGraph;
|
|
}
|
|
return depGraph;
|
|
}
|
|
exports.pruneGraph = pruneGraph;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 86978:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.reachableVulnsRemovalMessage = exports.validatePayload = exports.serializeCallGraphWithMetrics = void 0;
|
|
const graphlib = __webpack_require__(39322);
|
|
const package_managers_1 = __webpack_require__(53847);
|
|
const feature_flags_1 = __webpack_require__(63011);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const is_multi_project_scan_1 = __webpack_require__(62435);
|
|
const theme = __webpack_require__(86988);
|
|
const featureFlag = 'reachableVulns';
|
|
function serializeCallGraphWithMetrics(callGraph) {
|
|
return {
|
|
callGraph: graphlib.json.write(callGraph),
|
|
nodeCount: callGraph.nodeCount(),
|
|
edgeCount: callGraph.edgeCount(),
|
|
};
|
|
}
|
|
exports.serializeCallGraphWithMetrics = serializeCallGraphWithMetrics;
|
|
async function validatePayload(org, options, packageManager) {
|
|
if (packageManager &&
|
|
!is_multi_project_scan_1.isMultiProjectScan(options) &&
|
|
!package_managers_1.REACHABLE_VULNS_SUPPORTED_PACKAGE_MANAGERS.includes(packageManager)) {
|
|
throw new errors_1.FeatureNotSupportedByPackageManagerError('Reachable vulns', packageManager, `For a list of supported package managers go to https://support.snyk.io/hc/en-us/articles/360010554837-Reachable-Vulnerabilities`);
|
|
}
|
|
const reachableVulnsSupportedRes = await feature_flags_1.isFeatureFlagSupportedForOrg(featureFlag, org);
|
|
if (reachableVulnsSupportedRes.code === 401) {
|
|
throw errors_1.AuthFailedError(reachableVulnsSupportedRes.error, reachableVulnsSupportedRes.code);
|
|
}
|
|
if (reachableVulnsSupportedRes.userMessage) {
|
|
throw new errors_1.UnsupportedFeatureFlagError(featureFlag, reachableVulnsSupportedRes.userMessage);
|
|
}
|
|
return true;
|
|
}
|
|
exports.validatePayload = validatePayload;
|
|
exports.reachableVulnsRemovalMessage = theme.color.status.warn(`${theme.icon.WARNING} Starting on 15 August 2022, the reachable (--reachable) flag will no longer be supported and will have no effect. We are currently developing new and improved capabilities for prioritizing vulnerabilities. Please follow https://updates.snyk.io for news and updates. We are sorry for the temporary inconvenience.\n`);
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 90430:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.makeRequestRest = exports.makeRequest = void 0;
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const request = __webpack_require__(52050);
|
|
async function makeRequest(payload) {
|
|
return new Promise((resolve, reject) => {
|
|
request.makeRequest(payload, (error, res, body) => {
|
|
if (error) {
|
|
return reject(error);
|
|
}
|
|
if (res.statusCode !== 200) {
|
|
return reject({
|
|
code: res.statusCode,
|
|
message: body === null || body === void 0 ? void 0 : body.message,
|
|
});
|
|
}
|
|
resolve(body);
|
|
});
|
|
});
|
|
}
|
|
exports.makeRequest = makeRequest;
|
|
/**
|
|
* All rest request will essentially be the same and are JSON by default
|
|
* Thus if no headers provided default headers are used
|
|
* @param {any} payload for the request
|
|
* @returns
|
|
*/
|
|
async function makeRequestRest(payload) {
|
|
return new Promise((resolve, reject) => {
|
|
var _a;
|
|
payload.headers = (_a = payload.headers) !== null && _a !== void 0 ? _a : {
|
|
'Content-Type': 'application/vnd.api+json',
|
|
authorization: api_token_1.getAuthHeader(),
|
|
};
|
|
payload.json = true;
|
|
request.makeRequest(payload, (error, res, body) => {
|
|
if (error) {
|
|
return reject(error);
|
|
}
|
|
if (res.statusCode >= 400) {
|
|
return reject({
|
|
code: res.statusCode,
|
|
body: JSON.parse(body),
|
|
});
|
|
}
|
|
resolve(JSON.parse(body));
|
|
});
|
|
});
|
|
}
|
|
exports.makeRequestRest = makeRequestRest;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 80627:
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.rightPadWithSpaces = void 0;
|
|
function rightPadWithSpaces(s, padding) {
|
|
const padLength = padding - s.length;
|
|
if (padLength <= 0) {
|
|
return s;
|
|
}
|
|
return s + ' '.repeat(padLength);
|
|
}
|
|
exports.rightPadWithSpaces = rightPadWithSpaces;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 38080:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.assembleEcosystemPayloads = void 0;
|
|
const path = __webpack_require__(85622);
|
|
const config_1 = __webpack_require__(25425);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const ecosystems_1 = __webpack_require__(5168);
|
|
const common_1 = __webpack_require__(53110);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const policy_1 = __webpack_require__(4669);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const errors_1 = __webpack_require__(55191);
|
|
async function assembleEcosystemPayloads(ecosystem, options) {
|
|
// For --all-projects packageManager is yet undefined here. Use 'all'
|
|
let analysisTypeText = 'all dependencies for ';
|
|
if (options.docker) {
|
|
analysisTypeText = 'container dependencies for ';
|
|
}
|
|
else if (options.packageManager) {
|
|
analysisTypeText = options.packageManager + ' dependencies for ';
|
|
}
|
|
const spinnerLbl = 'Analyzing ' +
|
|
analysisTypeText +
|
|
(path.relative('.', path.join(options.path, options.file || '')) ||
|
|
path.relative('..', '.') + ' project dir');
|
|
spinner_1.spinner.clear(spinnerLbl)();
|
|
if (!options.quiet) {
|
|
await spinner_1.spinner(spinnerLbl);
|
|
}
|
|
try {
|
|
const plugin = ecosystems_1.getPlugin(ecosystem);
|
|
const pluginResponse = await plugin.scan(options);
|
|
const payloads = [];
|
|
// TODO: This is a temporary workaround until the plugins themselves can read policy files and set names!
|
|
for (const scanResult of pluginResponse.scanResults) {
|
|
// WARNING! This mutates the payload. Policy logic should be in the plugin.
|
|
const policy = await policy_1.findAndLoadPolicyForScanResult(scanResult, options);
|
|
if (policy !== undefined) {
|
|
scanResult.policy = policy.toString();
|
|
}
|
|
// WARNING! This mutates the payload. The project name logic should be handled in the plugin.
|
|
scanResult.name =
|
|
options['project-name'] || config_1.default.PROJECT_NAME || scanResult.name;
|
|
payloads.push({
|
|
method: 'POST',
|
|
url: `${config_1.default.API}${options.testDepGraphDockerEndpoint ||
|
|
'/test-dependencies'}`,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
body: {
|
|
scanResult,
|
|
},
|
|
qs: common_1.assembleQueryString(options),
|
|
});
|
|
}
|
|
return payloads;
|
|
}
|
|
catch (error) {
|
|
if (ecosystem === 'docker' && error.message === 'authentication required') {
|
|
throw new errors_1.DockerImageNotFoundError(options.path);
|
|
}
|
|
if (ecosystem === 'docker' && error.message === 'invalid image format') {
|
|
throw new errors_1.DockerImageNotFoundError(options.path);
|
|
}
|
|
throw error;
|
|
}
|
|
finally {
|
|
spinner_1.spinner.clear(spinnerLbl)();
|
|
}
|
|
}
|
|
exports.assembleEcosystemPayloads = assembleEcosystemPayloads;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 34013:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.convertTestDepGraphResultToLegacy = exports.REACHABILITY = exports.SEVERITY = void 0;
|
|
const values = __webpack_require__(17720);
|
|
const common_1 = __webpack_require__(53110);
|
|
var SEVERITY;
|
|
(function (SEVERITY) {
|
|
SEVERITY["LOW"] = "low";
|
|
SEVERITY["MEDIUM"] = "medium";
|
|
SEVERITY["HIGH"] = "high";
|
|
SEVERITY["CRITICAL"] = "critical";
|
|
})(SEVERITY = exports.SEVERITY || (exports.SEVERITY = {}));
|
|
var REACHABILITY;
|
|
(function (REACHABILITY) {
|
|
REACHABILITY["FUNCTION"] = "function";
|
|
REACHABILITY["PACKAGE"] = "package";
|
|
REACHABILITY["NOT_REACHABLE"] = "not-reachable";
|
|
REACHABILITY["NO_INFO"] = "no-info";
|
|
})(REACHABILITY = exports.REACHABILITY || (exports.REACHABILITY = {}));
|
|
function convertTestDepGraphResultToLegacy(res, depGraph, packageManager, options) {
|
|
const result = res.result;
|
|
const upgradePathsMap = new Map();
|
|
for (const pkgInfo of values(result.affectedPkgs)) {
|
|
for (const pkgIssue of values(pkgInfo.issues)) {
|
|
if (pkgIssue.fixInfo && pkgIssue.fixInfo.upgradePaths) {
|
|
for (const upgradePath of pkgIssue.fixInfo.upgradePaths) {
|
|
const legacyFromPath = pkgPathToLegacyPath(upgradePath.path);
|
|
const vulnPathString = getVulnPathString(pkgIssue.issueId, legacyFromPath);
|
|
upgradePathsMap[vulnPathString] = toLegacyUpgradePath(upgradePath.path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// generate the legacy vulns array (vuln-data + metada per vulnerable path).
|
|
// use the upgradePathsMap to find available upgrade-paths
|
|
const vulns = [];
|
|
for (const pkgInfo of values(result.affectedPkgs)) {
|
|
for (const vulnPkgPath of depGraph.pkgPathsToRoot(pkgInfo.pkg)) {
|
|
const legacyFromPath = pkgPathToLegacyPath(vulnPkgPath.reverse());
|
|
for (const pkgIssue of values(pkgInfo.issues)) {
|
|
const vulnPathString = getVulnPathString(pkgIssue.issueId, legacyFromPath);
|
|
const upgradePath = upgradePathsMap[vulnPathString] || [];
|
|
// TODO: we need the full issue-data for every path only for the --json output,
|
|
// consider picking only the required fields,
|
|
// and append the full data only for --json, to minimize chance of out-of-memory
|
|
const annotatedIssue = Object.assign({}, result.issuesData[pkgIssue.issueId], {
|
|
from: legacyFromPath,
|
|
upgradePath,
|
|
isUpgradable: !!upgradePath[0] || !!upgradePath[1],
|
|
isPatchable: pkgIssue.fixInfo.isPatchable,
|
|
name: pkgInfo.pkg.name,
|
|
version: pkgInfo.pkg.version,
|
|
nearestFixedInVersion: pkgIssue.fixInfo.nearestFixedInVersion,
|
|
}); // TODO(kyegupov): get rid of type assertion
|
|
vulns.push(annotatedIssue);
|
|
}
|
|
}
|
|
}
|
|
const dockerRes = result.docker;
|
|
if (dockerRes && dockerRes.binariesVulns) {
|
|
const binariesVulns = dockerRes.binariesVulns;
|
|
for (const pkgInfo of values(binariesVulns.affectedPkgs)) {
|
|
for (const pkgIssue of values(pkgInfo.issues)) {
|
|
const pkgAndVersion = (pkgInfo.pkg.name +
|
|
'@' +
|
|
pkgInfo.pkg.version);
|
|
const annotatedIssue = Object.assign({}, binariesVulns.issuesData[pkgIssue.issueId], {
|
|
from: ['Upstream', pkgAndVersion],
|
|
upgradePath: [],
|
|
isUpgradable: false,
|
|
isPatchable: false,
|
|
name: pkgInfo.pkg.name,
|
|
version: pkgInfo.pkg.version,
|
|
nearestFixedInVersion: pkgIssue.fixInfo.nearestFixedInVersion,
|
|
}); // TODO(kyegupov): get rid of forced type assertion
|
|
vulns.push(annotatedIssue);
|
|
}
|
|
}
|
|
}
|
|
const meta = res.meta || {};
|
|
const severityThreshold = options.severityThreshold === SEVERITY.LOW
|
|
? undefined
|
|
: options.severityThreshold;
|
|
const legacyRes = {
|
|
vulnerabilities: vulns,
|
|
ok: vulns.length === 0,
|
|
dependencyCount: depGraph.getPkgs().length - 1,
|
|
org: meta.org,
|
|
policy: meta.policy,
|
|
isPrivate: !meta.isPublic,
|
|
licensesPolicy: meta.licensesPolicy || null,
|
|
packageManager,
|
|
projectId: meta.projectId,
|
|
ignoreSettings: meta.ignoreSettings || null,
|
|
docker: result.docker,
|
|
summary: getSummary(vulns, severityThreshold),
|
|
severityThreshold,
|
|
remediation: result.remediation,
|
|
};
|
|
return legacyRes;
|
|
}
|
|
exports.convertTestDepGraphResultToLegacy = convertTestDepGraphResultToLegacy;
|
|
function getVulnPathString(issueId, vulnPath) {
|
|
return issueId + '|' + JSON.stringify(vulnPath);
|
|
}
|
|
function pkgPathToLegacyPath(pkgPath) {
|
|
return pkgPath.map(toLegacyPkgId);
|
|
}
|
|
function toLegacyUpgradePath(upgradePath) {
|
|
return upgradePath
|
|
.filter((item) => !item.isDropped)
|
|
.map((item) => {
|
|
if (!item.newVersion) {
|
|
return false;
|
|
}
|
|
return `${item.name}@${item.newVersion}`;
|
|
});
|
|
}
|
|
function toLegacyPkgId(pkg) {
|
|
return `${pkg.name}@${pkg.version || '*'}`;
|
|
}
|
|
function getSummary(vulns, severityThreshold) {
|
|
const count = vulns.length;
|
|
let countText = '' + count;
|
|
const severityFilters = [];
|
|
const severitiesArray = common_1.SEVERITIES.map((s) => s.verboseName);
|
|
if (severityThreshold) {
|
|
severitiesArray
|
|
.slice(severitiesArray.indexOf(severityThreshold))
|
|
.forEach((sev) => {
|
|
severityFilters.push(sev);
|
|
});
|
|
}
|
|
if (!count) {
|
|
if (severityFilters.length) {
|
|
return `No ${severityFilters.join(' or ')} severity vulnerabilities`;
|
|
}
|
|
return 'No known vulnerabilities';
|
|
}
|
|
if (severityFilters.length) {
|
|
countText += ' ' + severityFilters.join(' or ') + ' severity';
|
|
}
|
|
return `${countText} vulnerable dependency ${pl('path', count)}`;
|
|
}
|
|
function pl(word, count) {
|
|
const ext = {
|
|
y: 'ies',
|
|
default: 's',
|
|
};
|
|
const last = word.split('').pop();
|
|
if (count > 1) {
|
|
return word.slice(0, -1) + (ext[last] || last + ext.default);
|
|
}
|
|
return word;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 7964:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.runTest = void 0;
|
|
const fs = __webpack_require__(35747);
|
|
const get = __webpack_require__(29208);
|
|
const path = __webpack_require__(85622);
|
|
const pathUtil = __webpack_require__(85622);
|
|
const debugModule = __webpack_require__(15158);
|
|
const chalk_1 = __webpack_require__(32589);
|
|
const theme_1 = __webpack_require__(86988);
|
|
const snyk_module_1 = __webpack_require__(60390);
|
|
const depGraphLib = __webpack_require__(71479);
|
|
const theme = __webpack_require__(86988);
|
|
const legacy_1 = __webpack_require__(34013);
|
|
const errors_1 = __webpack_require__(55191);
|
|
const snyk = __webpack_require__(9146);
|
|
const is_ci_1 = __webpack_require__(10090);
|
|
const common = __webpack_require__(53110);
|
|
const config_1 = __webpack_require__(25425);
|
|
const analytics = __webpack_require__(82744);
|
|
const print_deps_1 = __webpack_require__(79792);
|
|
const projectMetadata = __webpack_require__(3594);
|
|
const prune_1 = __webpack_require__(87725);
|
|
const get_deps_from_plugin_1 = __webpack_require__(4842);
|
|
const extract_package_manager_1 = __webpack_require__(22805);
|
|
const get_extra_project_count_1 = __webpack_require__(34355);
|
|
const reachable_vulns_1 = __webpack_require__(86978);
|
|
const options_validator_1 = __webpack_require__(1570);
|
|
const policy_1 = __webpack_require__(32615);
|
|
const alerts = __webpack_require__(21696);
|
|
const error_format_1 = __webpack_require__(59369);
|
|
const api_token_1 = __webpack_require__(95181);
|
|
const ecosystems_1 = __webpack_require__(5168);
|
|
const assemble_payloads_1 = __webpack_require__(38080);
|
|
const request_1 = __webpack_require__(52050);
|
|
const spinner_1 = __webpack_require__(86766);
|
|
const debug = debugModule('snyk:run-test');
|
|
const ANALYTICS_PAYLOAD_MAX_LENGTH = 1024;
|
|
function prepareResponseForParsing(payload, response, options) {
|
|
const ecosystem = ecosystems_1.getEcosystem(options);
|
|
return ecosystem
|
|
? prepareEcosystemResponseForParsing(payload, response, options)
|
|
: prepareLanguagesResponseForParsing(payload);
|
|
}
|
|
function prepareEcosystemResponseForParsing(payload, response, options) {
|
|
var _a, _b, _c, _d, _e, _f;
|
|
const testDependenciesRequest = payload.body;
|
|
const payloadBody = testDependenciesRequest === null || testDependenciesRequest === void 0 ? void 0 : testDependenciesRequest.scanResult;
|
|
const depGraphData = (_a = response === null || response === void 0 ? void 0 : response.result) === null || _a === void 0 ? void 0 : _a.depGraphData;
|
|
const depGraph = depGraphData !== undefined
|
|
? depGraphLib.createFromJSON(depGraphData)
|
|
: undefined;
|
|
const imageUserInstructions = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.facts.find((fact) => fact.type === 'dockerfileAnalysis' ||
|
|
fact.type === 'autoDetectedUserInstructions');
|
|
const dockerfilePackages = (_b = imageUserInstructions === null || imageUserInstructions === void 0 ? void 0 : imageUserInstructions.data) === null || _b === void 0 ? void 0 : _b.dockerfilePackages;
|
|
const projectName = (payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.name) || (depGraph === null || depGraph === void 0 ? void 0 : depGraph.rootPkg.name);
|
|
const packageManager = (_c = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.identity) === null || _c === void 0 ? void 0 : _c.type;
|
|
const targetFile = ((_d = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.identity) === null || _d === void 0 ? void 0 : _d.targetFile) || options.file;
|
|
const platform = (_f = (_e = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.identity) === null || _e === void 0 ? void 0 : _e.args) === null || _f === void 0 ? void 0 : _f.platform;
|
|
analytics.add('depGraph', !!depGraph);
|
|
analytics.add('isDocker', !!options.docker);
|
|
return {
|
|
depGraph,
|
|
dockerfilePackages,
|
|
projectName,
|
|
targetFile,
|
|
pkgManager: packageManager,
|
|
displayTargetFile: targetFile,
|
|
foundProjectCount: undefined,
|
|
payloadPolicy: payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.policy,
|
|
platform,
|
|
scanResult: payloadBody,
|
|
};
|
|
}
|
|
function prepareLanguagesResponseForParsing(payload) {
|
|
const payloadBody = payload.body;
|
|
const payloadPolicy = payloadBody && payloadBody.policy;
|
|
const depGraph = payloadBody && payloadBody.depGraph;
|
|
const pkgManager = depGraph &&
|
|
depGraph.pkgManager &&
|
|
depGraph.pkgManager.name;
|
|
const targetFile = payloadBody && payloadBody.targetFile;
|
|
const projectName = (payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.projectNameOverride) || (payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.originalProjectName);
|
|
const foundProjectCount = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.foundProjectCount;
|
|
const displayTargetFile = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.displayTargetFile;
|
|
let dockerfilePackages;
|
|
if (payloadBody &&
|
|
payloadBody.docker &&
|
|
payloadBody.docker.dockerfilePackages) {
|
|
dockerfilePackages = payloadBody.docker.dockerfilePackages;
|
|
}
|
|
analytics.add('depGraph', !!depGraph);
|
|
analytics.add('isDocker', !!(payloadBody && payloadBody.docker));
|
|
return {
|
|
depGraph,
|
|
payloadPolicy,
|
|
pkgManager,
|
|
targetFile,
|
|
projectName,
|
|
foundProjectCount,
|
|
displayTargetFile,
|
|
dockerfilePackages,
|
|
};
|
|
}
|
|
function isTestDependenciesResponse(response) {
|
|
var _a;
|
|
const assumedTestDependenciesResponse = response;
|
|
return ((_a = assumedTestDependenciesResponse === null || assumedTestDependenciesResponse === void 0 ? void 0 : assumedTestDependenciesResponse.result) === null || _a === void 0 ? void 0 : _a.issues) !== undefined;
|
|
}
|
|
function convertIssuesToAffectedPkgs(response) {
|
|
if (!response.result) {
|
|
return response;
|
|
}
|
|
if (!isTestDependenciesResponse(response)) {
|
|
return response;
|
|
}
|
|
response.result['affectedPkgs'] = getAffectedPkgsFromIssues(response.result.issues);
|
|
return response;
|
|
}
|
|
function getAffectedPkgsFromIssues(issues) {
|
|
const result = {};
|
|
for (const issue of issues) {
|
|
const packageId = `${issue.pkgName}@${issue.pkgVersion || ''}`;
|
|
if (result[packageId] === undefined) {
|
|
result[packageId] = {
|
|
pkg: { name: issue.pkgName, version: issue.pkgVersion },
|
|
issues: {},
|
|
};
|
|
}
|
|
result[packageId].issues[issue.issueId] = issue;
|
|
}
|
|
return result;
|
|
}
|
|
async function sendAndParseResults(payloads, spinnerLbl, root, options) {
|
|
const results = [];
|
|
for (const payload of payloads) {
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
if (!options.quiet) {
|
|
await spinner_1.spinner(spinnerLbl);
|
|
}
|
|
/** sendTestPayload() deletes the request.body from the payload once completed. */
|
|
const payloadCopy = Object.assign({}, payload);
|
|
const res = await sendTestPayload(payload);
|
|
const { depGraph, payloadPolicy, pkgManager, targetFile, projectName, foundProjectCount, displayTargetFile, dockerfilePackages, platform, scanResult, } = prepareResponseForParsing(payloadCopy, res, options);
|
|
const ecosystem = ecosystems_1.getEcosystem(options);
|
|
if (ecosystem && options['print-deps']) {
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
await print_deps_1.maybePrintDepGraph(options, depGraph);
|
|
}
|
|
const legacyRes = convertIssuesToAffectedPkgs(res);
|
|
const result = await parseRes(depGraph, pkgManager, legacyRes, options, payload, payloadPolicy, root, dockerfilePackages);
|
|
results.push({
|
|
...result,
|
|
targetFile,
|
|
projectName,
|
|
foundProjectCount,
|
|
displayTargetFile,
|
|
platform,
|
|
scanResult,
|
|
});
|
|
}
|
|
return results;
|
|
}
|
|
async function runTest(projectType, root, options) {
|
|
const spinnerLbl = 'Querying vulnerabilities database...';
|
|
try {
|
|
await options_validator_1.validateOptions(options, options.packageManager);
|
|
const payloads = await assemblePayloads(root, options);
|
|
return await sendAndParseResults(payloads, spinnerLbl, root, options);
|
|
}
|
|
catch (error) {
|
|
debug('Error running test', { error });
|
|
// handling denial from registry because of the feature flag
|
|
// currently done for go.mod
|
|
const isFeatureNotAllowed = error.code === 403 && error.message.includes('Feature not allowed');
|
|
const hasFailedToGetVulnerabilities = error.code === 404 &&
|
|
error.name.includes('FailedToGetVulnerabilitiesError') &&
|
|
!error.userMessage;
|
|
if (isFeatureNotAllowed) {
|
|
throw errors_1.NoSupportedManifestsFoundError([root]);
|
|
}
|
|
if (hasFailedToGetVulnerabilities) {
|
|
throw errors_1.FailedToGetVulnsFromUnavailableResource(root, error.code);
|
|
}
|
|
if (ecosystems_1.getEcosystem(options) === 'docker' &&
|
|
error.statusCode === 401 &&
|
|
[
|
|
'authentication required',
|
|
'{"details":"incorrect username or password"}\n',
|
|
].includes(error.message)) {
|
|
throw new errors_1.DockerImageNotFoundError(root);
|
|
}
|
|
throw new errors_1.FailedToRunTestError(error.userMessage ||
|
|
error.message ||
|
|
`Failed to test ${projectType} project`, error.code);
|
|
}
|
|
finally {
|
|
spinner_1.spinner.clear(spinnerLbl)();
|
|
}
|
|
}
|
|
exports.runTest = runTest;
|
|
async function parseRes(depGraph, pkgManager, res, options, payload, payloadPolicy, root, dockerfilePackages) {
|
|
var _a;
|
|
// TODO: docker doesn't have a package manager
|
|
// so this flow will not be applicable
|
|
// refactor to separate
|
|
if (depGraph && pkgManager) {
|
|
res = legacy_1.convertTestDepGraphResultToLegacy(res, // Double "as" required by Typescript for dodgy assertions
|
|
depGraph, pkgManager, options);
|
|
// For Node.js: inject additional information (for remediation etc.) into the response.
|
|
if (payload.modules) {
|
|
res.dependencyCount =
|
|
payload.modules.numDependencies || depGraph.getPkgs().length - 1;
|
|
if (res.vulnerabilities) {
|
|
res.vulnerabilities.forEach((vuln) => {
|
|
if (payload.modules && payload.modules.pluck) {
|
|
const plucked = payload.modules.pluck(vuln.from, vuln.name, vuln.version);
|
|
vuln.__filename = plucked.__filename;
|
|
vuln.shrinkwrap = plucked.shrinkwrap;
|
|
vuln.bundled = plucked.bundled;
|
|
// this is an edgecase when we're testing the directly vuln pkg
|
|
if (vuln.from.length === 1) {
|
|
return;
|
|
}
|
|
const parentPkg = snyk_module_1.parsePackageString(vuln.from[1]);
|
|
const parent = payload.modules.pluck(vuln.from.slice(0, 2), parentPkg.name, parentPkg.version);
|
|
vuln.parentDepType = parent.depType;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
// TODO: is this needed? we filter on the other side already based on policy
|
|
// this will move to be filtered server side soon & it will support `'ignore-policy'`
|
|
analytics.add('vulns-pre-policy', res.vulnerabilities.length);
|
|
res.filesystemPolicy = !!payloadPolicy;
|
|
if (!options['ignore-policy']) {
|
|
res.policy = res.policy || payloadPolicy;
|
|
const policy = await snyk.policy.loadFromText(res.policy);
|
|
res = policy.filter(res, root);
|
|
}
|
|
analytics.add('vulns', res.vulnerabilities.length);
|
|
if (res.docker && dockerfilePackages) {
|
|
res.vulnerabilities = res.vulnerabilities.map((vuln) => {
|
|
const dockerfilePackage = dockerfilePackages[vuln.name.split('/')[0]];
|
|
if (dockerfilePackage) {
|
|
vuln.dockerfileInstruction =
|
|
dockerfilePackage.installCommand;
|
|
}
|
|
vuln.dockerBaseImage = res.docker.baseImage;
|
|
return vuln;
|
|
});
|
|
}
|
|
if (options.docker && ((_a = res.docker) === null || _a === void 0 ? void 0 : _a.baseImage) &&
|
|
options['exclude-base-image-vulns']) {
|
|
const filteredVulns = res.vulnerabilities.filter((vuln) => vuln.dockerfileInstruction);
|
|
// `exclude-base-image-vulns` might have left us with no vulns, so `ok` is now `true`
|
|
if (res.vulnerabilities.length !== 0 &&
|
|
filteredVulns.length === 0 &&
|
|
!res.ok) {
|
|
res.ok = true;
|
|
}
|
|
res.vulnerabilities = filteredVulns;
|
|
}
|
|
res.uniqueCount = countUniqueVulns(res.vulnerabilities);
|
|
return res;
|
|
}
|
|
function sendTestPayload(payload) {
|
|
var _a;
|
|
const payloadBody = payload.body;
|
|
const filesystemPolicy = payload.body && !!((payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.policy) || ((_a = payloadBody === null || payloadBody === void 0 ? void 0 : payloadBody.scanResult) === null || _a === void 0 ? void 0 : _a.policy));
|
|
return new Promise((resolve, reject) => {
|
|
request_1.makeRequest(payload, (error, res, body) => {
|
|
if (error) {
|
|
return reject(error);
|
|
}
|
|
if (res.statusCode !== 200) {
|
|
const err = handleTestHttpErrorResponse(res, body);
|
|
return reject(err);
|
|
}
|
|
body.filesystemPolicy = filesystemPolicy;
|
|
resolve(body);
|
|
});
|
|
});
|
|
}
|
|
function handleTestHttpErrorResponse(res, body) {
|
|
const { statusCode } = res;
|
|
let err;
|
|
const userMessage = body && body.userMessage;
|
|
switch (statusCode) {
|
|
case 401:
|
|
case 403:
|
|
err = errors_1.AuthFailedError(userMessage, statusCode);
|
|
err.innerError = body.stack;
|
|
break;
|
|
case 404:
|
|
err = new errors_1.NotFoundError(userMessage);
|
|
err.innerError = body.stack;
|
|
break;
|
|
case 405:
|
|
err = new errors_1.UnsupportedFeatureFlagError('reachableVulns');
|
|
err.innerError = body.stack;
|
|
break;
|
|
case 500:
|
|
err = new errors_1.InternalServerError(userMessage);
|
|
err.innerError = body.stack;
|
|
break;
|
|
default:
|
|
err = new errors_1.FailedToGetVulnerabilitiesError(userMessage, statusCode);
|
|
err.innerError = body.error;
|
|
}
|
|
return err;
|
|
}
|
|
function assemblePayloads(root, options) {
|
|
let isLocal;
|
|
if (options.docker) {
|
|
isLocal = true;
|
|
}
|
|
else {
|
|
// TODO: Refactor this check so we don't require files when tests are using mocks
|
|
isLocal = fs.existsSync(root);
|
|
}
|
|
analytics.add('local', isLocal);
|
|
const ecosystem = ecosystems_1.getEcosystem(options);
|
|
if (ecosystem) {
|
|
return assemble_payloads_1.assembleEcosystemPayloads(ecosystem, options);
|
|
}
|
|
if (isLocal) {
|
|
return assembleLocalPayloads(root, options);
|
|
}
|
|
return assembleRemotePayloads(root, options);
|
|
}
|
|
// Payload to send to the Registry for scanning a package from the local filesystem.
|
|
async function assembleLocalPayloads(root, options) {
|
|
var _a, _b;
|
|
// For --all-projects packageManager is yet undefined here. Use 'all'
|
|
let analysisTypeText = 'all dependencies for ';
|
|
if (options.docker) {
|
|
analysisTypeText = 'docker dependencies for ';
|
|
}
|
|
else if (options.packageManager) {
|
|
analysisTypeText = options.packageManager + ' dependencies for ';
|
|
}
|
|
const spinnerLbl = 'Analyzing ' +
|
|
analysisTypeText +
|
|
(path.relative('.', path.join(root, options.file || '')) ||
|
|
path.relative('..', '.') + ' project dir');
|
|
try {
|
|
const payloads = [];
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
if (!options.quiet) {
|
|
await spinner_1.spinner(spinnerLbl);
|
|
}
|
|
const deps = await get_deps_from_plugin_1.getDepsFromPlugin(root, options);
|
|
const failedResults = deps.failedResults;
|
|
if (failedResults === null || failedResults === void 0 ? void 0 : failedResults.length) {
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
if (!options.json && !options.quiet) {
|
|
console.warn(chalk_1.default.bold.red(`${theme_1.icon.ISSUE} ${failedResults.length}/${failedResults.length +
|
|
deps.scannedProjects
|
|
.length} potential projects failed to get dependencies.`));
|
|
failedResults.forEach((f) => {
|
|
if (f.targetFile) {
|
|
console.warn(theme.color.status.error(`${f.targetFile}:`));
|
|
}
|
|
console.warn(theme.color.status.error(` ${f.errMessage}`));
|
|
});
|
|
}
|
|
debug('getDepsFromPlugin returned failed results, cannot run test/monitor', failedResults);
|
|
if (options['fail-fast']) {
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your test request could not be completed.'));
|
|
}
|
|
}
|
|
analytics.add('pluginName', deps.plugin.name);
|
|
const javaVersion = get(deps.plugin, 'meta.versionBuildInfo.metaBuildVersion.javaVersion', null);
|
|
const mvnVersion = get(deps.plugin, 'meta.versionBuildInfo.metaBuildVersion.mvnVersion', null);
|
|
const sbtVersion = get(deps.plugin, 'meta.versionBuildInfo.metaBuildVersion.sbtVersion', null);
|
|
if (javaVersion) {
|
|
analytics.add('javaVersion', javaVersion);
|
|
}
|
|
if (mvnVersion) {
|
|
analytics.add('mvnVersion', mvnVersion);
|
|
}
|
|
if (sbtVersion) {
|
|
analytics.add('sbtVersion', sbtVersion);
|
|
}
|
|
for (const scannedProject of deps.scannedProjects) {
|
|
if (!scannedProject.depTree && !scannedProject.depGraph) {
|
|
debug('scannedProject is missing depGraph or depTree, cannot run test/monitor');
|
|
throw new errors_1.FailedToRunTestError(errors_1.errorMessageWithRetry('Your test request could not be completed.'));
|
|
}
|
|
// prefer dep-graph fallback on dep tree
|
|
// TODO: clean up once dep-graphs only
|
|
const pkg = scannedProject.depGraph
|
|
? scannedProject.depGraph
|
|
: scannedProject.depTree;
|
|
if (options['print-deps']) {
|
|
if (scannedProject.depGraph) {
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
print_deps_1.maybePrintDepGraph(options, pkg);
|
|
}
|
|
else {
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
print_deps_1.maybePrintDepTree(options, pkg);
|
|
}
|
|
}
|
|
const project = scannedProject;
|
|
const packageManager = extract_package_manager_1.extractPackageManager(project, deps, options);
|
|
if (pkg.docker) {
|
|
const baseImageFromDockerfile = pkg.docker.baseImage;
|
|
if (!baseImageFromDockerfile && options['base-image']) {
|
|
pkg.docker.baseImage = options['base-image'];
|
|
}
|
|
if (baseImageFromDockerfile && deps.plugin && deps.plugin.imageLayers) {
|
|
analytics.add('BaseImage', baseImageFromDockerfile);
|
|
analytics.add('imageLayers', deps.plugin.imageLayers);
|
|
}
|
|
}
|
|
// todo: normalize what target file gets used across plugins and functions
|
|
const targetFile = scannedProject.targetFile || deps.plugin.targetFile || options.file;
|
|
// Forcing options.path to be a string as pathUtil requires is to be stringified
|
|
const targetFileRelativePath = targetFile
|
|
? pathUtil.resolve(pathUtil.resolve(`${options.path || root}`), targetFile)
|
|
: '';
|
|
let targetFileDir;
|
|
if (targetFileRelativePath) {
|
|
const { dir } = path.parse(targetFileRelativePath);
|
|
targetFileDir = dir;
|
|
}
|
|
const policy = await policy_1.findAndLoadPolicy(root, options.docker ? 'docker' : packageManager, options,
|
|
// TODO: fix this and send only send when we used resolve-deps for node
|
|
// it should be a ExpandedPkgTree type instead
|
|
pkg, targetFileDir);
|
|
analytics.add('packageManager', packageManager);
|
|
if (scannedProject.depGraph) {
|
|
const depGraph = pkg;
|
|
addPackageAnalytics(depGraph.rootPkg.name, depGraph.rootPkg.version);
|
|
}
|
|
if (scannedProject.depTree) {
|
|
const depTree = pkg;
|
|
addPackageAnalytics(depTree.name, depTree.version);
|
|
}
|
|
let target;
|
|
if (scannedProject.depGraph) {
|
|
target = await projectMetadata.getInfo(scannedProject, options);
|
|
}
|
|
else {
|
|
target = await projectMetadata.getInfo(scannedProject, options, pkg);
|
|
}
|
|
const originalProjectName = scannedProject.depGraph
|
|
? pkg.rootPkg.name
|
|
: pkg.name;
|
|
const body = {
|
|
// WARNING: be careful changing this as it affects project uniqueness
|
|
targetFile: project.plugin.targetFile,
|
|
// TODO: Remove relativePath prop once we gather enough ruby related logs
|
|
targetFileRelativePath: `${targetFileRelativePath}`,
|
|
targetReference: options['target-reference'],
|
|
projectNameOverride: options.projectName,
|
|
originalProjectName,
|
|
policy: policy ? policy.toString() : undefined,
|
|
foundProjectCount: await get_extra_project_count_1.getExtraProjectCount(root, options, deps),
|
|
displayTargetFile: targetFile,
|
|
docker: pkg.docker,
|
|
hasDevDependencies: pkg.hasDevDependencies,
|
|
target,
|
|
};
|
|
let depGraph;
|
|
if (scannedProject.depGraph) {
|
|
depGraph = scannedProject.depGraph;
|
|
}
|
|
else {
|
|
// Graphs are more compact and robust representations.
|
|
// Legacy parts of the code are still using trees, but will eventually be fully migrated.
|
|
debug('converting dep-tree to dep-graph', {
|
|
name: pkg.name,
|
|
targetFile: scannedProject.targetFile || options.file,
|
|
});
|
|
depGraph = await depGraphLib.legacy.depTreeToGraph(pkg, packageManager);
|
|
debug('done converting dep-tree to dep-graph', {
|
|
uniquePkgsCount: depGraph.getPkgs().length,
|
|
});
|
|
}
|
|
const pruneIsRequired = options.pruneRepeatedSubdependencies;
|
|
if (packageManager) {
|
|
depGraph = await prune_1.pruneGraph(depGraph, packageManager, pruneIsRequired);
|
|
}
|
|
body.depGraph = depGraph;
|
|
if (options.reachableVulns && ((_a = scannedProject.callGraph) === null || _a === void 0 ? void 0 : _a.message)) {
|
|
const err = scannedProject.callGraph;
|
|
const analyticsError = err.innerError || err;
|
|
analytics.add('callGraphError', {
|
|
errorType: (_b = analyticsError.constructor) === null || _b === void 0 ? void 0 : _b.name,
|
|
message: error_format_1.abridgeErrorMessage(analyticsError.message.toString(), ANALYTICS_PAYLOAD_MAX_LENGTH),
|
|
});
|
|
alerts.registerAlerts([
|
|
{
|
|
type: 'error',
|
|
name: 'missing-call-graph',
|
|
msg: err.message,
|
|
},
|
|
]);
|
|
}
|
|
else if (scannedProject.callGraph) {
|
|
const { callGraph, nodeCount, edgeCount, } = reachable_vulns_1.serializeCallGraphWithMetrics(scannedProject.callGraph);
|
|
debug(`Adding call graph to payload, node count: ${nodeCount}, edge count: ${edgeCount}`);
|
|
const callGraphMetrics = get(deps.plugin, 'meta.callGraphMetrics', {});
|
|
analytics.add('callGraphMetrics', {
|
|
callGraphEdgeCount: edgeCount,
|
|
callGraphNodeCount: nodeCount,
|
|
...callGraphMetrics,
|
|
});
|
|
body.callGraph = callGraph;
|
|
}
|
|
const reqUrl = config_1.default.API +
|
|
(options.testDepGraphDockerEndpoint ||
|
|
options.vulnEndpoint ||
|
|
'/test-dep-graph');
|
|
const payload = {
|
|
method: 'POST',
|
|
url: reqUrl,
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
qs: common.assembleQueryString(options),
|
|
body,
|
|
};
|
|
if (packageManager && ['yarn', 'npm'].indexOf(packageManager) !== -1) {
|
|
const isLockFileBased = targetFile &&
|
|
(targetFile.endsWith('package-lock.json') ||
|
|
targetFile.endsWith('yarn.lock'));
|
|
if (!isLockFileBased || options.traverseNodeModules) {
|
|
payload.modules = pkg; // See the output of resolve-deps
|
|
}
|
|
}
|
|
payloads.push(payload);
|
|
}
|
|
return payloads;
|
|
}
|
|
finally {
|
|
await spinner_1.spinner.clear(spinnerLbl)();
|
|
}
|
|
}
|
|
// Payload to send to the Registry for scanning a remote package.
|
|
async function assembleRemotePayloads(root, options) {
|
|
const pkg = snyk_module_1.parsePackageString(root);
|
|
debug('testing remote: %s', pkg.name + '@' + pkg.version);
|
|
addPackageAnalytics(pkg.name, pkg.version);
|
|
const encodedName = encodeURIComponent(pkg.name + '@' + pkg.version);
|
|
// options.vulnEndpoint is only used by `snyk protect` (i.e. local filesystem tests)
|
|
const url = `${config_1.default.API}${options.vulnEndpoint ||
|
|
`/vuln/${options.packageManager}`}/${encodedName}`;
|
|
return [
|
|
{
|
|
method: 'GET',
|
|
url,
|
|
qs: common.assembleQueryString(options),
|
|
json: true,
|
|
headers: {
|
|
'x-is-ci': is_ci_1.isCI(),
|
|
authorization: api_token_1.getAuthHeader(),
|
|
},
|
|
},
|
|
];
|
|
}
|
|
function addPackageAnalytics(name, version) {
|
|
analytics.add('packageName', name);
|
|
analytics.add('packageVersion', version);
|
|
analytics.add('package', name + '@' + version);
|
|
}
|
|
function countUniqueVulns(vulns) {
|
|
const seen = {};
|
|
for (const curr of vulns) {
|
|
seen[curr.id] = true;
|
|
}
|
|
return Object.keys(seen).length;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 66487:
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
exports.execute = void 0;
|
|
const childProcess = __webpack_require__(63129);
|
|
function execute(command, args, options) {
|
|
const spawnOptions = { shell: true };
|
|
if (options && options.cwd) {
|
|
spawnOptions.cwd = options.cwd;
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
let stdout = '';
|
|
let stderr = '';
|
|
const proc = childProcess.spawn(command, args, spawnOptions);
|
|
if (proc.stdout) {
|
|
proc.stdout.on('data', (data) => {
|
|
stdout += data;
|
|
});
|
|
}
|
|
if (proc.stderr) {
|
|
proc.stderr.on('data', (data) => {
|
|
stderr += data;
|
|
});
|
|
}
|
|
proc.on('close', (code) => {
|
|
if (code !== 0) {
|
|
return reject(stdout || stderr);
|
|
}
|
|
resolve(stdout || stderr);
|
|
});
|
|
});
|
|
}
|
|
exports.execute = execute;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 9146:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
const snykConfig = __webpack_require__(25425);
|
|
|
|
// This module is kind of "world object" that is used to indirectly import modules.
|
|
// This also introduces some circular imports.
|
|
|
|
// TODO(kyegupov): untangle this, resolve circular imports, convert to Typescript
|
|
|
|
const snyk = {};
|
|
module.exports = snyk;
|
|
|
|
snyk.id = snykConfig.id;
|
|
|
|
const apiToken = __webpack_require__(95181);
|
|
|
|
// make snyk.api *always* get the latest api token from the config store
|
|
Object.defineProperty(snyk, 'api', {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get: function() {
|
|
return apiToken.api();
|
|
},
|
|
set: function(value) {
|
|
snykConfig.api = value;
|
|
},
|
|
});
|
|
|
|
snyk.test = __webpack_require__(53378);
|
|
snyk.policy = __webpack_require__(70535);
|
|
|
|
// this is the user config, and not the internal config
|
|
snyk.config = __webpack_require__(28137).config;
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 97467:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
const gemfile = __webpack_require__(10635);
|
|
|
|
module.exports = gemfileLockToDependencies;
|
|
|
|
const detectCycles = (dep, chain) => {
|
|
if (chain.indexOf(dep) >= 0) {
|
|
const error = Error('Cyclic dependency detected in lockfile');
|
|
const UNPROCESSABLE_ENTITY = 422;
|
|
error.code = UNPROCESSABLE_ENTITY;
|
|
error.meta = { dep, chain };
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const gemfileReducer = (lockFile, allDeps, ancestors) => (deps, dep) => {
|
|
const gemspec = lockFile.specs[dep];
|
|
// If for some reason a dependency isn't included in the specs then its
|
|
// better to just ignore it (otherwise all processing fails).
|
|
// This happens for bundler itself, it isn't included in the Gemfile.lock
|
|
// specs, even if its a dependency! (and that isn't documented anywhere)
|
|
if (gemspec) {
|
|
detectCycles(dep, ancestors);
|
|
if (allDeps.has(dep)) {
|
|
deps[dep] = allDeps.get(dep);
|
|
} else {
|
|
deps[dep] = {
|
|
name: dep,
|
|
version: gemspec.version,
|
|
};
|
|
allDeps.set(dep, deps[dep]);
|
|
deps[dep].dependencies = Object.keys(gemspec)
|
|
.filter((k) => k !== 'version')
|
|
.reduce(gemfileReducer(lockFile, allDeps, ancestors.concat([dep])), {});
|
|
}
|
|
}
|
|
return deps;
|
|
};
|
|
|
|
function gemfileLockToDependencies(fileContents) {
|
|
const lockFile = gemfile.interpret(fileContents, true);
|
|
|
|
return (
|
|
Object.keys(lockFile.dependencies || {})
|
|
// this is required to sanitise git deps with no exact version
|
|
// listed as `rspec!`
|
|
.map((dep) => dep.match(/[^!]+/)[0])
|
|
.reduce(gemfileReducer(lockFile, new Map(), []), {})
|
|
);
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ 53378:
|
|
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
|
|
|
|
module.exports = test;
|
|
|
|
const detect = __webpack_require__(45318);
|
|
const { runTest } = __webpack_require__(7964);
|
|
const chalk = __webpack_require__(32589);
|
|
const pm = __webpack_require__(53847);
|
|
const { UnsupportedPackageManagerError } = __webpack_require__(55191);
|
|
const { isMultiProjectScan } = __webpack_require__(62435);
|
|
|
|
async function test(root, options, callback) {
|
|
if (typeof options === 'function') {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
const promise = executeTest(root, options);
|
|
if (callback) {
|
|
promise
|
|
.then((res) => {
|
|
callback(null, res);
|
|
})
|
|
.catch(callback);
|
|
}
|
|
return promise;
|
|
}
|
|
|
|
async function executeTest(root, options) {
|
|
try {
|
|
if (!options.allProjects) {
|
|
options.packageManager = detect.detectPackageManager(root, options);
|
|
}
|
|
return run(root, options).then((results) => {
|
|
for (const res of results) {
|
|
if (!res.packageManager) {
|
|
res.packageManager = options.packageManager;
|
|
}
|
|
}
|
|
if (results.length === 1) {
|
|
// Return only one result if only one found as this is the default usecase
|
|
return results[0];
|
|
}
|
|
// For gradle, yarnWorkspaces, allProjects we may be returning more than one result
|
|
return results;
|
|
});
|
|
} catch (error) {
|
|
return Promise.reject(
|
|
chalk.red.bold(error.message ? error.message : error),
|
|
);
|
|
}
|
|
}
|
|
|
|
function run(root, options) {
|
|
const projectType = options.packageManager;
|
|
validateProjectType(options, projectType);
|
|
return runTest(projectType, root, options);
|
|
}
|
|
|
|
function validateProjectType(options, projectType) {
|
|
if (
|
|
!(
|
|
options.docker ||
|
|
isMultiProjectScan(options) ||
|
|
pm.SUPPORTED_PACKAGE_MANAGER_NAME[projectType]
|
|
)
|
|
) {
|
|
throw new UnsupportedPackageManagerError(projectType);
|
|
}
|
|
}
|
|
|
|
|
|
/***/ })
|
|
|
|
};
|
|
;
|
|
//# sourceMappingURL=784.index.js.map
|