"Fossies" - the Fresh Open Source Software Archive

Member "cli-1.1280.1/src/lib/ecosystems/monitor.ts" (20 Feb 2024, 7496 Bytes) of package /linux/misc/snyk-cli-1.1280.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 import { InspectResult } from '@snyk/cli-interface/legacy/plugin';
    2 import chalk from 'chalk';
    3 import config from '../config';
    4 import { isCI } from '../is-ci';
    5 import { makeRequest } from '../request/promise';
    6 import { Contributor, MonitorResult, Options, PolicyOptions } from '../types';
    7 import { spinner } from '../../lib/spinner';
    8 import { getPlugin } from './plugins';
    9 import { BadResult, GoodResult } from '../../cli/commands/monitor/types';
   10 import { formatErrorMonitorOutput, formatMonitorOutput } from '../formatters';
   11 import { getExtraProjectCount } from '../plugins/get-extra-project-count';
   12 import {
   13   AuthFailedError,
   14   DockerImageNotFoundError,
   15   MonitorError,
   16 } from '../errors';
   17 import {
   18   Ecosystem,
   19   ScanResult,
   20   EcosystemMonitorResult,
   21   EcosystemMonitorError,
   22   MonitorDependenciesRequest,
   23   MonitorDependenciesResponse,
   24 } from './types';
   25 import { findAndLoadPolicyForScanResult } from './policy';
   26 import { getAuthHeader } from '../api-token';
   27 import { resolveAndMonitorFacts } from './resolve-monitor-facts';
   28 import {
   29   generateProjectAttributes,
   30   generateTags,
   31   validateProjectAttributes,
   32   validateTags,
   33 } from '../../cli/commands/monitor';
   34 import { isUnmanagedEcosystem } from './common';
   35 import { findAndLoadPolicy } from '../policy';
   36 
   37 const SEPARATOR = '\n-------------------------------------------------------\n';
   38 
   39 export async function monitorEcosystem(
   40   ecosystem: Ecosystem,
   41   paths: string[],
   42   options: Options & PolicyOptions,
   43   contributors?: Contributor[],
   44 ): Promise<[EcosystemMonitorResult[], EcosystemMonitorError[]]> {
   45   const plugin = getPlugin(ecosystem);
   46 
   47   validateTags(options);
   48   validateProjectAttributes(options);
   49 
   50   const scanResultsByPath: { [dir: string]: ScanResult[] } = {};
   51   for (const path of paths) {
   52     try {
   53       await spinner(`Analyzing dependencies in ${path}`);
   54       options.path = path;
   55       const pluginResponse = await plugin.scan(options);
   56       scanResultsByPath[path] = pluginResponse.scanResults;
   57 
   58       const policy = await findAndLoadPolicy(path, 'cpp', options);
   59       if (policy) {
   60         scanResultsByPath[path].forEach(
   61           (scanResult) => (scanResult.policy = policy.toString()),
   62         );
   63       }
   64     } catch (error) {
   65       if (
   66         ecosystem === 'docker' &&
   67         error.statusCode === 401 &&
   68         error.message === 'authentication required'
   69       ) {
   70         throw new DockerImageNotFoundError(path);
   71       }
   72       if (ecosystem === 'docker' && error.message === 'invalid image format') {
   73         throw new DockerImageNotFoundError(path);
   74       }
   75 
   76       throw error;
   77     } finally {
   78       spinner.clearAll();
   79     }
   80   }
   81   const [monitorResults, errors] = await selectAndExecuteMonitorStrategy(
   82     ecosystem,
   83     scanResultsByPath,
   84     options,
   85     contributors,
   86   );
   87   return [monitorResults, errors];
   88 }
   89 
   90 async function selectAndExecuteMonitorStrategy(
   91   ecosystem: Ecosystem,
   92   scanResultsByPath: { [dir: string]: ScanResult[] },
   93   options: Options,
   94   contributors?: Contributor[],
   95 ): Promise<[EcosystemMonitorResult[], EcosystemMonitorError[]]> {
   96   return isUnmanagedEcosystem(ecosystem)
   97     ? await resolveAndMonitorFacts(scanResultsByPath, options, contributors)
   98     : await monitorDependencies(scanResultsByPath, options);
   99 }
  100 
  101 export async function generateMonitorDependenciesRequest(
  102   scanResult: ScanResult,
  103   options: Options,
  104 ): Promise<MonitorDependenciesRequest> {
  105   // WARNING! This mutates the payload. The project name logic should be handled in the plugin.
  106   scanResult.name =
  107     options['project-name'] || config.PROJECT_NAME || scanResult.name;
  108   // WARNING! This mutates the payload. Policy logic should be in the plugin.
  109   const policy = await findAndLoadPolicyForScanResult(scanResult, options);
  110   if (policy !== undefined) {
  111     scanResult.policy = policy.toString();
  112   }
  113 
  114   return {
  115     scanResult,
  116     method: 'cli',
  117     projectName: options['project-name'] || config.PROJECT_NAME || undefined,
  118     tags: generateTags(options),
  119     attributes: generateProjectAttributes(options),
  120   };
  121 }
  122 
  123 async function monitorDependencies(
  124   scans: {
  125     [dir: string]: ScanResult[];
  126   },
  127   options: Options,
  128 ): Promise<[EcosystemMonitorResult[], EcosystemMonitorError[]]> {
  129   const results: EcosystemMonitorResult[] = [];
  130   const errors: EcosystemMonitorError[] = [];
  131   for (const [path, scanResults] of Object.entries(scans)) {
  132     await spinner(`Monitoring dependencies in ${path}`);
  133     for (const scanResult of scanResults) {
  134       const monitorDependenciesRequest = await generateMonitorDependenciesRequest(
  135         scanResult,
  136         options,
  137       );
  138 
  139       const configOrg = config.org ? decodeURIComponent(config.org) : undefined;
  140 
  141       const payload = {
  142         method: 'PUT',
  143         url: `${config.API}/monitor-dependencies`,
  144         json: true,
  145         headers: {
  146           'x-is-ci': isCI(),
  147           authorization: getAuthHeader(),
  148         },
  149         body: monitorDependenciesRequest,
  150         qs: {
  151           org: options.org || configOrg,
  152         },
  153       };
  154       try {
  155         const response = await makeRequest<MonitorDependenciesResponse>(
  156           payload,
  157         );
  158         results.push({
  159           ...response,
  160           path,
  161           scanResult,
  162         });
  163       } catch (error) {
  164         if (error.code === 401) {
  165           throw AuthFailedError();
  166         }
  167         if (error.code >= 400 && error.code < 500) {
  168           throw new MonitorError(error.code, error.message);
  169         }
  170         errors.push({
  171           error: 'Could not monitor dependencies in ' + path,
  172           path,
  173           scanResult,
  174         });
  175       }
  176     }
  177     spinner.clearAll();
  178   }
  179   return [results, errors];
  180 }
  181 
  182 export async function getFormattedMonitorOutput(
  183   results: Array<GoodResult | BadResult>,
  184   monitorResults: EcosystemMonitorResult[],
  185   errors: EcosystemMonitorError[],
  186   options: Options,
  187 ): Promise<string> {
  188   for (const monitorResult of monitorResults) {
  189     let monOutput = '';
  190     if (monitorResult.ok) {
  191       monOutput = formatMonitorOutput(
  192         monitorResult.scanResult.identity.type,
  193         monitorResult as MonitorResult,
  194         options,
  195         monitorResult.projectName,
  196         await getExtraProjectCount(
  197           monitorResult.path,
  198           options,
  199           // TODO: Fix to pass the old "inspectResult.plugin.meta.allSubProjectNames", which ecosystem uses this?
  200           // "allSubProjectNames" can become a Fact returned by a plugin.
  201           {} as InspectResult,
  202         ),
  203       );
  204     } else {
  205       monOutput = formatErrorMonitorOutput(
  206         monitorResult.scanResult.identity.type,
  207         monitorResult as MonitorResult,
  208         options,
  209       );
  210     }
  211     results.push({
  212       ok: true,
  213       data: monOutput,
  214       path: monitorResult.path,
  215       projectName: monitorResult.id,
  216     });
  217   }
  218   for (const monitorError of errors) {
  219     results.push({
  220       ok: false,
  221       data: new MonitorError(500, monitorError.error),
  222       path: monitorError.path,
  223     });
  224   }
  225 
  226   const outputString = results
  227     .map((res) => {
  228       if (res.ok) {
  229         return res.data;
  230       }
  231 
  232       const errorMessage =
  233         res.data && res.data.userMessage
  234           ? chalk.bold.red(res.data.userMessage)
  235           : res.data
  236           ? res.data.message
  237           : 'Unknown error occurred.';
  238 
  239       return (
  240         chalk.bold.white('\nMonitoring ' + res.path + '...\n\n') + errorMessage
  241       );
  242     })
  243     .join('\n' + SEPARATOR);
  244 
  245   if (results.every((res) => res.ok)) {
  246     return outputString;
  247   }
  248 
  249   throw new Error(outputString);
  250 }