@coana-tech/cli 14.12.210 → 15.0.2

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
@@ -199708,7 +199708,7 @@ var {
199708
199708
  } = import_index.default;
199709
199709
 
199710
199710
  // dist/index.js
199711
- import { mkdir as mkdir7, mkdtemp as mkdtemp2, readFile as readFile38, rm as rm3, writeFile as writeFile17 } from "fs/promises";
199711
+ import { mkdir as mkdir7, mkdtemp as mkdtemp2, readFile as readFile38, rm as rm4, writeFile as writeFile17 } from "fs/promises";
199712
199712
  import { tmpdir as tmpdir5 } from "os";
199713
199713
  import { dirname as dirname26, join as join35, resolve as resolve44 } from "path";
199714
199714
 
@@ -203822,6 +203822,8 @@ function getPurlType(ecosystem) {
203822
203822
  return "cargo" /* CARGO */;
203823
203823
  case "RUBYGEMS":
203824
203824
  return "gem" /* GEM */;
203825
+ case "COMPOSER":
203826
+ return "composer" /* COMPOSER */;
203825
203827
  default:
203826
203828
  throw Error(`Unsupported ecosystem: ${ecosystem}`);
203827
203829
  }
@@ -203842,6 +203844,8 @@ function getAdvisoryEcosystemFromPurlType(purlType) {
203842
203844
  return "RUST";
203843
203845
  case "gem" /* GEM */:
203844
203846
  return "RUBYGEMS";
203847
+ case "composer" /* COMPOSER */:
203848
+ return "COMPOSER";
203845
203849
  default:
203846
203850
  return void 0;
203847
203851
  }
@@ -203892,6 +203896,8 @@ function getNameFromNamespaceAndName(purlType, namespace2, name2) {
203892
203896
  return name2;
203893
203897
  case "golang" /* GOLANG */:
203894
203898
  return namespace2 ? `${namespace2}/${name2}` : name2;
203899
+ case "composer" /* COMPOSER */:
203900
+ return namespace2 ? `${namespace2}/${name2}` : name2;
203895
203901
  default:
203896
203902
  return name2;
203897
203903
  }
@@ -212117,7 +212123,7 @@ var ecosystemToSupport = {
212117
212123
  }
212118
212124
  },
