@coana-tech/cli 14.12.143 → 14.12.144

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.
@@ -35534,8 +35534,8 @@ var require_follow_redirects = __commonJS({
35534
35534
  }
35535
35535
  return parsed;
35536
35536
  }
35537
- function resolveUrl(relative10, base) {
35538
- return useNativeURL ? new URL3(relative10, base) : parseUrl(url2.resolve(base, relative10));
35537
+ function resolveUrl(relative11, base) {
35538
+ return useNativeURL ? new URL3(relative11, base) : parseUrl(url2.resolve(base, relative11));
35539
35539
  }
35540
35540
  function validateUrl(input) {
35541
35541
  if (/^\[/.test(input.hostname) && !/^\[[:0-9a-f]+\]$/i.test(input.hostname)) {
@@ -46796,7 +46796,7 @@ var require_mock_interceptor = __commonJS({
46796
46796
  var require_mock_client = __commonJS({
46797
46797
  "../../node_modules/.pnpm/undici@5.28.5/node_modules/undici/lib/mock/mock-client.js"(exports, module) {
46798
46798
  "use strict";
46799
- var { promisify } = __require("util");
46799
+ var { promisify: promisify2 } = __require("util");
46800
46800
  var Client = require_client();
46801
46801
  var { buildMockDispatch } = require_mock_utils();
46802
46802
  var {
@@ -46836,7 +46836,7 @@ var require_mock_client = __commonJS({
46836
46836
  return new MockInterceptor(opts, this[kDispatches]);
46837
46837
  }
46838
46838
  async [kClose]() {
46839
- await promisify(this[kOriginalClose])();
46839
+ await promisify2(this[kOriginalClose])();
46840
46840
  this[kConnected] = 0;
46841
46841
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
46842
46842
  }
@@ -46849,7 +46849,7 @@ var require_mock_client = __commonJS({
46849
46849
  var require_mock_pool = __commonJS({
46850
46850
  "../../node_modules/.pnpm/undici@5.28.5/node_modules/undici/lib/mock/mock-pool.js"(exports, module) {
46851
46851
  "use strict";
46852
- var { promisify } = __require("util");
46852
+ var { promisify: promisify2 } = __require("util");
46853
46853
  var Pool = require_pool();
46854
46854
  var { buildMockDispatch } = require_mock_utils();
46855
46855
  var {
@@ -46889,7 +46889,7 @@ var require_mock_pool = __commonJS({
46889
46889
  return new MockInterceptor(opts, this[kDispatches]);
46890
46890
  }
46891
46891
  async [kClose]() {
46892
- await promisify(this[kOriginalClose])();
46892
+ await promisify2(this[kOriginalClose])();
46893
46893
  this[kConnected] = 0;
46894
46894
  this[kMockAgent][Symbols.kClients].delete(this[kOrigin]);
46895
46895
  }
@@ -57757,13 +57757,13 @@ var require_tmp = __commonJS({
57757
57757
  var require_tmp_promise = __commonJS({
57758
57758
  "../../node_modules/.pnpm/tmp-promise@3.0.3/node_modules/tmp-promise/index.js"(exports, module) {
57759
57759
  "use strict";
57760
- var { promisify } = __require("util");
57760
+ var { promisify: promisify2 } = __require("util");
57761
57761
  var tmp = require_tmp();
57762
57762
  module.exports.fileSync = tmp.fileSync;
57763
- var fileWithOptions = promisify(
57763
+ var fileWithOptions = promisify2(
57764
57764
  (options, cb) => tmp.file(
57765
57765
  options,
57766
- (err, path10, fd, cleanup) => err ? cb(err) : cb(void 0, { path: path10, fd, cleanup: promisify(cleanup) })
57766
+ (err, path10, fd, cleanup) => err ? cb(err) : cb(void 0, { path: path10, fd, cleanup: promisify2(cleanup) })
57767
57767
  )
57768
57768
  );
57769
57769
  module.exports.file = async (options) => fileWithOptions(options);
@@ -57776,10 +57776,10 @@ var require_tmp_promise = __commonJS({
57776
57776
  }
57777
57777
  };
57778
57778
  module.exports.dirSync = tmp.dirSync;
57779
- var dirWithOptions = promisify(
57779
+ var dirWithOptions = promisify2(
57780
57780
  (options, cb) => tmp.dir(
57781
57781
  options,
57782
- (err, path10, cleanup) => err ? cb(err) : cb(void 0, { path: path10, cleanup: promisify(cleanup) })
57782
+ (err, path10, cleanup) => err ? cb(err) : cb(void 0, { path: path10, cleanup: promisify2(cleanup) })
57783
57783
  )
57784
57784
  );
57785
57785
  module.exports.dir = async (options) => dirWithOptions(options);
@@ -57792,7 +57792,7 @@ var require_tmp_promise = __commonJS({
57792
57792
  }
57793
57793
  };
57794
57794
  module.exports.tmpNameSync = tmp.tmpNameSync;
57795
- module.exports.tmpName = promisify(tmp.tmpName);
57795
+ module.exports.tmpName = promisify2(tmp.tmpName);
57796
57796
  module.exports.tmpdir = tmp.tmpdir;
57797
57797
  module.exports.setGracefulCleanup = tmp.setGracefulCleanup;
57798
57798
  }
@@ -59955,8 +59955,8 @@ var require_utils4 = __commonJS({
59955
59955
  exports.toPosixSlashes = (str) => str.replace(REGEX_BACKSLASH, "/");
59956
59956
  exports.isWindows = () => {
59957
59957
  if (typeof navigator !== "undefined" && navigator.platform) {
59958
- const platform6 = navigator.platform.toLowerCase();
59959
- return platform6 === "win32" || platform6 === "windows";
59958
+ const platform7 = navigator.platform.toLowerCase();
59959
+ return platform7 === "win32" || platform7 === "windows";
59960
59960
  }
59961
59961
  if (typeof process !== "undefined" && process.platform) {
59962
59962
  return process.platform === "win32";
@@ -79999,7 +79999,7 @@ function deserializeRustDependencyChainNode(s2) {
79999
79999
 
80000
80000
  // dist/main.js
80001
80001
  var import_lodash22 = __toESM(require_lodash(), 1);
80002
- import { relative as relative9, resolve as resolve22 } from "path";
80002
+ import { relative as relative10, resolve as resolve22 } from "path";
80003
80003
 
80004
80004
  // ../utils/src/dashboard-api/coana-api.ts
80005
80005
  import { writeFile } from "fs/promises";
@@ -80226,15 +80226,6 @@ var import_form_data2 = __toESM(require_form_data(), 1);
80226
80226
  import { readFile as readFile2 } from "node:fs/promises";
80227
80227
  import { join } from "node:path";
80228
80228
 
80229
- // ../web-compat-utils/src/ghsa.ts
80230
- function extractGHSAIdFromUrl(url2) {
80231
- const match2 = url2.match(/(GHSA-[a-z0-9-]+)/);
80232
- if (match2) {
80233
- return match2[1];
80234
- }
80235
- return void 0;
80236
- }
80237
-
80238
80229
  // ../../node_modules/.pnpm/remeda@2.21.2/node_modules/remeda/dist/chunk-ANXBDSUI.js
80239
80230
  var s = { done: false, hasNext: false };
80240
80231
 
@@ -80536,30 +80527,7 @@ async function registerCLIProgressSocket(isStartEvent, cliProgressEvent, tier1Sc
80536
80527
  handleError(error, "Error registering CLI progress", false);
80537
80528
  }
80538
80529
  }
80539
- async function registerAnalysisMetadataSocket(subprojectPath, workspacePath, ecosystem, analysisMetadata, tier1ScanId) {
80540
- if (!tier1ScanId) {
80541
- return;
80542
- }
80543
- const abnormalExit = analysisMetadata.analysisDiagnostics.aborted ? "ABORTED" : analysisMetadata.analysisDiagnostics.timeout ? "TIMEOUT" : "NONE";
80544
- try {
80545
- const url2 = getSocketApiUrl("tier1-reachability-scan/analysis-metadata");
80546
- const data2 = {
80547
- tier1_reachability_scan_id: tier1ScanId,
80548
- subproject_path: subprojectPath,
80549
- workspace_path: workspacePath,
80550
- ghsa_ids: analysisMetadata.vulnUrls.map((vUrl) => extractGHSAIdFromUrl(vUrl)).filter((ghsaId) => ghsaId !== void 0),
80551
- analysis_diagnostics: analysisMetadata.analysisDiagnostics,
80552
- heuristic_name: analysisMetadata.heuristicName,
80553
- is_final_result: analysisMetadata.finalResult,
80554
- purl_type: getPurlType(ecosystem),
80555
- error_message: analysisMetadata.errorMessage,
80556
- experiment_name: analysisMetadata.experiment,
80557
- abnormal_exit: abnormalExit
80558
- };
80559
- await axios2.put(url2, data2, { headers: getAuthHeaders() });
80560
- } catch (error) {
80561
- handleError(error, "Error registering analysis metadata", false);
80562
- }
80530
+ async function registerAnalysisMetadataSocket(_subprojectPath, _workspacePath, _ecosystem, _analysisMetadata, _tier1ScanId) {
80563
80531
  }
80564
80532
  async function getLatestBucketsSocket(subprojectPath, workspacePath) {
80565
80533
  try {
@@ -80667,6 +80635,54 @@ async function sendLogChunkSocket(reportId, logs) {
80667
80635
  console.warn("Failed to send log chunk to Socket:", error.message);
80668
80636
  }
80669
80637
  }
80638
+ async function createAnalysisMetadataSocket(tier1ScanId, subprojectPath, workspacePath, ecosystem, ghsaIds, heuristicName, experimentName) {
80639
+ if (!tier1ScanId) return void 0;
80640
+ try {
80641
+ const url2 = getSocketApiUrl("tier1-reachability-scan/create-analysis-metadata");
80642
+ const data2 = {
80643
+ tier1_reachability_scan_id: tier1ScanId,
80644
+ subproject_path: subprojectPath,
80645
+ workspace_path: workspacePath,
80646
+ purl_type: getPurlType(ecosystem),
80647
+ ghsa_ids: ghsaIds,
80648
+ heuristic_name: heuristicName,
80649
+ experiment_name: experimentName
80650
+ };
80651
+ const response = await axios2.post(url2, data2, { headers: getAuthHeaders() });
80652
+ return response.data.analysis_metadata_id;
80653
+ } catch (error) {
80654
+ handleError(error, "Error creating analysis metadata", false);
80655
+ return void 0;
80656
+ }
80657
+ }
80658
+ async function sendTelemetrySocket(analysisMetadataId, telemetry) {
80659
+ try {
80660
+ const url2 = getSocketApiUrl("tier1-reachability-scan/add-telemetry");
80661
+ const data2 = {
80662
+ analysis_metadata_id: analysisMetadataId,
80663
+ telemetry
80664
+ };
80665
+ await axios2.post(url2, data2, { headers: getAuthHeaders() });
80666
+ } catch (error) {
80667
+ console.warn("Failed to send telemetry to Socket:", error.message);
80668
+ }
80669
+ }
80670
+ async function registerDiagnosticsToAnalysisMetadataSocket(analysisMetadataId, diagnosticsData) {
80671
+ const abnormalExit = diagnosticsData.analysisDiagnostics.aborted ? "ABORTED" : diagnosticsData.analysisDiagnostics.timeout ? "TIMEOUT" : "NONE";
80672
+ try {
80673
+ const url2 = getSocketApiUrl("tier1-reachability-scan/register-diagnostics-to-analysis-metadata");
80674
+ const data2 = {
80675
+ analysis_metadata_id: analysisMetadataId,
80676
+ analysis_diagnostics: diagnosticsData.analysisDiagnostics,
80677
+ is_final_result: diagnosticsData.finalResult,
80678
+ error_message: diagnosticsData.errorMessage,
80679
+ abnormal_exit: abnormalExit
80680
+ };
80681
+ await axios2.post(url2, data2, { headers: getAuthHeaders() });
80682
+ } catch (error) {
80683
+ handleError(error, "Error registering diagnostics to analysis metadata", false);
80684
+ }
80685
+ }
80670
80686
  function getSocketAPI() {
80671
80687
  return {
80672
80688
  createSocketTier1Scan,
@@ -80679,7 +80695,10 @@ function getSocketAPI() {
80679
80695
  finalizeAutofixRun,
80680
80696
  registerUpgradePurlRun,
80681
80697
  finalizeUpgradePurlRun,
80682
- sendLogChunkSocket
80698
+ sendLogChunkSocket,
80699
+ createAnalysisMetadataSocket,
80700
+ sendTelemetrySocket,
80701
+ registerDiagnosticsToAnalysisMetadataSocket
80683
80702
  };
80684
80703
  }
80685
80704
 
@@ -80796,6 +80815,39 @@ var DashboardAPI = class {
80796
80815
  await this.socketAPI.sendLogChunkSocket(reportId, logs);
80797
80816
  }
80798
80817
  }
80818
+ async createAnalysisMetadata(tier1ScanId, subprojectPath, workspacePath, ecosystem, ghsaIds, heuristicName, experimentName) {
80819
+ if (this.disableAnalyticsSharing) {
80820
+ return void 0;
80821
+ }
80822
+ if (this.socketMode) {
80823
+ return await this.socketAPI.createAnalysisMetadataSocket(
80824
+ tier1ScanId,
80825
+ subprojectPath,
80826
+ workspacePath,
80827
+ ecosystem,
80828
+ ghsaIds,
80829
+ heuristicName,
80830
+ experimentName
80831
+ );
80832
+ }
80833
+ return void 0;
80834
+ }
80835
+ async sendTelemetry(analysisMetadataId, telemetry) {
80836
+ if (this.disableAnalyticsSharing) {
80837
+ return;
80838
+ }
80839
+ if (this.socketMode) {
80840
+ await this.socketAPI.sendTelemetrySocket(analysisMetadataId, telemetry);
80841
+ }
80842
+ }
80843
+ async registerDiagnosticsToAnalysisMetadata(analysisMetadataId, diagnosticsData) {
80844
+ if (this.disableAnalyticsSharing || !analysisMetadataId) {
80845
+ return;
80846
+ }
80847
+ if (this.socketMode) {
80848
+ this.socketAPI.registerDiagnosticsToAnalysisMetadataSocket(analysisMetadataId, diagnosticsData).catch((err) => logger.debug("Failed to register diagnostics to analysis metadata:", err));
80849
+ }
80850
+ }
80799
80851
  };
80800
80852
 
80801
80853
  // dist/analyzers/go-analyzer.js
@@ -80823,7 +80875,68 @@ import { join as join3 } from "path";
80823
80875
 
80824
80876
  // ../utils/src/command-utils.ts
80825
80877
  import assert from "assert";
80878
+ import { execFile as execFile2 } from "child_process";
80879
+
80880
+ // ../utils/src/telemetry/telemetry-collector.ts
80826
80881
  import { execFile } from "child_process";
80882
+ import { platform } from "os";
80883
+ import { promisify } from "util";
80884
+ var execFileAsync = promisify(execFile);
80885
+ var TelemetryCollector = class {
80886
+ /**
80887
+ * Collect memory metrics for a child process by PID.
80888
+ * Uses OS-specific commands to query memory usage.
80889
+ *
80890
+ * @param pid - The process ID to query
80891
+ * @returns TelemetryMetrics or undefined if the process doesn't exist or query fails
80892
+ */
80893
+ async collectChildProcessMetrics(pid) {
80894
+ if (!Number.isInteger(pid) || pid <= 0) {
80895
+ return void 0;
80896
+ }
80897
+ try {
80898
+ const currentPlatform = platform();
80899
+ if (currentPlatform === "darwin" || currentPlatform === "linux") {
80900
+ return await this.collectUnixProcessMetrics(pid);
80901
+ }
80902
+ return void 0;
80903
+ } catch {
80904
+ return void 0;
80905
+ }
80906
+ }
80907
+ /**
80908
+ * Collect metrics for a Unix-like system (macOS, Linux) using ps command.
80909
+ */
80910
+ async collectUnixProcessMetrics(pid) {
80911
+ try {
80912
+ const { stdout } = await execFileAsync("ps", ["-o", "rss=,pcpu=", "-p", String(pid)], {
80913
+ timeout: 5e3
80914
+ });
80915
+ const trimmed = stdout.trim();
80916
+ if (!trimmed) {
80917
+ return void 0;
80918
+ }
80919
+ const parts = trimmed.split(/\s+/);
80920
+ if (parts.length < 2) {
80921
+ return void 0;
80922
+ }
80923
+ const rssKb = parseInt(parts[0], 10);
80924
+ const cpuPercent = parseFloat(parts[1]);
80925
+ if (isNaN(rssKb) || isNaN(cpuPercent)) {
80926
+ return void 0;
80927
+ }
80928
+ const rssBytes = rssKb * 1024;
80929
+ return {
80930
+ rss: rssBytes,
80931
+ cpuPercent
80932
+ };
80933
+ } catch {
80934
+ return void 0;
80935
+ }
80936
+ }
80937
+ };
80938
+
80939
+ // ../utils/src/command-utils.ts
80827
80940
  var DEFAULT_TIMEOUT_MS = 30 * 60 * 1e3;
80828
80941
  async function execAndLogOnFailure(cmd, dir, options, logLevel = "info") {
80829
80942
  const result = await execNeverFail(cmd, dir, options);
@@ -80852,14 +80965,35 @@ function startHeartbeat(options) {
80852
80965
  timer.unref?.();
80853
80966
  return () => clearInterval(timer);
80854
80967
  }
80968
+ var DEFAULT_TELEMETRY_INTERVAL_MS = 5e3;
80969
+ function startTelemetry(pid, handler) {
80970
+ const collector = new TelemetryCollector();
80971
+ const intervalMs = handler.intervalMs ?? DEFAULT_TELEMETRY_INTERVAL_MS;
80972
+ const collectAndReport = async () => {
80973
+ const metrics = await collector.collectChildProcessMetrics(pid);
80974
+ if (metrics) {
80975
+ handler.onTelemetry(metrics);
80976
+ }
80977
+ };
80978
+ collectAndReport().catch((err) => {
80979
+ logger.debug("Initial telemetry collection failed:", err);
80980
+ });
80981
+ const timer = setInterval(() => {
80982
+ collectAndReport().catch(() => {
80983
+ });
80984
+ }, intervalMs);
80985
+ timer.unref?.();
80986
+ return () => clearInterval(timer);
80987
+ }
80855
80988
  async function execNeverFail(cmd, dir, options) {
80856
80989
  const stopHeartbeat = options?.heartbeat ? startHeartbeat(options.heartbeat) : void 0;
80990
+ let stopTelemetry;
80857
80991
  try {
80858
80992
  return await new Promise((resolve23) => {
80859
80993
  let args;
80860
80994
  if (typeof cmd !== "string") [cmd, ...args] = cmd;
80861
80995
  const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS;
80862
- const childProcess = execFile(
80996
+ const childProcess = execFile2(
80863
80997
  cmd,
80864
80998
  args,
80865
80999
  { ...options, cwd: dir, maxBuffer: 1024 * 1024 * 1024, shell: args === void 0, timeout },
@@ -80867,6 +81001,9 @@ async function execNeverFail(cmd, dir, options) {
80867
81001
  resolve23({ error, stdout, stderr });
80868
81002
  }
80869
81003
  );
81004
+ if (options?.telemetryHandler && childProcess.pid) {
81005
+ stopTelemetry = startTelemetry(childProcess.pid, options.telemetryHandler);
81006
+ }
80870
81007
  if (options?.pipe) {
80871
81008
  childProcess.stdout?.on("data", (data2) => {
80872
81009
  Spinner.instance().suspend(() => {
@@ -80884,6 +81021,7 @@ async function execNeverFail(cmd, dir, options) {
80884
81021
  });
80885
81022
  } finally {
80886
81023
  stopHeartbeat?.();
81024
+ stopTelemetry?.();
80887
81025
  }
80888
81026
  }
80889
81027
  async function exec(cmd, dir, options) {
@@ -86442,7 +86580,7 @@ var Pattern = class _Pattern {
86442
86580
  #isUNC;
86443
86581
  #isAbsolute;
86444
86582
  #followGlobstar = true;
86445
- constructor(patternList, globList, index2, platform6) {
86583
+ constructor(patternList, globList, index2, platform7) {
86446
86584
  if (!isPatternList(patternList)) {
86447
86585
  throw new TypeError("empty pattern list");
86448
86586
  }
@@ -86459,7 +86597,7 @@ var Pattern = class _Pattern {
86459
86597
  this.#patternList = patternList;
86460
86598
  this.#globList = globList;
86461
86599
  this.#index = index2;
86462
- this.#platform = platform6;
86600
+ this.#platform = platform7;
86463
86601
  if (this.#index === 0) {
86464
86602
  if (this.isUNC()) {
86465
86603
  const [p0, p1, p2, p3, ...prest] = this.#patternList;
@@ -86601,12 +86739,12 @@ var Ignore = class {
86601
86739
  absoluteChildren;
86602
86740
  platform;
86603
86741
  mmopts;
86604
- constructor(ignored, { nobrace, nocase, noext, noglobstar, platform: platform6 = defaultPlatform2 }) {
86742
+ constructor(ignored, { nobrace, nocase, noext, noglobstar, platform: platform7 = defaultPlatform2 }) {
86605
86743
  this.relative = [];
86606
86744
  this.absolute = [];
86607
86745
  this.relativeChildren = [];
86608
86746
  this.absoluteChildren = [];
86609
- this.platform = platform6;
86747
+ this.platform = platform7;
86610
86748
  this.mmopts = {
86611
86749
  dot: true,
86612
86750
  nobrace,
@@ -86614,7 +86752,7 @@ var Ignore = class {
86614
86752
  noext,
86615
86753
  noglobstar,
86616
86754
  optimizationLevel: 2,
86617
- platform: platform6,
86755
+ platform: platform7,
86618
86756
  nocomment: true,
86619
86757
  nonegate: true
86620
86758
  };
@@ -86652,10 +86790,10 @@ var Ignore = class {
86652
86790
  ignored(p) {
86653
86791
  const fullpath = p.fullpath();
86654
86792
  const fullpaths = `${fullpath}/`;
86655
- const relative10 = p.relative() || ".";
86656
- const relatives = `${relative10}/`;
86793
+ const relative11 = p.relative() || ".";
86794
+ const relatives = `${relative11}/`;
86657
86795
  for (const m of this.relative) {
86658
- if (m.match(relative10) || m.match(relatives))
86796
+ if (m.match(relative11) || m.match(relatives))
86659
86797
  return true;
86660
86798
  }
86661
86799
  for (const m of this.absolute) {
@@ -86666,9 +86804,9 @@ var Ignore = class {
86666
86804
  }
86667
86805
  childrenIgnored(p) {
86668
86806
  const fullpath = p.fullpath() + "/";
86669
- const relative10 = (p.relative() || ".") + "/";
86807
+ const relative11 = (p.relative() || ".") + "/";
86670
86808
  for (const m of this.relativeChildren) {
86671
- if (m.match(relative10))
86809
+ if (m.match(relative11))
86672
86810
  return true;
86673
86811
  }
86674
86812
  for (const m of this.absoluteChildren) {
@@ -87493,6 +87631,24 @@ var import_semver4 = __toESM(require_semver2(), 1);
87493
87631
  import assert8 from "assert";
87494
87632
  import { relative as relative7 } from "path";
87495
87633
 
87634
+ // ../utils/src/telemetry/telemetry-options-factory.ts
87635
+ function createTelemetryHandler(dashboardAPI4, analysisMetadataId) {
87636
+ if (!analysisMetadataId) {
87637
+ return void 0;
87638
+ }
87639
+ return {
87640
+ onTelemetry: (metrics) => {
87641
+ dashboardAPI4.sendTelemetry(analysisMetadataId, [
87642
+ {
87643
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
87644
+ metrics
87645
+ }
87646
+ ]).catch((err) => logger.debug("Failed to send telemetry:", err));
87647
+ },
87648
+ intervalMs: 5e3
87649
+ };
87650
+ }
87651
+
87496
87652
  // ../utils/src/promise-queue.ts
87497
87653
  var PromiseQueue = class {
87498
87654
  /*
@@ -87569,6 +87725,25 @@ var PromiseQueue = class {
87569
87725
  // ../web-compat-utils/src/analysis-error-keys.ts
87570
87726
  var FAILED_TO_INSTALL_PACKAGE_KEY = "[UNABLE_TO_INSTALL_PACKAGE_ERROR]: ";
87571
87727
 
87728
+ // ../web-compat-utils/src/ghsa.ts
87729
+ function extractGHSAIdFromUrl(url2) {
87730
+ const match2 = url2.match(/(GHSA-[a-z0-9-]+)/);
87731
+ if (match2) {
87732
+ return match2[1];
87733
+ }
87734
+ return void 0;
87735
+ }
87736
+ function extractGhsaIdsFromVulnUrls(vulnUrls) {
87737
+ const ghsaIds = /* @__PURE__ */ new Set();
87738
+ for (const url2 of vulnUrls) {
87739
+ const ghsaId = extractGHSAIdFromUrl(url2);
87740
+ if (ghsaId) {
87741
+ ghsaIds.add(ghsaId);
87742
+ }
87743
+ }
87744
+ return Array.from(ghsaIds);
87745
+ }
87746
+
87572
87747
  // ../web-compat-utils/src/detected-occurrence-utils.ts
87573
87748
  function hasReachableMatches(detectedOccurrences) {
87574
87749
  if (!detectedOccurrences) return false;
@@ -87919,14 +88094,14 @@ function findBestWheel(packageName, version3, packageData) {
87919
88094
  logger.debug(`Invalid wheel file name: ${filename}`);
87920
88095
  return void 0;
87921
88096
  }
87922
- let [distribution, whVersion, pyver, abi, platform6] = parts;
88097
+ let [distribution, whVersion, pyver, abi, platform7] = parts;
87923
88098
  if (whVersion !== version3) return void 0;
87924
88099
  if (normalizePackageName(distribution) !== normalized)
87925
88100
  logger.debug(`Distribution name mismatch: ${distribution} != ${normalized}`);
87926
88101
  if (pyver === "py2.py3") pyver = "py3";
87927
88102
  if (!pyver.startsWith("py") && !pyver.startsWith("cp")) return void 0;
87928
88103
  if (pyver[2] === "2") return void 0;
87929
- return { url: url2, version: pyver.slice(2), abi, platforms: platform6.split(".") };
88104
+ return { url: url2, version: pyver.slice(2), abi, platforms: platform7.split(".") };
87930
88105
  }).filter((w) => w !== void 0);
87931
88106
  if (wheels.length === 0) {
87932
88107
  logger.debug(`No suitable wheel file found for ${packageName}==${version3}`);
@@ -87939,9 +88114,9 @@ function findBestWheel(packageName, version3, packageData) {
87939
88114
  });
87940
88115
  for (const re of [/^any$/, /linux.*x86_64/, /linux/, /(?:x86_|amd)64/, /./]) {
87941
88116
  for (const candidate of wheels) {
87942
- const platform6 = candidate.platforms.find((p) => re.test(p));
87943
- if (platform6 !== void 0)
87944
- return { url: candidate.url, pythonVersion: candidate.version, abi: candidate.abi, platform: platform6 };
88117
+ const platform7 = candidate.platforms.find((p) => re.test(p));
88118
+ if (platform7 !== void 0)
88119
+ return { url: candidate.url, pythonVersion: candidate.version, abi: candidate.abi, platform: platform7 };
87945
88120
  }
87946
88121
  }
87947
88122
  }
@@ -88268,9 +88443,9 @@ var ToolPathResolver = class {
88268
88443
  /**
88269
88444
  * Get the path to the Goana binary for the current platform and architecture
88270
88445
  */
88271
- static getGoanaBinaryPath(platform6, arch) {
88446
+ static getGoanaBinaryPath(platform7, arch) {
88272
88447
  const rarch = arch === "arm" ? "arm64" : arch === "x64" ? "amd64" : arch;
88273
- const binaryName = `goana-${platform6}-${rarch}.gz`;
88448
+ const binaryName = `goana-${platform7}-${rarch}.gz`;
88274
88449
  return resolve6(COANA_REPOS_PATH(), "goana", "bin", binaryName);
88275
88450
  }
88276
88451
  /**
@@ -90054,8 +90229,8 @@ var parseKVLine = (set, line) => {
90054
90229
  };
90055
90230
 
90056
90231
  // ../../node_modules/.pnpm/tar@7.4.3/node_modules/tar/dist/esm/normalize-windows-path.js
90057
- var platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
90058
- var normalizeWindowsPath = platform !== "win32" ? (p) => p : (p) => p && p.replace(/\\/g, "/");
90232
+ var platform2 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
90233
+ var normalizeWindowsPath = platform2 !== "win32" ? (p) => p : (p) => p && p.replace(/\\/g, "/");
90059
90234
 
90060
90235
  // ../../node_modules/.pnpm/tar@7.4.3/node_modules/tar/dist/esm/read-entry.js
90061
90236
  var ReadEntry = class extends Minipass {
@@ -91882,8 +92057,8 @@ import path7 from "node:path";
91882
92057
 
91883
92058
  // ../../node_modules/.pnpm/tar@7.4.3/node_modules/tar/dist/esm/get-write-flag.js
91884
92059
  import fs5 from "fs";
91885
- var platform2 = process.env.__FAKE_PLATFORM__ || process.platform;
91886
- var isWindows = platform2 === "win32";
92060
+ var platform3 = process.env.__FAKE_PLATFORM__ || process.platform;
92061
+ var isWindows = platform3 === "win32";
91887
92062
  var { O_CREAT, O_TRUNC, O_WRONLY } = fs5.constants;
91888
92063
  var UV_FS_O_FILEMAP = Number(process.env.__FAKE_FS_O_FILENAME__) || fs5.constants.UV_FS_O_FILEMAP || 0;
91889
92064
  var fMapEnabled = isWindows && !!UV_FS_O_FILEMAP;
@@ -92138,7 +92313,7 @@ var mkdirpNative = Object.assign(async (path10, options) => {
92138
92313
 
92139
92314
  // ../../node_modules/.pnpm/mkdirp@3.0.1/node_modules/mkdirp/dist/mjs/path-arg.js
92140
92315
  import { parse as parse6, resolve as resolve7 } from "path";
92141
- var platform3 = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform;
92316
+ var platform4 = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform;
92142
92317
  var pathArg = (path10) => {
92143
92318
  if (/\0/.test(path10)) {
92144
92319
  throw Object.assign(new TypeError("path must be a string without null bytes"), {
@@ -92147,7 +92322,7 @@ var pathArg = (path10) => {
92147
92322
  });
92148
92323
  }
92149
92324
  path10 = resolve7(path10);
92150
- if (platform3 === "win32") {
92325
+ if (platform4 === "win32") {
92151
92326
  const badWinChars = /[*|"<>?:]/;
92152
92327
  const { root: root3 } = parse6(path10);
92153
92328
  if (badWinChars.test(path10.substring(root3.length))) {
@@ -92407,8 +92582,8 @@ var normalizeUnicode = (s2) => {
92407
92582
 
92408
92583
  // ../../node_modules/.pnpm/tar@7.4.3/node_modules/tar/dist/esm/path-reservations.js
92409
92584
  import { join as join9 } from "node:path";
92410
- var platform4 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
92411
- var isWindows2 = platform4 === "win32";
92585
+ var platform5 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
92586
+ var isWindows2 = platform5 === "win32";
92412
92587
  var getDirs = (path10) => {
92413
92588
  const dirs = path10.split("/").slice(0, -1).reduce((set, path11) => {
92414
92589
  const s2 = set[set.length - 1];
@@ -92564,8 +92739,8 @@ var DOCHOWN = Symbol("doChown");
92564
92739
  var UID = Symbol("uid");
92565
92740
  var GID = Symbol("gid");
92566
92741
  var CHECKED_CWD = Symbol("checkedCwd");
92567
- var platform5 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
92568
- var isWindows3 = platform5 === "win32";
92742
+ var platform6 = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform;
92743
+ var isWindows3 = platform6 === "win32";
92569
92744
  var DEFAULT_MAX_DEPTH = 1024;
92570
92745
  var unlinkFile = (path10, cb) => {
92571
92746
  if (!isWindows3) {
@@ -93511,13 +93686,13 @@ async function getNodeExecutable(overridePath) {
93511
93686
  logger.debug("Extracting Node.js binary for spawned processes...");
93512
93687
  extractedNodePath = (async () => {
93513
93688
  const extractedPath = await extractTool("nodejs-binaries", "nodejs-binaries");
93514
- const { platform: platform6, arch } = process;
93689
+ const { platform: platform7, arch } = process;
93515
93690
  const nodeArch = arch === "arm" ? "arm64" : arch;
93516
- const isWindows4 = platform6 === "win32";
93517
- const binaryName = isWindows4 ? `node-${platform6}-${nodeArch}.exe.gz` : `node-${platform6}-${nodeArch}.gz`;
93691
+ const isWindows4 = platform7 === "win32";
93692
+ const binaryName = isWindows4 ? `node-${platform7}-${nodeArch}.exe.gz` : `node-${platform7}-${nodeArch}.gz`;
93518
93693
  const compressedBinaryPath = join10(extractedPath, binaryName);
93519
93694
  if (!await exists(compressedBinaryPath)) {
93520
- throw new Error(`Node.js binary not found: ${compressedBinaryPath}. Platform: ${platform6}-${nodeArch}`);
93695
+ throw new Error(`Node.js binary not found: ${compressedBinaryPath}. Platform: ${platform7}-${nodeArch}`);
93521
93696
  }
93522
93697
  const tmpDir = join10(getExtractionBaseDir(), "node-runtime");
93523
93698
  await mkdir4(tmpDir, { recursive: true });
@@ -95871,7 +96046,7 @@ var DotnetCodeAwareVulnerabilityScanner = class _DotnetCodeAwareVulnerabilitySca
95871
96046
  return packageIds?.map((packageId) => this.depIdToPurl.get(packageId)).filter((purl) => purl !== void 0).map((purl) => purl.name ?? "");
95872
96047
  });
95873
96048
  }
95874
- async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, _experiment) {
96049
+ async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, _experiment, telemetryHandler) {
95875
96050
  try {
95876
96051
  this.statusUpdater?.("Preparing code for analysis...");
95877
96052
  const packagesToAnalyze = heuristic.getPackagesToAnalyze(vulnerabilities);
@@ -95880,12 +96055,12 @@ var DotnetCodeAwareVulnerabilityScanner = class _DotnetCodeAwareVulnerabilitySca
95880
96055
  const purl = this.depIdToPurl.get(packageId);
95881
96056
  return purl?.name && packagesToAnalyzeSet.has(purl.name);
95882
96057
  }));
95883
- return await this.actuallyRunAnalysis(vulnerabilities.flatMap((v) => v.vulnerabilityAccessPaths), timeoutInSeconds, filteredDeps);
96058
+ return await this.actuallyRunAnalysis(vulnerabilities.flatMap((v) => v.vulnerabilityAccessPaths), timeoutInSeconds, filteredDeps, telemetryHandler);
95884
96059
  } catch (e) {
95885
96060
  return { type: "error", message: e.message };
95886
96061
  }
95887
96062
  }
95888
- async actuallyRunAnalysis(vulnerabilityAccessPaths, timeoutInSeconds, filteredDeps) {
96063
+ async actuallyRunAnalysis(vulnerabilityAccessPaths, timeoutInSeconds, filteredDeps, telemetryHandler) {
95889
96064
  this.statusUpdater?.("Running analysis...");
95890
96065
  return withTmpDirectory("dotnet-run-analysis", async (tmpDir) => {
95891
96066
  try {
@@ -95903,7 +96078,7 @@ var DotnetCodeAwareVulnerabilityScanner = class _DotnetCodeAwareVulnerabilitySca
95903
96078
  const outputFile = resolve9(tmpDir, "output.json");
95904
96079
  await writeFile4(inputFile, JSON.stringify(options));
95905
96080
  const timeoutMs = Math.max(timeoutInSeconds * 1.5, timeoutInSeconds + 30) * 1e3;
95906
- const result = await execNeverFail2(cmdt`${await getNodeExecutable(ToolPathResolver.nodeExecutablePath)} ${getClassGraphAnalysisCliPath()} runDotnetReachabilityAnalysis -i ${inputFile} -o ${outputFile} --cocoa ${getCocoaPath()} --tree-sitter-c-sharp ${getTreeSitterCSharpPath()}`, void 0, { timeout: timeoutMs, killSignal: "SIGKILL", heartbeat: HEARTBEATS.dotnet });
96081
+ const result = await execNeverFail2(cmdt`${await getNodeExecutable(ToolPathResolver.nodeExecutablePath)} ${getClassGraphAnalysisCliPath()} runDotnetReachabilityAnalysis -i ${inputFile} -o ${outputFile} --cocoa ${getCocoaPath()} --tree-sitter-c-sharp ${getTreeSitterCSharpPath()}`, void 0, { timeout: timeoutMs, killSignal: "SIGKILL", heartbeat: HEARTBEATS.dotnet, telemetryHandler });
95907
96082
  if (result.error)
95908
96083
  return { type: "error", message: result.error.message ?? "unknown error" };
95909
96084
  const { success, error, analysisDiagnostics: diagnostics, vulnerablePaths, reachablePackageIds } = JSON.parse(await readFile6(outputFile, "utf-8")).result;
@@ -97697,10 +97872,10 @@ function compareDocumentPosition(nodeA, nodeB) {
97697
97872
  function uniqueSort(nodes) {
97698
97873
  nodes = nodes.filter((node, i4, arr) => !arr.includes(node, i4 + 1));
97699
97874
  nodes.sort((a2, b) => {
97700
- const relative10 = compareDocumentPosition(a2, b);
97701
- if (relative10 & DocumentPosition.PRECEDING) {
97875
+ const relative11 = compareDocumentPosition(a2, b);
97876
+ if (relative11 & DocumentPosition.PRECEDING) {
97702
97877
  return -1;
97703
- } else if (relative10 & DocumentPosition.FOLLOWING) {
97878
+ } else if (relative11 & DocumentPosition.FOLLOWING) {
97704
97879
  return 1;
97705
97880
  }
97706
97881
  return 0;
@@ -109847,7 +110022,7 @@ var JavaCodeAwareVulnerabilityScanner = class _JavaCodeAwareVulnerabilityScanner
109847
110022
  return packageIds?.map((packageId) => this.depIdToPurl.get(packageId)).filter((purl) => purl !== void 0).map((purl) => `${purl.namespace}:${purl.name}}`);
109848
110023
  });
109849
110024
  }
109850
- async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, _experiment) {
110025
+ async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, _experiment, telemetryHandler) {
109851
110026
  try {
109852
110027
  this.statusUpdater?.("Preparing code for analysis...");
109853
110028
  const packagesToAnalyze = heuristic.getPackagesToAnalyze(vulnerabilities);
@@ -109856,12 +110031,12 @@ var JavaCodeAwareVulnerabilityScanner = class _JavaCodeAwareVulnerabilityScanner
109856
110031
  const purl = this.depIdToPurl.get(packageId);
109857
110032
  return purl && packagesToAnalyzeSet.has(`${purl.namespace}:${purl.name}}`);
109858
110033
  }));
109859
- return await this.actuallyRunAnalysis(vulnerabilities.flatMap((v) => v.vulnerabilityAccessPaths), timeoutInSeconds, filteredDeps);
110034
+ return await this.actuallyRunAnalysis(vulnerabilities.flatMap((v) => v.vulnerabilityAccessPaths), timeoutInSeconds, filteredDeps, telemetryHandler);
109860
110035
  } catch (e) {
109861
110036
  return { type: "error", message: e.message };
109862
110037
  }
109863
110038
  }
109864
- async actuallyRunAnalysis(vulnerabilityAccessPaths, timeoutInSeconds, filteredDeps) {
110039
+ async actuallyRunAnalysis(vulnerabilityAccessPaths, timeoutInSeconds, filteredDeps, telemetryHandler) {
109865
110040
  this.statusUpdater?.("Running analysis...");
109866
110041
  return withTmpDirectory("java-run-analysis", async (tmpDir) => {
109867
110042
  try {
@@ -109879,7 +110054,7 @@ var JavaCodeAwareVulnerabilityScanner = class _JavaCodeAwareVulnerabilityScanner
109879
110054
  const outputFile = resolve10(tmpDir, "output.json");
109880
110055
  await writeFile5(inputFile, JSON.stringify(options));
109881
110056
  const timeoutMs = Math.max(timeoutInSeconds * 1.5, timeoutInSeconds + 30) * 1e3;
109882
- const result = await execNeverFail2(cmdt`${await getNodeExecutable(ToolPathResolver.nodeExecutablePath)} ${getClassGraphAnalysisCliPath()} runJvmReachabilityAnalysis -i ${inputFile} -o ${outputFile} --javap-service ${getJavapServicePath()} --tree-sitter-java ${getTreeSitterJavaPath()} --tree-sitter-kotlin ${getTreeSitterKotlinPath()} --tree-sitter-scala ${getTreeSitterScalaPath()}`, void 0, { timeout: timeoutMs, killSignal: "SIGKILL", heartbeat: HEARTBEATS.java });
110057
+ const result = await execNeverFail2(cmdt`${await getNodeExecutable(ToolPathResolver.nodeExecutablePath)} ${getClassGraphAnalysisCliPath()} runJvmReachabilityAnalysis -i ${inputFile} -o ${outputFile} --javap-service ${getJavapServicePath()} --tree-sitter-java ${getTreeSitterJavaPath()} --tree-sitter-kotlin ${getTreeSitterKotlinPath()} --tree-sitter-scala ${getTreeSitterScalaPath()}`, void 0, { timeout: timeoutMs, killSignal: "SIGKILL", heartbeat: HEARTBEATS.java, telemetryHandler });
109883
110058
  if (result.error)
109884
110059
  return { type: "error", message: result.error.message ?? "unknown error" };
109885
110060
  const { success, error, analysisDiagnostics: diagnostics, vulnerablePaths, reachablePackageIds } = JSON.parse(await readFile7(outputFile, "utf-8")).result;
@@ -110467,7 +110642,7 @@ import { readFile as readFile8, realpath as realpath2, rm as rm2, writeFile as w
110467
110642
  import { relative as relative6, resolve as resolve13 } from "path";
110468
110643
  var { map: map2, uniq: uniq4 } = import_lodash10.default;
110469
110644
  var PRINT_JELLY_COMMAND = false;
110470
- async function runJellyAnalysis(mainProjectRoot, projectRoot, jellyOptions, reachabilityAnalysisOptions, timeoutInSeconds, vulnerabilities, experiment) {
110645
+ async function runJellyAnalysis(mainProjectRoot, projectRoot, jellyOptions, reachabilityAnalysisOptions, timeoutInSeconds, vulnerabilities, experiment, telemetryHandler) {
110471
110646
  const tmpFolder = await createTmpDirectory("jelly-analysis");
110472
110647
  try {
110473
110648
  const filesToAnalyze = reachabilityAnalysisOptions.entryPoints ?? [projectRoot];
@@ -110511,7 +110686,12 @@ async function runJellyAnalysis(mainProjectRoot, projectRoot, jellyOptions, reac
110511
110686
  jellyCmd,
110512
110687
  void 0,
110513
110688
  // Use SIGKILL to ensure termination even if the process is unresponsive 50% above the timeout (e.g., due to GC pressure).
110514
- { timeout: timeoutInSeconds * 1e3 * 1.5, killSignal: "SIGKILL", heartbeat: HEARTBEATS.js }
110689
+ {
110690
+ timeout: timeoutInSeconds * 1e3 * 1.5,
110691
+ killSignal: "SIGKILL",
110692
+ heartbeat: HEARTBEATS.js,
110693
+ telemetryHandler
110694
+ }
110515
110695
  );
110516
110696
  if (reachabilityAnalysisOptions.printLogFile)
110517
110697
  logger.info("JS analysis log file:", await readFile8(logFile, "utf-8"));
@@ -110544,7 +110724,7 @@ async function runJellyAnalysis(mainProjectRoot, projectRoot, jellyOptions, reac
110544
110724
  await rm2(tmpFolder, { recursive: true });
110545
110725
  }
110546
110726
  }
110547
- async function runJellyPhantomDependencyAnalysis(projectRoot, options) {
110727
+ async function runJellyPhantomDependencyAnalysis(projectRoot, options, telemetryHandler) {
110548
110728
  const tmpFolder = await createTmpDirectory("jelly-analysis");
110549
110729
  try {
110550
110730
  const jellyExecutable = ToolPathResolver.jellyPath;
@@ -110555,14 +110735,15 @@ async function runJellyPhantomDependencyAnalysis(projectRoot, options) {
110555
110735
  await runCommandResolveStdOut2(jellyCmd, void 0, {
110556
110736
  timeout: options.timeoutSeconds.allVulnRuns * 1e3,
110557
110737
  killSignal: "SIGKILL",
110558
- heartbeat: HEARTBEATS.js
110738
+ heartbeat: HEARTBEATS.js,
110739
+ telemetryHandler
110559
110740
  });
110560
110741
  return JSON.parse(await readFile8(reachablePackagesFile, "utf-8")).packages;
110561
110742
  } finally {
110562
110743
  await rm2(tmpFolder, { recursive: true });
110563
110744
  }
110564
110745
  }
110565
- async function runJellyImportReachabilityAnalysis(mainProjectRoot, projectRoot, vulnerabilities, options) {
110746
+ async function runJellyImportReachabilityAnalysis(mainProjectRoot, projectRoot, vulnerabilities, options, telemetryHandler) {
110566
110747
  const tmpFolder = await createTmpDirectory("jelly-analysis");
110567
110748
  try {
110568
110749
  const includePackages = computePackagesOnVulnPath(vulnerabilities, { includeLeafPackages: true });
@@ -110576,7 +110757,8 @@ async function runJellyImportReachabilityAnalysis(mainProjectRoot, projectRoot,
110576
110757
  await runCommandResolveStdOut2(jellyCmd, void 0, {
110577
110758
  timeout: options.timeoutSeconds.allVulnRuns * 1e3,
110578
110759
  killSignal: "SIGKILL",
110579
- heartbeat: HEARTBEATS.js
110760
+ heartbeat: HEARTBEATS.js,
110761
+ telemetryHandler
110580
110762
  });
110581
110763
  return JSON.parse(await readFile8(reachableModulesFile, "utf-8"));
110582
110764
  } finally {
@@ -110631,11 +110813,11 @@ var JSCodeAwareVulnerabilityScanner = class _JSCodeAwareVulnerabilityScanner {
110631
110813
  const { failedPackages } = await prepareNpmDependencies(state.rootWorkingDir, this.projectDir, state.workspaceData.type === "coana" ? state.workspaceData.data.dependencyTree.transitiveDependencies : Object.fromEntries(state.workspaceData.data.artifacts.map((d) => [d.id, d])), state.workspaceData.type === "coana" ? state.workspaceData.data.dependencyTree.dependencies ?? [] : state.workspaceData.data.artifacts.filter((a2) => a2.direct).map((a2) => a2.id), packagesToInstall);
110632
110814
  this.packagesExcludedUnrelatedToHeuristic = failedPackages.map((p) => getPackageName(p));
110633
110815
  }
110634
- async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, experiment) {
110816
+ async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, experiment, telemetryHandler) {
110635
110817
  const analysisOptionsFromHeuristic = heuristic.getOptions(vulnerabilities);
110636
110818
  try {
110637
110819
  analysisOptionsFromHeuristic.approx = process.env.JELLY_APPROX === "true" || experiment === "JELLY_APPROX";
110638
- const analysisRes = await runJellyAnalysis(this.mainProjectDir, this.projectDir, analysisOptionsFromHeuristic, this.options, timeoutInSeconds, vulnerabilities, experiment);
110820
+ const analysisRes = await runJellyAnalysis(this.mainProjectDir, this.projectDir, analysisOptionsFromHeuristic, this.options, timeoutInSeconds, vulnerabilities, experiment, telemetryHandler);
110639
110821
  const { analysisDiagnostics: diagnostics, matches } = analysisRes;
110640
110822
  return {
110641
110823
  type: "success",
@@ -110814,7 +110996,7 @@ var GoCodeAwareVulnerabilityScanner = class {
110814
110996
  this.projectDir = projectDir;
110815
110997
  this.options = options;
110816
110998
  }
110817
- async runAnalysis(vulns, heuristic, timeoutInSeconds) {
110999
+ async runAnalysis(vulns, heuristic, timeoutInSeconds, _experiment, telemetryHandler) {
110818
111000
  logger.info("Started instantiating Go code-aware analysis");
110819
111001
  if (!existsSync10(join15(this.projectDir, "go.mod")))
110820
111002
  throw new Error("go.mod file not found in the project directory");
@@ -110838,7 +111020,8 @@ var GoCodeAwareVulnerabilityScanner = class {
110838
111020
  timeout: timeoutInSeconds * 1e3,
110839
111021
  killSignal: "SIGKILL",
110840
111022
  env: memoryLimitInMB ? { ...process.env, GOMEMLIMIT: `${memoryLimitInMB}MiB` } : void 0,
110841
- heartbeat: HEARTBEATS.go
111023
+ heartbeat: HEARTBEATS.go,
111024
+ telemetryHandler
110842
111025
  });
110843
111026
  if (error) {
110844
111027
  logger.error("Error running Go code-aware analysis", error);
@@ -111244,7 +111427,7 @@ var RustCodeAwareVulnerabilityScanner = class _RustCodeAwareVulnerabilityScanner
111244
111427
  return packageIds?.map((packageId) => this.depIdToPurl.get(packageId)?.name).filter((name2) => name2 !== void 0).map((name2) => name2);
111245
111428
  });
111246
111429
  }
111247
- async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds) {
111430
+ async runAnalysis(vulnerabilities, heuristic, timeoutInSeconds, _experiment, telemetryHandler) {
111248
111431
  try {
111249
111432
  this.statusUpdater?.("Preparing code for analysis...");
111250
111433
  const packagesToAnalyze = heuristic.getPackagesToAnalyze(vulnerabilities);
@@ -111253,12 +111436,12 @@ var RustCodeAwareVulnerabilityScanner = class _RustCodeAwareVulnerabilityScanner
111253
111436
  const purl = this.depIdToPurl.get(packageId);
111254
111437
  return purl?.name && packagesToAnalyzeSet.has(purl.name);
111255
111438
  }));
111256
- return await this.actuallyRunAnalysis(vulnerabilities.flatMap((v) => v.vulnerabilityAccessPaths), timeoutInSeconds, filteredDeps);
111439
+ return await this.actuallyRunAnalysis(vulnerabilities.flatMap((v) => v.vulnerabilityAccessPaths), timeoutInSeconds, filteredDeps, telemetryHandler);
111257
111440
  } catch (e) {
111258
111441
  return { type: "error", message: e.message };
111259
111442
  }
111260
111443
  }
111261
- async actuallyRunAnalysis(vulnerabilityAccessPaths, timeoutInSeconds, filteredDeps) {
111444
+ async actuallyRunAnalysis(vulnerabilityAccessPaths, timeoutInSeconds, filteredDeps, telemetryHandler) {
111262
111445
  this.statusUpdater?.("Running analysis...");
111263
111446
  return withTmpDirectory("rust-run-analysis", async (tmpDir) => {
111264
111447
  const effectiveTimeout = timeoutInSeconds;
@@ -111273,7 +111456,7 @@ var RustCodeAwareVulnerabilityScanner = class _RustCodeAwareVulnerabilityScanner
111273
111456
  const outputFile = resolve15(tmpDir, "output.json");
111274
111457
  await writeFile8(inputFile, JSON.stringify(options));
111275
111458
  const timeoutMs = Math.max(effectiveTimeout * 1.5, effectiveTimeout + 30) * 1e3;
111276
- const result = await execNeverFail2(cmdt`${await getNodeExecutable(ToolPathResolver.nodeExecutablePath)} ${getClassGraphAnalysisCliPath()} runRustReachabilityAnalysis -i ${inputFile} -o ${outputFile} --tree-sitter-rust ${getTreeSitterRustPath()}`, void 0, { timeout: timeoutMs, killSignal: "SIGKILL", heartbeat: HEARTBEATS.rust });
111459
+ const result = await execNeverFail2(cmdt`${await getNodeExecutable(ToolPathResolver.nodeExecutablePath)} ${getClassGraphAnalysisCliPath()} runRustReachabilityAnalysis -i ${inputFile} -o ${outputFile} --tree-sitter-rust ${getTreeSitterRustPath()}`, void 0, { timeout: timeoutMs, killSignal: "SIGKILL", heartbeat: HEARTBEATS.rust, telemetryHandler });
111277
111460
  if (result.error)
111278
111461
  return { type: "error", message: result.error.message ?? "unknown error" };
111279
111462
  const { success, error, analysisDiagnostics: diagnostics, vulnerablePaths, reachablePackageIds } = JSON.parse(await readFile10(outputFile, "utf-8")).result;
@@ -111744,7 +111927,7 @@ var PythonCodeAwareVulnerabilityScanner = class {
111744
111927
  logger.info("Done setting up virtual environment");
111745
111928
  }
111746
111929
  }
111747
- async runAnalysis(vulns, heuristic, timeoutInSeconds) {
111930
+ async runAnalysis(vulns, heuristic, timeoutInSeconds, _experiment, telemetryHandler) {
111748
111931
  if (!this.virtualEnvInfo)
111749
111932
  throw new Error("Virtual environment not set up");
111750
111933
  this.mambaladeVenvPath ??= await setupMambalade();
@@ -111798,7 +111981,8 @@ var PythonCodeAwareVulnerabilityScanner = class {
111798
111981
  // Use SIGKILL to ensure termination even if the process is unresponsive.
111799
111982
  timeout: (timeoutInSeconds * 1.5 + 15) * 1e3,
111800
111983
  killSignal: "SIGKILL",
111801
- heartbeat: HEARTBEATS.python
111984
+ heartbeat: HEARTBEATS.python,
111985
+ telemetryHandler
111802
111986
  });
111803
111987
  logger.debug("Done running mambalade");
111804
111988
  const errors = stderr.split("\n").filter((line) => line.startsWith("ERROR:") && !/^ERROR: Excluded distribution/.test(line));
@@ -112562,12 +112746,14 @@ async function analyzeWithHeuristics(state, vulns, heuristicsInOrder, doNotRecom
112562
112746
  };
112563
112747
  const vulnDepIdentifierToVulns = groupBy(bucket.vulnerabilities, getVulnDepIdentifier);
112564
112748
  const vulnDepIdentifiers = Object.keys(vulnDepIdentifierToVulns);
112749
+ const ghsaIds = extractGhsaIdsFromVulnUrls(vulnsForBucket.map((v) => v.url));
112750
+ const analysisMetadataId = COANA_REPORT_ID ? await dashboardAPI.createAnalysisMetadata(COANA_REPORT_ID, relative7(state.rootWorkingDir, state.subprojectDir) || ".", state.workspacePath, ecosystem, ghsaIds, bucket.heuristic.name, experiment) : void 0;
112565
112751
  try {
112566
112752
  newAnalysisRunListener();
112567
112753
  const initialBucketContainingAllVulns = buckets.length === 1 && buckets[0] === bucket;
112568
112754
  const timeoutInSeconds = initialBucketContainingAllVulns ? state.reachabilityAnalysisOptions.timeoutSeconds.allVulnRuns : state.reachabilityAnalysisOptions.timeoutSeconds.bucketedRuns;
112569
112755
  logger.info(`Running full reachability analysis for ${vulnsForBucket.length}/${vulnerabilities.length} vulnerabilities.`);
112570
- const result = await codeAwareScanner.runAnalysis(vulnsForBucket, bucket.heuristic, timeoutInSeconds, experiment);
112756
+ const result = await codeAwareScanner.runAnalysis(vulnsForBucket, bucket.heuristic, timeoutInSeconds, experiment, createTelemetryHandler(dashboardAPI, analysisMetadataId));
112571
112757
  const allowSplitInBuckets = !disableBucketing && bucket.heuristic.splitAnalysisInBuckets && !state.otherAnalysisOptions.disableBucketing && vulnDepIdentifiers.length > 1 && (result.type === "error" || result.reachedDependencies);
112572
112758
  if (result.type === "success") {
112573
112759
  result.diagnostics.timings ??= {};
@@ -112581,11 +112767,13 @@ async function analyzeWithHeuristics(state, vulns, heuristicsInOrder, doNotRecom
112581
112767
  const analysisMetadata = { ...partialWithDiagnostics, finalResult: true };
112582
112768
  bucketedAnalysisRes.analysisMetadata.push(analysisMetadata);
112583
112769
  await analysisMetadataCollector2?.(analysisMetadata);
112770
+ dashboardAPI.registerDiagnosticsToAnalysisMetadata(analysisMetadataId, analysisMetadata).catch((err) => logger.debug("Failed to register diagnostics to analysis metadata:", err));
112584
112771
  return;
112585
112772
  }
112586
112773
  result.diagnostics.timings.totalTime = Date.now() - bucketStartTime;
112587
112774
  const finalAnalysisMetadata = { ...partialWithDiagnostics, finalResult: false };
112588
112775
  await analysisMetadataCollector2?.(finalAnalysisMetadata);
112776
+ dashboardAPI.registerDiagnosticsToAnalysisMetadata(analysisMetadataId, finalAnalysisMetadata).catch((err) => logger.debug("Failed to register diagnostics to analysis metadata:", err));
112589
112777
  } else {
112590
112778
  result.type;
112591
112779
  await sendErrorAnalysisMetadata(result.message, !allowSplitInBuckets && isLastHeuristic(bucket.heuristic.name), !allowSplitInBuckets);
@@ -112625,6 +112813,7 @@ async function analyzeWithHeuristics(state, vulns, heuristicsInOrder, doNotRecom
112625
112813
  if (pushMetadata)
112626
112814
  bucketedAnalysisRes.analysisMetadata.push(analysisMetadataToSend);
112627
112815
  await analysisMetadataCollector2?.(analysisMetadataToSend);
112816
+ dashboardAPI.registerDiagnosticsToAnalysisMetadata(analysisMetadataId, analysisMetadataToSend).catch((err) => logger.debug("Failed to register diagnostics to analysis metadata:", err));
112628
112817
  }
112629
112818
  }
112630
112819
  }
@@ -112775,7 +112964,10 @@ function findDuplicateVulnsInBuckets(bucketsFromLastAnalysis) {
112775
112964
  function transformVulnsToUrlToReachability(oldHeuristicAugmentedVulnerabilities) {
112776
112965
  return Object.fromEntries(oldHeuristicAugmentedVulnerabilities.map((v) => [
112777
112966
  v.url,
112778
- { reachability: getVulnReachability(v.results), terminatedEarly: v.results.type === "success" && v.results.terminatedEarly }
112967
+ {
112968
+ reachability: getVulnReachability(v.results),
112969
+ terminatedEarly: v.results.type === "success" && v.results.terminatedEarly
112970
+ }
112779
112971
  ]));
112780
112972
  }
112781
112973
 
@@ -112927,7 +113119,7 @@ var import_lodash19 = __toESM(require_lodash(), 1);
112927
113119
  var import_picomatch4 = __toESM(require_picomatch2(), 1);
112928
113120
  import { existsSync as existsSync13 } from "fs";
112929
113121
  import { rm as rm6 } from "fs/promises";
112930
- import { resolve as resolve20 } from "path";
113122
+ import { relative as relative8, resolve as resolve20 } from "path";
112931
113123
 
112932
113124
  // ../web-compat-utils/src/pluralize.ts
112933
113125
  function pluralize(count, word) {
@@ -112941,6 +113133,8 @@ function pluralize(count, word) {
112941
113133
 
112942
113134
  // dist/analyzers/npm-analyzer.js
112943
113135
  var { partition: partition3, memoize: memoize2 } = import_lodash19.default;
113136
+ var SOCKET_MODE2 = process.env.SOCKET_MODE === "true";
113137
+ var dashboardAPI2 = new DashboardAPI(SOCKET_MODE2, process.env.DISABLE_ANALYTICS_SHARING === "true");
112944
113138
  var NpmAnalyzer = class {
112945
113139
  state;
112946
113140
  projectDir;
@@ -112973,12 +113167,36 @@ var NpmAnalyzer = class {
112973
113167
  await vulnerabilityScanner.prepareDependencies(this.state, heuristicsInOrder[0]);
112974
113168
  logger.info(`Running import reachability analysis for ${vulns.length} ${pluralize(vulns.length, "vulnerability")}`);
112975
113169
  let reachable;
113170
+ const ghsaIds = extractGhsaIdsFromVulnUrls(vulns.map((v) => v.url));
113171
+ const importAnalysisMetadataId = COANA_REPORT_ID ? await dashboardAPI2.createAnalysisMetadata(COANA_REPORT_ID, relative8(this.state.rootWorkingDir, this.state.subprojectDir) || ".", this.state.workspacePath, "NPM", ghsaIds, heuristics.IMPORT_REACHABILITY.name) : void 0;
113172
+ if (COANA_REPORT_ID && !importAnalysisMetadataId) {
113173
+ logger.debug("Failed to create analysis metadata for import analysis");
113174
+ }
113175
+ const importAnalysisStartTime = Date.now();
112976
113176
  try {
112977
113177
  statusUpdater?.("Running import reachability analysis");
112978
113178
  logger.debug("Starting jelly import reachability analysis");
112979
- reachable = await runJellyImportReachabilityAnalysis(this.state.rootWorkingDir, this.projectDir, vulns, this.state.reachabilityAnalysisOptions);
113179
+ reachable = await runJellyImportReachabilityAnalysis(this.state.rootWorkingDir, this.projectDir, vulns, this.state.reachabilityAnalysisOptions, createTelemetryHandler(dashboardAPI2, importAnalysisMetadataId));
113180
+ dashboardAPI2.registerDiagnosticsToAnalysisMetadata(importAnalysisMetadataId, {
113181
+ analysisDiagnostics: {
113182
+ timeout: false,
113183
+ aborted: false,
113184
+ timings: { totalTime: Date.now() - importAnalysisStartTime }
113185
+ },
113186
+ finalResult: true
113187
+ }).catch((err) => logger.debug("Failed to register diagnostics to analysis metadata:", err));
112980
113188
  } catch (e) {
112981
113189
  logger.debug("Error while running jelly import reachability analysis:", e);
113190
+ dashboardAPI2.registerDiagnosticsToAnalysisMetadata(importAnalysisMetadataId, {
113191
+ analysisDiagnostics: {
113192
+ timeout: false,
113193
+ aborted: true,
113194
+ timings: { totalTime: Date.now() - importAnalysisStartTime }
113195
+ },
113196
+ errorMessage: e instanceof Error ? `${e.message}
113197
+ ${e.stack}` : String(e),
113198
+ finalResult: true
113199
+ }).catch((err) => logger.debug("Failed to register diagnostics to analysis metadata:", err));
112982
113200
  }
112983
113201
  let [unreachableVulns, otherVulns] = [[], vulns];
112984
113202
  logger.debug(`Reachable modules from import reachability analysis: ${reachable?.modules.length}`);
@@ -113148,7 +113366,7 @@ import { resolve as resolve21 } from "path";
113148
113366
  var import_lodash20 = __toESM(require_lodash(), 1);
113149
113367
  import { createWriteStream as createWriteStream4, existsSync as existsSync14 } from "fs";
113150
113368
  import { mkdir as mkdir9, readdir as readdir5, readFile as readFile12, rm as rm7 } from "fs/promises";
113151
- import { join as join17, relative as relative8 } from "path";
113369
+ import { join as join17, relative as relative9 } from "path";
113152
113370
  import { pipeline as pipeline3 } from "stream/promises";
113153
113371
  var PRINT_ANALYSIS_COMMAND = false;
113154
113372
  var { uniqBy: uniqBy2, sortedUniq: sortedUniq2 } = import_lodash20.default;
@@ -113195,7 +113413,7 @@ var RubyCodeAwareVulnerabilityScanner = class {
113195
113413
  this.vendorDirWasCreated = true;
113196
113414
  logger.info("Done setting up vendor directory");
113197
113415
  }
113198
- async runAnalysis(vulns, heuristic, timeoutInSeconds, _experiment) {
113416
+ async runAnalysis(vulns, heuristic, timeoutInSeconds, _experiment, telemetryHandler) {
113199
113417
  return await withTmpDirectory("ruby-analyzer-output", async (tmpDir) => {
113200
113418
  if (!this.vendorDir)
113201
113419
  throw new Error("Assertion error: The vendor directory is not correctly initialized");
@@ -113236,10 +113454,11 @@ var RubyCodeAwareVulnerabilityScanner = class {
113236
113454
  await exec2(cmd, this.projectDir, {
113237
113455
  timeout: (timeoutInSeconds * 1.5 + 10) * 1e3,
113238
113456
  killSignal: "SIGKILL",
113239
- heartbeat: HEARTBEATS.ruby
113457
+ heartbeat: HEARTBEATS.ruby,
113458
+ telemetryHandler
113240
113459
  });
113241
113460
  const result = JSON.parse(await readFile12(vulnsOutputFile, "utf-8"));
113242
- const relativeLoadPathsToPackageNames = new Map([...loadPathsToPackageNames.entries()].map(([k, v]) => [join17("vendor", relative8(this.vendorDir, k)), v]));
113461
+ const relativeLoadPathsToPackageNames = new Map([...loadPathsToPackageNames.entries()].map(([k, v]) => [join17("vendor", relative9(this.vendorDir, k)), v]));
113243
113462
  const { timedOut, ...diagnostics } = JSON.parse(await readFile12(diagnosticsOutputFile, "utf-8"));
113244
113463
  const reachedPackages = JSON.parse(await readFile12(reachedPackagesOutputFile, "utf-8"));
113245
113464
  logger.debug("Reached packages: %O", reachedPackages);
@@ -113338,9 +113557,9 @@ async function downloadAndExtractGem(gemName, version3, vendorDir) {
113338
113557
  return;
113339
113558
  }
113340
113559
  const tempGemFile = join17(vendorDir, `${gemName}-${version3}.gem`);
113341
- const downloadAndExtract = async (platform6) => {
113560
+ const downloadAndExtract = async (platform7) => {
113342
113561
  logger.debug(`Downloading gem ${gemName}@${version3}`);
113343
- const response = await fetch(`https://rubygems.org/gems/${gemName}-${version3}${platform6 ? `-${platform6}` : ""}.gem`);
113562
+ const response = await fetch(`https://rubygems.org/gems/${gemName}-${version3}${platform7 ? `-${platform7}` : ""}.gem`);
113344
113563
  if (!response.ok)
113345
113564
  throw new Error(`Failed to download gem: ${response.statusText}`);
113346
113565
  if (!response.body)
@@ -113360,10 +113579,10 @@ async function downloadAndExtractGem(gemName, version3, vendorDir) {
113360
113579
  await downloadAndExtract(void 0);
113361
113580
  } catch (e) {
113362
113581
  try {
113363
- const platform6 = await axios_default.get(`https://gem.coop/api/v1/versions/${gemName}.json`).then((res) => {
113582
+ const platform7 = await axios_default.get(`https://gem.coop/api/v1/versions/${gemName}.json`).then((res) => {
113364
113583
  return res.data.find((v) => v.number === version3)?.platform;
113365
113584
  });
113366
- await downloadAndExtract(platform6);
113585
+ await downloadAndExtract(platform7);
113367
113586
  } catch (e2) {
113368
113587
  await rm7(gemDir, { recursive: true, force: true });
113369
113588
  await rm7(tempGemFile, { force: true });
@@ -113518,18 +113737,18 @@ var ecosystemAnalyzer = {
113518
113737
  RUBYGEMS: RubyGemsAnalyzer
113519
113738
  };
113520
113739
  var apiKey2 = COANA_API_KEY ? { type: "present", value: COANA_API_KEY } : { type: "missing" };
113521
- var dashboardAPI2 = new DashboardAPI(process.env.SOCKET_MODE === "true", process.env.DISABLE_ANALYTICS_SHARING === "true");
113740
+ var dashboardAPI3 = new DashboardAPI(process.env.SOCKET_MODE === "true", process.env.DISABLE_ANALYTICS_SHARING === "true");
113522
113741
  async function runReachabilityAnalysis(state) {
113523
113742
  const projectDir = resolve22(state.subprojectDir, state.workspacePath);
113524
113743
  const ecosystem = state.workspaceData.data.type;
113525
- logger.info(`Preparing to run reachability analysis for project at "${relative9(state.rootWorkingDir, projectDir) || "."}" (${ecosystem})`);
113744
+ logger.info(`Preparing to run reachability analysis for project at "${relative10(state.rootWorkingDir, projectDir) || "."}" (${ecosystem})`);
113526
113745
  const constructor = ecosystemAnalyzer[ecosystem];
113527
113746
  if (!constructor)
113528
113747
  throw Error(`No analyzer associated with ecosystem ${ecosystem}`);
113529
113748
  const analyzer = new constructor(state, projectDir);
113530
113749
  const [vulnerabilitiesWithPrecomputedResults, vulnerabilitiesWithoutPrecomputedResults] = partition4(state.vulnerabilities, (v) => "results" in v);
113531
113750
  const augmentedVulnerabilities = await runWholeProgramCodeAwareVulnerabilityScanner(analyzer, vulnerabilitiesWithoutPrecomputedResults, async (amd) => {
113532
- await dashboardAPI2.registerAnalysisMetadata(relative9(state.rootWorkingDir, state.subprojectDir) || ".", state.workspacePath, state.workspaceData.data.type, amd, COANA_REPORT_ID, apiKey2);
113751
+ await dashboardAPI3.registerAnalysisMetadata(relative10(state.rootWorkingDir, state.subprojectDir) || ".", state.workspacePath, state.workspaceData.data.type, amd, COANA_REPORT_ID, apiKey2);
113533
113752
  });
113534
113753
  const diagnostics = await analyzer.getWorkspaceDiagnostics();
113535
113754
  return {