snyk
Version:
snyk library and cli utility
1,283 lines (1,189 loc) • 279 kB
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 types_1 = __webpack_require__(94055);
const config_1 = __webpack_require__(22541);
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 && options['remote-repo-url']) {
throw new Error('`--remote-repo-url` is not supported for container scans');
}
// 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);
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;
}
}
/***/ }),
/***/ 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__(22541);
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) {
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);
return [monitorResults, errors];
}
exports.monitorEcosystem = monitorEcosystem;
async function selectAndExecuteMonitorStrategy(ecosystem, scanResultsByPath, options) {
return common_1.isUnmanagedEcosystem(ecosystem)
? await resolve_monitor_facts_1.resolveAndMonitorFacts(scanResultsByPath, options)
: 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) {
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);
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) {
const results = [];
const errors = [];
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);
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,
});
}
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__(22541);
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__(22541);
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 =