Skip to content

Commit 767c973

Browse files
jfblaaclaude
andcommitted
feat(scan): add --dynamic-sbom-inference-ecosystems to choose which ecosystems use Socket facts
scan create, scan reach and fix take purl types (maven covers Gradle, sbt and Maven; nuget covers .NET). Build tools of unselected types are not discovered, so their manifest files are used as they are and Coana does not treat them as facts-only; with --auto-manifest, auto-generation still runs for them at cwd. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent c81b1ac commit 767c973

29 files changed

Lines changed: 366 additions & 28 deletions

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

77
## [Unreleased]
88

9+
### Added
10+
- 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.
11+
912
### Changed
1013
- 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.
1114
- With `--dynamic-sbom-inference`, reachability analysis and `socket fix` now stop with clear next steps when a .NET build cannot be read because no .NET SDK is installed, and warn that builds skipped in `socket.json` are left out. Reachability analysis also stops with next steps when no build produced Socket facts.

‎src/commands/ci/handle-ci.mts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
gitBranch,
1111
} from '../../utils/git.mts'
1212
import { serializeResultJson } from '../../utils/serialize-result-json.mts'
13+
import { FACTS_PURL_TYPES } from '../manifest/scripts/build-tool.mts'
1314
import { handleCreateNewScan } from '../scan/handle-create-new-scan.mts'
1415

