snyk
Version:
snyk library and cli utility
1,220 lines (1,138 loc) • 311 kB
JavaScript
exports.id = 533;
exports.ids = [533];
exports.modules = {
/***/ 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;
/***/ }),
/***/ 81024:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.APP_VULNS_OPTION = exports.EXCLUDE_APP_VULNS_OPTION = exports.INCLUDE_SYSTEM_JARS_OPTION = exports.CONTAINER_CLI_APP_VULNS_ENABLED_FEATURE_FLAG = exports.SCAN_USR_LIB_JARS_FEATURE_FLAG = void 0;
// Feature flag names
exports.SCAN_USR_LIB_JARS_FEATURE_FLAG = 'scanUsrLibJars';
exports.CONTAINER_CLI_APP_VULNS_ENABLED_FEATURE_FLAG = 'containerCliAppVulnsEnabled';
// CLI option names
exports.INCLUDE_SYSTEM_JARS_OPTION = 'include-system-jars';
exports.EXCLUDE_APP_VULNS_OPTION = 'exclude-app-vulns';
exports.APP_VULNS_OPTION = 'app-vulns';
/***/ }),
/***/ 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__(57147);
const Debug = __webpack_require__(15158);
const pathUtil = __webpack_require__(71017);
const cli_interface_1 = __webpack_require__(65266);
const check_paths_1 = __webpack_require__(94501);
const theme = __webpack_require__(86988);
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);
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 feature_flags_1 = __webpack_require__(63011);
const constants_1 = __webpack_require__(81024);
const package_managers_1 = __webpack_require__(53847);
const normalize_target_file_1 = __webpack_require__(97682);
const SEPARATOR = '\n-------------------------------------------------------\n';
const debug = Debug('snyk');
const appVulnsReleaseWarningMsg = `${theme.icon.WARNING} Important: Beginning January 24th, 2023, application dependencies in container
images will be scanned by default when using the snyk container test/monitor
commands. If you are using Snyk in a CI pipeline, action may be required. Read
https://snyk.io/blog/securing-container-applications-using-the-snyk-cli/ for
more info.`;
// 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 } = (0, 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) {
(0, 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');
}
if (options.docker) {
// order is important here, we want:
// 1) exclude-app-vulns set -> no app vulns
// 2) app-vulns set -> app-vulns
// 3) neither set -> containerAppVulnsEnabled
if (options[constants_1.EXCLUDE_APP_VULNS_OPTION]) {
options[constants_1.EXCLUDE_APP_VULNS_OPTION] = true;
}
else if (options[constants_1.APP_VULNS_OPTION]) {
options[constants_1.EXCLUDE_APP_VULNS_OPTION] = false;
}
else {
options[constants_1.EXCLUDE_APP_VULNS_OPTION] = !(await (0, feature_flags_1.hasFeatureFlagOrDefault)(constants_1.CONTAINER_CLI_APP_VULNS_ENABLED_FEATURE_FLAG, options, false));
// we can't print the warning message with JSON output as that would make
// the JSON output invalid.
// We also only want to print the message if the user did not overwrite
// the default with one of the flags.
if (options[constants_1.EXCLUDE_APP_VULNS_OPTION] &&
!options['json'] &&
!options['sarif']) {
console.log(theme.color.status.warn(appVulnsReleaseWarningMsg));
}
}
// Check scanUsrLibJars feature flag and add --include-system-jars parameter
const scanUsrLibJarsEnabled = await (0, feature_flags_1.hasFeatureFlagOrDefault)(constants_1.SCAN_USR_LIB_JARS_FEATURE_FLAG, options, false);
if (scanUsrLibJarsEnabled) {
options[constants_1.INCLUDE_SYSTEM_JARS_OPTION] = 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();
}
(0, api_token_1.apiOrOAuthTokenExists)();
let contributors = [];
if (!options.docker && analytics.allowAnalytics()) {
try {
contributors = await (0, dev_count_analysis_1.getContributors)();
}
catch (err) {
debug('error getting repo contributors', err);
}
}
const ecosystem = (0, ecosystems_1.getEcosystem)(options);
if (ecosystem) {
const commandResult = await (0, ecosystems_1.monitorEcosystem)(ecosystem, paths, options, contributors);
const [monitorResults, monitorErrors] = commandResult;
return await (0, monitor_2.getFormattedMonitorOutput)(results, monitorResults, monitorErrors, options);
}
let hasPnpmSupport = false;
let hasImprovedDotnetWithoutPublish = false;
let enableMavenDverboseExhaustiveDeps = false;
try {
hasPnpmSupport = (await (0, feature_flags_1.hasFeatureFlag)(package_managers_1.PNPM_FEATURE_FLAG, options));
if (options['dotnet-runtime-resolution']) {
hasImprovedDotnetWithoutPublish = (await (0, feature_flags_1.hasFeatureFlag)(package_managers_1.DOTNET_WITHOUT_PUBLISH_FEATURE_FLAG, options));
}
}
catch (err) {
hasPnpmSupport = false;
}
try {
const args = options['_doubleDashArgs'] || [];
const verboseEnabled = args.includes('-Dverbose') ||
args.includes('-Dverbose=true') ||
!!options['print-graph'];
if (verboseEnabled) {
enableMavenDverboseExhaustiveDeps = (await (0, feature_flags_1.hasFeatureFlag)(package_managers_1.MAVEN_DVERBOSE_EXHAUSTIVE_DEPS_FF, options));
if (enableMavenDverboseExhaustiveDeps) {
options.mavenVerboseIncludeAllVersions =
enableMavenDverboseExhaustiveDeps;
}
}
}
catch (err) {
enableMavenDverboseExhaustiveDeps = false;
}
const featureFlags = hasPnpmSupport
? new Set([package_managers_1.PNPM_FEATURE_FLAG])
: new Set();
if (hasImprovedDotnetWithoutPublish) {
options.useImprovedDotnetWithoutPublish = true;
}
// 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 ((0, is_multi_project_scan_1.isMultiProjectScan)(options)) {
analysisType = 'all';
}
else if (options.docker) {
analysisType = 'docker';
}
else {
packageManager = detect.detectPackageManager(path, options, featureFlags);
}
const unsupportedPackageManagers = [];
const unsupportedPackageManager = unsupportedPackageManagers.find((pm) => pm.name === packageManager);
if (unsupportedPackageManager) {
return `${unsupportedPackageManager.label} projects do not currently support "snyk monitor"`;
}
const targetFile = !options.scanAllUnmanaged && options.docker && !options.file // snyk monitor --docker (without --file)
? undefined
: options.file || detect.detectPackageFile(path, featureFlags);
const displayPath = pathUtil.relative('.', pathUtil.join(path, targetFile || ''));
const analyzingDepsSpinnerLabel = 'Analyzing ' +
(packageManager ? packageManager : analysisType) +
' dependencies for ' +
displayPath;
await (0, 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((0, get_deps_from_plugin_1.getDepsFromPlugin)(path, {
...options,
path,
packageManager,
}, featureFlags), 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 = (0, convert_single_splugin_res_to_multi_custom_1.convertSingleResultToMultiCustom)(inspectResult);
}
else {
perProjectResult = (0, 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 (0, 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 = (0, 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}...`);
(0, print_deps_1.maybePrintDepGraph)(options, projectDeps.depGraph);
}
if (projectDeps.depTree) {
debug(`Processing ${projectDeps.depTree.name}...`);
(0, print_deps_1.maybePrintDepTree)(options, projectDeps.depTree);
}
const tFile = (0, normalize_target_file_1.normalizeTargetFile)(projectDeps, projectDeps.plugin, targetFile);
const targetFileRelativePath = tFile
? pathUtil.resolve(pathUtil.resolve(path), tFile)
: '';
const res = await promiseOrCleanup((0, 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 = (0, formatters_1.formatMonitorOutput)(extractedPackageManager, res, options, projectName, await (0, 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 (0, 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'],
assetsProjectName: options['assets-project-name'],
reachabilityScanId: options['reachability-id'],
};
}
/**
* 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-tags=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 = '';
this.jsonData = {};
}
getJsonResult() {
return this.jsonResult;
}
getSarifResult() {
return this.sarifResult;
}
getJsonData() {
return this.jsonData;
}
static createHumanReadableTestCommandResult(humanReadableResult, jsonResult, sarifResult, jsonData) {
return new HumanReadableTestCommandResult(humanReadableResult, jsonResult, sarifResult, jsonData);
}
static createJsonTestCommandResult(stdout, jsonResult, sarifResult, jsonPayload) {
return new JsonTestCommandResult(stdout, jsonResult, sarifResult, jsonPayload);
}
}
exports.TestCommandResult = TestCommandResult;
class HumanReadableTestCommandResult extends TestCommandResult {
constructor(humanReadableResult, jsonResult, sarifResult, jsonData) {
super(humanReadableResult);
this.jsonResult = '';
this.sarifResult = '';
this.jsonData = {};
this.jsonResult = jsonResult;
if (sarifResult) {
this.sarifResult = sarifResult;
}
if (jsonData) {
this.jsonData = jsonData;
}
}
getJsonResult() {
return this.jsonResult;
}
getSarifResult() {
return this.sarifResult;
}
getJsonData() {
return this.jsonData;
}
}
class JsonTestCommandResult extends TestCommandResult {
constructor(stdout, jsonResult, sarifResult, jsonData) {
super(stdout);
if (jsonResult) {
this.jsonResult = jsonResult;
}
if (sarifResult) {
this.sarifResult = sarifResult;
}
else {
this.jsonResult = stdout;
}
if (jsonData) {
this.jsonData = jsonData;
}
}
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' && (0, detect_1.isPathToPackageFile)(path)) {
throw (0, 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.getCodeClientProxyUrl = void 0;
const config_1 = __webpack_require__(25425);
function getCodeClientProxyUrl() {
const url = new URL(config_1.default.API);
const domain = url.origin;
const routeToAPI = isFedramp(domain);
return (config_1.default.CODE_CLIENT_PROXY_URL ||
domain.replace(/\/\/(ap[pi]\.)?/, routeToAPI ? '//api.' : '//deeproxy.'));
}
exports.getCodeClientProxyUrl = getCodeClientProxyUrl;
function isFedramp(domain) {
return domain.includes('snykgov.io');
}
/***/ }),
/***/ 73399:
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.hasUnknownVersions = void 0;
function hasUnknownVersions(depGraph) {
if (!depGraph) {
return false;
}
for (const pkg of depGraph.getPkgs()) {
if (pkg.version === 'unknown') {
return true;
}
}
return false;
}
exports.hasUnknownVersions = hasUnknownVersions;
/***/ }),
/***/ 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 = exports.getPlugin = exports.monitorEcosystem = exports.testEcosystem = 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 process_json_monitor_1 = __webpack_require__(21506);
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 = (0, plugins_1.getPlugin)(ecosystem);
(0, monitor_1.validateTags)(options);
(0, monitor_1.validateProjectAttributes)(options);
const scanResultsByPath = {};
for (const path of paths) {
try {
await (0, spinner_1.spinner)(`Analyzing dependencies in ${path}`);
options.path = path;
const pluginResponse = await plugin.scan(options);
scanResultsByPath[path] = pluginResponse.scanResults;
const policy = await (0, 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 (0, common_1.isUnmanagedEcosystem)(ecosystem)
? await (0, 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;
scanResult.targetReference = options['target-reference'];
// WARNING! This mutates the payload. Policy logic should be in the plugin.
const policy = await (0, 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: (0, monitor_1.generateTags)(options),
attributes: (0, monitor_1.generateProjectAttributes)(options),
pruneRepeatedSubdependencies: options.pruneRepeatedSubdependencies,
};
}
exports.generateMonitorDependenciesRequest = generateMonitorDependenciesRequest;
async function monitorDependencies(scans, options) {
const results = [];
const errors = [];
for (const [path, scanResults] of Object.entries(scans)) {
await (0, 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': (0, is_ci_1.isCI)(),
authorization: (0, api_token_1.getAuthHeader)(),
},
body: monitorDependenciesRequest,
qs: {
org: options.org || configOrg,
},
};
try {
const response = await (0, promise_1.makeRequest)(payload);
results.push({
...response,
path,
scanResult,
});
}
catch (error) {
if (error.code === 401) {
throw (0, 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 = (0, formatters_1.formatMonitorOutput)(monitorResult.scanResult.identity.type, monitorResult, options, monitorResult.projectName, await (0, 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 = (0, 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,
});
}
if (options.json) {
return (0, process_json_monitor_1.processJsonMonitorResponse)(results);
}
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__(71017);
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 (0, 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 = allResourcesRule['*'].expires &&
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 (0, spinner_1.spinner)(`Resolving and Monitoring fileSignatures in ${path}`);
for (const scanResult of scanResults) {
try {
const res = await (0, polling_monitor_1.requestMonitorPollingToken)(options, true, scanResult);
if (scanResult.analytics) {
(0, plugin_analytics_1.extractAndApplyPluginAnalytics)(scanResult.analytics, res.token);
}
const resolutionMeta = (0, common_1.extractResolutionMetaFromScanResult)(scanResult);
const { maxAttempts, pollInterval } = res.pollingTask;
const attemptsCount = 0;
const response = await (0, 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 (0, 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.resolveAndTestFactsRegistry = exports.resolveAndTestFactsUnmanagedDeps = exports.pollDepGraphAttributes = exports.submitHashes = exports.resolveAndTestFacts = void 0;
const spinner_1 = __webpack_require__(86766);
const common_1 = __webpack_require__(70527);
const errors_1 = __webpack_require__(55191);
const policy_1 = __webpack_require__(32615);
const polling_test_1 = __webpack_require__(77584);
const common_2 = __webpack_require__(53110);
const plugin_analytics_1 = __webpack_require__(33077);
const policy_2 = __webpack_require__(4669);
const utils_1 = __webpack_require__(97875);
async function resolveAndTestFacts(ecosystem, scans, options) {
try {
return await resolveAndTestFactsUnmanagedDeps(scans, options);
}
catch (error) {
const unauthorizedErrorCode = error.code === 401 || error.code === 403;
const missingApiToken = error.isMissingApiToken;
// Decide if the error is an authorization error other than being
// unauthenticated (missing or invalid API token). An account lacking
// permission, for example.
const otherUnauthorized = unauthorizedErrorCode && !missingApiToken;
if (otherUnauthorized) {
throw (0, errors_1.AuthFailedError)('Unauthorized request to unmanaged service', error.code);
}
throw error;
}
}
exports.resolveAndTestFacts = resolveAndTestFacts;
async function submitHashes(hashes, orgId) {
const response = await (0, polling_test_1.createDepGraph)(hashes, orgId);
return response.data.id;
}
exports.submitHashes = submitHashes;
async function pollDepGraphAttributes(id, orgId) {
const minIntervalMs = 2000;
const maxIntervalMs = 20000;
let totalElaspedTime = 0;
let attempts = 1;
const maxElapsedTime = 1800000; // 30 mins in ms
// Loop until we receive a response that is not in progress,
// or we receive something else than http status code 200.
while (totalElaspedTime <= maxElapsedTime) {
const graph = await (0, polling_test_1.getDepGraph)(id, orgId);
if (graph.data.attributes.in_progress) {
const pollInterval = Math.min(minIntervalMs * attempts, maxIntervalMs);
await (0, common_1.sleep)(pollInterval);
totalElaspedTime += pollInterval;
attempts++;
continue;
}
return graph.data.attributes;
}
throw new Error('max retries reached');
}
exports.pollDepGraphAttributes = pollDepGraphAttributes;
async function fetchIssues(start_time, dep_graph_data, component_details, target_severity, orgId) {
var _a, _b;
const response = await (0, polling_test_1.getIssues)({
dep_graph: dep_graph_data,
start_time,
component_details,
target_severity,
}, orgId);
const issues = response.data.result.issues.map((issue) => {
const converted = (0, utils_1.convertToCamelCase)(issue);
converted.fixInfo = (0, utils_1.convertToCamelCase)(converted.fixInfo);
return converted;
});
const issuesData = (0, utils_1.convertMapCasing)(response.data.result.issues_data);
const depGraphData = (0, utils_1.convertDepGraph)(response.data.result.dep_graph);
const dependencyCount = (_b = (_a = response.data.result.dep_graph.graph.nodes.find((graphNode) => {
return graphNode.node_id === 'root-node';
})) === null || _a === void 0 ? void 0 : _a.deps) === null || _b === void 0 ? void 0 : _b.length;
const depsFilePaths = response.data.result.deps_file_paths;
const fileSignaturesDetails = (0, utils_1.convertMapCasing)(response.data.result.file_signatures_details);
return {
issues,
issuesData,
depGraphData,
dependencyCount,
depsFilePaths,
fileSignaturesDetails,
};
}
function buildVulnerabilityFromIssue(issueData, issue, packageManager) {
const pkgCoordinate = `${issue.pkgName}@${issue.pkgVersion}`;
issueData.from = [pkgCoordinate];
issueData.name = pkgCoordinate;
issueData.packageManager = packageManager;
issueData.version = issue.pkgVersion || '';
issueData.upgradePath = [false];
issueData.isPatchable = false;
return issueData;
}
async function resolveAndTestFactsUnmanagedDeps(scans, options) {
var _a, _b, _c, _d;
const results = [];
const errors = [];
const packageManager = 'Unmanaged (C/C++)';
const displayTargetFile = '';
const orgId = await (0, utils_1.getOrg)(options.org);
const target_severity = options.severityThreshold || common_2.SEVERITY.LOW;
if (orgId === '') {
errors.push('organisation-id missing');
return [results, errors];
}
for (const [path, scanResults] of Object.entries(scans)) {
await (0, spinner_1.spinner)(`Resolving and Testing fileSignatures in ${path}`);
for (const scanResult of scanResults) {
try {
const id = await submitHashes({ hashes: (_a = scanResult === null || scanResult === void 0 ? void 0 : scanResult.facts[0]) === null || _a === void 0 ? void 0 : _a.data }, orgId);
if (scanResult.analytics) {
(0, plugin_analytics_1.extractAndApplyPluginAnalytics)(scanResult.analytics, id);
}
const { start_time, dep_graph_data, component_details } = await pollDepGraphAttributes(id, orgId);
const { issues, issuesData, depGraphData, dependencyCount, depsFilePaths, fileSignaturesDetails, } = await fetchIssues(start_time, dep_graph_data, component_details, target_severity, orgId);
const issuesMap = new Map();
issues.forEach((i) => {
issuesMap.set(i.issueId, i);
});
const policy = await (0, policy_1.findAndLoadPolicy)(path, 'cpp', options);
const [issuesFiltered, issuesDataFiltered] = (0, policy_2.filterIgnoredIssues)(issues, issuesData, policy);
// Build vulnerabilities array from filtered data.
const vulnerabilities = [];
for (const issuesDataKey in issuesDataFiltered) {
const issue = issuesMap.get(issuesDataKey);
if (issue) {
const issueData = issuesDataFiltered[issuesDataKey];
vulnerabilities.push(buildVulnerabilityFromIssue(issueData, issue, packageManager));
}
}
// Build filtered.ignore array with ignored vulnerabilities
const filteredIgnore = [];
for (const issuesDataKey in issuesData) {
// If the issue was in the original data but not in the filtered data, it was ignored
if (!(issuesDataKey in issuesDataFiltered)) {
const issue = issuesMap.get(issuesDataKey);
if (issue) {
const issueData = {