212119
212125
  COMPOSER: {
212120
- supportStatus: "no-support",
212126
+ supportStatus: "reachability-analysis-support",
212121
212127
  fixesComputation: false,
212122
212128
  packageManagers: {
212123
212129
  COMPOSER: {
@@ -231970,6 +231976,16 @@ import assert15 from "node:assert";
231970
231976
  import { platform as platform8 } from "os";
231971
231977
  import { join as join27, posix as posix2, relative as relative18, sep as sep3 } from "path";
231972
231978
 
231979
+ // ../web-compat-utils/dist/analysis-error-keys.js
231980
+ var InstallError = class extends Error {
231981
+ failedPackages;
231982
+ constructor(failedPackages) {
231983
+ super(`Failed to install packages: ${failedPackages.join(", ")}`);
231984
+ this.failedPackages = failedPackages;
231985
+ this.name = "InstallError";
231986
+ }
231987
+ };
231988
+
231973
231989
  // ../utils/src/tmp-file.ts
231974
231990
  import { rm, mkdtemp, cp as cp4, lstat as lstat2 } from "fs/promises";
231975
231991
  import { tmpdir as tmpdir4 } from "os";
@@ -232170,7 +232186,7 @@ var ecosystemToSupport2 = {
232170
232186
  }
232171
232187
  },
232172
232188
  COMPOSER: {
232173
- supportStatus: "no-support",
232189
+ supportStatus: "reachability-analysis-support",
232174
232190
  fixesComputation: false,
232175
232191
  packageManagers: {
232176
232192
  COMPOSER: {
@@ -232364,6 +232380,7 @@ var pullDockerImage = memoize(async (image) => {
232364
232380
  var ONE_DAY_IN_MS = 24 * 60 * 60 * 1e3;
232365
232381
  var TMP_DIR_IN_DOCKER = "/coana-tmp";
232366
232382
  var SOCKET_PATH_IN_DOCKER = "/coana.sock";
232383
+ var PREINSTALL_DIR_IN_DOCKER = "/coana-preinstall";
232367
232384
  var OtherModulesCommunicator = class {
232368
232385
  constructor(rootWorkingDir, options, apiKey) {
232369
232386
  this.rootWorkingDir = rootWorkingDir;
@@ -232400,6 +232417,8 @@ var OtherModulesCommunicator = class {
232400
232417
  switch (cmd) {
232401
232418
  case "runReachabilityAnalysis":
232402
232419
  return "Running reachability analysis";
232420
+ case "installDependencies":
232421
+ return "Pre-installing dependencies";
232403
232422
  case "runOnDependencyChain":
232404
232423
  return "Running reachability analysis on dependency chain";
232405
232424
  case "runOnPackageRegistryPackage":
@@ -232471,7 +232490,7 @@ var OtherModulesCommunicator = class {
232471
232490
  );
232472
232491
  return JSON.parse(await readFile32(outputFilePathThisProcess, "utf-8")).result;
232473
232492
  }
232474
- async runReachabilityAnalyzerCommand(commandName, ecosystem, subprojectPath, workspacePath, args2, env, rootWorkingDirOverride, displaySubprojectPath) {
232493
+ async runReachabilityAnalyzerCommand(commandName, ecosystem, subprojectPath, workspacePath, args2, env, rootWorkingDirOverride, displaySubprojectPath, extraDockerArgs) {
232475
232494
  const tmpDir = await this.getTmpDirForSubproject(displaySubprojectPath ?? subprojectPath);
232476
232495
  const effectiveRootWorkingDir = rootWorkingDirOverride ?? this.rootWorkingDir;
232477
232496
  const effectiveSubprojectPath = rootWorkingDirOverride ?? subprojectPath;
@@ -232518,7 +232537,8 @@ var OtherModulesCommunicator = class {
232518
232537
  finalArgs,
232519
232538
  subprojectPath,
232520
232539
  tmpDir,
232521
- env
232540
+ env,
232541
+ extraDockerArgs
232522
232542
  );
232523
232543
  }
232524
232544
  if (!succeeded)
@@ -232528,7 +232548,7 @@ var OtherModulesCommunicator = class {
232528
232548
  }
232529
232549
  );
232530
232550
  }
232531
- async runReachabilityAnalyzerCommandWithOutput(commandName, ecosystem, subprojectPath, workspacePath, args2, env, rootWorkingDirOverride, displaySubprojectPath) {
232551
+ async runReachabilityAnalyzerCommandWithOutput(commandName, ecosystem, subprojectPath, workspacePath, args2, env, rootWorkingDirOverride, displaySubprojectPath, extraDockerArgs) {
232532
232552
  const tmpDir = await this.getTmpDirForSubproject(displaySubprojectPath ?? subprojectPath);
232533
232553
  const outputFileName = `${v4_default()}-${commandName}-output.json`;
232534
232554
  const outputFilePathThisProcess = join27(tmpDir, outputFileName);
@@ -232541,16 +232561,22 @@ var OtherModulesCommunicator = class {
232541
232561
  [...args2, "-o", outputFilePathOtherProcess],
232542
232562
  env,
232543
232563
  rootWorkingDirOverride,
232544
- displaySubprojectPath
232564
+ displaySubprojectPath,
232565
+ extraDockerArgs
232545
232566
  );
232546
- return JSON.parse(await readFile32(outputFilePathThisProcess, "utf-8")).result;
232567
+ const output = JSON.parse(await readFile32(outputFilePathThisProcess, "utf-8"));
232568
+ if (output.error?.type === "InstallError") {
232569
+ throw new InstallError(output.error.failedPackages);
232570
+ }
232571
+ return output.result;
232547
232572
  }
232548
- async runInDocker(ecosystem, image, entryPoint, commandName, args2, subprojectPath, tmpDir, env = process.env) {
232573
+ async runInDocker(ecosystem, image, entryPoint, commandName, args2, subprojectPath, tmpDir, env = process.env, extraDockerArgs) {
232549
232574
  if (!await pullDockerImage(image)) return false;
232550
232575
  const envArgs = Object.keys(env).filter((key) => DOCKER_ENV_WHITE_LIST.some((whiteListedKey) => key.includes(whiteListedKey))).flatMap((key) => ["-e", key]);
232551
232576
  const cmd = cmdt`docker run --pull=never --rm -v ${this.rootWorkingDir}:/project -v ${tmpDir}:${TMP_DIR_IN_DOCKER}
232552
232577
  -v=${this.options.coanaSocketPath}:${SOCKET_PATH_IN_DOCKER}
232553
232578
  ${await getEcosystemSpecificDockerArgs(ecosystem)}
232579
+ ${extraDockerArgs ?? []}
232554
232580
  ${envArgs} ${image} ${entryPoint} ${commandName} ${args2}`;
232555
232581
  return execPipeAndLogOnFailure2(cmd, subprojectPath, { env, timeout: ONE_DAY_IN_MS });
232556
232582
  }
@@ -232611,7 +232637,42 @@ var OtherModulesCommunicator = class {
232611
232637
  workspacePaths
232612
232638
  );
232613
232639
  }
232614
- async runReachabilityAnalysis(subprojectPath, workspacePath, workspaceData, ecosystem, vulnerabilities, reachabilityAnalysisOptions, otherAnalysisOptions, rootWorkingDirOverride, displaySubprojectPath) {
232640
+ async installDependencies(subprojectPath, workspacePath, workspaceData, ecosystem, vulnerabilities, reachabilityAnalysisOptions, otherAnalysisOptions, preinstallDir) {
232641
+ const tmpDir = await this.getTmpDirForSubproject(subprojectPath);
232642
+ if (isNexeMode()) {
232643
+ await extractAllToolsForNexeMode();
232644
+ }
232645
+ const inputFileName = `${v4_default()}-installDependencies-input.json`;
232646
+ const inputFileThisProcess = join27(tmpDir, inputFileName);
232647
+ const inputFileOtherProcess = this.options.runWithoutDocker ? inputFileThisProcess : posix2.join(TMP_DIR_IN_DOCKER, inputFileName);
232648
+ const preinstallDirOtherProcess = this.options.runWithoutDocker ? preinstallDir : PREINSTALL_DIR_IN_DOCKER;
232649
+ const extraDockerArgs = this.options.runWithoutDocker ? void 0 : ["-v", `${preinstallDir}:${PREINSTALL_DIR_IN_DOCKER}`];
232650
+ await writeFile12(
232651
+ inputFileThisProcess,
232652
+ JSON.stringify({
232653
+ workspaceData,
232654
+ vulnerabilities,
232655
+ reachabilityAnalysisOptions,
232656
+ otherAnalysisOptions
232657
+ })
232658
+ );
232659
+ return this.runReachabilityAnalyzerCommandWithOutput(
232660
+ "installDependencies",
232661
+ ecosystem,
232662
+ subprojectPath,
232663
+ workspacePath,
232664
+ argt`-i ${inputFileOtherProcess} --preinstall-dir ${preinstallDirOtherProcess}`,
232665
+ {
232666
+ ...process.env,
232667
+ COANA_REPORT_ID: this.options.reportId,
232668
+ COANA_API_KEY: this.apiKey.type === "present" ? this.apiKey.value : ""
232669
+ },
232670
+ void 0,
232671
+ void 0,
232672
+ extraDockerArgs
232673
+ );
232674
+ }
232675
+ async runReachabilityAnalysis(subprojectPath, workspacePath, workspaceData, ecosystem, vulnerabilities, reachabilityAnalysisOptions, otherAnalysisOptions, rootWorkingDirOverride, displaySubprojectPath, preinstallDir) {
232615
232676
  const tmpDir = await this.getTmpDirForSubproject(displaySubprojectPath ?? subprojectPath);
232616
232677
  if (isNexeMode()) {
232617
232678
  await extractAllToolsForNexeMode();
@@ -232619,6 +232680,8 @@ var OtherModulesCommunicator = class {
232619
232680
  const inputFileName = `${v4_default()}-runReachabilityAnalysis-input.json`;
232620
232681
  const inputFileThisProcess = join27(tmpDir, inputFileName);
232621
232682
  const inputFileOtherProcess = this.options.runWithoutDocker ? inputFileThisProcess : posix2.join(TMP_DIR_IN_DOCKER, inputFileName);
232683
+ const preinstallDirOtherProcess = preinstallDir ? this.options.runWithoutDocker ? preinstallDir : PREINSTALL_DIR_IN_DOCKER : void 0;
232684
+ const extraDockerArgs = preinstallDir && !this.options.runWithoutDocker ? ["-v", `${preinstallDir}:${PREINSTALL_DIR_IN_DOCKER}`] : void 0;
232622
232685
  await writeFile12(
232623
232686
  inputFileThisProcess,
232624
232687
  JSON.stringify({
@@ -232628,19 +232691,21 @@ var OtherModulesCommunicator = class {
232628
232691
  otherAnalysisOptions
232629
232692
  })
232630
232693
  );
232694
+ const preinstallArgs = preinstallDirOtherProcess ? argt`--preinstall-dir ${preinstallDirOtherProcess}` : [];
232631
232695
  return this.runReachabilityAnalyzerCommandWithOutput(
232632
232696
  "runReachabilityAnalysis",
232633
232697
  ecosystem,
232634
232698
  subprojectPath,
232635
232699
  workspacePath,
232636
- argt`-i ${inputFileOtherProcess}`,
232700
+ [...argt`-i ${inputFileOtherProcess}`, ...preinstallArgs],
232637
232701
  {
232638
232702
  ...process.env,
232639
232703
  COANA_REPORT_ID: this.options.reportId,
232640
232704
  COANA_API_KEY: this.apiKey.type === "present" ? this.apiKey.value : ""
232641
232705
  },
232642
232706
  rootWorkingDirOverride,
232643
- displaySubprojectPath
232707
+ displaySubprojectPath,
232708
+ extraDockerArgs
232644
232709
  );
232645
232710
  }
232646
232711
  };
@@ -234897,7 +234962,7 @@ function prettyApplyFixesTo(applyFixesToOption) {
234897
234962
  // dist/cli-core.js
234898
234963
  import assert16 from "node:assert";
234899
234964
  import { existsSync as existsSync30, writeFileSync as writeFileSync3 } from "fs";
234900
- import { mkdir as mkdir6, writeFile as writeFile15 } from "fs/promises";
234965
+ import { mkdir as mkdir6, rm as rm3, writeFile as writeFile15 } from "fs/promises";
234901
234966
  var import_lodash15 = __toESM(require_lodash(), 1);
234902
234967
  import os2 from "os";
234903
234968
  import { join as join34, relative as relative22, resolve as resolve42 } from "path";
@@ -235363,6 +235428,20 @@ function isShortestPath(root3, vulnPath) {
235363
235428
  var FAILED_TO_INSTALL_PACKAGE_KEY = "[UNABLE_TO_INSTALL_PACKAGE_ERROR]: ";
235364
235429
  var CLI_ANALYSIS_ERROR_MESSAGE = "Sharing log due to analysis error";
235365
235430
  var ANALYSIS_LOW_CONFIDENCE_MESSAGE = "Analysis had low confidence in result";
235431
+ var InstallError2 = class extends Error {
235432
+ constructor(failedPackages) {
235433
+ super(`Failed to install packages: ${failedPackages.join(", ")}`);
235434
+ this.failedPackages = failedPackages;
235435
+ this.name = "InstallError";
235436
+ }
235437
+ };
235438
+ var AnalysisHaltError = class extends Error {
235439
+ constructor(errorMessages) {
235440
+ super(`Analysis failed with errors: ${errorMessages.join("; ")}`);
235441
+ this.errorMessages = errorMessages;
235442
+ this.name = "AnalysisHaltError";
235443
+ }
235444
+ };
235366
235445
 
235367
235446
  // ../web-compat-utils/src/pluralize.ts
235368
235447
  function pluralize(count, word) {
@@ -236280,9 +236359,10 @@ function toSocketReachabilitySchema(vulnerability) {
236280
236359
  if (codeAwareScanResult.type === "analysisError") {
236281
236360
  return { type: "error", error: codeAwareScanResult.message };
236282
236361
  }
236362
+ if (codeAwareScanResult.type === "skippedUnsupportedEcosystem") {
236363
+ return { type: "unknown", reason: codeAwareScanResult.message };
236364
+ }
236283
236365
  if (codeAwareScanResult.type === "otherError") {
236284
- if (codeAwareScanResult.message.includes("Reachability analysis for languages using"))
236285
- return { type: "unknown", reason: codeAwareScanResult.message };
236286
236366
  return { type: "error", error: codeAwareScanResult.message };
236287
236367
  }
236288
236368
  if (codeAwareScanResult.type === "success") {
@@ -251742,7 +251822,7 @@ async function onlineScan(dependencyTree, apiKey, timeout) {
251742
251822
  }
251743
251823
 
251744
251824
  // dist/version.js
251745
- var version3 = "14.12.210";
251825
+ var version3 = "15.0.2";
251746
251826
 
251747
251827
  // dist/cli-core.js
251748
251828
  var { mapValues, omit, partition, pickBy: pickBy2 } = import_lodash15.default;
@@ -251956,8 +252036,11 @@ var CliCore = class {
251956
252036
  await this.cleanupLogging();
251957
252037
  } catch (e) {
251958
252038
  await this.spinner.fail();
251959
- logger.error("CLI failed with error:", e);
251960
- await this.shareErrorLogWithBackend(e, true, "cli-error");
252039
+ const isExpectedHalt = e instanceof InstallError2 || e instanceof AnalysisHaltError;
252040
+ if (!isExpectedHalt) {
252041
+ logger.error("CLI failed with error:", e);
252042
+ }
252043
+ await this.shareErrorLogWithBackend(e, !isExpectedHalt, "cli-error");
251961
252044
  await this.cleanupLogging();
251962
252045
  process.exit(1);
251963
252046
  }
@@ -252005,46 +252088,79 @@ var CliCore = class {
252005
252088
  logger.info(bold(` ${ecosystem} (${workspaces.length}):`));
252006
252089
  workspaces.forEach((workspace) => logger.info(bold(` ${workspace}`)));
252007
252090
  });
252091
+ let preinstallDir;
252092
+ try {
252093
+ logger.info(bold("Pre-installing dependencies for all projects..."));
252094
+ preinstallDir = await createTmpDirectory("coana-preinstall");
252095
+ await this.preInstallAllDependencies(preinstallDir, ecosystemToWorkspaceToAnalysisData, ecosystemToWorkspaceToVulnerabilities, otherModulesCommunicator);
252096
+ logger.info(bold("All dependencies pre-installed successfully"));
252097
+ } catch (e) {
252098
+ if (this.options.reachContinueOnInstallErrors) {
252099
+ logger.info("Continuing with pre-installed dependencies despite errors");
252100
+ } else {
252101
+ if (preinstallDir) {
252102
+ await rm3(preinstallDir, { recursive: true, force: true }).catch((err) => logger.debug(`Failed to cleanup preinstall dir: ${err instanceof Error ? err.message : String(err)}`));
252103
+ }
252104
+ throw e;
252105
+ }
252106
+ }
252008
252107
  const vulnsWithResults = [];
252009
252108
  const allWorkspaceDiagnostics = [];
252010
252109
  const allWorkspaceTimings = /* @__PURE__ */ new Map();
252011
252110
  const allEcosystems = Object.entries(ecosystemToWorkspaceToAnalysisData);
252012
252111
  const totalEcosystems = allEcosystems.length;
252013
252112
  let currentOverallWorkspace = 0;
252014
- for (const [ecosystemIndex, [ecosystem, workspaceToAnalysisData]] of allEcosystems.entries()) {
252015
- this.sendProgress("RUN_ON_SUBPROJECT", true, this.rootWorkingDirectory);
252016
- const isEcosystemToAnalyze = !this.options.purlTypes || this.options.purlTypes.some((purlType) => getAdvisoryEcosystemFromPurlType(purlType) === ecosystem);
252017
- if (!isEcosystemToAnalyze) {
252018
- logger.info(`Skipping reachability analysis for ecosystem ${getPurlType(ecosystem)} since it is not included in the list of ecosystems to analyze.`);
252019
- }
252020
- const { vulnerabilities, diagnostics, timings } = await this.runReachabilityAnalysisForWorkspaces(
252021
- workspaceToAnalysisData,
252022
- ecosystemToWorkspaceToVulnerabilities[ecosystem] ?? {},
252023
- {},
252024
- // We do not need the direct dependencies for socket mode
252025
- otherModulesCommunicator,
252026
- this.rootWorkingDirectory,
252027
- ecosystem,
252028
- ["NPM", "PIP", "GO", "MAVEN", "NUGET", "RUST", "RUBYGEMS"].includes(ecosystem) && isEcosystemToAnalyze,
252029
- (workspaceName, workspaceNumber, totalWorkspacesForCurrentEcosystem) => {
252030
- currentOverallWorkspace++;
252031
- logger.info(bold(`Analyzing ecosystem ${ecosystem} for project ${workspaceName} (${workspaceNumber}/${totalWorkspacesForCurrentEcosystem}) - Overall progress: Project ${currentOverallWorkspace}/${totalWorkspaces}, ecosystem ${ecosystemIndex + 1}/${totalEcosystems}`));
252032
- }
252033
- );
252034
- vulnsWithResults.push(...Object.values(vulnerabilities).flat());
252035
- for (const [workspacePath, workspaceDiagnostics] of Object.entries(diagnostics)) {
252036
- allWorkspaceDiagnostics.push({
252037
- // the workspace path is the subproject path in socket mode
252038
- subprojectPath: workspacePath ?? ".",
252039
- workspacePath: ".",
252040
- purl_type: getPurlType(ecosystem),
252041
- diagnostics: workspaceDiagnostics
252042
- });
252043
- if (timings[workspacePath] !== void 0) {
252044
- allWorkspaceTimings.set(`${ecosystem}:${workspacePath}`, timings[workspacePath]);
252113
+ try {
252114
+ for (const [ecosystemIndex, [ecosystem, workspaceToAnalysisData]] of allEcosystems.entries()) {
252115
+ this.sendProgress("RUN_ON_SUBPROJECT", true, this.rootWorkingDirectory);
252116
+ const isEcosystemToAnalyze = !this.options.purlTypes || this.options.purlTypes.some((purlType) => getAdvisoryEcosystemFromPurlType(purlType) === ecosystem);
252117
+ if (!isEcosystemToAnalyze) {
252118
+ logger.info(`Skipping reachability analysis for ecosystem ${getPurlType(ecosystem)} since it is not included in the list of ecosystems to analyze.`);
252119
+ }
252120
+ const ecosystemPreinstallDir = preinstallDir ? join34(preinstallDir, ecosystem) : void 0;
252121
+ const { vulnerabilities, diagnostics, timings } = await this.runReachabilityAnalysisForWorkspaces(
252122
+ workspaceToAnalysisData,
252123
+ ecosystemToWorkspaceToVulnerabilities[ecosystem] ?? {},
252124
+ {},
252125
+ // We do not need the direct dependencies for socket mode
252126
+ otherModulesCommunicator,
252127
+ this.rootWorkingDirectory,
252128
+ ecosystem,
252129
+ [
252130
+ "NPM",
252131
+ "PIP",
252132
+ "GO",
252133
+ "MAVEN",
252134
+ "NUGET",
252135
+ "RUST",
252136
+ "RUBYGEMS",
252137
+ ...process.env.COANA_ENABLE_PHP_ANALYSIS ? ["COMPOSER"] : []
252138
+ ].includes(ecosystem) && isEcosystemToAnalyze,
252139
+ (workspaceName, workspaceNumber, totalWorkspacesForCurrentEcosystem) => {
252140
+ currentOverallWorkspace++;
252141
+ logger.info(bold(`Analyzing ecosystem ${ecosystem} for project ${workspaceName} (${workspaceNumber}/${totalWorkspacesForCurrentEcosystem}) - Overall progress: Project ${currentOverallWorkspace}/${totalWorkspaces}, ecosystem ${ecosystemIndex + 1}/${totalEcosystems}`));
252142
+ },
252143
+ ecosystemPreinstallDir
252144
+ );
252145
+ vulnsWithResults.push(...Object.values(vulnerabilities).flat());
252146
+ for (const [workspacePath, workspaceDiagnostics] of Object.entries(diagnostics)) {
252147
+ allWorkspaceDiagnostics.push({
252148
+ // the workspace path is the subproject path in socket mode
252149
+ subprojectPath: workspacePath ?? ".",
252150
+ workspacePath: ".",
252151
+ purl_type: getPurlType(ecosystem),
252152
+ diagnostics: workspaceDiagnostics
252153
+ });
252154
+ if (timings[workspacePath] !== void 0) {
252155
+ allWorkspaceTimings.set(`${ecosystem}:${workspacePath}`, timings[workspacePath]);
252156
+ }
252045
252157
  }
252158
+ this.sendProgress("RUN_ON_SUBPROJECT", false, this.rootWorkingDirectory);
252159
+ }
252160
+ } finally {
252161
+ if (preinstallDir) {
252162
+ await rm3(preinstallDir, { recursive: true, force: true }).catch((err) => logger.debug(`Failed to cleanup preinstall dir: ${err instanceof Error ? err.message : String(err)}`));
252046
252163
  }
252047
- this.sendProgress("RUN_ON_SUBPROJECT", false, this.rootWorkingDirectory);
252048
252164
  }
252049
252165
  for (const vuln of vulnsWithResults) {
252050
252166
  if (vuln.codeAwareScanResult.type === "success" && vuln.codeAwareScanResult.lowConfidence === true && vuln.codeAwareScanResult.detectedOccurrences?.stacks?.length === 0) {
@@ -252055,6 +252171,15 @@ var CliCore = class {
252055
252171
  vuln.reachability = "UNKNOWN";
252056
252172
  }
252057
252173
  }
252174
+ if (!this.options.reachContinueOnAnalysisErrors) {
252175
+ const isInstallError = (msg) => msg.startsWith(FAILED_TO_INSTALL_PACKAGE_KEY);
252176
+ const errorMessages = vulnsWithResults.filter((v) => v.codeAwareScanResult.type === "analysisError" || v.codeAwareScanResult.type === "otherError").map((v) => v.codeAwareScanResult.message).filter((msg) => !this.options.reachContinueOnInstallErrors || !isInstallError(msg));
252177
+ if (errorMessages.length > 0) {
252178
+ const uniqueErrors = [...new Set(errorMessages)];
252179
+ this.logAnalysisErrors(errorMessages.length, uniqueErrors);
252180
+ throw new AnalysisHaltError(uniqueErrors);
252181
+ }
252182
+ }
252058
252183
  displayResultsSummary(vulnsWithResults, allWorkspaceTimings);
252059
252184
  displayWorkspaceDiagnosticsSummary(allWorkspaceDiagnostics, vulnsWithResults);
252060
252185
  await this.shareLogIfAnalysisError(vulnsWithResults);
@@ -252308,7 +252433,7 @@ Subproject: ${subproject}`);
252308
252433
  this.sendProgress("RUN_ON_SUBPROJECT", false, subProjAndWsPath.subprojectPath);
252309
252434
  }
252310
252435
  }
252311
- async runReachabilityAnalysisForWorkspaces(workspacePathToDataForAnalysis, workspaceToVulnerabilities, workspaceToDirectDependencies, otherModulesCommunicator, subprojectPath, ecosystem, reachabilitySupported, analysisStarting) {
252436
+ async runReachabilityAnalysisForWorkspaces(workspacePathToDataForAnalysis, workspaceToVulnerabilities, workspaceToDirectDependencies, otherModulesCommunicator, subprojectPath, ecosystem, reachabilitySupported, analysisStarting, preinstallDir) {
252312
252437
  const workspaces = Object.keys(workspacePathToDataForAnalysis);
252313
252438
  const totalWorkspaces = workspaces.length;
252314
252439
  const concurrency = Number(this.options.concurrency);
@@ -252373,6 +252498,8 @@ Subproject: ${subproject}`);
252373
252498
  [effectiveSubprojectPath, releaseDir] = await npmProjectDirPool.acquire();
252374
252499
  }
252375
252500
  try {
252501
+ const perProjectEcosystems = ["PIP", "RUBYGEMS", "COMPOSER"];
252502
+ const effectivePreinstallDir = preinstallDir && perProjectEcosystems.includes(ecosystem) ? join34(preinstallDir, workspacePath.replace(/\//g, "_")) : preinstallDir;
252376
252503
  const resAndDiagnostics = await this.runReachabilityAnalysis(
252377
252504
  otherModulesCommunicator,
252378
252505
  effectiveSubprojectPath,
@@ -252384,7 +252511,8 @@ Subproject: ${subproject}`);
252384
252511
  // When using temp copy for concurrent analysis, override rootWorkingDir
252385
252512
  npmProjectDirPool ? effectiveSubprojectPath : void 0,
252386
252513
  // Pass original subprojectPath for display when using temp copy
252387
- npmProjectDirPool ? subprojectPath : void 0
252514
+ npmProjectDirPool ? subprojectPath : void 0,
252515
+ effectivePreinstallDir
252388
252516
  );
252389
252517
  augmentedVulnerabilitiesToAnalyze = resAndDiagnostics.vulnerabilities;
252390
252518
  workspaceDiagnostics = resAndDiagnostics.diagnostics;
@@ -252395,7 +252523,7 @@ Subproject: ${subproject}`);
252395
252523
  augmentedVulnerabilitiesToAnalyze = vulnerabilitiesToAnalyze.map((v) => ({
252396
252524
  ...v,
252397
252525
  results: {
252398
- type: "otherError",
252526
+ type: "skippedUnsupportedEcosystem",
252399
252527
  message: `Reachability analysis for languages using ${ecosystem} not supported yet`
252400
252528
  }
252401
252529
  }));
@@ -252407,6 +252535,10 @@ Subproject: ${subproject}`);
252407
252535
  ];
252408
252536
  return [workspacePath, { vulnerabilities: augmentedVulnerabilities, diagnostics: workspaceDiagnostics }];
252409
252537
  } catch (e) {
252538
+ if (e instanceof InstallError2) {
252539
+ this.logInstallError(e.failedPackages, ecosystem);
252540
+ throw e;
252541
+ }
252410
252542
  logger.error(`${workspacePrefix}Reachability analysis failed for workspace ${workspacePath} in subproject ${subprojectPath}: ${e.message}`);
252411
252543
  return [
252412
252544
  workspacePath,
@@ -252483,6 +252615,141 @@ Subproject: ${subproject}`);
252483
252615
  }
252484
252616
  }
252485
252617
  }
252618
+ /**
252619
+ * Pre-installs all dependencies across all ecosystems and workspaces to the given dir.
252620
+ * If any installations fail, reports all failures and throws an InstallError.
252621
+ */
252622
+ async preInstallAllDependencies(preinstallDir, ecosystemToWorkspaceToAnalysisData, ecosystemToWorkspaceToVulnerabilities, otherModulesCommunicator) {
252623
+ const installTasks = [];
252624
+ for (const [ecosystem, workspaceToAnalysisData] of Object.entries(ecosystemToWorkspaceToAnalysisData)) {
252625
+ if (ecosystem === "GO" && this.options.reachContinueOnInstallErrors)
252626
+ continue;
252627
+ const ecosystemDir = join34(preinstallDir, ecosystem);
252628
+ await mkdir6(ecosystemDir, { recursive: true });
252629
+ for (const [workspace, analysisData] of Object.entries(workspaceToAnalysisData)) {
252630
+ const perProjectEcosystems = ["PIP", "RUBYGEMS", "COMPOSER"];
252631
+ const installDir = perProjectEcosystems.includes(ecosystem) ? join34(ecosystemDir, workspace.replace(/\//g, "_")) : ecosystemDir;
252632
+ if (installDir !== ecosystemDir) {
252633
+ await mkdir6(installDir, { recursive: true });
252634
+ }
252635
+ installTasks.push({
252636
+ ecosystem,
252637
+ workspace,
252638
+ analysisData,
252639
+ vulnerabilities: ecosystemToWorkspaceToVulnerabilities[ecosystem]?.[workspace] ?? [],
252640
+ installDir
252641
+ });
252642
+ }
252643
+ }
252644
+ const allFailures = [];
252645
+ await asyncMap(installTasks, async ({ ecosystem, workspace, analysisData, vulnerabilities, installDir }) => {
252646
+ try {
252647
+ const result = await otherModulesCommunicator.installDependencies(workspace, ".", analysisData, ecosystem, vulnerabilities, {
252648
+ timeoutSeconds: {
252649
+ allVulnRuns: this.analysisTimeoutInSeconds,
252650
+ bucketedRuns: bucketedAnalysisTimeoutInSeconds
252651
+ },
252652
+ memoryLimitInMB: this.analysisMemoryLimitInMb
252653
+ }, {
252654
+ haltOnInstallErrors: false
252655
+ }, installDir);
252656
+ if (result.failedPackages.length > 0) {
252657
+ logger.info(` ${ecosystem}/${workspace}: failed to install ${result.failedPackages.join(", ")}`);
252658
+ allFailures.push({ ecosystem, workspace, failedPackages: result.failedPackages });
252659
+ } else {
252660
+ logger.info(` ${ecosystem}/${workspace}: all packages installed successfully`);
252661
+ }
252662
+ } catch (e) {
252663
+ const message2 = e instanceof Error ? e.message : String(e);
252664
+ logger.info(` ${ecosystem}/${workspace}: pre-install failed (${message2})`);
252665
+ allFailures.push({ ecosystem, workspace, failedPackages: [`(pre-install error: ${message2})`] });
252666
+ }
252667
+ }, Number(this.options.concurrency));
252668
+ if (allFailures.length > 0) {
252669
+ this.logAggregatedInstallErrors(allFailures);
252670
+ const allFailed = allFailures.flatMap((f6) => f6.failedPackages);
252671
+ throw new InstallError2(allFailed);
252672
+ }
252673
+ }
252674
+ logAggregatedInstallErrors(failures) {
252675
+ const displayLines = [""];
252676
+ const goFailures = failures.filter((f6) => f6.ecosystem === "GO");
252677
+ const installFailures = failures.filter((f6) => f6.ecosystem !== "GO");
252678
+ if (installFailures.length > 0) {
252679
+ displayLines.push(kleur_default.red().bold("Installation Errors"));
252680
+ displayLines.push("The following packages failed to install during dependency pre-installation:");
252681
+ displayLines.push("");
252682
+ const byEcosystem = /* @__PURE__ */ new Map();
252683
+ for (const f6 of installFailures) {
252684
+ if (!byEcosystem.has(f6.ecosystem))
252685
+ byEcosystem.set(f6.ecosystem, []);
252686
+ byEcosystem.get(f6.ecosystem).push(f6);
252687
+ }
252688
+ for (const [ecosystem, ecosystemFailures] of byEcosystem) {
252689
+ displayLines.push(kleur_default.bold(` ${ecosystem}:`));
252690
+ for (const f6 of ecosystemFailures) {
252691
+ const pkgList = f6.failedPackages.sort();
252692
+ const pkgLines = pkgList.slice(0, 10).map((pkg) => ` - ${pkg}`);
252693
+ if (pkgList.length > 10) {
252694
+ pkgLines.push(` ... and ${pkgList.length - 10} more`);
252695
+ }
252696
+ displayLines.push(` ${f6.workspace}:`);
252697
+ displayLines.push(...pkgLines);
252698
+ }
252699
+ displayLines.push("");
252700
+ }
252701
+ displayLines.push("Pre-installing dependencies before running the analysis might fix this problem.", "");
252702
+ }
252703
+ if (goFailures.length > 0) {
252704
+ displayLines.push(kleur_default.red().bold("Build Errors"));
252705
+ displayLines.push("The following Go projects failed to build:");
252706
+ displayLines.push("");
252707
+ for (const f6 of goFailures) {
252708
+ displayLines.push(` ${f6.workspace}`);
252709
+ }
252710
+ displayLines.push("");
252711
+ }
252712
+ displayLines.push("Use --reach-continue-on-install-errors to proceed with precomputed (Tier 2) reachability results for affected vulnerabilities.", "");
252713
+ logger.error(displayLines.join("\n"));
252714
+ }
252715
+ logInstallError(failedPackages, ecosystem) {
252716
+ const pkgList = failedPackages.sort();
252717
+ const pkgLines = pkgList.slice(0, 20).map((pkg) => ` - ${pkg}`);
252718
+ if (pkgList.length > 20) {
252719
+ pkgLines.push(` ... and ${pkgList.length - 20} more`);
252720
+ }
252721
+ const installCmd = getInstallCommandForEcosystem(ecosystem);
252722
+ const displayLines = [
252723
+ "",
252724
+ kleur_default.red().bold("Installation Error"),
252725
+ "The following packages failed to install during reachability analysis:",
252726
+ ...pkgLines,
252727
+ "",
252728
+ "To fix this, pre-install dependencies before running the analysis:",
252729
+ "",
252730
+ ` ${installCmd}`,
252731
+ "",
252732
+ "Alternatively, use --reach-continue-on-install-errors to proceed with precomputed (Tier 2) reachability results for affected vulnerabilities.",
252733
+ ""
252734
+ ];
252735
+ logger.error(displayLines.join("\n"));
252736
+ }
252737
+ logAnalysisErrors(totalErrorCount, uniqueErrors) {
252738
+ const errorLines = uniqueErrors.slice(0, 20).map((msg) => ` - ${msg}`);
252739
+ if (uniqueErrors.length > 20) {
252740
+ errorLines.push(` ... and ${uniqueErrors.length - 20} more`);
252741
+ }
252742
+ const displayLines = [
252743
+ "",
252744
+ kleur_default.red().bold("Analysis Error"),
252745
+ `Reachability analysis failed for ${totalErrorCount} ${totalErrorCount === 1 ? "vulnerability" : "vulnerabilities"}:`,
252746
+ ...errorLines,
252747
+ "",
252748
+ "Use --reach-continue-on-analysis-errors to continue when encountering analysis errors and fallback to precomputed (Tier 2) reachability results for affected vulnerabilities.",
252749
+ ""
252750
+ ];
252751
+ logger.error(displayLines.join("\n"));
252752
+ }
252486
252753
  shouldExcludeAnalyzingWorkspace(subprojectPath, workspacePath, workspacePrefix = "") {
252487
252754
  const shouldExcludeWorkspaceForAnalysis = shouldIgnoreDueToExcludeDirsOrChangedFiles({
252488
252755
  mainProjectDir: this.rootWorkingDirectory,
@@ -252495,7 +252762,7 @@ Subproject: ${subproject}`);
252495
252762
  }
252496
252763
  return shouldExcludeWorkspaceForAnalysis;
252497
252764
  }
252498
- async runReachabilityAnalysis(otherModulesCommunicator, subprojectPath, workspacePath, workspaceData, ecosystem, vulnerabilities, workspacePrefix = "", rootWorkingDirOverride, displaySubprojectPath) {
252765
+ async runReachabilityAnalysis(otherModulesCommunicator, subprojectPath, workspacePath, workspaceData, ecosystem, vulnerabilities, workspacePrefix = "", rootWorkingDirOverride, displaySubprojectPath, preinstallDir) {
252499
252766
  const [vulnsWithActualAPIPatterns, result] = partition(vulnerabilities, (v) => Array.isArray(v.vulnerabilityAccessPaths));
252500
252767
  for (const v of result)
252501
252768
  v.results = typeof v.vulnerabilityAccessPaths === "string" ? { type: "noAnalysisCheck", message: v.vulnerabilityAccessPaths } : { type: "missingVulnerabilityPattern" };
@@ -252521,8 +252788,9 @@ Subproject: ${subproject}`);
252521
252788
  }, {
252522
252789
  disableBucketing: !!this.options.disableAnalysisSplitting,
252523
252790
  lightweightReachability: this.options.lightweightReachability,
252524
- skipCacheUsage: this.options.skipCacheUsage
252525
- }, rootWorkingDirOverride, displaySubprojectPath);
252791
+ skipCacheUsage: this.options.skipCacheUsage,
252792
+ haltOnInstallErrors: !!this.options.socketMode && !this.options.reachContinueOnInstallErrors
252793
+ }, rootWorkingDirOverride, displaySubprojectPath, preinstallDir);
252526
252794
  result.push(...analysisResult.vulnerabilities);
252527
252795
  this.sendProgress("REACHABILITY_ANALYSIS", false, subprojectPath, workspacePath);
252528
252796
  return { vulnerabilities: result, diagnostics: analysisResult.diagnostics };
@@ -252661,6 +252929,37 @@ async function getGitDataToMetadataIfAvailable(rootWorkingDirectory) {
252661
252929
  logger.debug("Unable to get git data. Is the folder even in a git repository?", e);
252662
252930
  }
252663
252931
  }
252932
+ function getInstallCommandForEcosystem(ecosystem) {
252933
+ switch (ecosystem) {
252934
+ case "NPM":
252935
+ return "npm install";
252936
+ case "PIP":
252937
+ return "uv sync (or your project-specific install command)";
252938
+ case "GO":
252939
+ return "go mod download";
252940
+ case "MAVEN":
252941
+ return "mvn dependency:resolve";
252942
+ case "NUGET":
252943
+ return "dotnet restore";
252944
+ case "RUST":
252945
+ return "cargo fetch";
252946
+ case "RUBYGEMS":
252947
+ return "bundle config set --local deployment true; bundle install";
252948
+ case "COMPOSER":
252949
+ return "composer install";
252950
+ case "ERLANG":
252951
+ return "rebar3 get-deps";
252952
+ case "PUB":
252953
+ return "dart pub get";
252954
+ case "SWIFT":
252955
+ return "swift package resolve";
252956
+ case "ACTIONS":
252957
+ return "Install project dependencies using your package manager";
252958
+ default:
252959
+ ecosystem;
252960
+ return "Install project dependencies using your package manager";
252961
+ }
252962
+ }
252664
252963
 
252665
252964
  // dist/internal/analysis-debug-info-transformer.js
252666
252965
  import { writeFile as writeFile16 } from "fs/promises";
@@ -252783,7 +253082,7 @@ async function writeAnalysisDebugInfo(outputFilePath, ecosystemToWorkspaceToVuln
252783
253082
  handleNexeBinaryMode();
252784
253083
  var program2 = new Command();
252785
253084
  var run2 = new Command();
252786
- 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. NPM reachability analysis does not support concurrent execution, so the concurrency level is ignored for NPM.", "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("--lazy-mode", "Enable lazy analysis mode for JavaScript/TypeScript. This can significantly speed up analysis by only analyzing code that is actually relevant for the vulnerabilities being analyzed.").default(false).hideHelp()).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).addOption(new Option("--use-only-pregenerated-sboms", "Only include artifacts that have CDX or SPDX files in their manifest files.").default(false).hideHelp()).option("--disable-external-tool-checks", "Disable validation of external tools (npm, python, go, etc.) before running analysis.", false).version(version3).configureHelp({ sortOptions: true }).action(async (path9, options) => {
253085
+ 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. NPM reachability analysis does not support concurrent execution, so the concurrency level is ignored for NPM.", "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).option("--reach-continue-on-install-errors", "Continue analysis when package installation fails, falling back to precomputed (Tier 2) reachability results. By default, the CLI halts on installation errors in socket mode.", process.env.COANA_CONTINUE_ON_INSTALL_ERRORS === "true").option("--reach-continue-on-analysis-errors", "Continue analysis when errors occur (timeouts, OOM, parse errors, etc.), falling back to precomputed (Tier 2) reachability results. By default, the CLI halts on analysis errors in socket mode.", 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("--lazy-mode", "Enable lazy analysis mode for JavaScript/TypeScript. This can significantly speed up analysis by only analyzing code that is actually relevant for the vulnerabilities being analyzed.").default(false).hideHelp()).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).addOption(new Option("--use-only-pregenerated-sboms", "Only include artifacts that have CDX or SPDX files in their manifest files.").default(false).hideHelp()).option("--disable-external-tool-checks", "Disable validation of external tools (npm, python, go, etc.) before running analysis.", false).version(version3).configureHelp({ sortOptions: true }).action(async (path9, options) => {
252787
253086
  process.env.DOCKER_IMAGE_TAG ??= version3;
252788
253087
  options.ecosystems = options.ecosystems?.map((e) => e.toUpperCase());
252789
253088
  options.minSeverity = options.minSeverity?.toUpperCase();
@@ -252838,7 +253137,7 @@ computeFixesAndUpgradePurlsCmd.name("compute-fixes-and-upgrade-purls").argument(
252838
253137
  await writeFile17(outputFile, JSON.stringify(output, null, 2));
252839
253138
  logger.info(`Result written to: ${outputFile}`);
252840
253139
  }
252841
- await rm3(tmpDir, { recursive: true, force: true });
253140
+ await rm4(tmpDir, { recursive: true, force: true });
252842
253141
  } catch (error) {
252843
253142
  handleUpgradeError(error, logFile);
252844
253143
  }