@coana-tech/cli 14.12.72 → 14.12.73

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli.mjs CHANGED
@@ -205801,9 +205801,13 @@ async function fetchManifestFilesFromManifestsTarHash(manifestsTarHash) {
205801
205801
  throw new Error("we should never reach this point");
205802
205802
  }
205803
205803
  }
205804
- async function fetchArtifactsFromManifestsTarHash(manifestsTarHash) {
205804
+ async function fetchArtifactsFromManifestsTarHash(manifestsTarHash, includePrecomputedReachabilityResults) {
205805
205805
  try {
205806
- const url2 = getSocketApiUrl(`orgs/${process.env.SOCKET_ORG_SLUG}/compute-artifacts?tarHash=${manifestsTarHash}`);
205806
+ const params = new URLSearchParams({
205807
+ tarHash: manifestsTarHash,
205808
+ includePrecomputedReachabilityResults: String(includePrecomputedReachabilityResults ?? false)
205809
+ });
205810
+ const url2 = getSocketApiUrl(`orgs/${process.env.SOCKET_ORG_SLUG}/compute-artifacts?${params.toString()}`);
205807
205811
  const responseData = (await axios2.post(url2, {}, { headers: getAuthHeaders() })).data;
205808
205812
  return parseComputeArtifactsResponse(responseData);
205809
205813
  } catch (e) {
@@ -228738,6 +228742,9 @@ function vulnerabilitiesDiff(oldVulnerabilities, newVulnerabilities, dismissedVu
228738
228742
  // ../web-compat-utils/src/vulnerability-reachability.ts
228739
228743
  function getVulnReachability(c3) {
228740
228744
  if (c3.type === "noAnalysisCheck") return "REACHABLE";
228745
+ if (c3.type === "precomputed") {
228746
+ return c3.results.type === "unreachable" ? "UNREACHABLE" : "UNKNOWN";
228747
+ }
228741
228748
  if (c3.type !== "success") {
228742
228749
  return "UNKNOWN";
228743
228750
  }
@@ -229845,11 +229852,11 @@ function getAllToplevelAncestors(artifactMap, artifactId) {
229845
229852
  findAncestors(artifactId);
229846
229853
  return Array.from(toplevelAncestors);
229847
229854
  }
229848
- async function fetchArtifactsFromSocket(rootWorkingDirectory, manifestsTarHash, mode) {
229855
+ async function fetchArtifactsFromSocket(rootWorkingDirectory, manifestsTarHash, mode, includePrecomputedReachabilityResults) {
229849
229856
  logger.info("Fetching artifacts from Socket backend (this may take a while)");
229850
229857
  logger.debug("manifests tar hash", manifestsTarHash);
229851
229858
  try {
229852
- const { artifacts } = await fetchArtifactsFromManifestsTarHash(manifestsTarHash);
229859
+ const { artifacts } = await fetchArtifactsFromManifestsTarHash(manifestsTarHash, includePrecomputedReachabilityResults);
229853
229860
  const properPythonProjects = [];
229854
229861
  const pipArtifactToRepresentativeManifest = {};
229855
229862
  for (const artifact of artifacts) {
@@ -229955,7 +229962,8 @@ async function fetchArtifactsFromSocket(rootWorkingDirectory, manifestsTarHash,
229955
229962
  vulnChainDetails: computeVulnChainDetails(artifacts, artifact.id),
229956
229963
  vulnerabilityAccessPaths: vuln.reachabilityData?.undeterminableReachability ? vuln.reachabilityData.publicComment ?? "" : vuln.reachabilityData?.pattern ?? null,
229957
229964
  ecosystem,
229958
- artifactId: artifact.id
229965
+ artifactId: artifact.id,
229966
+ precomputedReachabilityResult: vuln.reachabilityData?.precomputedReachabilityResult ?? null
229959
229967
  };
229960
229968
  const vulnId = `${ecosystem}-${workspace}-${artifact.namespace}-${artifact.name}-${artifact.version}-${vulnerability.url}`;
229961
229969
  if (!ecosystemWorkspaceVulnIds.has(vulnId)) {
@@ -230458,6 +230466,16 @@ function isShortestPath(root3, vulnPath) {
230458
230466
  // ../web-compat-utils/src/analysis-error-keys.ts
230459
230467
  var CLI_ANALYSIS_ERROR_MESSAGE = "Sharing log due to analysis error";
230460
230468
 
230469
+ // ../web-compat-utils/src/pluralize.ts
230470
+ function pluralize(count, word) {
230471
+ if (word === "vulnerability") {
230472
+ return count === 1 ? "vulnerability" : "vulnerabilities";
230473
+ } else {
230474
+ word;
230475
+ throw new Error(`Invalid word: ${word}`);
230476
+ }
230477
+ }
230478
+
230461
230479
  // ../web-compat-utils/src/package-utils.ts
230462
230480
  function extractNameAndVersion(packageNameAtVersion) {
230463
230481
  const atNpmIdx = packageNameAtVersion.indexOf("@npm:");
@@ -230875,11 +230893,15 @@ function toSocketFacts(report, dependencyTrees, subPjToWsPathToDirectDependencie
230875
230893
  };
230876
230894
  component.reachability.push(reachabilityForGHSA);
230877
230895
  }
230878
- reachabilityForGHSA.reachability.push({
230879
- ...toSocketReachabilitySchema(vulnerability),
230880
- workspacePath: vulnerability.workspacePath,
230881
- subprojectPath: vulnerability.subprojectPath
230882
- });
230896
+ if (vulnerability.codeAwareScanResult.type === "precomputed") {
230897
+ reachabilityForGHSA.reachability.push(vulnerability.codeAwareScanResult);
230898
+ } else {
230899
+ reachabilityForGHSA.reachability.push({
230900
+ ...toSocketReachabilitySchema(vulnerability),
230901
+ workspacePath: vulnerability.workspacePath,
230902
+ subprojectPath: vulnerability.subprojectPath
230903
+ });
230904
+ }
230883
230905
  }
230884
230906
  return {
230885
230907
  components
@@ -230958,11 +230980,15 @@ function toSocketFactsSocketDependencyTree(artifacts, vulnerabilities, tier1Reac
230958
230980
  };
230959
230981
  component.reachability.push(reachabilityForGHSA);
230960
230982
  }
230961
- reachabilityForGHSA.reachability.push({
230962
- ...toSocketReachabilitySchema(vulnerability),
230963
- workspacePath: vulnerability.workspacePath,
230964
- subprojectPath: vulnerability.subprojectPath
230965
- });
230983
+ if (vulnerability.codeAwareScanResult.type === "precomputed") {
230984
+ reachabilityForGHSA.reachability.push(vulnerability.codeAwareScanResult);
230985
+ } else {
230986
+ reachabilityForGHSA.reachability.push({
230987
+ ...toSocketReachabilitySchema(vulnerability),
230988
+ workspacePath: vulnerability.workspacePath,
230989
+ subprojectPath: vulnerability.subprojectPath
230990
+ });
230991
+ }
230966
230992
  }
230967
230993
  return {
230968
230994
  components: artifacts,
@@ -245858,7 +245884,7 @@ async function onlineScan(dependencyTree, apiKey, timeout) {
245858
245884
  }
245859
245885
 
245860
245886
  // dist/version.js
245861
- var version2 = "14.12.72";
245887
+ var version2 = "14.12.73";
245862
245888
 
245863
245889
  // dist/cli-core.js
245864
245890
  var { mapValues, omit, partition, pick } = import_lodash15.default;
@@ -246020,7 +246046,7 @@ var CliCore = class {
246020
246046
  async computeAndOutputReportSocketMode(otherModulesCommunicator) {
246021
246047
  logger.info("Fetching artifacts from Socket backend");
246022
246048
  this.sendProgress("SCAN_FOR_VULNERABILITIES", true, ".", ".");
246023
- const { artifacts, ecosystemToWorkspaceToAnalysisData, ecosystemToWorkspaceToVulnerabilities } = await fetchArtifactsFromSocket(this.rootWorkingDirectory, this.options.manifestsTarHash, "reachability");
246049
+ const { artifacts, ecosystemToWorkspaceToAnalysisData, ecosystemToWorkspaceToVulnerabilities } = await fetchArtifactsFromSocket(this.rootWorkingDirectory, this.options.manifestsTarHash, "reachability", this.options.useUnreachableFromPrecomputation);
246024
246050
  this.sendProgress("SCAN_FOR_VULNERABILITIES", false, ".", ".");
246025
246051
  const subProjects = Object.entries(ecosystemToWorkspaceToAnalysisData).flatMap(([ecosystem, workspaceToAnalysisData]) => {
246026
246052
  return Object.entries(workspaceToAnalysisData).map(([workspace, analysisData]) => {
@@ -246245,7 +246271,9 @@ Subproject: ${subproject}`);
246245
246271
  "timeout",
246246
246272
  "analysisTimeout",
246247
246273
  "concurrency",
246248
- "excludeDirs"
246274
+ "excludeDirs",
246275
+ "useUnreachableFromPrecomputation",
246276
+ "minSeverity"
246249
246277
  ]), null, 2));
246250
246278
  logger.info("available memory: %i MiB", os.freemem() / (1024 * 1024));
246251
246279
  try {
@@ -246323,9 +246351,11 @@ Subproject: ${subproject}`);
246323
246351
  const workspaceToAugmentedVulnerabilities = Object.fromEntries(await asyncMap(workspaces, async (workspacePath, index2) => {
246324
246352
  analysisStarting?.(workspacePath, index2 + 1, totalWorkspaces);
246325
246353
  const vulnerabilities = workspaceToVulnerabilities[workspacePath] ?? [];
246354
+ logger.info(`Process workspace ${workspacePath} with ${vulnerabilities.length} ${pluralize(vulnerabilities.length, "vulnerability")}`);
246326
246355
  try {
246327
246356
  const dataForAnalysis = workspacePathToDataForAnalysis[workspacePath];
246328
- const [vulnerabilitiesToAnalyze, vulnerabilitiesBelowThreshold] = this.options.minSeverity ? partition(vulnerabilities, (v) => !v.severity || shouldAnalyzeBasedOnSeverity(v.severity, this.options.minSeverity)) : [vulnerabilities, []];
246357
+ const [vulnsSatisfyingThreshold, vulnerabilitiesBelowThreshold] = this.options.minSeverity ? partition(vulnerabilities, (v) => !v.severity || shouldAnalyzeBasedOnSeverity(v.severity, this.options.minSeverity)) : [vulnerabilities, []];
246358
+ const [vulnsUnreachableFromPrecomputation, vulnerabilitiesToAnalyze] = this.options.useUnreachableFromPrecomputation ? partition(vulnsSatisfyingThreshold, (v) => "precomputedReachabilityResult" in v && v.precomputedReachabilityResult?.type === "unreachable") : [[], vulnsSatisfyingThreshold];
246329
246359
  const vulnerabilitiesBelowThresholdWithResults = vulnerabilitiesBelowThreshold.map((v) => ({
246330
246360
  ...v,
246331
246361
  results: {
@@ -246333,10 +246363,23 @@ Subproject: ${subproject}`);
246333
246363
  message: `Reachability analysis not run since the severity of the vulnerability (${v.severity}) is lower than the min severity threshold: ${this.options.minSeverity}`
246334
246364
  }
246335
246365
  }));
246366
+ const vulnerabilitiesDeterminedUnreachableFromPrecomputation = vulnsUnreachableFromPrecomputation.map((v) => ({
246367
+ ...v,
246368
+ results: {
246369
+ type: "precomputed",
246370
+ results: v.precomputedReachabilityResult
246371
+ }
246372
+ }));
246336
246373
  if (vulnerabilitiesBelowThreshold.length > 0) {
246337
- logger.info(`Reachability analysis not run for ${vulnerabilitiesBelowThreshold.length} vulnerabilities with severity level ${this.options.minSeverity} in workspace ${workspacePath}`);
246374
+ logger.info(`Reachability analysis skipped for ${vulnerabilitiesBelowThreshold.length} ${pluralize(vulnerabilities.length, "vulnerability")} with severity below the min severity threshold: ${this.options.minSeverity}`);
246375
+ }
246376
+ if (vulnsUnreachableFromPrecomputation.length > 0) {
246377
+ logger.info(`Reachability analysis skipped for ${vulnsUnreachableFromPrecomputation.length} ${pluralize(vulnerabilities.length, "vulnerability")} that ${vulnerabilities.length !== 1 ? "are" : "is"} already known to be unreachable from precomputed (Tier 2) reachability analysis`);
246378
+ }
246379
+ if (vulnerabilitiesToAnalyze.length > 0) {
246380
+ logger.info(`Running reachability analysis for ${vulnerabilitiesToAnalyze.length} ${pluralize(vulnerabilities.length, "vulnerability")}`);
246338
246381
  }
246339
- const augmentedVulnerabilitiesToAnalyze = reachabilitySupported && !this.shouldExcludeAnalyzingWorkspace(subprojectPath, workspacePath) ? await this.runReachabilityAnalysis(otherModulesCommunicator, subprojectPath, workspacePath, dataForAnalysis, ecosystem, vulnerabilitiesToAnalyze) : vulnerabilitiesToAnalyze.map((v) => ({
246382
+ const augmentedVulnerabilitiesToAnalyze = vulnerabilitiesToAnalyze.length === 0 ? [] : reachabilitySupported && !this.shouldExcludeAnalyzingWorkspace(subprojectPath, workspacePath) ? await this.runReachabilityAnalysis(otherModulesCommunicator, subprojectPath, workspacePath, dataForAnalysis, ecosystem, vulnerabilitiesToAnalyze) : vulnerabilitiesToAnalyze.map((v) => ({
246340
246383
  ...v,
246341
246384
  results: {
246342
246385
  type: "otherError",
@@ -246344,6 +246387,7 @@ Subproject: ${subproject}`);
246344
246387
  }
246345
246388
  }));
246346
246389
  const augmentedVulnerabilities = [
246390
+ ...vulnerabilitiesDeterminedUnreachableFromPrecomputation,
246347
246391
  ...augmentedVulnerabilitiesToAnalyze,
246348
246392
  ...vulnerabilitiesBelowThresholdWithResults
246349
246393
  ];
@@ -246430,6 +246474,7 @@ Subproject: ${subproject}`);
246430
246474
  const [vulnsWithActualAPIPatterns, result] = partition(vulnerabilities, (v) => Array.isArray(v.vulnerabilityAccessPaths));
246431
246475
  for (const v of result)
246432
246476
  v.results = typeof v.vulnerabilityAccessPaths === "string" ? { type: "noAnalysisCheck", message: v.vulnerabilityAccessPaths } : { type: "missingVulnerabilityPattern" };
246477
+ logger.info(`Reachability analysis not possible for ${result.length} of the ${pluralize(vulnerabilities.length, "vulnerability")}`);
246433
246478
  if (!vulnsWithActualAPIPatterns.length)
246434
246479
  return result;
246435
246480
  this.sendProgress("REACHABILITY_ANALYSIS", true, subprojectPath, workspacePath);
@@ -246472,7 +246517,7 @@ Subproject: ${subproject}`);
246472
246517
  }
246473
246518
  return {
246474
246519
  vulnerabilityUrl: v.url,
246475
- vulnerabilityUnreachableByPrecomputation: v.unreachableByPrecomputation ? v.unreachableByPrecomputation : "NOT_COMPUTED",
246520
+ vulnerabilityUnreachableByPrecomputation: "NOT_COMPUTED",
246476
246521
  // vulnChainDetails is always present
246477
246522
  // we only keep it as optional (potentially undefined) to
246478
246523
  // handle requests to the backend from old version of the CLI.
@@ -246597,7 +246642,7 @@ async function getGitDataToMetadataIfAvailable(rootWorkingDirectory) {
246597
246642
  // dist/index.js
246598
246643
  var program2 = new Command();
246599
246644
  var run2 = new Command();
246600
- run2.name("run").argument("<path>", "File system path to folder containing the project").option("-o, --output-dir <path>", "Write json report to <path>/coana-report.json").option("-d, --debug", "Enable debug logging", false).option("-s, --silent", "Silence all debug/warning output", false).option("--silent-spinner", "Silence spinner", "CI" in process.env || !process.stdin.isTTY).option("-p, --print-report", "Print the report to the console", false).option("--offline-database <path>", "Path to a coana-offline-db.json file for running the CLI without internet connectivity", void 0).option("-t, --timeout <timeout>", "Set API <timeout> in milliseconds to Coana backend.", "300000").option("-a, --analysis-timeout <timeout>", "Set <timeout> in seconds for each reachability analysis run").option("--memory-limit <memoryInMB>", "Set memory limit for analysis to <memoryInMB> megabytes of memory.", "8192").option("-c, --concurrency <concurrency>", "Set the maximum number of concurrent reachability analysis runs. It's recommended to choose a concurrency level that ensures that each analysis run has at least the --memory-limit amount of memory available.", "1").option("--api-key <key>", "Set the Coana dashboard API key. By setting you also enable the dashboard integration.").addOption(new Option("--write-report-to-file", "Write the report dashboard-compatible report to dashboard-report.json. This report may help the Coana team debug issues with the report insertion mechanism.").default(false).hideHelp()).option("--project-name <repoName>", "Set the name of the repository. Used for dashboard integration.").option("--repo-url <repoUrl>", "Set the URL of the repository. Used for dashboard integration.").option("--include-dirs <relativeDirs...>", "globs for directories to include from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, projects that are not included may still be scanned if they are referenced from included projects.").option("--exclude-dirs <relativeDirs...>", "globs for directories to exclude from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, excluded projects may still be scanned if they are referenced from non-excluded projects.").option("--disable-analysis-splitting", "Limits Coana to at most 1 reachability analysis run per workspace").option("--print-analysis-log-file", "Store log output from the JavaScript/TypeScript reachability analysis in the file js-analysis.log file in the root of each workspace", false).option("--entry-points <entryPoints...>", "List of files to analyze for root workspace. The reachability analysis automatically analyzes all files used by the entry points. If not provided, all JavaScript and TypeScript files are considered entry points. For non-root workspaces, all JavaScript and TypeScript files are analyzed as well.").option("--include-projects-with-no-reachability-support", "Also runs Coana on projects where we support traditional SCA, but does not yet support reachability analysis.", false).option("--ecosystems <ecosystems...>", "List of ecosystems to analyze (space-separated). Currently NPM, PIP, MAVEN, NUGET and GO are supported. Default is all supported ecosystems.").addOption(new Option("--purl-types <purlTypes...>", "List of PURL types to analyze (space-separated). Currently npm, pypi, maven, nuget, golang and cargo are supported. Default is all supported purl types.").hideHelp()).option("--changed-files <files...>", "List of files that have changed. If provided, Coana only analyzes workspaces and modules that contain changed files.").option("--disable-report-submission", "Disable the submission of the report to the Coana dashboard. Used by the pipeline blocking feature.", false).option("--disable-analytics-sharing", "Disable analytics sharing.", false).option("--provider-project <path>", "File system path to folder containing the provider project (Only supported for Maven, Gradle, and SBT)").option("--provider-workspaces <dirs...>", "List of workspaces that build the provided runtime environment (Only supported for Maven, Gradle, and SBT)", (paths) => paths.split(" ")).option("--lightweight-reachability", "Runs Coana in lightweight mode. This increases analysis speed but also raises the risk of Coana misclassifying the reachability of certain complex vulnerabilities. Recommended only for use with Coana Guardrail mode.", false).addOption(new Option("--run-without-docker", "Run package managers and reachability analyzers without using docker").default(process.env.RUN_WITHOUT_DOCKER === "true").hideHelp()).addOption(new Option("--run-env <env>", "Specifies the environment in which the CLI is run. So far only MANAGED_SCAN and UNKNOWN are supported.").default("UNKNOWN").choices(["UNKNOWN", "MANAGED_SCAN"]).hideHelp()).addOption(new Option("--guardrail-mode", "Run Coana in guardrail mode. This mode is used to prevent new reachable vulnerabilities from being introduced into the codebase. Usually run as a CI check when pushing new commits to a pull request.")).option("--ignore-failing-workspaces", "Continue processing when a workspace fails instead of exiting. Failed workspaces will be logged at termination.", false).addOption(new Option("--socket-mode <output-file>", "Run Coana in socket mode and write report to <output-file>").hideHelp()).addOption(new Option("--manifests-tar-hash <hash>", "Hash of the tarball containing all manifest files already uploaded to Socket. If provided, Socket will be used for computing dependency trees.").hideHelp()).option("--skip-cache-usage", "Do not attempt to use cached analysis configuration from previous runs", false).addOption(new Option("--min-severity <severity>", "Set the minimum severity of vulnerabilities to analyze. Supported severities are info, low, moderate, high and critical.").choices(["info", "INFO", "low", "LOW", "moderate", "MODERATE", "high", "HIGH", "critical", "CRITICAL"])).version(version2).configureHelp({ sortOptions: true }).action(async (path2, options) => {
246645
+ run2.name("run").argument("<path>", "File system path to folder containing the project").option("-o, --output-dir <path>", "Write json report to <path>/coana-report.json").option("-d, --debug", "Enable debug logging", false).option("-s, --silent", "Silence all debug/warning output", false).option("--silent-spinner", "Silence spinner", "CI" in process.env || !process.stdin.isTTY).option("-p, --print-report", "Print the report to the console", false).option("--offline-database <path>", "Path to a coana-offline-db.json file for running the CLI without internet connectivity", void 0).option("-t, --timeout <timeout>", "Set API <timeout> in milliseconds to Coana backend.", "300000").option("-a, --analysis-timeout <timeout>", "Set <timeout> in seconds for each reachability analysis run").option("--memory-limit <memoryInMB>", "Set memory limit for analysis to <memoryInMB> megabytes of memory.", "8192").option("-c, --concurrency <concurrency>", "Set the maximum number of concurrent reachability analysis runs. It's recommended to choose a concurrency level that ensures that each analysis run has at least the --memory-limit amount of memory available.", "1").option("--api-key <key>", "Set the Coana dashboard API key. By setting you also enable the dashboard integration.").addOption(new Option("--write-report-to-file", "Write the report dashboard-compatible report to dashboard-report.json. This report may help the Coana team debug issues with the report insertion mechanism.").default(false).hideHelp()).option("--project-name <repoName>", "Set the name of the repository. Used for dashboard integration.").option("--repo-url <repoUrl>", "Set the URL of the repository. Used for dashboard integration.").option("--include-dirs <relativeDirs...>", "globs for directories to include from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, projects that are not included may still be scanned if they are referenced from included projects.").option("--exclude-dirs <relativeDirs...>", "globs for directories to exclude from the detection of subprojects (space-separated)(use relative paths from the project root). Notice, excluded projects may still be scanned if they are referenced from non-excluded projects.").option("--disable-analysis-splitting", "Limits Coana to at most 1 reachability analysis run per workspace").option("--print-analysis-log-file", "Store log output from the JavaScript/TypeScript reachability analysis in the file js-analysis.log file in the root of each workspace", false).option("--entry-points <entryPoints...>", "List of files to analyze for root workspace. The reachability analysis automatically analyzes all files used by the entry points. If not provided, all JavaScript and TypeScript files are considered entry points. For non-root workspaces, all JavaScript and TypeScript files are analyzed as well.").option("--include-projects-with-no-reachability-support", "Also runs Coana on projects where we support traditional SCA, but does not yet support reachability analysis.", false).option("--ecosystems <ecosystems...>", "List of ecosystems to analyze (space-separated). Currently NPM, PIP, MAVEN, NUGET and GO are supported. Default is all supported ecosystems.").addOption(new Option("--purl-types <purlTypes...>", "List of PURL types to analyze (space-separated). Currently npm, pypi, maven, nuget, golang and cargo are supported. Default is all supported purl types.").hideHelp()).option("--changed-files <files...>", "List of files that have changed. If provided, Coana only analyzes workspaces and modules that contain changed files.").option("--disable-report-submission", "Disable the submission of the report to the Coana dashboard. Used by the pipeline blocking feature.", false).option("--disable-analytics-sharing", "Disable analytics sharing.", false).option("--provider-project <path>", "File system path to folder containing the provider project (Only supported for Maven, Gradle, and SBT)").option("--provider-workspaces <dirs...>", "List of workspaces that build the provided runtime environment (Only supported for Maven, Gradle, and SBT)", (paths) => paths.split(" ")).option("--lightweight-reachability", "Runs Coana in lightweight mode. This increases analysis speed but also raises the risk of Coana misclassifying the reachability of certain complex vulnerabilities. Recommended only for use with Coana Guardrail mode.", false).addOption(new Option("--run-without-docker", "Run package managers and reachability analyzers without using docker").default(process.env.RUN_WITHOUT_DOCKER === "true").hideHelp()).addOption(new Option("--run-env <env>", "Specifies the environment in which the CLI is run. So far only MANAGED_SCAN and UNKNOWN are supported.").default("UNKNOWN").choices(["UNKNOWN", "MANAGED_SCAN"]).hideHelp()).addOption(new Option("--guardrail-mode", "Run Coana in guardrail mode. This mode is used to prevent new reachable vulnerabilities from being introduced into the codebase. Usually run as a CI check when pushing new commits to a pull request.")).option("--ignore-failing-workspaces", "Continue processing when a workspace fails instead of exiting. Failed workspaces will be logged at termination.", false).addOption(new Option("--socket-mode <output-file>", "Run Coana in socket mode and write report to <output-file>").hideHelp()).addOption(new Option("--manifests-tar-hash <hash>", "Hash of the tarball containing all manifest files already uploaded to Socket. If provided, Socket will be used for computing dependency trees.").hideHelp()).option("--skip-cache-usage", "Do not attempt to use cached analysis configuration from previous runs", false).addOption(new Option("--min-severity <severity>", "Set the minimum severity of vulnerabilities to analyze. Supported severities are info, low, moderate, high and critical.").choices(["info", "INFO", "low", "LOW", "moderate", "MODERATE", "high", "HIGH", "critical", "CRITICAL"])).option("--use-unreachable-from-precomputation", "Skip the reachability analysis for vulnerabilities that are already known to be unreachable from the precomputed reachability analysis (Tier 2).", false).version(version2).configureHelp({ sortOptions: true }).action(async (path2, options) => {
246601
246646
  process.env.DOCKER_IMAGE_TAG ??= version2;
246602
246647
  options.ecosystems = options.ecosystems?.map((e) => e.toUpperCase());
246603
246648
  options.minSeverity = options.minSeverity?.toUpperCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coana-tech/cli",
3
- "version": "14.12.72",
3
+ "version": "14.12.73",
4
4
  "description": "Coana CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -80792,6 +80792,9 @@ function hasReachableMatches(detectedOccurrences) {
80792
80792
  // ../web-compat-utils/src/vulnerability-reachability.ts
80793
80793
  function getVulnReachability(c) {
80794
80794
  if (c.type === "noAnalysisCheck") return "REACHABLE";
80795
+ if (c.type === "precomputed") {
80796
+ return c.results.type === "unreachable" ? "UNREACHABLE" : "UNKNOWN";
80797
+ }
80795
80798
  if (c.type !== "success") {
80796
80799
  return "UNKNOWN";
80797
80800
  }
@@ -112171,7 +112174,7 @@ async function analyzeWithHeuristics(state, vulns, heuristicsInOrder, doNotRecom
112171
112174
  try {
112172
112175
  newAnalysisRunListener();
112173
112176
  const initialBucketContainingAllVulns = buckets.length === 1 && buckets[0] === bucket;
112174
- logger.info(`Running analysis for ${vulnsForBucket.length}/${vulnerabilities.length} vulnerabilities.`);
112177
+ logger.info(`Running full reachability analysis for ${vulnsForBucket.length}/${vulnerabilities.length} vulnerabilities.`);
112175
112178
  const result = await codeAwareScanner.runAnalysis(vulnsForBucket, bucket.heuristic, initialBucketContainingAllVulns, experiment);
112176
112179
  const allowSplitInBuckets = !disableBucketing && bucket.heuristic.splitAnalysisInBuckets && !state.otherAnalysisOptions.disableBucketing && vulnDepIdentifiers.length > 1 && (result.type === "error" || result.reachedDependencies);
112177
112180
  if (result.type === "success") {
@@ -112461,6 +112464,18 @@ var import_picomatch4 = __toESM(require_picomatch4(), 1);
112461
112464
  import { existsSync as existsSync13 } from "fs";
112462
112465
  import { rm as rm6 } from "fs/promises";
112463
112466
  import { resolve as resolve20 } from "path";
112467
+
112468
+ // ../web-compat-utils/src/pluralize.ts
112469
+ function pluralize(count, word) {
112470
+ if (word === "vulnerability") {
112471
+ return count === 1 ? "vulnerability" : "vulnerabilities";
112472
+ } else {
112473
+ word;
112474
+ throw new Error(`Invalid word: ${word}`);
112475
+ }
112476
+ }
112477
+
112478
+ // dist/analyzers/npm-analyzer.js
112464
112479
  var { partition: partition3, memoize: memoize2 } = import_lodash19.default;
112465
112480
  var NpmAnalyzer = class {
112466
112481
  state;
@@ -112486,6 +112501,7 @@ var NpmAnalyzer = class {
112486
112501
  try {
112487
112502
  const vulnerabilityScanner = new JSCodeAwareVulnerabilityScanner(this.state.rootWorkingDir, this.projectDir, this.state.reachabilityAnalysisOptions);
112488
112503
  await vulnerabilityScanner.prepareDependencies(this.state, heuristicsInOrder[0]);
112504
+ logger.info(`Running import reachability analysis for ${vulns.length} ${pluralize(vulns.length, "vulnerability")}`);
112489
112505
  let reachable;
112490
112506
  try {
112491
112507
  statusUpdater?.("Running import reachability analysis");
@@ -112519,6 +112535,7 @@ var NpmAnalyzer = class {
112519
112535
  return /[*?{}]/.test(modGlob) ? checkGlob(modGlob) : !reachableModuleNames.has(modGlob);
112520
112536
  });
112521
112537
  }));
112538
+ logger.info(`Found ${unreachableVulns.length} ${pluralize(unreachableVulns.length, "vulnerability")} that ${unreachableVulns.length !== 1 ? "are" : "is"} unreachable from the import graph`);
112522
112539
  }
112523
112540
  const res = otherVulns.length ? await analyzeWithHeuristics(this.state, otherVulns, heuristicsInOrder, true, vulnerabilityScanner, analysisMetadataCollector, statusUpdater) : [];
112524
112541
  if (unreachableVulns.length) {