@socketsecurity/cli-with-sentry 1.13.1 → 1.14.1

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +5 -5
  3. package/dist/cli.js +136 -137
  4. package/dist/cli.js.map +1 -1
  5. package/dist/constants.js +4 -4
  6. package/dist/constants.js.map +1 -1
  7. package/dist/manifest-scripts/dotnet-tool/socket-facts-dotnet.dll +0 -0
  8. package/dist/manifest-scripts/maven-extension/coana-maven-extension.jar +0 -0
  9. package/dist/tsconfig.dts.tsbuildinfo +1 -1
  10. package/dist/types/commands/fix/cmd-fix.d.mts.map +1 -1
  11. package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
  12. package/dist/types/commands/fix/generated-socket-facts.d.mts +4 -2
  13. package/dist/types/commands/fix/generated-socket-facts.d.mts.map +1 -1
  14. package/dist/types/commands/fix/types.d.mts +3 -1
  15. package/dist/types/commands/fix/types.d.mts.map +1 -1
  16. package/dist/types/commands/manifest/discover-manifest-roots.d.mts +3 -1
  17. package/dist/types/commands/manifest/discover-manifest-roots.d.mts.map +1 -1
  18. package/dist/types/commands/manifest/generate-recursive-manifests.d.mts +4 -2
  19. package/dist/types/commands/manifest/generate-recursive-manifests.d.mts.map +1 -1
  20. package/dist/types/commands/manifest/generate_auto_manifest.d.mts.map +1 -1
  21. package/dist/types/commands/manifest/scripts/build-tool.d.mts +6 -0
  22. package/dist/types/commands/manifest/scripts/build-tool.d.mts.map +1 -1
  23. package/dist/types/commands/manifest/scripts/run.d.mts +0 -3
  24. package/dist/types/commands/manifest/scripts/run.d.mts.map +1 -1
  25. package/dist/types/commands/manifest/scripts/sidecar.d.mts +0 -4
  26. package/dist/types/commands/manifest/scripts/sidecar.d.mts.map +1 -1
  27. package/dist/types/commands/manifest/setup-recursive-manifest-config.d.mts.map +1 -1
  28. package/dist/types/commands/scan/cmd-scan-create.d.mts.map +1 -1
  29. package/dist/types/commands/scan/cmd-scan-reach.d.mts.map +1 -1
  30. package/dist/types/commands/scan/handle-create-new-scan.d.mts.map +1 -1
  31. package/dist/types/commands/scan/handle-scan-reach.d.mts.map +1 -1
  32. package/dist/types/commands/scan/perform-reachability-analysis.d.mts +5 -1
  33. package/dist/types/commands/scan/perform-reachability-analysis.d.mts.map +1 -1
  34. package/dist/types/commands/scan/reachability-flags.d.mts +6 -0
  35. package/dist/types/commands/scan/reachability-flags.d.mts.map +1 -1
  36. package/dist/types/commands/scan/run-dynamic-sbom-inference.d.mts +16 -9
  37. package/dist/types/commands/scan/run-dynamic-sbom-inference.d.mts.map +1 -1
  38. package/dist/types/constants.d.mts +1 -1
  39. package/dist/types/utils/coana.d.mts +6 -0
  40. package/dist/types/utils/coana.d.mts.map +1 -1
  41. package/dist/types/utils/ecosystem.d.mts +1 -0
  42. package/dist/types/utils/ecosystem.d.mts.map +1 -1
  43. package/dist/utils.js +20 -7
  44. package/dist/utils.js.map +1 -1
  45. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [1.14.1](https://github.com/SocketDev/socket-cli/releases/tag/v1.14.1) - 2026-11-06
8
+
9
+ ### Changed
10
+ - Updated the Coana CLI to v `15.14.2`.
11
+
12
+ ## [1.14.0](https://github.com/SocketDev/socket-cli/releases/tag/v1.14.0) - 2026-11-05
13
+
14
+ ### Added
15
+ - Choose which ecosystems `--dynamic-sbom-inference` covers with `--dynamic-sbom-inference-ecosystems` (`maven` for Gradle, sbt and Maven; `nuget` for .NET) on `socket scan create`, `socket scan reach` and `socket fix`. Ecosystems left out keep their manifest files as they are.
16
+
17
+ ### Changed
18
+ - With `--dynamic-sbom-inference`, reachability analysis and `socket fix` now take the dependencies of every Gradle, sbt, Maven and .NET build they find from Socket facts only, so results no longer depend on which other builds produced facts.
19
+ - `socket manifest auto`, `--auto-manifest` and `--dynamic-sbom-inference` (with its setup) now fail a .NET build that has no usable .NET SDK, as they do a Maven build without Maven, instead of skipping it. With `--dynamic-sbom-inference`, reachability analysis and `socket fix` warn that builds skipped in `socket.json` are left out, and reachability analysis stops with next steps when no build produced Socket facts.
20
+
7
21
  ## [1.13.1](https://github.com/SocketDev/socket-cli/releases/tag/v1.13.1) - 2026-11-03
8
22
 
9
23
  ### Fixed
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Socket CLI
2
2
 
3
3
  [![Socket Badge](https://socket.dev/api/badge/npm/package/socket)](https://socket.dev/npm/package/socket)
4
- [![Follow @SocketSecurity](https://raw.githubusercontent.com/SocketDev/socket-cli/v1.13.1/assets/fleet/badge-follow-x.svg)](https://twitter.com/SocketSecurity)
5
- [![Follow @socket.dev on Bluesky](https://raw.githubusercontent.com/SocketDev/socket-cli/v1.13.1/assets/fleet/badge-follow-bluesky.svg)](https://bsky.app/profile/socket.dev)
4
+ [![Follow @SocketSecurity](https://raw.githubusercontent.com/SocketDev/socket-cli/v1.14.1/assets/fleet/badge-follow-x.svg)](https://twitter.com/SocketSecurity)
5
+ [![Follow @socket.dev on Bluesky](https://raw.githubusercontent.com/SocketDev/socket-cli/v1.14.1/assets/fleet/badge-follow-bluesky.svg)](https://bsky.app/profile/socket.dev)
6
6
 
7
7
  CLI for [Socket.dev](https://socket.dev) — scan dependencies, audit packages, apply security patches, and gate installs from your terminal or CI. This is the 1.x line of the published `socket` package on npm.
8
8
 
@@ -170,8 +170,8 @@ MIT
170
170
  <br/>
171
171
  <div align="center">
172
172
  <picture>
173
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SocketDev/socket-cli/v1.13.1/assets/fleet/socket-combomark-dark.svg">
174
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/SocketDev/socket-cli/v1.13.1/assets/fleet/socket-combomark-light.svg">
175
- <img width="324" height="104" alt="Socket" src="https://raw.githubusercontent.com/SocketDev/socket-cli/v1.13.1/assets/fleet/socket-combomark-light.svg">
173
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/SocketDev/socket-cli/v1.14.1/assets/fleet/socket-combomark-dark.svg">
174
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/SocketDev/socket-cli/v1.14.1/assets/fleet/socket-combomark-light.svg">
175
+ <img width="324" height="104" alt="Socket" src="https://raw.githubusercontent.com/SocketDev/socket-cli/v1.14.1/assets/fleet/socket-combomark-light.svg">
176
176
  </picture>
177
177
  </div>
package/dist/cli.js CHANGED
@@ -1914,23 +1914,11 @@ function serializeSidecar(acc) {
1914
1914
  return result;
1915
1915
  }
1916
1916
 
1917
- // Tells Coana which ecosystems' artifacts only these facts files attribute.
1918
- // Maven's always are (the dynamic-SBOM-inference contract), under its own flag
1919
- // every Coana release knows unless another ecosystem's facts are present too.
1920
- function useOnlySocketFactsArtifactsFlags(sidecar) {
1921
- const types = new Set(['maven']);
1922
- for (const entry of Object.values(sidecar ?? {})) {
1923
- for (const item of [...entry.projects, ...entry.components]) {
1924
- types.add(item.type);
1925
- }
1926
- }
1927
- return types.size === 1 ? ['--maven-use-only-socket-facts'] : ['--use-only-socket-facts-artifacts', [...types].sort().join(',')];
1928
- }
1929
-
1930
1917
  async function performReachabilityAnalysis(options) {
1931
1918
  const {
1932
1919
  branchName,
1933
1920
  cwd = process.cwd(),
1921
+ factsOnlyTypes,
1934
1922
  orgSlug,
1935
1923
  outputKind = 'text',
1936
1924
  outputPath,
@@ -2023,21 +2011,18 @@ async function performReachabilityAnalysis(options) {
2023
2011
  spinner?.infoAndStop('Running reachability analysis with Coana...');
2024
2012
 
2025
2013
  // Temp file for --compute-artifacts-sidecar, removed in the finally below.
2026
- // Written even when empty under dynamicSbomInference, since the
2027
- // facts-only flag below requires one to be present.
2014
+ // Written even when empty with factsOnlyTypes: `coana run` rejects the
2015
+ // facts-only flag without one.
2028
2016
  let sidecarPath;
2029
- if (reachabilityOptions.dynamicSbomInference || resolvedPathsSidecar && hasResolvedPathsSidecarEntries(resolvedPathsSidecar)) {
2017
+ if (factsOnlyTypes || resolvedPathsSidecar && hasResolvedPathsSidecarEntries(resolvedPathsSidecar)) {
2030
2018
  sidecarPath = path.join(os.tmpdir(), `socket-compute-artifacts-sidecar-${require$$0.randomUUID()}.json`);
2031
- await u.promises.writeFile(sidecarPath, JSON.stringify(resolvedPathsSidecar ?? {}), 'utf8');
2019
+ await u.promises.writeFile(sidecarPath, JSON.stringify(resolvedPathsSidecar ?? {}));
2032
2020
  }
2033
2021
 
2034
2022
  // Build Coana arguments.
2035
2023
  const coanaArgs = ['run', analysisTarget, '--output-dir', path.dirname(outputFilePath), '--socket-mode', outputFilePath, '--disable-report-submission', ...(isOmittedReachValue(reachabilityOptions.reachAnalysisTimeout) ? [] : ['--analysis-timeout', reachabilityOptions.reachAnalysisTimeout]), ...(isOmittedReachValue(reachabilityOptions.reachAnalysisMemoryLimit) ? [] : ['--memory-limit', reachabilityOptions.reachAnalysisMemoryLimit]), ...(reachabilityOptions.reachConcurrency ? ['--concurrency', `${reachabilityOptions.reachConcurrency}`] : []), ...(reachabilityOptions.reachContinueOnAnalysisErrors ? ['--reach-continue-on-analysis-errors'] : []), ...(reachabilityOptions.reachContinueOnInstallErrors ? ['--reach-continue-on-install-errors'] : []), ...(reachabilityOptions.reachContinueOnMissingLockFiles ? ['--reach-continue-on-missing-lock-files'] : []), ...(reachabilityOptions.reachContinueOnNoSourceFiles ? ['--reach-continue-on-no-source-files'] : []), ...(reachabilityOptions.reachDebug ? ['--debug'] : []), ...(reachabilityOptions.reachDetailedAnalysisLogFile ? ['--print-analysis-log-file'] : []), ...(reachabilityOptions.reachDisableAnalytics ? ['--disable-analytics-sharing'] : []), ...(reachabilityOptions.reachDisableDependencyDownloads ? ['--disable-dependency-downloads'] : []), ...(reachabilityOptions.reachDisableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(reachabilityOptions.reachEnableAnalysisSplitting ? [] : ['--disable-analysis-splitting']), '--run-without-docker', '--manifests-tar-hash', tarHash,
2036
2024
  // Empty reachEcosystems implies scanning all ecosystems.
2037
- ...(reachabilityOptions.reachEcosystems.length ? ['--purl-types', ...reachabilityOptions.reachEcosystems] : []), ...(reachabilityOptions.reachAdvisories.length ? ['--ghsas', ...reachabilityOptions.reachAdvisories] : []), ...(reachabilityOptions.reachExcludePaths.length ? ['--exclude-dirs', ...reachabilityOptions.reachExcludePaths] : []),
2038
- // sidecarPath is always set above when this is true - Coana rejects this
2039
- // flag without one.
2040
- ...(reachabilityOptions.dynamicSbomInference ? useOnlySocketFactsArtifactsFlags(resolvedPathsSidecar) : []), ...(reachabilityOptions.reachSkipCache ? ['--skip-cache-usage'] : []), ...(reachabilityOptions.reachUseOnlyPregeneratedSboms ? ['--use-only-pregenerated-sboms'] : []), ...(sidecarPath ? ['--compute-artifacts-sidecar', sidecarPath] : [])];
2025
+ ...(reachabilityOptions.reachEcosystems.length ? ['--purl-types', ...reachabilityOptions.reachEcosystems] : []), ...(reachabilityOptions.reachAdvisories.length ? ['--ghsas', ...reachabilityOptions.reachAdvisories] : []), ...(reachabilityOptions.reachExcludePaths.length ? ['--exclude-dirs', ...reachabilityOptions.reachExcludePaths] : []), ...(factsOnlyTypes ? utils.useOnlySocketFactsArtifactsFlags(factsOnlyTypes) : []), ...(reachabilityOptions.reachSkipCache ? ['--skip-cache-usage'] : []), ...(reachabilityOptions.reachUseOnlyPregeneratedSboms ? ['--use-only-pregenerated-sboms'] : []), ...(sidecarPath ? ['--compute-artifacts-sidecar', sidecarPath] : [])];
2041
2026
 
2042
2027
  // Build environment variables.
2043
2028
  const coanaEnv = {};
@@ -2114,6 +2099,21 @@ const PURL_TYPE_BY_TOOL = {
2114
2099
  maven: 'maven',
2115
2100
  sbt: 'maven'
2116
2101
  };
2102
+ const BUILD_TOOLS = Object.keys(PURL_TYPE_BY_TOOL);
2103
+ const ECOSYSTEM_LABELS = {
2104
+ __proto__: null,
2105
+ dotnet: '.NET',
2106
+ gradle: 'Gradle',
2107
+ maven: 'Maven',
2108
+ sbt: 'sbt'
2109
+ };
2110
+ const FACTS_PURL_TYPES = [...new Set(Object.values(PURL_TYPE_BY_TOOL))].sort();
2111
+ function buildToolsFor(purlTypes) {
2112
+ return BUILD_TOOLS.filter(tool => purlTypes.includes(PURL_TYPE_BY_TOOL[tool]));
2113
+ }
2114
+ function buildToolLabelsFor(purlTypes) {
2115
+ return buildToolsFor(purlTypes).map(tool => ECOSYSTEM_LABELS[tool]);
2116
+ }
2117
2117
 
2118
2118
  // Purl types whose packages have no namespace (NuGet ids are flat).
2119
2119
  const NAMESPACELESS_PURL_TYPES = new Set(['nuget']);
@@ -3505,25 +3505,6 @@ async function readDotnetSdkVersion(bin, opts) {
3505
3505
  }
3506
3506
  return out.stdout.trim();
3507
3507
  }
3508
- const noSdkWarnings = new Set();
3509
-
3510
- // True, after a warning, when no .NET SDK runs in `dir` (none installed, or
3511
- // none matching its global.json), so its .NET builds are skipped.
3512
- async function skipWithoutDotnetSdk(bin, dir) {
3513
- try {
3514
- await dotnetSdkVersion(bin, {
3515
- projectDir: dir
3516
- });
3517
- return false;
3518
- } catch (e) {
3519
- const reason = utils.getErrorMessageOr(e, `\`${bin} --version\` failed`).split('\n')[0];
3520
- if (!noSdkWarnings.has(reason)) {
3521
- noSdkWarnings.add(reason);
3522
- logger.logger.warn(`Skipping .NET builds where no .NET SDK runs (${reason}); their project files are scanned as they are.`);
3523
- }
3524
- return true;
3525
- }
3526
- }
3527
3508
 
3528
3509
  // Runs the bundled tool against the build's own dotnet, in `mode` (none: a
3529
3510
  // full facts run), and returns its exit and records.
@@ -3594,7 +3575,6 @@ async function readDotnetEntryProjects(opts) {
3594
3575
  return parsed.members.map(m => path.resolve(opts.projectDir, m));
3595
3576
  }
3596
3577
 
3597
- const BUILD_TOOLS = ['dotnet', 'gradle', 'maven', 'sbt'];
3598
3578
  const BUILD_FILE_MATCHERS = new Map(BUILD_TOOLS.map(tool => {
3599
3579
  const files = BUILD_FILES[tool];
3600
3580
  return [tool, files.byFile ? vendor.micromatchExports.matcher(files.nocaseGlob, {
@@ -3741,10 +3721,11 @@ function sortByDepthThenPath(roots, cwd, tool) {
3741
3721
  async function findBuildToolCandidates({
3742
3722
  cwd,
3743
3723
  excludePaths,
3724
+ purlTypes = FACTS_PURL_TYPES,
3744
3725
  sockJson,
3745
3726
  socketConfig
3746
3727
  }) {
3747
- const enabledTools = BUILD_TOOLS.filter(tool => !sockJson.defaults?.manifest?.[tool]?.disabled);
3728
+ const enabledTools = buildToolsFor(purlTypes).filter(tool => !sockJson.defaults?.manifest?.[tool]?.disabled);
3748
3729
  const result = new Map();
3749
3730
  if (!enabledTools.length) {
3750
3731
  return result;
@@ -4727,16 +4708,6 @@ async function runEcosystemCandidates({
4727
4708
  });
4728
4709
  continue;
4729
4710
  }
4730
- if (ecosystem === 'dotnet' && (
4731
- // eslint-disable-next-line no-await-in-loop
4732
- await skipWithoutDotnetSdk(dirConfig.bin, dir))) {
4733
- outcomes.push({
4734
- ...root,
4735
- ecosystem,
4736
- status: 'skippedNoSdk'
4737
- });
4738
- continue;
4739
- }
4740
4711
  // eslint-disable-next-line no-await-in-loop
4741
4712
  const result = await buildUncoveredRoot({
4742
4713
  config: {
@@ -4866,6 +4837,7 @@ async function generateRecursiveManifests({
4866
4837
  excludePaths,
4867
4838
  ignoreUnresolved,
4868
4839
  optionFlags = new Set(),
4840
+ purlTypes,
4869
4841
  sbtTmpDir,
4870
4842
  sidecarAcc,
4871
4843
  verbose,
@@ -4882,6 +4854,7 @@ async function generateRecursiveManifests({
4882
4854
  const candidatesByTool = await findBuildToolCandidates({
4883
4855
  cwd,
4884
4856
  excludePaths,
4857
+ purlTypes,
4885
4858
  socketConfig: utils.loadSocketConfig(cwd),
4886
4859
  sockJson: withoutDisabledFlags(rootSockJson)
4887
4860
  });
@@ -4971,38 +4944,64 @@ function resolveEcosystemConfig(ecosystem, dir, sockJson) {
4971
4944
  };
4972
4945
  }
4973
4946
 
4947
+ // Who consumes the facts: Coana for `fix` and `reach` (which also resolves
4948
+ // artifact paths), or only the scan upload.
4949
+
4950
+ // Coana takes the discovered ecosystems' artifacts only from facts files, so a
4951
+ // build root without them is one it leaves out.
4952
+ function checkFactsForCoana(outcomes, consumer, hasFacts) {
4953
+ const hasDisabled = outcomes.some(o => o.status === 'skippedDisabled');
4954
+ // Coana (15.14.1) refuses a facts-only reachability run without any facts.
4955
+ if (consumer === 'reach' && !hasFacts) {
4956
+ throw new utils.InputError(['No build root produced Socket facts, which reachability analysis with --dynamic-sbom-inference needs.', '', ...(hasDisabled ? [`- Re-enable the builds that socket.json skips (\`${DSI_SETUP_COMMAND}\` walks you through it).`] : []), '- Or select other ecosystems with --dynamic-sbom-inference-ecosystems.', '- Or remove --dynamic-sbom-inference to analyze the manifest files as they are.'].join('\n'));
4957
+ }
4958
+ if (hasDisabled) {
4959
+ logger.logger.warn('The analysis leaves out the build roots socket.json skips: with --dynamic-sbom-inference, it takes their dependencies only from Socket facts.');
4960
+ }
4961
+ }
4962
+
4974
4963
  // Recursively discovers and generates Socket facts for every independent
4975
- // gradle/sbt/maven build root under `cwd`, returning the generated facts
4976
- // paths plus the resolved-paths sidecar that reachability forwards to Coana.
4964
+ // build root of the selected ecosystems under `cwd`, returning the generated
4965
+ // facts paths plus the resolved-paths sidecar that reachability forwards to
4966
+ // Coana.
4977
4967
  async function runDynamicSbomInference({
4968
+ consumer,
4978
4969
  cwd,
4979
4970
  excludePaths,
4980
- sbtTmpDir,
4981
- sidecar,
4982
- withFiles
4971
+ purlTypes,
4972
+ sbtTmpDir
4983
4973
  }) {
4984
- const sidecarAcc = sidecar ?? withFiles ? new Map() : undefined;
4974
+ const withFiles = consumer === 'reach';
4975
+ const coanaReadsFacts = consumer === 'fix' || consumer === 'reach';
4976
+ const sidecarAcc = coanaReadsFacts ? new Map() : undefined;
4985
4977
  const outcomes = await generateRecursiveManifests({
4986
4978
  cwd,
4987
4979
  excludePaths,
4980
+ purlTypes,
4988
4981
  sbtTmpDir: withFiles ? sbtTmpDir : undefined,
4989
4982
  sidecarAcc,
4990
4983
  verbose: false,
4991
4984
  withFiles
4992
4985
  });
4986
+ const factsOnlyTypes = [...new Set(outcomes.map(o => PURL_TYPE_BY_TOOL[o.ecosystem]))];
4993
4987
  // No candidates discovered at all (distinct from candidates that were found
4994
4988
  // but produced no generated facts - empty/skippedDisabled are already warned
4995
4989
  // about elsewhere and are not this kind of mistake).
4996
- if (!outcomes.length) {
4997
- throw new utils.InputError(['No Gradle, sbt, Maven or .NET build root was found.', '', '- Remove --dynamic-sbom-inference; it only applies to these ecosystems.', '- Make sure to run it from the correct dir (use --cwd to target another dir).'].join('\n'));
4990
+ if (!utils.isFactsOnlyTypes(factsOnlyTypes)) {
4991
+ throw new utils.InputError([`No ${arrays.joinOr(buildToolLabelsFor(purlTypes))} build root was found.`, '', '- Select other ecosystems with --dynamic-sbom-inference-ecosystems, or remove --dynamic-sbom-inference.', '- Make sure to run it from the correct dir (use --cwd to target another dir).'].join('\n'));
4998
4992
  }
4999
4993
  // Fail loud rather than silently proceed with a partial multi-root result:
5000
4994
  // matches handleManifestDynamicSbomInference's own check.
5001
4995
  if (outcomes.some(o => o.status === 'failed')) {
5002
4996
  throw new utils.InputError('One or more independent build roots failed to generate Socket facts; aborting (see the errors above).');
5003
4997
  }
4998
+ const factsPaths = outcomes.filter(o => o.status === 'generated').map(o => o.factsPath);
4999
+ if (coanaReadsFacts) {
5000
+ checkFactsForCoana(outcomes, consumer, !!factsPaths.length);
5001
+ }
5004
5002
  return {
5005
- factsPaths: outcomes.filter(o => o.status === 'generated').map(o => o.factsPath),
5003
+ factsOnlyTypes,
5004
+ factsPaths,
5006
5005
  resolvedPathsSidecar: sidecarAcc && hasSidecarEntries(sidecarAcc) ? serializeSidecar(sidecarAcc) : undefined
5007
5006
  };
5008
5007
  }
@@ -8509,19 +8508,16 @@ async function generateAutoManifest({
8509
8508
  abortFactsRunIfFailed('maven', outcome);
8510
8509
  }
8511
8510
  if (!sockJson?.defaults?.manifest?.dotnet?.disabled && detected.dotnet) {
8512
- const config = resolveEcosystemConfig('dotnet', cwd, sockJson);
8513
- if (!(await skipWithoutDotnetSdk(config.bin, cwd))) {
8514
- logger.logger.log('Detected a .NET solution or project, generating Socket facts...');
8515
- const outcome = await convertDotnetToFacts({
8516
- config,
8517
- cwd,
8518
- excludePaths,
8519
- hints,
8520
- sidecarAcc,
8521
- withFiles: computeArtifactsSidecar
8522
- });
8523
- abortFactsRunIfFailed('dotnet', outcome);
8524
- }
8511
+ logger.logger.log('Detected a .NET solution or project, generating Socket facts...');
8512
+ const outcome = await convertDotnetToFacts({
8513
+ config: resolveEcosystemConfig('dotnet', cwd, sockJson),
8514
+ cwd,
8515
+ excludePaths,
8516
+ hints,
8517
+ sidecarAcc,
8518
+ withFiles: computeArtifactsSidecar
8519
+ });
8520
+ abortFactsRunIfFailed('dotnet', outcome);
8525
8521
  }
8526
8522
  if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) {
8527
8523
  const condaFile = sockJson.defaults?.manifest?.conda?.infile || detected.condaFile || constants.ENVIRONMENT_YML;
@@ -8646,9 +8642,10 @@ async function createNewScan({
8646
8642
 
8647
8643
  // Sidecar forwarded to reachability; populated only when reach runs.
8648
8644
  let resolvedPathsSidecar;
8645
+ let factsOnlyTypes;
8649
8646
 
8650
8647
  // sbt provisions its Scala toolchain under the directory passed as its
8651
- // isolated global base; withFiles' artifactPaths point into it, so it must
8648
+ // isolated global base; reach's artifactPaths point into it, so it must
8652
8649
  // stay on disk until the reachability analysis below has consumed those
8653
8650
  // paths. Always allocated (even when unused) to keep this uniform rather
8654
8651
  // than conditional on autoManifest/reach.
@@ -8662,18 +8659,20 @@ async function createNewScan({
8662
8659
  // one at cwd. This runs on its own, so nothing outside those
8663
8660
  // ecosystems is generated unless --auto-manifest also asked for it.
8664
8661
  const dynamicResult = await runDynamicSbomInference({
8662
+ consumer: reach.runReachabilityAnalysis ? 'reach' : 'upload',
8665
8663
  cwd,
8666
8664
  excludePaths: reach.excludePaths,
8665
+ purlTypes: reach.dynamicSbomInference,
8667
8666
  // sbt's Scala toolchain lives under its shared global base;
8668
- // withFiles' resolved paths point into it, so when reachability
8667
+ // reach's artifactPaths point into it, so when reachability
8669
8668
  // will consume them afterward, reuse manifestTmpDir (kept alive
8670
8669
  // until reach finishes below) instead of letting the call clean
8671
8670
  // its own ephemeral base up before reach ever reads those paths.
8672
- sbtTmpDir: manifestTmpDir,
8673
- withFiles: reach.runReachabilityAnalysis
8671
+ sbtTmpDir: manifestTmpDir
8674
8672
  });
8675
8673
  scanTargets = Array.from(new Set([...scanTargets, ...dynamicResult.factsPaths]));
8676
8674
  resolvedPathsSidecar = dynamicResult.resolvedPathsSidecar;
8675
+ factsOnlyTypes = dynamicResult.factsOnlyTypes;
8677
8676
  }
8678
8677
  if (autoManifest) {
8679
8678
  const sockJson = utils.readOrDefaultSocketJson(cwd);
@@ -8684,10 +8683,9 @@ async function createNewScan({
8684
8683
  if (reach.dynamicSbomInference) {
8685
8684
  // Already generated recursively above; resolving cwd's own build
8686
8685
  // root a second time would race on the same facts file.
8687
- detected.dotnet = false;
8688
- detected.gradle = false;
8689
- detected.sbt = false;
8690
- detected.maven = false;
8686
+ for (const tool of buildToolsFor(reach.dynamicSbomInference)) {
8687
+ detected[tool] = false;
8688
+ }
8691
8689
  }
8692
8690
  const autoManifestResult = await generateAutoManifest({
8693
8691
  computeArtifactsSidecar: reach.runReachabilityAnalysis,
@@ -8787,6 +8785,7 @@ async function createNewScan({
8787
8785
  const reachResult = await performReachabilityAnalysis({
8788
8786
  branchName,
8789
8787
  cwd,
8788
+ factsOnlyTypes,
8790
8789
  orgSlug,
8791
8790
  outputKind,
8792
8791
  packagePaths,
@@ -8973,7 +8972,7 @@ async function handleCi(autoManifest) {
8973
8972
  pendingHead: true,
8974
8973
  pullRequest: detectCiPullRequestNumber(),
8975
8974
  reach: {
8976
- dynamicSbomInference: false,
8975
+ dynamicSbomInference: undefined,
8977
8976
  excludePaths: [],
8978
8977
  reachAdvisories: [],
8979
8978
  reachAnalysisMemoryLimit: '',
@@ -10335,6 +10334,7 @@ async function generateSocketFactsForFix({
10335
10334
  cwd,
10336
10335
  excludePaths,
10337
10336
  findFactsPaths,
10337
+ purlTypes,
10338
10338
  tmpDir
10339
10339
  }) {
10340
10340
  const existingPaths = await findFactsPaths();
@@ -10348,24 +10348,24 @@ async function generateSocketFactsForFix({
10348
10348
  };
10349
10349
  let artifactsFlags;
10350
10350
  let paths;
10351
- let sidecarFile;
10351
+ const sidecarFile = path.join(tmpDir, 'sidecar.json');
10352
10352
  try {
10353
10353
  const {
10354
+ factsOnlyTypes,
10354
10355
  factsPaths,
10355
10356
  resolvedPathsSidecar
10356
10357
  } = await runDynamicSbomInference({
10358
+ consumer: 'fix',
10357
10359
  cwd,
10358
10360
  excludePaths,
10359
- sbtTmpDir: undefined,
10360
- sidecar: true,
10361
- withFiles: false
10361
+ purlTypes,
10362
+ sbtTmpDir: undefined
10362
10363
  });
10363
- artifactsFlags = useOnlySocketFactsArtifactsFlags(resolvedPathsSidecar);
10364
+ artifactsFlags = utils.useOnlySocketFactsArtifactsFlags(factsOnlyTypes);
10364
10365
  paths = factsPaths.map(p => path.resolve(cwd, p));
10365
- if (resolvedPathsSidecar) {
10366
- sidecarFile = path.join(tmpDir, 'sidecar.json');
10367
- await fs.writeFile(sidecarFile, JSON.stringify(resolvedPathsSidecar));
10368
- }
10366
+ // Written even when empty, as for reach; applying fixes looks each facts
10367
+ // file up in it, so an empty one is the same as none.
10368
+ await fs.writeFile(sidecarFile, JSON.stringify(resolvedPathsSidecar ?? {}));
10369
10369
  } catch (e) {
10370
10370
  // A failed build root aborts inference after others wrote their facts.
10371
10371
  await reset(await findFactsPaths());
@@ -10515,11 +10515,13 @@ function formatNotAppliedFixes(notApplied) {
10515
10515
  return notApplied.map(f => ` ${f.ghsa}: ${f.purl} -> ${f.fixedVersion}: ${f.reason}`).join('\n');
10516
10516
  }
10517
10517
  async function coanaFix(fixConfig) {
10518
- if (!fixConfig.dynamicSbomInference) {
10518
+ const purlTypes = fixConfig.dynamicSbomInference;
10519
+ if (!purlTypes) {
10519
10520
  return await coanaFixWithFacts(fixConfig, undefined);
10520
10521
  }
10521
10522
  return await utils.withTmpDir('socket-fix-facts-', async tmpDir => {
10522
10523
  const slot = {
10524
+ purlTypes,
10523
10525
  tmpDir
10524
10526
  };
10525
10527
  try {
@@ -10603,11 +10605,12 @@ async function coanaFixWithFacts(fixConfig, factsSlot) {
10603
10605
  if (factsSlot) {
10604
10606
  if (!silence) {
10605
10607
  spinner?.stop();
10606
- logger.logger.info('Generating Socket facts for .NET, Gradle, sbt and Maven builds ...');
10608
+ logger.logger.info('Generating Socket facts for each build ...');
10607
10609
  }
10608
10610
  factsSlot.generated = await generateSocketFactsForFix({
10609
10611
  cwd,
10610
10612
  excludePaths,
10613
+ purlTypes: factsSlot.purlTypes,
10611
10614
  findFactsPaths: async () => (await findScanFilepaths()).filter(utils.isSocketFactsFile),
10612
10615
  tmpDir: factsSlot.tmpDir
10613
10616
  });
@@ -11240,6 +11243,26 @@ async function handleFix({
11240
11243
  // differs: reachability is optional on `scan create` and always on for
11241
11244
  // `scan reach`.
11242
11245
  const DYNAMIC_SBOM_INFERENCE_DESCRIPTION = 'For Gradle, sbt, Maven and .NET: generate a Socket facts SBOM (produced directly by each package manager) per independent build root, instead of one synthetic root.';
11246
+
11247
+ // Not in reachabilityFlags: it applies to a `scan create` without --reach too.
11248
+ const dynamicSbomInferenceEcosystemsFlags = {
11249
+ dynamicSbomInferenceEcosystems: {
11250
+ type: 'string',
11251
+ isMultiple: true,
11252
+ description: `With --dynamic-sbom-inference: generate Socket facts only for these ecosystems, as either a comma separated value or as multiple flags; the others keep their manifest files as they are. Supported: ${arrays.joinAnd(FACTS_PURL_TYPES.map(t => `${t} (${arrays.joinAnd(buildToolLabelsFor([t]))})`))}. Defaults to all of them.`
11253
+ }
11254
+ };
11255
+ function parseDynamicSbomInferenceFlags(flags) {
11256
+ const raw = utils.cmdFlagValueToArray(flags['dynamicSbomInferenceEcosystems']);
11257
+ if (!flags['dynamicSbomInference']) {
11258
+ if (raw.length) {
11259
+ throw new utils.InputError('--dynamic-sbom-inference-ecosystems requires --dynamic-sbom-inference');
11260
+ }
11261
+ return undefined;
11262
+ }
11263
+ const selected = utils.parseEcosystemChoices(raw, FACTS_PURL_TYPES);
11264
+ return utils.isFactsOnlyTypes(selected) ? selected : FACTS_PURL_TYPES;
11265
+ }
11243
11266
  const reachabilityFlags = {
11244
11267
  reachVersion: {
11245
11268
  type: 'string',
@@ -11488,6 +11511,7 @@ Available styles:
11488
11511
  default: false,
11489
11512
  description: `${DYNAMIC_SBOM_INFERENCE_DESCRIPTION} Fixes are then attributed to the projects/modules that actually resolve each vulnerable dependency. The generated files are removed afterwards.`
11490
11513
  },
11514
+ ...dynamicSbomInferenceEcosystemsFlags,
11491
11515
  ecosystems: {
11492
11516
  type: 'string',
11493
11517
  default: [],
@@ -11622,7 +11646,6 @@ async function run$O(argv, importMeta, {
11622
11646
  autopilot,
11623
11647
  debug,
11624
11648
  disableExternalToolChecks,
11625
- dynamicSbomInference,
11626
11649
  ecosystems,
11627
11650
  exclude,
11628
11651
  excludePaths,
@@ -11648,23 +11671,10 @@ async function run$O(argv, importMeta, {
11648
11671
  const minSatisfying = cli.flags['minSatisfying'] || !maxSatisfying;
11649
11672
  const disableMajorUpdates = !majorUpdates;
11650
11673
  const outputKind = utils.getOutputKind(json, markdown);
11651
-
11652
- // Process comma-separated values for ecosystems flag.
11653
- // ALL_ECOSYSTEMS is lowercase, so normalize input for a case-insensitive
11654
- // match (mirrors --package-managers behavior).
11655
- const ecosystemsRaw = utils.cmdFlagValueToArray(ecosystems).map(s => s.toLowerCase());
11674
+ const dynamicSbomInference = parseDynamicSbomInferenceFlags(cli.flags);
11656
11675
 
11657
11676
  // Validate ecosystem values early, before dry-run check.
11658
- const validatedEcosystems = [];
11659
- const validEcosystemChoices = utils.getEcosystemChoicesForMeow();
11660
- for (const ecosystem of ecosystemsRaw) {
11661
- if (!validEcosystemChoices.includes(ecosystem)) {
11662
- logger.logger.fail(`Invalid ecosystem: "${ecosystem}". Valid values are: ${arrays.joinAnd(validEcosystemChoices)}`);
11663
- process.exitCode = 1;
11664
- return;
11665
- }
11666
- validatedEcosystems.push(ecosystem);
11667
- }
11677
+ const validatedEcosystems = utils.parseEcosystemChoices(utils.cmdFlagValueToArray(ecosystems), utils.getEcosystemChoicesForMeow());
11668
11678
 
11669
11679
  // Process and validate package manager values early, before dry-run check.
11670
11680
  // Coana normalizes input to uppercase and rejects unknown values, so do the
@@ -16249,13 +16259,6 @@ async function enumerateWorkspaces({
16249
16259
  // several POMs, so several roots, there.
16250
16260
 
16251
16261
  const ROOT_ECOSYSTEMS = ['maven', 'gradle', 'sbt', 'dotnet'];
16252
- const ECOSYSTEM_LABELS = {
16253
- __proto__: null,
16254
- dotnet: '.NET',
16255
- gradle: 'Gradle',
16256
- maven: 'Maven',
16257
- sbt: 'sbt'
16258
- };
16259
16262
 
16260
16263
  // The shallowest directory matching `--exclude-paths`, not necessarily a
16261
16264
  // project dir itself - one write here covers every project beneath it.
@@ -16428,12 +16431,6 @@ async function markWorkspaceCoverage({
16428
16431
  data: undefined
16429
16432
  };
16430
16433
  }
16431
- if (ecosystem === 'dotnet' && (await skipWithoutDotnetSdk(bin, dir))) {
16432
- return {
16433
- ok: true,
16434
- data: undefined
16435
- };
16436
- }
16437
16434
  for (const root of roots) {
16438
16435
  if (
16439
16436
  // eslint-disable-next-line no-await-in-loop
@@ -21514,6 +21511,7 @@ const generalFlags$1 = {
21514
21511
  default: false,
21515
21512
  description: `${DYNAMIC_SBOM_INFERENCE_DESCRIPTION} Combine with --reach to split the reachability analysis per project/module.`
21516
21513
  },
21514
+ ...dynamicSbomInferenceEcosystemsFlags,
21517
21515
  interactive: {
21518
21516
  type: 'boolean',
21519
21517
  default: true,
@@ -21666,7 +21664,6 @@ async function run$d(argv, importMeta, {
21666
21664
  committers,
21667
21665
  cwd: cwdOverride,
21668
21666
  defaultBranch,
21669
- dynamicSbomInference,
21670
21667
  interactive = true,
21671
21668
  json,
21672
21669
  markdown,
@@ -21701,6 +21698,7 @@ async function run$d(argv, importMeta, {
21701
21698
 
21702
21699
  // Validate ecosystem values against the reachability-supported set.
21703
21700
  const reachEcosystems = utils.parseReachEcosystems(utils.cmdFlagValueToArray(cli.flags['reachEcosystems']));
21701
+ const dynamicSbomInference = parseDynamicSbomInferenceFlags(cli.flags);
21704
21702
  warnSocketJsonOnlyFlags(cli.flags, `${parentName} ${CMD_NAME$a}`);
21705
21703
  const dryRun = !!cli.flags['dryRun'];
21706
21704
  const uvMembers = !!cli.flags['uvMembers'];
@@ -21942,7 +21940,7 @@ async function run$d(argv, importMeta, {
21942
21940
  pendingHead: Boolean(pendingHead),
21943
21941
  pullRequest: Number(pullRequest),
21944
21942
  reach: {
21945
- dynamicSbomInference: Boolean(dynamicSbomInference),
21943
+ dynamicSbomInference,
21946
21944
  excludePaths,
21947
21945
  reachAdvisories,
21948
21946
  reachAnalysisMemoryLimit,
@@ -22662,7 +22660,7 @@ async function scanOneRepo(repoSlug, {
22662
22660
  pendingHead: true,
22663
22661
  pullRequest: 0,
22664
22662
  reach: {
22665
- dynamicSbomInference: false,
22663
+ dynamicSbomInference: undefined,
22666
22664
  excludePaths: [],
22667
22665
  reachAdvisories: [],
22668
22666
  reachAnalysisMemoryLimit: '',
@@ -23867,18 +23865,17 @@ async function runScanReach({
23867
23865
  // Extra discovery targets beyond the user's own; the reachability target
23868
23866
  // itself stays `targets[0]`.
23869
23867
  let scanTargets = targets;
23870
- // Sidecar forwarded to reachability; populated by dynamic SBOM inference.
23871
- let resolvedPathsSidecar;
23868
+ let dynamicResult;
23872
23869
  if (reachabilityOptions.dynamicSbomInference) {
23873
- logger.logger.info('Generating Socket facts for each Gradle, sbt, Maven and .NET build root ...');
23874
- const dynamicResult = await runDynamicSbomInference({
23870
+ logger.logger.info('Generating Socket facts for each build root ...');
23871
+ dynamicResult = await runDynamicSbomInference({
23872
+ consumer: 'reach',
23875
23873
  cwd,
23876
23874
  excludePaths: reachabilityOptions.excludePaths,
23877
- sbtTmpDir,
23878
- withFiles: true
23875
+ purlTypes: reachabilityOptions.dynamicSbomInference,
23876
+ sbtTmpDir
23879
23877
  });
23880
23878
  scanTargets = Array.from(new Set([...scanTargets, ...dynamicResult.factsPaths]));
23881
- resolvedPathsSidecar = dynamicResult.resolvedPathsSidecar;
23882
23879
  }
23883
23880
 
23884
23881
  // Get supported file names.
@@ -23927,12 +23924,13 @@ async function runScanReach({
23927
23924
  spinner.start('Running reachability analysis...');
23928
23925
  const result = await performReachabilityAnalysis({
23929
23926
  cwd,
23927
+ factsOnlyTypes: dynamicResult?.factsOnlyTypes,
23930
23928
  orgSlug,
23931
23929
  outputKind,
23932
23930
  outputPath,
23933
23931
  packagePaths,
23934
23932
  reachabilityOptions: mergedReachabilityOptions,
23935
- resolvedPathsSidecar,
23933
+ resolvedPathsSidecar: dynamicResult?.resolvedPathsSidecar,
23936
23934
  spinner,
23937
23935
  target: targets[0]
23938
23936
  });
@@ -23978,6 +23976,7 @@ const reachabilityFlagsForReach = {
23978
23976
  default: false,
23979
23977
  description: `${DYNAMIC_SBOM_INFERENCE_DESCRIPTION} The reachability analysis is split per project/module accordingly.`
23980
23978
  },
23979
+ ...dynamicSbomInferenceEcosystemsFlags,
23981
23980
  // Fallback targets scan creation, which this command never performs.
23982
23981
  reachFallbackToRegularScan: {
23983
23982
  type: 'boolean',
@@ -24065,7 +24064,6 @@ async function run$7(argv, importMeta, {
24065
24064
  warnSocketJsonOnlyFlags(cli.flags, `${parentName} ${CMD_NAME$4}`);
24066
24065
  const {
24067
24066
  cwd: cwdOverride,
24068
- dynamicSbomInference,
24069
24067
  interactive = true,
24070
24068
  json,
24071
24069
  markdown,
@@ -24102,6 +24100,7 @@ async function run$7(argv, importMeta, {
24102
24100
 
24103
24101
  // Validate ecosystem values against the reachability-supported set.
24104
24102
  const reachEcosystems = utils.parseReachEcosystems(reachEcosystemsRaw);
24103
+ const dynamicSbomInference = parseDynamicSbomInferenceFlags(cli.flags);
24105
24104
  const processCwd = process.cwd();
24106
24105
  const cwd = cwdOverride && cwdOverride !== '.' && cwdOverride !== processCwd ? path.resolve(processCwd, cwdOverride) : processCwd;
24107
24106
 
@@ -24176,7 +24175,7 @@ async function run$7(argv, importMeta, {
24176
24175
  outputKind,
24177
24176
  outputPath: outputPath || '',
24178
24177
  reachabilityOptions: {
24179
- dynamicSbomInference: Boolean(dynamicSbomInference),
24178
+ dynamicSbomInference,
24180
24179
  excludePaths,
24181
24180
  reachAdvisories,
24182
24181
  reachAnalysisMemoryLimit,
@@ -26144,5 +26143,5 @@ process.on('unhandledRejection', async (reason, promise) => {
26144
26143
  // eslint-disable-next-line n/no-process-exit
26145
26144
  process.exit(1);
26146
26145
  });
26147
- //# debugId=9ce53b2e-0d3f-45d7-943f-f52e97dd2e26
26146
+ //# debugId=e8c87646-0d74-4bcc-9e38-e86a951f03ae
26148
26147
  //# sourceMappingURL=cli.js.map