@socketsecurity/cli 1.1.180 → 1.2.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.
package/dist/cli.js CHANGED
@@ -1878,11 +1878,16 @@ function sortByPurl(entries) {
1878
1878
  // A second call for the same factsFile (a dual-marker directory where two
1879
1879
  // build tools both target it) overwrites rather than merges, matching the
1880
1880
  // existing last-writer-wins convention for that case.
1881
- function accumulateSidecar(acc, facts, artifactPaths, factsFile) {
1881
+ function accumulateSidecar(acc, facts, artifactPaths, factsFile,
1882
+ // Off when artifact paths were not resolved; entries then omit `targets` and `sources`.
1883
+ withPaths = true) {
1884
+ const paths = entry => withPaths ? attachPaths(entry, artifactPaths) : {
1885
+ ...entry
1886
+ };
1882
1887
  acc.set(factsFile, {
1883
- components: facts.components.map(comp => attachPaths(comp, artifactPaths)),
1888
+ components: facts.components.map(paths),
1884
1889
  projects: (facts.projects ?? []).map(proj => ({
1885
- ...attachPaths(proj, artifactPaths),
1890
+ ...paths(proj),
1886
1891
  classpath: [...(artifactPaths.classpathByProject.get(projectClasspathKey(proj)) ?? [])]
1887
1892
  }))
1888
1893
  });
@@ -3695,12 +3700,12 @@ async function runManifestFacts({
3695
3700
  facts.metadata.socketCliVersion = socketCliVersion;
3696
3701
  }
3697
3702
  await fs$1.promises.writeFile(factsPath, JSON.stringify(facts), 'utf8');
3698
- if (withFiles && sidecarAcc) {
3703
+ if (sidecarAcc) {
3699
3704
  // Key by the symlink-resolved path so the sidecar's keys are comparable
3700
3705
  // regardless of which caller's cwd it was joined against (the recursive
3701
3706
  // discovery path already resolves symlinks before this point; the plain
3702
3707
  // single-root path does not).
3703
- accumulateSidecar(sidecarAcc, facts, artifactPaths, await utils.realpathOrResolved(factsPath));
3708
+ accumulateSidecar(sidecarAcc, facts, artifactPaths, await utils.realpathOrResolved(factsPath), !!withFiles);
3704
3709
  }
3705
3710
  logger.logger.success('Generated Socket facts');
3706
3711
  return {
@@ -3971,9 +3976,10 @@ async function runDynamicSbomInference({
3971
3976
  cwd,
3972
3977
  excludePaths,
3973
3978
  sbtTmpDir,
3979
+ sidecar,
3974
3980
  withFiles
3975
3981
  }) {
3976
- const sidecarAcc = withFiles ? new Map() : undefined;
3982
+ const sidecarAcc = sidecar ?? withFiles ? new Map() : undefined;
3977
3983
  const outcomes = await generateRecursiveManifests({
3978
3984
  cwd,
3979
3985
  excludePaths,
@@ -8817,6 +8823,52 @@ async function getFixEnv() {
8817
8823
  };
8818
8824
  }
8819
8825
 
8826
+ // Backed up so each fix attempt sees the pre-fix build after `git clean`.
8827
+ async function generateSocketFactsForFix({
8828
+ cwd,
8829
+ excludePaths,
8830
+ tmpDir
8831
+ }) {
8832
+ const {
8833
+ factsPaths,
8834
+ resolvedPathsSidecar
8835
+ } = await runDynamicSbomInference({
8836
+ cwd,
8837
+ excludePaths,
8838
+ sbtTmpDir: undefined,
8839
+ sidecar: true,
8840
+ withFiles: false
8841
+ });
8842
+ const sidecarFile = resolvedPathsSidecar ? path.join(tmpDir, 'sidecar.json') : undefined;
8843
+ if (sidecarFile) {
8844
+ await fs.writeFile(sidecarFile, JSON.stringify(resolvedPathsSidecar));
8845
+ }
8846
+ const paths = factsPaths.map(p => path.resolve(cwd, p));
8847
+ const backups = await Promise.all(paths.map(async (source, index) => {
8848
+ const backup = path.join(tmpDir, `${index}.json`);
8849
+ await fs.copyFile(source, backup);
8850
+ return {
8851
+ backup,
8852
+ source
8853
+ };
8854
+ }));
8855
+ return {
8856
+ paths,
8857
+ sidecarFile,
8858
+ async remove() {
8859
+ await Promise.all(paths.map(p => fs.rm(p, {
8860
+ force: true
8861
+ })));
8862
+ },
8863
+ async restore() {
8864
+ await Promise.all(backups.map(({
8865
+ backup,
8866
+ source
8867
+ }) => fs.copyFile(backup, source)));
8868
+ }
8869
+ };
8870
+ }
8871
+
8820
8872
  async function readStructuredDiscoveryResult(outputFile, silence) {
8821
8873
  let raw;
8822
8874
  try {
@@ -8876,6 +8928,7 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
8876
8928
  const {
8877
8929
  cwd = process.cwd(),
8878
8930
  ecosystems,
8931
+ factsFlags = [],
8879
8932
  packageManagers,
8880
8933
  silence = false,
8881
8934
  spinner
@@ -8888,7 +8941,7 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
8888
8941
  // filteredArtifactCount }) via --output-file (available since coana 15.9.7,
8889
8942
  // older than the pinned @coana-tech/cli version).
8890
8943
  const outputFile = path.join(os.tmpdir(), `socket-fix-discovery-${Date.now()}.json`);
8891
- const foundCResult = await utils.spawnCoanaDlx(['find-vulnerabilities', cwd, '--manifests-tar-hash', tarHash, '--output-file', outputFile, ...(ecosystems?.length ? ['--purl-types', ...ecosystems] : []), ...(packageManagers?.length ? ['--package-managers', ...packageManagers] : [])], orgSlug, {
8944
+ const foundCResult = await utils.spawnCoanaDlx(['find-vulnerabilities', cwd, '--manifests-tar-hash', tarHash, '--output-file', outputFile, ...(ecosystems?.length ? ['--purl-types', ...ecosystems] : []), ...(packageManagers?.length ? ['--package-managers', ...packageManagers] : []), ...factsFlags], orgSlug, {
8892
8945
  cwd,
8893
8946
  spinner: silence ? undefined : spinner,
8894
8947
  coanaVersion: options?.coanaVersion
@@ -8909,7 +8962,32 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
8909
8962
  }).catch(() => {});
8910
8963
  }
8911
8964
  }
8965
+ function isFactsFile(filepath) {
8966
+ return path.basename(filepath).toLowerCase() === constants.DOT_SOCKET_DOT_FACTS_JSON;
8967
+ }
8968
+ function readWrittenFiles(outputFile) {
8969
+ const result = fs$2.readJsonSync(outputFile, {
8970
+ throws: false
8971
+ });
8972
+ const files = result?.modifiedFiles;
8973
+ return Array.isArray(files) && files.every(f => typeof f === 'string') ? new Set(files) : undefined;
8974
+ }
8912
8975
  async function coanaFix(fixConfig) {
8976
+ if (!fixConfig.dynamicSbomInference) {
8977
+ return await coanaFixWithFacts(fixConfig, undefined);
8978
+ }
8979
+ return await utils.withTmpDir('socket-fix-facts-', async tmpDir => {
8980
+ const slot = {
8981
+ tmpDir
8982
+ };
8983
+ try {
8984
+ return await coanaFixWithFacts(fixConfig, slot);
8985
+ } finally {
8986
+ await slot.generated?.remove();
8987
+ }
8988
+ });
8989
+ }
8990
+ async function coanaFixWithFacts(fixConfig, factsSlot) {
8913
8991
  const {
8914
8992
  all,
8915
8993
  applyFixes,
@@ -8970,14 +9048,15 @@ async function coanaFix(fixConfig) {
8970
9048
  // sibling manifest's references). --exclude stays separate as a hidden
8971
9049
  // legacy escape hatch for the narrower "fix-application only" semantic.
8972
9050
  const coanaExcludePatterns = [...exclude, ...excludePaths];
8973
- const scanFilepaths = await utils.getPackageFilesForScan(['.'], supportedFiles, {
9051
+ const findScanFilepaths = () => utils.getPackageFilesForScan(['.'], supportedFiles, {
8974
9052
  additionalIgnores,
8975
9053
  config: socketConfig,
8976
9054
  cwd
8977
9055
  });
9056
+ const scanFilepaths = await findScanFilepaths();
8978
9057
  // Fail if any .socket.facts.json files are present in the scan folder.
8979
9058
  // These are analysis artifacts and must be removed before re-running fix.
8980
- const factsFiles = scanFilepaths.filter(p => path.basename(p).toLowerCase() === constants.DOT_SOCKET_DOT_FACTS_JSON);
9059
+ const factsFiles = scanFilepaths.filter(isFactsFile);
8981
9060
  if (factsFiles.length) {
8982
9061
  if (!silence) {
8983
9062
  spinner?.stop();
@@ -8988,6 +9067,35 @@ async function coanaFix(fixConfig) {
8988
9067
  cause: `Delete the following ${words.pluralize('file', factsFiles.length)} before running socket fix again:\n` + factsFiles.map(p => ` - ${p}`).join('\n')
8989
9068
  };
8990
9069
  }
9070
+ if (factsSlot) {
9071
+ if (!silence) {
9072
+ spinner?.stop();
9073
+ logger.logger.info('Generating Socket facts for Gradle, sbt and Maven builds ...');
9074
+ }
9075
+ try {
9076
+ factsSlot.generated = await generateSocketFactsForFix({
9077
+ cwd,
9078
+ excludePaths,
9079
+ tmpDir: factsSlot.tmpDir
9080
+ });
9081
+ } catch (e) {
9082
+ // A failed build root aborts inference after others wrote their facts.
9083
+ const partial = (await findScanFilepaths()).filter(isFactsFile);
9084
+ await Promise.all(partial.map(p => fs$1.promises.rm(p, {
9085
+ force: true
9086
+ })));
9087
+ throw e;
9088
+ }
9089
+ scanFilepaths.push(...factsSlot.generated.paths);
9090
+ if (!silence) {
9091
+ spinner?.start();
9092
+ }
9093
+ }
9094
+ const sidecarFile = factsSlot?.generated?.sidecarFile;
9095
+ // Discovery only needs which artifacts the facts files resolve; applying
9096
+ // fixes also needs each project's exact classpath from the sidecar.
9097
+ const discoveryFlags = factsSlot ? ['--maven-use-only-socket-facts'] : [];
9098
+ const factsFlags = [...discoveryFlags, ...(sidecarFile ? ['--compute-artifacts-sidecar', sidecarFile] : [])];
8991
9099
  const uploadCResult = await utils.handleApiCall(sockSdk.uploadManifestFiles(orgSlug, scanFilepaths, {
8992
9100
  pathsRelativeTo: cwd
8993
9101
  }), {
@@ -9038,6 +9146,7 @@ async function coanaFix(fixConfig) {
9038
9146
  coanaVersion,
9039
9147
  cwd,
9040
9148
  ecosystems,
9149
+ factsFlags: discoveryFlags,
9041
9150
  packageManagers,
9042
9151
  silence,
9043
9152
  spinner
@@ -9069,7 +9178,7 @@ async function coanaFix(fixConfig) {
9069
9178
  const tmpDir = os.tmpdir();
9070
9179
  const tmpFile = path.join(tmpDir, `socket-fix-${Date.now()}.json`);
9071
9180
  try {
9072
- const fixCResult = await utils.spawnCoanaDlx(['compute-fixes-and-upgrade-purls', cwd, '--manifests-tar-hash', tarHash, '--apply-fixes-to', ...ids, ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []), ...(minimumReleaseAge ? ['--minimum-release-age', minimumReleaseAge] : []), ...(include.length ? ['--include', ...include] : []), ...(coanaExcludePatterns.length ? ['--exclude', ...coanaExcludePatterns] : []), ...(ecosystems.length ? ['--purl-types', ...ecosystems] : []), ...(packageManagers.length ? ['--package-managers', ...packageManagers] : []), ...(!applyFixes ? [constants.FLAG_DRY_RUN] : []), '--output-file', tmpFile, ...(debug$1 ? ['--debug'] : []), ...(disableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(disableMajorUpdates ? ['--disable-major-updates'] : []), ...(showAffectedDirectDependencies ? ['--show-affected-direct-dependencies'] : []), ...fixConfig.unknownFlags], fixConfig.orgSlug, {
9181
+ const fixCResult = await utils.spawnCoanaDlx(['compute-fixes-and-upgrade-purls', cwd, '--manifests-tar-hash', tarHash, '--apply-fixes-to', ...ids, ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []), ...(minimumReleaseAge ? ['--minimum-release-age', minimumReleaseAge] : []), ...(include.length ? ['--include', ...include] : []), ...(coanaExcludePatterns.length ? ['--exclude', ...coanaExcludePatterns] : []), ...(ecosystems.length ? ['--purl-types', ...ecosystems] : []), ...(packageManagers.length ? ['--package-managers', ...packageManagers] : []), ...factsFlags, ...(!applyFixes ? [constants.FLAG_DRY_RUN] : []), '--output-file', tmpFile, ...(debug$1 ? ['--debug'] : []), ...(disableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(disableMajorUpdates ? ['--disable-major-updates'] : []), ...(showAffectedDirectDependencies ? ['--show-affected-direct-dependencies'] : []), ...fixConfig.unknownFlags], fixConfig.orgSlug, {
9073
9182
  coanaVersion,
9074
9183
  cwd,
9075
9184
  spinner: silence ? undefined : spinner,
@@ -9138,6 +9247,7 @@ async function coanaFix(fixConfig) {
9138
9247
  coanaVersion,
9139
9248
  cwd,
9140
9249
  ecosystems,
9250
+ factsFlags: discoveryFlags,
9141
9251
  packageManagers,
9142
9252
  silence,
9143
9253
  spinner
@@ -9186,13 +9296,17 @@ async function coanaFix(fixConfig) {
9186
9296
  const ghsaId = ids[i];
9187
9297
  debug.debugFn('notice', `check: ${ghsaId}`);
9188
9298
 
9299
+ // Resetting to the base branch cleans the untracked facts files away.
9300
+ // eslint-disable-next-line no-await-in-loop
9301
+ await factsSlot?.generated?.restore();
9302
+
9189
9303
  // Create a temporary file for Coana output.
9190
9304
  const tmpDir = os.tmpdir();
9191
9305
  const tmpFile = path.join(tmpDir, `socket-fix-${ghsaId}-${Date.now()}.json`);
9192
9306
 
9193
9307
  // Apply fix for single GHSA ID.
9194
9308
  // eslint-disable-next-line no-await-in-loop
9195
- const fixCResult = await utils.spawnCoanaDlx(['compute-fixes-and-upgrade-purls', cwd, '--manifests-tar-hash', tarHash, '--apply-fixes-to', ghsaId, ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []), ...(minimumReleaseAge ? ['--minimum-release-age', minimumReleaseAge] : []), ...(include.length ? ['--include', ...include] : []), ...(coanaExcludePatterns.length ? ['--exclude', ...coanaExcludePatterns] : []), ...(ecosystems.length ? ['--purl-types', ...ecosystems] : []), ...(packageManagers.length ? ['--package-managers', ...packageManagers] : []), ...(debug$1 ? ['--debug'] : []), ...(disableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(disableMajorUpdates ? ['--disable-major-updates'] : []), ...(showAffectedDirectDependencies ? ['--show-affected-direct-dependencies'] : []), '--output-file', tmpFile, ...fixConfig.unknownFlags], fixConfig.orgSlug, {
9309
+ const fixCResult = await utils.spawnCoanaDlx(['compute-fixes-and-upgrade-purls', cwd, '--manifests-tar-hash', tarHash, '--apply-fixes-to', ghsaId, ...(fixConfig.rangeStyle ? ['--range-style', fixConfig.rangeStyle] : []), ...(minimumReleaseAge ? ['--minimum-release-age', minimumReleaseAge] : []), ...(include.length ? ['--include', ...include] : []), ...(coanaExcludePatterns.length ? ['--exclude', ...coanaExcludePatterns] : []), ...(ecosystems.length ? ['--purl-types', ...ecosystems] : []), ...(packageManagers.length ? ['--package-managers', ...packageManagers] : []), ...factsFlags, ...(debug$1 ? ['--debug'] : []), ...(disableExternalToolChecks ? ['--disable-external-tool-checks'] : []), ...(disableMajorUpdates ? ['--disable-major-updates'] : []), ...(showAffectedDirectDependencies ? ['--show-affected-direct-dependencies'] : []), '--output-file', tmpFile, ...fixConfig.unknownFlags], fixConfig.orgSlug, {
9196
9310
  coanaVersion,
9197
9311
  cwd,
9198
9312
  spinner: silence ? undefined : spinner,
@@ -9215,7 +9329,12 @@ async function coanaFix(fixConfig) {
9215
9329
  // Check for modified files after applying the fix.
9216
9330
  // eslint-disable-next-line no-await-in-loop
9217
9331
  const unstagedCResult = await utils.gitUnstagedModifiedFiles(cwd);
9218
- const modifiedFiles = unstagedCResult.ok ? unstagedCResult.data.filter(relPath => scanBaseNames.has(path.basename(relPath))) : [];
9332
+ // Build scripts the fix edits need not be manifests the scan uploads,
9333
+ // and files it creates are untracked.
9334
+ const writtenFiles = readWrittenFiles(tmpFile);
9335
+ // eslint-disable-next-line no-await-in-loop
9336
+ const untrackedCResult = await utils.gitUntrackedFiles(cwd);
9337
+ const modifiedFiles = writtenFiles ? [...(unstagedCResult.ok ? unstagedCResult.data : []), ...(untrackedCResult.ok ? untrackedCResult.data : [])].filter(relPath => writtenFiles.has(relPath)) : unstagedCResult.ok ? unstagedCResult.data.filter(relPath => scanBaseNames.has(path.basename(relPath))) : [];
9219
9338
  if (!modifiedFiles.length) {
9220
9339
  debug.debugFn('notice', `skip: no changes for ${ghsaId}`);
9221
9340
  // Clean up temp file before continuing.
@@ -9531,6 +9650,7 @@ async function handleFix({
9531
9650
  debug: debug$1,
9532
9651
  disableExternalToolChecks,
9533
9652
  disableMajorUpdates,
9653
+ dynamicSbomInference,
9534
9654
  ecosystems,
9535
9655
  exclude,
9536
9656
  excludePaths,
@@ -9560,6 +9680,7 @@ async function handleFix({
9560
9680
  debug: debug$1,
9561
9681
  disableExternalToolChecks,
9562
9682
  disableMajorUpdates,
9683
+ dynamicSbomInference,
9563
9684
  ecosystems,
9564
9685
  exclude,
9565
9686
  excludePaths,
@@ -9586,6 +9707,7 @@ async function handleFix({
9586
9707
  debug: debug$1,
9587
9708
  disableExternalToolChecks,
9588
9709
  disableMajorUpdates,
9710
+ dynamicSbomInference,
9589
9711
  ecosystems,
9590
9712
  exclude,
9591
9713
  excludePaths,
@@ -9609,6 +9731,127 @@ async function handleFix({
9609
9731
  }), outputKind);
9610
9732
  }
9611
9733
 
9734
+ // Shared prefix so each command can append what the SBOMs are used for, which
9735
+ // differs: reachability is optional on `scan create` and always on for
9736
+ // `scan reach`.
9737
+ const DYNAMIC_SBOM_INFERENCE_DESCRIPTION = 'For Gradle, sbt, and Maven: generate a Socket facts SBOM (produced directly by each package manager) per independent build root, instead of one synthetic root.';
9738
+ const reachabilityFlags = {
9739
+ reachVersion: {
9740
+ type: 'string',
9741
+ description: `Override the version of @coana-tech/cli used for reachability analysis. Default: ${constants.default.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION}.`
9742
+ },
9743
+ reachAnalysisMemoryLimit: {
9744
+ type: 'string',
9745
+ default: '8192',
9746
+ description: 'The maximum memory for the reachability analysis as a whole number optionally followed by MB or GB (e.g. 512MB, 8GB). The default is 8GB.'
9747
+ },
9748
+ reachAnalysisTimeout: {
9749
+ type: 'string',
9750
+ default: '',
9751
+ description: 'Set the timeout for the reachability analysis as a whole number optionally followed by s, m or h (e.g. 90s, 10m, 1h). Defaults to 10m. Split analysis runs may cause the total scan time to exceed this timeout significantly.'
9752
+ },
9753
+ reachConcurrency: {
9754
+ type: 'number',
9755
+ default: 1,
9756
+ description: 'Set the maximum number of concurrent reachability analysis runs. It is recommended to choose a concurrency level that ensures each analysis run has at least the --reach-analysis-memory-limit amount of memory available.'
9757
+ },
9758
+ reachContinueOnAnalysisErrors: {
9759
+ type: 'boolean',
9760
+ default: false,
9761
+ description: 'Continue reachability analysis when errors occur (timeouts, OOM, parse errors, etc.), falling back to precomputed reachability results. By default, the CLI halts on analysis errors.'
9762
+ },
9763
+ reachContinueOnInstallErrors: {
9764
+ type: 'boolean',
9765
+ default: false,
9766
+ description: 'Continue reachability analysis when package installation fails, falling back to precomputed reachability results. By default, the CLI halts on installation errors.'
9767
+ },
9768
+ reachContinueOnMissingLockFiles: {
9769
+ type: 'boolean',
9770
+ default: false,
9771
+ description: 'Continue reachability analysis when a Gradle or SBT project is missing its lock file (or version catalog / pre-generated SBOM). By default, the CLI halts.'
9772
+ },
9773
+ reachContinueOnNoSourceFiles: {
9774
+ type: 'boolean',
9775
+ default: false,
9776
+ description: 'Continue reachability analysis when a workspace contains no source files for its ecosystem. By default, the CLI halts.'
9777
+ },
9778
+ reachDisableExternalToolChecks: {
9779
+ type: 'boolean',
9780
+ default: false,
9781
+ description: 'Disable external tool checks during reachability analysis.'
9782
+ },
9783
+ reachDebug: {
9784
+ type: 'boolean',
9785
+ default: false,
9786
+ description: 'Enable debug mode for reachability analysis. Provides verbose logging from the reachability CLI.'
9787
+ },
9788
+ reachDetailedAnalysisLogFile: {
9789
+ type: 'boolean',
9790
+ default: false,
9791
+ description: 'A log file with detailed analysis logs is written to root of each analyzed workspace.'
9792
+ },
9793
+ reachDisableAnalytics: {
9794
+ type: 'boolean',
9795
+ default: false,
9796
+ description: 'Disable reachability analytics sharing with Socket. Also disables caching-based optimizations.'
9797
+ },
9798
+ reachDisableAnalysisSplitting: {
9799
+ type: 'boolean',
9800
+ default: false,
9801
+ hidden: true,
9802
+ description: 'Deprecated: Analysis splitting is now disabled by default. This flag is a no-op.'
9803
+ },
9804
+ reachEnableAnalysisSplitting: {
9805
+ type: 'boolean',
9806
+ default: false,
9807
+ description: 'Allow the reachability analysis to partition CVEs into buckets that are processed in separate analysis runs. May improve accuracy, but not recommended by default.'
9808
+ },
9809
+ reachEcosystems: {
9810
+ type: 'string',
9811
+ isMultiple: true,
9812
+ description: `List of ecosystems to conduct reachability analysis on, as either a comma separated value or as multiple flags. Supported: ${utils.getReachabilityEcosystemChoices().join(', ')}. Defaults to all supported ecosystems.`
9813
+ },
9814
+ reachExcludePaths: {
9815
+ type: 'string',
9816
+ isMultiple: true,
9817
+ hidden: true,
9818
+ description: 'Deprecated: use --exclude-paths instead. List of paths to exclude from reachability analysis, as either a comma separated value or as multiple flags.'
9819
+ },
9820
+ reachFallbackToRegularScan: {
9821
+ type: 'boolean',
9822
+ default: false,
9823
+ description: 'If reachability analysis fails, continue with a regular SCA scan (without reachability results) instead of halting. By default, the CLI halts on reachability errors.'
9824
+ },
9825
+ reachLazyMode: {
9826
+ type: 'boolean',
9827
+ default: false,
9828
+ hidden: true,
9829
+ description: 'Deprecated: lazy mode is no longer supported. This flag is a no-op.'
9830
+ },
9831
+ reachRetainFactsFile: {
9832
+ type: 'boolean',
9833
+ default: false,
9834
+ description: 'Keep the `.socket.facts.json` reachability report that the analysis writes to the scan directory instead of deleting it after a successful scan. IMPORTANT: you must delete this file before running a fresh full application reachability scan. A stale `.socket.facts.json` left in place is picked up as a pre-generated input and silently overrides fresh analysis, so the new scan results will not be reliable.'
9835
+ },
9836
+ reachSkipCache: {
9837
+ type: 'boolean',
9838
+ default: false,
9839
+ description: 'Skip caching-based optimizations. By default, the reachability analysis will use cached configurations from previous runs to speed up the analysis.'
9840
+ },
9841
+ reachUseOnlyPregeneratedSboms: {
9842
+ type: 'boolean',
9843
+ default: false,
9844
+ description: 'When using this option, the scan is created based only on pre-generated CDX and SPDX files in your project.'
9845
+ }
9846
+ };
9847
+ const excludePathsFlag$1 = {
9848
+ excludePaths: {
9849
+ type: 'string',
9850
+ isMultiple: true,
9851
+ description: 'List of glob patterns to exclude from the scan, including SCA/SBOM manifest discovery and (when --reach is enabled) full application reachability analysis. Patterns are anchored micromatch globs matched relative to the Socket scan root, which is the command working directory (`--cwd` if set), not the reachability target: `tests` matches only `<cwd>/tests`; use `**/tests` to match at any depth. Negation patterns (`!path`) are not supported. Accepts a comma-separated value or multiple flags.'
9852
+ }
9853
+ };
9854
+
9612
9855
  const CMD_NAME$s = 'fix';
9613
9856
  const DEFAULT_LIMIT = 10;
9614
9857
  const description$z = 'Fix CVEs in dependencies';
@@ -9720,6 +9963,11 @@ Available styles:
9720
9963
  default: false,
9721
9964
  description: 'Disable external tool checks during fix analysis.'
9722
9965
  },
9966
+ dynamicSbomInference: {
9967
+ type: 'boolean',
9968
+ default: false,
9969
+ 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.`
9970
+ },
9723
9971
  ecosystems: {
9724
9972
  type: 'string',
9725
9973
  default: [],
@@ -9853,6 +10101,7 @@ async function run$N(argv, importMeta, {
9853
10101
  autopilot,
9854
10102
  debug,
9855
10103
  disableExternalToolChecks,
10104
+ dynamicSbomInference,
9856
10105
  ecosystems,
9857
10106
  exclude,
9858
10107
  excludePaths,
@@ -9985,6 +10234,7 @@ async function run$N(argv, importMeta, {
9985
10234
  debug,
9986
10235
  disableExternalToolChecks,
9987
10236
  disableMajorUpdates,
10237
+ dynamicSbomInference,
9988
10238
  ecosystems: validatedEcosystems,
9989
10239
  exclude: excludePatterns,
9990
10240
  excludePaths: excludePathsPatterns,
@@ -12232,7 +12482,7 @@ async function run$H(argv, importMeta, {
12232
12482
  // Manifest-scoped variant of `../scan/reachability-flags.mts`'s
12233
12483
  // `excludePathsFlag`: description avoids "scan"/reachability wording since
12234
12484
  // these commands only ever generate a manifest/facts file.
12235
- const excludePathsFlag$1 = {
12485
+ const excludePathsFlag = {
12236
12486
  excludePaths: {
12237
12487
  type: 'string',
12238
12488
  isMultiple: true,
@@ -12246,7 +12496,7 @@ const config$f = {
12246
12496
  hidden: false,
12247
12497
  flags: {
12248
12498
  ...flags.commonFlags,
12249
- ...excludePathsFlag$1,
12499
+ ...excludePathsFlag,
12250
12500
  verbose: {
12251
12501
  type: 'boolean',
12252
12502
  default: false,
@@ -12569,7 +12819,7 @@ const config$d = {
12569
12819
  flags: {
12570
12820
  ...flags.commonFlags,
12571
12821
  ...flags.outputFlags,
12572
- ...excludePathsFlag$1,
12822
+ ...excludePathsFlag,
12573
12823
  verbose: {
12574
12824
  type: 'boolean',
12575
12825
  default: false,
@@ -12684,7 +12934,7 @@ const config$c = {
12684
12934
  type: 'string',
12685
12935
  description: 'When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)'
12686
12936
  },
12687
- ...excludePathsFlag$1,
12937
+ ...excludePathsFlag,
12688
12938
  ignoreUnresolved: {
12689
12939
  type: 'boolean',
12690
12940
  description: 'When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -12938,7 +13188,7 @@ const config$b = {
12938
13188
  type: 'string',
12939
13189
  description: 'When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)'
12940
13190
  },
12941
- ...excludePathsFlag$1,
13191
+ ...excludePathsFlag,
12942
13192
  ignoreUnresolved: {
12943
13193
  type: 'boolean',
12944
13194
  description: 'When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -13177,7 +13427,7 @@ const config$a = {
13177
13427
  type: 'string',
13178
13428
  description: 'Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)'
13179
13429
  },
13180
- ...excludePathsFlag$1,
13430
+ ...excludePathsFlag,
13181
13431
  ignoreUnresolved: {
13182
13432
  type: 'boolean',
13183
13433
  description: 'Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -13376,7 +13626,7 @@ const config$9 = {
13376
13626
  type: 'string',
13377
13627
  description: 'When generating facts: comma-separated glob patterns; sbt configurations matching any pattern are skipped (applied after --include-configs)'
13378
13628
  },
13379
- ...excludePathsFlag$1,
13629
+ ...excludePathsFlag,
13380
13630
  ignoreUnresolved: {
13381
13631
  type: 'boolean',
13382
13632
  description: 'When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -19325,127 +19575,6 @@ const cmdRepository = {
19325
19575
  }
19326
19576
  };
19327
19577
 
19328
- // Shared prefix so each command can append what the SBOMs are used for, which
19329
- // differs: reachability is optional on `scan create` and always on for
19330
- // `scan reach`.
19331
- const DYNAMIC_SBOM_INFERENCE_DESCRIPTION = 'For Gradle, sbt, and Maven: generate a Socket facts SBOM (produced directly by each package manager) per independent build root, instead of one synthetic root.';
19332
- const reachabilityFlags = {
19333
- reachVersion: {
19334
- type: 'string',
19335
- description: `Override the version of @coana-tech/cli used for reachability analysis. Default: ${constants.default.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION}.`
19336
- },
19337
- reachAnalysisMemoryLimit: {
19338
- type: 'string',
19339
- default: '8192',
19340
- description: 'The maximum memory for the reachability analysis as a whole number optionally followed by MB or GB (e.g. 512MB, 8GB). The default is 8GB.'
19341
- },
19342
- reachAnalysisTimeout: {
19343
- type: 'string',
19344
- default: '',
19345
- description: 'Set the timeout for the reachability analysis as a whole number optionally followed by s, m or h (e.g. 90s, 10m, 1h). Defaults to 10m. Split analysis runs may cause the total scan time to exceed this timeout significantly.'
19346
- },
19347
- reachConcurrency: {
19348
- type: 'number',
19349
- default: 1,
19350
- description: 'Set the maximum number of concurrent reachability analysis runs. It is recommended to choose a concurrency level that ensures each analysis run has at least the --reach-analysis-memory-limit amount of memory available.'
19351
- },
19352
- reachContinueOnAnalysisErrors: {
19353
- type: 'boolean',
19354
- default: false,
19355
- description: 'Continue reachability analysis when errors occur (timeouts, OOM, parse errors, etc.), falling back to precomputed reachability results. By default, the CLI halts on analysis errors.'
19356
- },
19357
- reachContinueOnInstallErrors: {
19358
- type: 'boolean',
19359
- default: false,
19360
- description: 'Continue reachability analysis when package installation fails, falling back to precomputed reachability results. By default, the CLI halts on installation errors.'
19361
- },
19362
- reachContinueOnMissingLockFiles: {
19363
- type: 'boolean',
19364
- default: false,
19365
- description: 'Continue reachability analysis when a Gradle or SBT project is missing its lock file (or version catalog / pre-generated SBOM). By default, the CLI halts.'
19366
- },
19367
- reachContinueOnNoSourceFiles: {
19368
- type: 'boolean',
19369
- default: false,
19370
- description: 'Continue reachability analysis when a workspace contains no source files for its ecosystem. By default, the CLI halts.'
19371
- },
19372
- reachDisableExternalToolChecks: {
19373
- type: 'boolean',
19374
- default: false,
19375
- description: 'Disable external tool checks during reachability analysis.'
19376
- },
19377
- reachDebug: {
19378
- type: 'boolean',
19379
- default: false,
19380
- description: 'Enable debug mode for reachability analysis. Provides verbose logging from the reachability CLI.'
19381
- },
19382
- reachDetailedAnalysisLogFile: {
19383
- type: 'boolean',
19384
- default: false,
19385
- description: 'A log file with detailed analysis logs is written to root of each analyzed workspace.'
19386
- },
19387
- reachDisableAnalytics: {
19388
- type: 'boolean',
19389
- default: false,
19390
- description: 'Disable reachability analytics sharing with Socket. Also disables caching-based optimizations.'
19391
- },
19392
- reachDisableAnalysisSplitting: {
19393
- type: 'boolean',
19394
- default: false,
19395
- hidden: true,
19396
- description: 'Deprecated: Analysis splitting is now disabled by default. This flag is a no-op.'
19397
- },
19398
- reachEnableAnalysisSplitting: {
19399
- type: 'boolean',
19400
- default: false,
19401
- description: 'Allow the reachability analysis to partition CVEs into buckets that are processed in separate analysis runs. May improve accuracy, but not recommended by default.'
19402
- },
19403
- reachEcosystems: {
19404
- type: 'string',
19405
- isMultiple: true,
19406
- description: `List of ecosystems to conduct reachability analysis on, as either a comma separated value or as multiple flags. Supported: ${utils.getReachabilityEcosystemChoices().join(', ')}. Defaults to all supported ecosystems.`
19407
- },
19408
- reachExcludePaths: {
19409
- type: 'string',
19410
- isMultiple: true,
19411
- hidden: true,
19412
- description: 'Deprecated: use --exclude-paths instead. List of paths to exclude from reachability analysis, as either a comma separated value or as multiple flags.'
19413
- },
19414
- reachFallbackToRegularScan: {
19415
- type: 'boolean',
19416
- default: false,
19417
- description: 'If reachability analysis fails, continue with a regular SCA scan (without reachability results) instead of halting. By default, the CLI halts on reachability errors.'
19418
- },
19419
- reachLazyMode: {
19420
- type: 'boolean',
19421
- default: false,
19422
- hidden: true,
19423
- description: 'Deprecated: lazy mode is no longer supported. This flag is a no-op.'
19424
- },
19425
- reachRetainFactsFile: {
19426
- type: 'boolean',
19427
- default: false,
19428
- description: 'Keep the `.socket.facts.json` reachability report that the analysis writes to the scan directory instead of deleting it after a successful scan. IMPORTANT: you must delete this file before running a fresh full application reachability scan. A stale `.socket.facts.json` left in place is picked up as a pre-generated input and silently overrides fresh analysis, so the new scan results will not be reliable.'
19429
- },
19430
- reachSkipCache: {
19431
- type: 'boolean',
19432
- default: false,
19433
- description: 'Skip caching-based optimizations. By default, the reachability analysis will use cached configurations from previous runs to speed up the analysis.'
19434
- },
19435
- reachUseOnlyPregeneratedSboms: {
19436
- type: 'boolean',
19437
- default: false,
19438
- description: 'When using this option, the scan is created based only on pre-generated CDX and SPDX files in your project.'
19439
- }
19440
- };
19441
- const excludePathsFlag = {
19442
- excludePaths: {
19443
- type: 'string',
19444
- isMultiple: true,
19445
- description: 'List of glob patterns to exclude from the scan, including SCA/SBOM manifest discovery and (when --reach is enabled) full application reachability analysis. Patterns are anchored micromatch globs matched relative to the Socket scan root, which is the command working directory (`--cwd` if set), not the reachability target: `tests` matches only `<cwd>/tests`; use `**/tests` to match at any depth. Negation patterns (`!path`) are not supported. Accepts a comma-separated value or multiple flags.'
19446
- }
19447
- };
19448
-
19449
19578
  async function suggestTarget() {
19450
19579
  // We could prefill this with sub-dirs of the current
19451
19580
  // dir ... but is that going to be useful?
@@ -19624,7 +19753,7 @@ async function run$d(argv, importMeta, {
19624
19753
  hidden: hidden$a,
19625
19754
  flags: {
19626
19755
  ...generalFlags$1,
19627
- ...excludePathsFlag,
19756
+ ...excludePathsFlag$1,
19628
19757
  ...reachabilityFlags
19629
19758
  },
19630
19759
  help: command => `
@@ -19637,7 +19766,7 @@ async function run$d(argv, importMeta, {
19637
19766
  Options
19638
19767
  ${utils.getFlagListOutput({
19639
19768
  ...generalFlags$1,
19640
- ...excludePathsFlag
19769
+ ...excludePathsFlag$1
19641
19770
  })}
19642
19771
 
19643
19772
  Reachability Options (when --reach is used)
@@ -22028,7 +22157,7 @@ async function run$7(argv, importMeta, {
22028
22157
  hidden: hidden$4,
22029
22158
  flags: {
22030
22159
  ...generalFlags,
22031
- ...excludePathsFlag,
22160
+ ...excludePathsFlag$1,
22032
22161
  ...reachabilityFlagsForReach
22033
22162
  },
22034
22163
  help: command => `
@@ -22043,7 +22172,7 @@ async function run$7(argv, importMeta, {
22043
22172
 
22044
22173
  Reachability Options
22045
22174
  ${utils.getFlagListOutput({
22046
- ...excludePathsFlag,
22175
+ ...excludePathsFlag$1,
22047
22176
  ...reachabilityFlagsForReach
22048
22177
  })}
22049
22178
 
@@ -24146,5 +24275,5 @@ process.on('unhandledRejection', async (reason, promise) => {
24146
24275
  // eslint-disable-next-line n/no-process-exit
24147
24276
  process.exit(1);
24148
24277
  });
24149
- //# debugId=d61b6b1f-4e7a-4403-b8f9-087745dd936f
24278
+ //# debugId=45e6f932-d7fe-49d0-91f3-6a45a49eecd3
24150
24279
  //# sourceMappingURL=cli.js.map