@socketsecurity/cli 1.1.179 → 1.2.0

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 (32) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +5 -5
  3. package/dist/cli.js +299 -155
  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/maven-extension/coana-maven-extension.jar +0 -0
  8. package/dist/tsconfig.dts.tsbuildinfo +1 -1
  9. package/dist/types/commands/fix/cmd-fix.d.mts.map +1 -1
  10. package/dist/types/commands/fix/coana-fix.d.mts +4 -2
  11. package/dist/types/commands/fix/coana-fix.d.mts.map +1 -1
  12. package/dist/types/commands/fix/generated-socket-facts.d.mts +14 -0
  13. package/dist/types/commands/fix/generated-socket-facts.d.mts.map +1 -0
  14. package/dist/types/commands/fix/handle-fix.d.mts +1 -1
  15. package/dist/types/commands/fix/handle-fix.d.mts.map +1 -1
  16. package/dist/types/commands/fix/types.d.mts +1 -0
  17. package/dist/types/commands/fix/types.d.mts.map +1 -1
  18. package/dist/types/commands/manifest/cmd-manifest-conda.d.mts.map +1 -1
  19. package/dist/types/commands/manifest/detect-manifest-actions.d.mts +4 -0
  20. package/dist/types/commands/manifest/detect-manifest-actions.d.mts.map +1 -1
  21. package/dist/types/commands/manifest/generate_auto_manifest.d.mts.map +1 -1
  22. package/dist/types/commands/manifest/handle-manifest-conda.d.mts.map +1 -1
  23. package/dist/types/commands/manifest/run-manifest-facts.d.mts.map +1 -1
  24. package/dist/types/commands/manifest/scripts/sidecar.d.mts +3 -1
  25. package/dist/types/commands/manifest/scripts/sidecar.d.mts.map +1 -1
  26. package/dist/types/commands/scan/run-dynamic-sbom-inference.d.mts +4 -1
  27. package/dist/types/commands/scan/run-dynamic-sbom-inference.d.mts.map +1 -1
  28. package/dist/types/utils/git.d.mts +1 -0
  29. package/dist/types/utils/git.d.mts.map +1 -1
  30. package/dist/utils.js +21 -1
  31. package/dist/utils.js.map +1 -1
  32. package/package.json +2 -2
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,
@@ -4002,6 +4008,16 @@ async function runDynamicSbomInference({
4002
4008
  // The point here is to attempt to detect the various supported manifest files
4003
4009
  // the CLI can generate. This would be environments that we can't do server side
4004
4010
 
4011
+ // Conda reads both spellings of the environment file.
4012
+ function findCondaFile(cwd) {
4013
+ if (fs$1.existsSync(path.join(cwd, constants.ENVIRONMENT_YML))) {
4014
+ return constants.ENVIRONMENT_YML;
4015
+ }
4016
+ if (fs$1.existsSync(path.join(cwd, constants.ENVIRONMENT_YAML))) {
4017
+ return constants.ENVIRONMENT_YAML;
4018
+ }
4019
+ return '';
4020
+ }
4005
4021
  async function detectManifestActions(
4006
4022
  // Passing in null means we attempt detection for every supported language
4007
4023
  // regardless of local socket.json status. Sometimes we want that.
@@ -4012,6 +4028,7 @@ sockJson, cwd = process.cwd()) {
4012
4028
  // TODO
4013
4029
  count: 0,
4014
4030
  conda: false,
4031
+ condaFile: '',
4015
4032
  gradle: false,
4016
4033
  maven: false,
4017
4034
  sbt: false
@@ -4049,13 +4066,11 @@ sockJson, cwd = process.cwd()) {
4049
4066
  if (sockJson?.defaults?.manifest?.conda?.disabled) {
4050
4067
  debug.debugLog('notice', `[DEBUG] - conda auto-detection is disabled in ${constants.SOCKET_JSON}`);
4051
4068
  } else {
4052
- const envyml = path.join(cwd, constants.ENVIRONMENT_YML);
4053
- const hasEnvyml = fs$1.existsSync(envyml);
4054
- const envyaml = path.join(cwd, constants.ENVIRONMENT_YAML);
4055
- const hasEnvyaml = !hasEnvyml && fs$1.existsSync(envyaml);
4056
- if (hasEnvyml || hasEnvyaml) {
4057
- debug.debugLog('notice', '[DEBUG] - Detected an environment.yml Conda file');
4069
+ const condaFile = findCondaFile(cwd);
4070
+ if (condaFile) {
4071
+ debug.debugLog('notice', `[DEBUG] - Detected the Conda file ${condaFile}`);
4058
4072
  output.conda = true;
4073
+ output.condaFile = condaFile;
4059
4074
  output.count += 1;
4060
4075
  }
4061
4076
  }
@@ -6898,7 +6913,10 @@ async function handleManifestConda({
6898
6913
  verbose
6899
6914
  }) {
6900
6915
  const data = await convertCondaToRequirements(filename, cwd, verbose);
6901
- await outputRequirements(data, outputKind, out);
6916
+
6917
+ // --auto-manifest only collects the generated file when it lands inside
6918
+ // the dir being scanned.
6919
+ await outputRequirements(data, outputKind, out === '-' ? out : path.resolve(cwd, out));
6902
6920
  }
6903
6921
 
6904
6922
  // Under --auto-manifest, a manifest generator that failed — raising the exit
@@ -7018,14 +7036,17 @@ async function generateAutoManifest({
7018
7036
  abortManifestRunIfFailed('maven', beforeExitCode);
7019
7037
  }
7020
7038
  if (!sockJson?.defaults?.manifest?.conda?.disabled && detected.conda) {
7021
- logger.logger.log('Detected an environment.yml file, running default Conda generator...');
7039
+ const condaFile = sockJson.defaults?.manifest?.conda?.infile || detected.condaFile || constants.ENVIRONMENT_YML;
7040
+ logger.logger.log(`Detected ${condaFile}, running default Conda generator...`);
7041
+ const beforeExitCode = process.exitCode;
7022
7042
  await handleManifestConda({
7023
7043
  cwd,
7024
- filename: sockJson.defaults?.manifest?.conda?.infile ?? 'environment.yml',
7044
+ filename: condaFile,
7025
7045
  outputKind,
7026
7046
  out: sockJson.defaults?.manifest?.conda?.outfile ?? constants.REQUIREMENTS_TXT,
7027
7047
  verbose: Boolean(sockJson.defaults?.manifest?.conda?.verbose)
7028
7048
  });
7049
+ abortManifestRunIfFailed('conda', beforeExitCode);
7029
7050
  }
7030
7051
  if (!sockJson?.defaults?.manifest?.bazel?.disabled && detected.bazel) {
7031
7052
  const bazelConfig = sockJson?.defaults?.manifest?.bazel;
@@ -8802,6 +8823,52 @@ async function getFixEnv() {
8802
8823
  };
8803
8824
  }
8804
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
+
8805
8872
  async function readStructuredDiscoveryResult(outputFile, silence) {
8806
8873
  let raw;
8807
8874
  try {
@@ -8861,6 +8928,7 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
8861
8928
  const {
8862
8929
  cwd = process.cwd(),
8863
8930
  ecosystems,
8931
+ factsFlags = [],
8864
8932
  packageManagers,
8865
8933
  silence = false,
8866
8934
  spinner
@@ -8873,7 +8941,7 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
8873
8941
  // filteredArtifactCount }) via --output-file (available since coana 15.9.7,
8874
8942
  // older than the pinned @coana-tech/cli version).
8875
8943
  const outputFile = path.join(os.tmpdir(), `socket-fix-discovery-${Date.now()}.json`);
8876
- 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, {
8877
8945
  cwd,
8878
8946
  spinner: silence ? undefined : spinner,
8879
8947
  coanaVersion: options?.coanaVersion
@@ -8894,7 +8962,32 @@ async function discoverGhsaIds(orgSlug, tarHash, options) {
8894
8962
  }).catch(() => {});
8895
8963
  }
8896
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
+ }
8897
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) {
8898
8991
  const {
8899
8992
  all,
8900
8993
  applyFixes,
@@ -8955,14 +9048,15 @@ async function coanaFix(fixConfig) {
8955
9048
  // sibling manifest's references). --exclude stays separate as a hidden
8956
9049
  // legacy escape hatch for the narrower "fix-application only" semantic.
8957
9050
  const coanaExcludePatterns = [...exclude, ...excludePaths];
8958
- const scanFilepaths = await utils.getPackageFilesForScan(['.'], supportedFiles, {
9051
+ const findScanFilepaths = () => utils.getPackageFilesForScan(['.'], supportedFiles, {
8959
9052
  additionalIgnores,
8960
9053
  config: socketConfig,
8961
9054
  cwd
8962
9055
  });
9056
+ const scanFilepaths = await findScanFilepaths();
8963
9057
  // Fail if any .socket.facts.json files are present in the scan folder.
8964
9058
  // These are analysis artifacts and must be removed before re-running fix.
8965
- const factsFiles = scanFilepaths.filter(p => path.basename(p).toLowerCase() === constants.DOT_SOCKET_DOT_FACTS_JSON);
9059
+ const factsFiles = scanFilepaths.filter(isFactsFile);
8966
9060
  if (factsFiles.length) {
8967
9061
  if (!silence) {
8968
9062
  spinner?.stop();
@@ -8973,6 +9067,35 @@ async function coanaFix(fixConfig) {
8973
9067
  cause: `Delete the following ${words.pluralize('file', factsFiles.length)} before running socket fix again:\n` + factsFiles.map(p => ` - ${p}`).join('\n')
8974
9068
  };
8975
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] : [])];
8976
9099
  const uploadCResult = await utils.handleApiCall(sockSdk.uploadManifestFiles(orgSlug, scanFilepaths, {
8977
9100
  pathsRelativeTo: cwd
8978
9101
  }), {
@@ -9023,6 +9146,7 @@ async function coanaFix(fixConfig) {
9023
9146
  coanaVersion,
9024
9147
  cwd,
9025
9148
  ecosystems,
9149
+ factsFlags: discoveryFlags,
9026
9150
  packageManagers,
9027
9151
  silence,
9028
9152
  spinner
@@ -9054,7 +9178,7 @@ async function coanaFix(fixConfig) {
9054
9178
  const tmpDir = os.tmpdir();
9055
9179
  const tmpFile = path.join(tmpDir, `socket-fix-${Date.now()}.json`);
9056
9180
  try {
9057
- 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, {
9058
9182
  coanaVersion,
9059
9183
  cwd,
9060
9184
  spinner: silence ? undefined : spinner,
@@ -9123,6 +9247,7 @@ async function coanaFix(fixConfig) {
9123
9247
  coanaVersion,
9124
9248
  cwd,
9125
9249
  ecosystems,
9250
+ factsFlags: discoveryFlags,
9126
9251
  packageManagers,
9127
9252
  silence,
9128
9253
  spinner
@@ -9171,13 +9296,17 @@ async function coanaFix(fixConfig) {
9171
9296
  const ghsaId = ids[i];
9172
9297
  debug.debugFn('notice', `check: ${ghsaId}`);
9173
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
+
9174
9303
  // Create a temporary file for Coana output.
9175
9304
  const tmpDir = os.tmpdir();
9176
9305
  const tmpFile = path.join(tmpDir, `socket-fix-${ghsaId}-${Date.now()}.json`);
9177
9306
 
9178
9307
  // Apply fix for single GHSA ID.
9179
9308
  // eslint-disable-next-line no-await-in-loop
9180
- 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, {
9181
9310
  coanaVersion,
9182
9311
  cwd,
9183
9312
  spinner: silence ? undefined : spinner,
@@ -9200,7 +9329,12 @@ async function coanaFix(fixConfig) {
9200
9329
  // Check for modified files after applying the fix.
9201
9330
  // eslint-disable-next-line no-await-in-loop
9202
9331
  const unstagedCResult = await utils.gitUnstagedModifiedFiles(cwd);
9203
- 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))) : [];
9204
9338
  if (!modifiedFiles.length) {
9205
9339
  debug.debugFn('notice', `skip: no changes for ${ghsaId}`);
9206
9340
  // Clean up temp file before continuing.
@@ -9516,6 +9650,7 @@ async function handleFix({
9516
9650
  debug: debug$1,
9517
9651
  disableExternalToolChecks,
9518
9652
  disableMajorUpdates,
9653
+ dynamicSbomInference,
9519
9654
  ecosystems,
9520
9655
  exclude,
9521
9656
  excludePaths,
@@ -9545,6 +9680,7 @@ async function handleFix({
9545
9680
  debug: debug$1,
9546
9681
  disableExternalToolChecks,
9547
9682
  disableMajorUpdates,
9683
+ dynamicSbomInference,
9548
9684
  ecosystems,
9549
9685
  exclude,
9550
9686
  excludePaths,
@@ -9571,6 +9707,7 @@ async function handleFix({
9571
9707
  debug: debug$1,
9572
9708
  disableExternalToolChecks,
9573
9709
  disableMajorUpdates,
9710
+ dynamicSbomInference,
9574
9711
  ecosystems,
9575
9712
  exclude,
9576
9713
  excludePaths,
@@ -9594,6 +9731,127 @@ async function handleFix({
9594
9731
  }), outputKind);
9595
9732
  }
9596
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
+
9597
9855
  const CMD_NAME$s = 'fix';
9598
9856
  const DEFAULT_LIMIT = 10;
9599
9857
  const description$z = 'Fix CVEs in dependencies';
@@ -9705,6 +9963,11 @@ Available styles:
9705
9963
  default: false,
9706
9964
  description: 'Disable external tool checks during fix analysis.'
9707
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
+ },
9708
9971
  ecosystems: {
9709
9972
  type: 'string',
9710
9973
  default: [],
@@ -9838,6 +10101,7 @@ async function run$N(argv, importMeta, {
9838
10101
  autopilot,
9839
10102
  debug,
9840
10103
  disableExternalToolChecks,
10104
+ dynamicSbomInference,
9841
10105
  ecosystems,
9842
10106
  exclude,
9843
10107
  excludePaths,
@@ -9970,6 +10234,7 @@ async function run$N(argv, importMeta, {
9970
10234
  debug,
9971
10235
  disableExternalToolChecks,
9972
10236
  disableMajorUpdates,
10237
+ dynamicSbomInference,
9973
10238
  ecosystems: validatedEcosystems,
9974
10239
  exclude: excludePatterns,
9975
10240
  excludePaths: excludePathsPatterns,
@@ -12217,7 +12482,7 @@ async function run$H(argv, importMeta, {
12217
12482
  // Manifest-scoped variant of `../scan/reachability-flags.mts`'s
12218
12483
  // `excludePathsFlag`: description avoids "scan"/reachability wording since
12219
12484
  // these commands only ever generate a manifest/facts file.
12220
- const excludePathsFlag$1 = {
12485
+ const excludePathsFlag = {
12221
12486
  excludePaths: {
12222
12487
  type: 'string',
12223
12488
  isMultiple: true,
@@ -12231,7 +12496,7 @@ const config$f = {
12231
12496
  hidden: false,
12232
12497
  flags: {
12233
12498
  ...flags.commonFlags,
12234
- ...excludePathsFlag$1,
12499
+ ...excludePathsFlag,
12235
12500
  verbose: {
12236
12501
  type: 'boolean',
12237
12502
  default: false,
@@ -12420,7 +12685,7 @@ async function run$F(argv, importMeta, {
12420
12685
  filename = sockJson.defaults?.manifest?.conda?.infile;
12421
12686
  logger.logger.info(`Using default --file from ${constants.SOCKET_JSON}:`, filename);
12422
12687
  } else {
12423
- filename = constants.ENVIRONMENT_YML;
12688
+ filename = findCondaFile(cwd) || constants.ENVIRONMENT_YML;
12424
12689
  }
12425
12690
  }
12426
12691
  if (stdout === undefined && sockJson.defaults?.manifest?.conda?.stdout !== undefined) {
@@ -12554,7 +12819,7 @@ const config$d = {
12554
12819
  flags: {
12555
12820
  ...flags.commonFlags,
12556
12821
  ...flags.outputFlags,
12557
- ...excludePathsFlag$1,
12822
+ ...excludePathsFlag,
12558
12823
  verbose: {
12559
12824
  type: 'boolean',
12560
12825
  default: false,
@@ -12669,7 +12934,7 @@ const config$c = {
12669
12934
  type: 'string',
12670
12935
  description: 'When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)'
12671
12936
  },
12672
- ...excludePathsFlag$1,
12937
+ ...excludePathsFlag,
12673
12938
  ignoreUnresolved: {
12674
12939
  type: 'boolean',
12675
12940
  description: 'When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -12923,7 +13188,7 @@ const config$b = {
12923
13188
  type: 'string',
12924
13189
  description: 'When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)'
12925
13190
  },
12926
- ...excludePathsFlag$1,
13191
+ ...excludePathsFlag,
12927
13192
  ignoreUnresolved: {
12928
13193
  type: 'boolean',
12929
13194
  description: 'When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -13162,7 +13427,7 @@ const config$a = {
13162
13427
  type: 'string',
13163
13428
  description: 'Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)'
13164
13429
  },
13165
- ...excludePathsFlag$1,
13430
+ ...excludePathsFlag,
13166
13431
  ignoreUnresolved: {
13167
13432
  type: 'boolean',
13168
13433
  description: 'Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -13361,7 +13626,7 @@ const config$9 = {
13361
13626
  type: 'string',
13362
13627
  description: 'When generating facts: comma-separated glob patterns; sbt configurations matching any pattern are skipped (applied after --include-configs)'
13363
13628
  },
13364
- ...excludePathsFlag$1,
13629
+ ...excludePathsFlag,
13365
13630
  ignoreUnresolved: {
13366
13631
  type: 'boolean',
13367
13632
  description: 'When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)'
@@ -19310,127 +19575,6 @@ const cmdRepository = {
19310
19575
  }
19311
19576
  };
19312
19577
 
19313
- // Shared prefix so each command can append what the SBOMs are used for, which
19314
- // differs: reachability is optional on `scan create` and always on for
19315
- // `scan reach`.
19316
- 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.';
19317
- const reachabilityFlags = {
19318
- reachVersion: {
19319
- type: 'string',
19320
- description: `Override the version of @coana-tech/cli used for reachability analysis. Default: ${constants.default.ENV.INLINED_SOCKET_CLI_COANA_TECH_CLI_VERSION}.`
19321
- },
19322
- reachAnalysisMemoryLimit: {
19323
- type: 'string',
19324
- default: '8192',
19325
- 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.'
19326
- },
19327
- reachAnalysisTimeout: {
19328
- type: 'string',
19329
- default: '',
19330
- 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.'
19331
- },
19332
- reachConcurrency: {
19333
- type: 'number',
19334
- default: 1,
19335
- 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.'
19336
- },
19337
- reachContinueOnAnalysisErrors: {
19338
- type: 'boolean',
19339
- default: false,
19340
- 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.'
19341
- },
19342
- reachContinueOnInstallErrors: {
19343
- type: 'boolean',
19344
- default: false,
19345
- description: 'Continue reachability analysis when package installation fails, falling back to precomputed reachability results. By default, the CLI halts on installation errors.'
19346
- },
19347
- reachContinueOnMissingLockFiles: {
19348
- type: 'boolean',
19349
- default: false,
19350
- 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.'
19351
- },
19352
- reachContinueOnNoSourceFiles: {
19353
- type: 'boolean',
19354
- default: false,
19355
- description: 'Continue reachability analysis when a workspace contains no source files for its ecosystem. By default, the CLI halts.'
19356
- },
19357
- reachDisableExternalToolChecks: {
19358
- type: 'boolean',
19359
- default: false,
19360
- description: 'Disable external tool checks during reachability analysis.'
19361
- },
19362
- reachDebug: {
19363
- type: 'boolean',
19364
- default: false,
19365
- description: 'Enable debug mode for reachability analysis. Provides verbose logging from the reachability CLI.'
19366
- },
19367
- reachDetailedAnalysisLogFile: {
19368
- type: 'boolean',
19369
- default: false,
19370
- description: 'A log file with detailed analysis logs is written to root of each analyzed workspace.'
19371
- },
19372
- reachDisableAnalytics: {
19373
- type: 'boolean',
19374
- default: false,
19375
- description: 'Disable reachability analytics sharing with Socket. Also disables caching-based optimizations.'
19376
- },
19377
- reachDisableAnalysisSplitting: {
19378
- type: 'boolean',
19379
- default: false,
19380
- hidden: true,
19381
- description: 'Deprecated: Analysis splitting is now disabled by default. This flag is a no-op.'
19382
- },
19383
- reachEnableAnalysisSplitting: {
19384
- type: 'boolean',
19385
- default: false,
19386
- 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.'
19387
- },
19388
- reachEcosystems: {
19389
- type: 'string',
19390
- isMultiple: true,
19391
- 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.`
19392
- },
19393
- reachExcludePaths: {
19394
- type: 'string',
19395
- isMultiple: true,
19396
- hidden: true,
19397
- description: 'Deprecated: use --exclude-paths instead. List of paths to exclude from reachability analysis, as either a comma separated value or as multiple flags.'
19398
- },
19399
- reachFallbackToRegularScan: {
19400
- type: 'boolean',
19401
- default: false,
19402
- 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.'
19403
- },
19404
- reachLazyMode: {
19405
- type: 'boolean',
19406
- default: false,
19407
- hidden: true,
19408
- description: 'Deprecated: lazy mode is no longer supported. This flag is a no-op.'
19409
- },
19410
- reachRetainFactsFile: {
19411
- type: 'boolean',
19412
- default: false,
19413
- 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.'
19414
- },
19415
- reachSkipCache: {
19416
- type: 'boolean',
19417
- default: false,
19418
- description: 'Skip caching-based optimizations. By default, the reachability analysis will use cached configurations from previous runs to speed up the analysis.'
19419
- },
19420
- reachUseOnlyPregeneratedSboms: {
19421
- type: 'boolean',
19422
- default: false,
19423
- description: 'When using this option, the scan is created based only on pre-generated CDX and SPDX files in your project.'
19424
- }
19425
- };
19426
- const excludePathsFlag = {
19427
- excludePaths: {
19428
- type: 'string',
19429
- isMultiple: true,
19430
- 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.'
19431
- }
19432
- };
19433
-
19434
19578
  async function suggestTarget() {
19435
19579
  // We could prefill this with sub-dirs of the current
19436
19580
  // dir ... but is that going to be useful?
@@ -19609,7 +19753,7 @@ async function run$d(argv, importMeta, {
19609
19753
  hidden: hidden$a,
19610
19754
  flags: {
19611
19755
  ...generalFlags$1,
19612
- ...excludePathsFlag,
19756
+ ...excludePathsFlag$1,
19613
19757
  ...reachabilityFlags
19614
19758
  },
19615
19759
  help: command => `
@@ -19622,7 +19766,7 @@ async function run$d(argv, importMeta, {
19622
19766
  Options
19623
19767
  ${utils.getFlagListOutput({
19624
19768
  ...generalFlags$1,
19625
- ...excludePathsFlag
19769
+ ...excludePathsFlag$1
19626
19770
  })}
19627
19771
 
19628
19772
  Reachability Options (when --reach is used)
@@ -22013,7 +22157,7 @@ async function run$7(argv, importMeta, {
22013
22157
  hidden: hidden$4,
22014
22158
  flags: {
22015
22159
  ...generalFlags,
22016
- ...excludePathsFlag,
22160
+ ...excludePathsFlag$1,
22017
22161
  ...reachabilityFlagsForReach
22018
22162
  },
22019
22163
  help: command => `
@@ -22028,7 +22172,7 @@ async function run$7(argv, importMeta, {
22028
22172
 
22029
22173
  Reachability Options
22030
22174
  ${utils.getFlagListOutput({
22031
- ...excludePathsFlag,
22175
+ ...excludePathsFlag$1,
22032
22176
  ...reachabilityFlagsForReach
22033
22177
  })}
22034
22178
 
@@ -24131,5 +24275,5 @@ process.on('unhandledRejection', async (reason, promise) => {
24131
24275
  // eslint-disable-next-line n/no-process-exit
24132
24276
  process.exit(1);
24133
24277
  });
24134
- //# debugId=3a134c7c-6936-4bbb-befc-4c0c7084f488
24278
+ //# debugId=45e6f932-d7fe-49d0-91f3-6a45a49eecd3
24135
24279
  //# sourceMappingURL=cli.js.map