1516
/**
@@ -66,6 +67,7 @@ export async function handleCi(autoManifest: boolean): Promise<void> {
6667
pullRequest: detectCiPullRequestNumber(),
6768
reach: {
6869
dynamicSbomInference: false,
70+
dynamicSbomInferenceEcosystems: FACTS_PURL_TYPES,
6971
excludePaths: [],
7072
reachAdvisories: [],
7173
reachAnalysisMemoryLimit: '',

‎src/commands/fix/cmd-fix.integration.test.mts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ describe('socket fix', async () => {
170170
--debug Enable debug logging in the Coana-based Socket Fix CLI invocation.
171171
--disable-external-tool-checks Disable external tool checks during fix analysis.
172172
--dynamic-sbom-inference 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. Fixes are then attributed to the projects/modules that actually resolve each vulnerable dependency. The generated files are removed afterwards.
173+
--dynamic-sbom-inference-ecosystems 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: maven (Gradle, sbt and Maven) and nuget (.NET). Defaults to both.
173174
--ecosystems Limit fix analysis to specific ecosystems. Accepts space- or comma-separated values and is case-insensitive. Defaults to all ecosystems.
174175
--exclude-paths Skip matching paths from the scan entirely: manifests under these paths are not uploaded, and fixes are not applied to workspaces under them. Patterns are anchored micromatch globs matched relative to the target directory (CWD); \`data/postgres/pgdata\` matches that exact path, \`**/pgdata\` matches at any depth. Use this to skip directories the current user cannot read so they do not abort manifest collection. Negation patterns (\`!path\`) are not supported. Accepts a comma-separated value or multiple flags.
175176
--fix-version Override the version of @coana-tech/cli used for fix analysis. Default: <coana-version>.

‎src/commands/fix/cmd-fix.mts‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ import {
3333
import { RangeStyles } from '../../utils/semver.mts'
3434
import { getDefaultOrgSlug } from '../ci/fetch-default-org-slug.mts'
3535
import { assertValidExcludePaths } from '../scan/exclude-paths.mts'
36-
import { DYNAMIC_SBOM_INFERENCE_DESCRIPTION } from '../scan/reachability-flags.mts'
36+
import {
37+
DYNAMIC_SBOM_INFERENCE_DESCRIPTION,
38+
dynamicSbomInferenceEcosystemsFlags,
39+
parseDynamicSbomInferenceEcosystems,
40+
} from '../scan/reachability-flags.mts'
3741

3842
import type { MeowFlag, MeowFlags } from '../../flags.mts'
3943
import type { PURL_Type } from '../../utils/ecosystem.mts'
@@ -190,6 +194,7 @@ Available styles:
190194
default: false,
191195
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.`,
192196
},
197+
...dynamicSbomInferenceEcosystemsFlags,
193198
ecosystems: {
194199
type: 'string',
195200
default: [],
@@ -398,6 +403,13 @@ async function run(
398403

399404
const outputKind = getOutputKind(json, markdown)
400405

406+
const dynamicSbomInferenceEcosystemsRaw = cmdFlagValueToArray(
407+
cli.flags['dynamicSbomInferenceEcosystems'],
408+
)
409+
const dynamicSbomInferenceEcosystems = parseDynamicSbomInferenceEcosystems(
410+
dynamicSbomInferenceEcosystemsRaw,
411+
)
412+
401413
// Process comma-separated values for ecosystems flag.
402414
// ALL_ECOSYSTEMS is lowercase, so normalize input for a case-insensitive
403415
// match (mirrors --package-managers behavior).
@@ -446,6 +458,13 @@ async function run(
446458

447459
const wasValidInput = checkCommandInput(
448460
outputKind,
461+
{
462+
nook: true,
463+
test: dynamicSbomInference || !dynamicSbomInferenceEcosystemsRaw.length,
464+
message:
465+
'--dynamic-sbom-inference-ecosystems requires --dynamic-sbom-inference',
466+
fail: 'add --dynamic-sbom-inference',
467+
},
449468
{
450469
test: RangeStyles.includes(rangeStyle),
451470
message: `Expecting range style of ${joinOr(RangeStyles)}`,
@@ -538,6 +557,7 @@ async function run(
538557
disableExternalToolChecks,
539558
disableMajorUpdates,
540559
dynamicSbomInference,
560+
dynamicSbomInferenceEcosystems,
541561
ecosystems: validatedEcosystems,
542562
exclude: excludePatterns,
543563
excludePaths: excludePathsPatterns,

‎src/commands/fix/coana-fix-dynamic-sbom-inference.test.mts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ describe('socket fix --dynamic-sbom-inference', () => {
103103
disableExternalToolChecks: false,
104104
disableMajorUpdates: false,
105105
dynamicSbomInference: true,
106+
dynamicSbomInferenceEcosystems: ['maven'],
106107
ecosystems: [],
107108
exclude: [],
108109
excludePaths: [],
@@ -147,6 +148,9 @@ describe('socket fix --dynamic-sbom-inference', () => {
147148
const result = await coanaFix(baseConfig)
148149

149150
expect(result.ok).toBe(true)
151+
expect(mockGenerateSocketFactsForFix).toHaveBeenCalledWith(
152+
expect.objectContaining({ purlTypes: ['maven'] }),
153+
)
150154
expect(uploadManifestFiles).toHaveBeenCalledWith(
151155
'test-org',
152156
['/test/cwd/app/build.gradle', FACTS],

‎src/commands/fix/coana-fix.mts‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,13 +391,12 @@ async function coanaFixWithFacts(
391391
if (factsSlot) {
392392
if (!silence) {
393393
spinner?.stop()
394-
logger.info(
395-
'Generating Socket facts for .NET, Gradle, sbt and Maven builds ...',
396-
)
394+
logger.info('Generating Socket facts for each build ...')
397395
}
398396
factsSlot.generated = await generateSocketFactsForFix({
399397
cwd,
400398
excludePaths,
399+
purlTypes: fixConfig.dynamicSbomInferenceEcosystems,
401400
findFactsPaths: async () =>
402401
(await findScanFilepaths()).filter(isSocketFactsFile),
403402
tmpDir: factsSlot.tmpDir,

‎src/commands/fix/generated-socket-facts.mts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import path from 'node:path'
44
import { useOnlySocketFactsArtifactsFlags } from '../manifest/scripts/sidecar.mts'
55
import { runDynamicSbomInference } from '../scan/run-dynamic-sbom-inference.mts'
66

7+
import type { PURL_Type } from '../../utils/ecosystem.mts'
8+
79
export type GeneratedSocketFacts = {
810
// Coana's flags selecting what the facts files resolve.
911
artifactsFlags: string[]
@@ -39,11 +41,13 @@ export async function generateSocketFactsForFix({
3941
cwd,
4042
excludePaths,
4143
findFactsPaths,
44+
purlTypes,
4245
tmpDir,
4346
}: {
4447
cwd: string
4548
excludePaths: string[]
4649
findFactsPaths: () => Promise<string[]>
50+
purlTypes: readonly PURL_Type[]
4751
tmpDir: string
4852
}): Promise<GeneratedSocketFacts> {
4953
const existingPaths = await findFactsPaths()
@@ -64,6 +68,7 @@ export async function generateSocketFactsForFix({
6468
consumer: 'fix',
6569
cwd,
6670
excludePaths,
71+
purlTypes,
6772
sbtTmpDir: undefined,
6873
})
6974
artifactsFlags = useOnlySocketFactsArtifactsFlags(factsOnlyTypes)

‎src/commands/fix/generated-socket-facts.test.mts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe('generateSocketFactsForFix', () => {
3737
cwd,
3838
excludePaths: [],
3939
findFactsPaths: async () => [existing, fresh].filter(p => existsSync(p)),
40+
purlTypes: ['maven'],
4041
tmpDir,
4142
})
4243
}
@@ -75,7 +76,7 @@ describe('generateSocketFactsForFix', () => {
7576
const { artifactsFlags, sidecarFile } = await generate()
7677

7778
expect(mockRunDynamicSbomInference).toHaveBeenCalledWith(
78-
expect.objectContaining({ consumer: 'fix' }),
79+
expect.objectContaining({ consumer: 'fix', purlTypes: ['maven'] }),
7980
)
8081
expect(artifactsFlags).toEqual([
8182
'--use-only-socket-facts-artifacts',

‎src/commands/fix/handle-fix.mts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export async function handleFix({
6969
disableExternalToolChecks,
7070
disableMajorUpdates,
7171
dynamicSbomInference,
72+
dynamicSbomInferenceEcosystems,
7273
ecosystems,
7374
exclude,
7475
excludePaths,
@@ -100,6 +101,7 @@ export async function handleFix({
100101
disableExternalToolChecks,
101102
disableMajorUpdates,
102103
dynamicSbomInference,
104+
dynamicSbomInferenceEcosystems,
103105
ecosystems,
104106
exclude,
105107
excludePaths,
@@ -130,6 +132,7 @@ export async function handleFix({
130132
disableExternalToolChecks,
131133
disableMajorUpdates,
132134
dynamicSbomInference,
135+
dynamicSbomInferenceEcosystems,
133136
ecosystems,
134137
exclude,
135138
excludePaths,

‎src/commands/fix/types.mts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type FixConfig = {
1313
disableExternalToolChecks: boolean
1414
disableMajorUpdates: boolean
1515
dynamicSbomInference: boolean
16+
dynamicSbomInferenceEcosystems: readonly PURL_Type[]
1617
ecosystems: PURL_Type[]
1718
exclude: string[]
1819
excludePaths: string[]

0 commit comments

Comments
 (0)