Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [1.1.126](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.126) - 2026-06-22

### Added
- New `socket manifest dotnet` command generates a Socket facts file (`.socket.facts.json`) from a .NET project (`.csproj`/`.fsproj`/`.vbproj`/`.sln`). It runs the `dotnet` host on PATH to resolve NuGet/MSBuild dependencies (SDK-style and legacy `packages.config` projects), auto-detects your project, and plugs into `socket manifest auto` and the `socket manifest setup` configurator. Use `--bin` to point at a specific dotnet host and `--dotnet-opts` to pass options through.

### Changed
- Updated the Coana CLI to v `15.5.6`.

## [1.1.125](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.125) - 2026-06-22

### Added
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "socket",
"version": "1.1.125",
"version": "1.1.126",
"description": "CLI for Socket.dev",
"homepage": "https://github.com/SocketDev/socket-cli",
"license": "MIT",
Expand Down Expand Up @@ -96,7 +96,7 @@
"@babel/preset-typescript": "7.27.1",
"@babel/runtime": "7.28.4",
"@biomejs/biome": "2.2.4",
"@coana-tech/cli": "15.5.5",
"@coana-tech/cli": "15.5.6",
"@cyclonedx/cdxgen": "12.1.2",
"@dotenvx/dotenvx": "1.49.0",
"@eslint/compat": "1.3.2",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/commands/manifest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ have a working cdxgen configuration.
Converts a Conda `environment.yml` file to a Python `requirements.txt` so the
Socket scan pipeline can consume the resulting manifest.

## socket manifest dotnet [beta]

Generates a Socket facts file (`.socket.facts.json`) from a .NET project
(`*.csproj` / `*.fsproj` / `*.vbproj` / `*.sln`), using the `dotnet` host on
PATH to run a bundled NuGet/MSBuild resolver (SDK-style and legacy
`packages.config` projects are both supported). Override the host with `--bin`
and pass extra options through with `--dotnet-opts`. Unlike the JVM generators
there are no configuration filters (`--include-configs` / `--exclude-configs`
do not apply).

## socket manifest gradle [beta]

Uses Gradle (via the project's `gradlew`) to emit a `pom.xml` per subproject,
Expand Down
195 changes: 195 additions & 0 deletions src/commands/manifest/cmd-manifest-dotnet.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import path from 'node:path'

import { debugFn } from '@socketsecurity/registry/lib/debug'
import { logger } from '@socketsecurity/registry/lib/logger'

import { convertDotnetToFacts } from './convert-dotnet-to-facts.mts'
import { parseBuildToolOpts } from './parse-build-tool-opts.mts'
import constants, { SOCKET_JSON } from '../../constants.mts'
import { commonFlags } from '../../flags.mts'
import { checkCommandInput } from '../../utils/check-input.mts'
import { getOutputKind } from '../../utils/get-output-kind.mts'
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
import { getFlagListOutput } from '../../utils/output-formatting.mts'
import { readOrDefaultSocketJson } from '../../utils/socket-json.mts'

import type {
CliCommandConfig,
CliCommandContext,
} from '../../utils/meow-with-subcommands.mts'

const config: CliCommandConfig = {
commandName: 'dotnet',
description:
'[beta] Generate a Socket facts file from a .NET project (`.csproj`/`.sln`/etc)',
hidden: false,
flags: {
...commonFlags,
bin: {
type: 'string',
description:
'Location of the dotnet host to use, default: dotnet on PATH',
},
ignoreUnresolved: {
type: 'boolean',
description:
'Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)',
},
dotnetOpts: {
type: 'string',
description: 'Additional options to pass on to the bundled dotnet tool',
},
verbose: {
type: 'boolean',
description: 'Print debug messages',
},
},
help: (command, config) => `
Usage
$ ${command} [options] [CWD=.]

Options
${getFlagListOutput(config.flags)}

Emits a single \`.socket.facts.json\` describing the resolved dependency
graph of your .NET project, using the \`dotnet\` host on PATH to run a
bundled NuGet/MSBuild resolver (SDK-style projects and legacy
\`packages.config\` are both supported). An unresolved dependency is a fatal
error; pass --ignore-unresolved to warn and continue instead.

Unlike the JVM generators there are no configuration filters: .NET
resolution has no equivalent of Gradle/Maven configurations, so
--include-configs / --exclude-configs do not apply.

You can specify --bin to override the path to the \`dotnet\` host to invoke,
and --dotnet-opts to pass extra options through to the bundled tool.

Support is beta. Please report issues or give us feedback on what's missing.

Examples

$ ${command} .
$ ${command} --bin=/usr/local/share/dotnet/dotnet .
`,
}

export const cmdManifestDotnet = {
description: config.description,
hidden: config.hidden,
run,
}

async function run(
argv: string[] | readonly string[],
importMeta: ImportMeta,
{ parentName }: CliCommandContext,
): Promise<void> {
const cli = meowOrExit({
argv,
config,
importMeta,
parentName,
})

const { json = false, markdown = false } = cli.flags

const dryRun = !!cli.flags['dryRun']

// TODO: Implement json/md further.
const outputKind = getOutputKind(json, markdown)

let [cwd = '.'] = cli.input
// Note: path.resolve vs .join:
// If given path is absolute then cwd should not affect it.
cwd = path.resolve(process.cwd(), cwd)

const sockJson = readOrDefaultSocketJson(cwd)

debugFn(
'inspect',
`override: ${SOCKET_JSON} dotnet`,
sockJson?.defaults?.manifest?.dotnet,
)

let { bin, dotnetOpts, ignoreUnresolved, verbose } = cli.flags

// Set defaults for any flag/arg that is not given. Check socket.json first.
if (!bin) {
if (sockJson.defaults?.manifest?.dotnet?.bin) {
bin = sockJson.defaults?.manifest?.dotnet?.bin
logger.info(`Using default --bin from ${SOCKET_JSON}:`, bin)
} else {
bin = 'dotnet'
}
}
if (!dotnetOpts) {
if (sockJson.defaults?.manifest?.dotnet?.dotnetOpts) {
dotnetOpts = sockJson.defaults?.manifest?.dotnet?.dotnetOpts
logger.info(
`Using default --dotnet-opts from ${SOCKET_JSON}:`,
dotnetOpts,
)
} else {
dotnetOpts = ''
}
}
if (ignoreUnresolved === undefined) {
if (sockJson.defaults?.manifest?.dotnet?.ignoreUnresolved !== undefined) {
ignoreUnresolved = sockJson.defaults?.manifest?.dotnet?.ignoreUnresolved
logger.info(
`Using default --ignore-unresolved from ${SOCKET_JSON}:`,
ignoreUnresolved,
)
} else {
ignoreUnresolved = false
}
}
if (verbose === undefined) {
if (sockJson.defaults?.manifest?.dotnet?.verbose !== undefined) {
verbose = sockJson.defaults?.manifest?.dotnet?.verbose
logger.info(`Using default --verbose from ${SOCKET_JSON}:`, verbose)
} else {
verbose = false
}
}

if (verbose) {
logger.group('- ', parentName, config.commandName, ':')
logger.group('- flags:', cli.flags)
logger.groupEnd()
logger.log('- input:', cli.input)
logger.groupEnd()
}

const wasValidInput = checkCommandInput(outputKind, {
nook: true,
test: cli.input.length <= 1,
message: 'Can only accept one DIR (make sure to escape spaces!)',
fail: 'received ' + cli.input.length,
})
if (!wasValidInput) {
return
}

if (verbose) {
logger.group()
logger.info('- cwd:', cwd)
logger.info('- dotnet bin:', bin)
logger.groupEnd()
}

if (dryRun) {
logger.log(constants.DRY_RUN_BAILING_NOW)
return
}

const parsedDotnetOpts = parseBuildToolOpts(String(dotnetOpts || ''))

await convertDotnetToFacts({
bin: String(bin),
cwd,
dotnetOpts: parsedDotnetOpts,
ignoreUnresolved: Boolean(ignoreUnresolved),
verbose: Boolean(verbose),
})
}
82 changes: 82 additions & 0 deletions src/commands/manifest/cmd-manifest-dotnet.test.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { describe, expect } from 'vitest'

import constants, {
FLAG_CONFIG,
FLAG_DRY_RUN,
FLAG_HELP,
} from '../../../src/constants.mts'
import { cmdit, spawnSocketCli } from '../../../test/utils.mts'

describe('socket manifest dotnet', async () => {
const { binCliPath } = constants

cmdit(
['manifest', 'dotnet', FLAG_HELP, FLAG_CONFIG, '{}'],
`should support ${FLAG_HELP}`,
async cmd => {
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
expect(stdout).toMatchInlineSnapshot(`
"[beta] Generate a Socket facts file from a .NET project (\`.csproj\`/\`.sln\`/etc)

Usage
$ socket manifest dotnet [options] [CWD=.]

Options
--bin Location of the dotnet host to use, default: dotnet on PATH
--dotnet-opts Additional options to pass on to the bundled dotnet tool
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
--verbose Print debug messages

Emits a single \`.socket.facts.json\` describing the resolved dependency
graph of your .NET project, using the \`dotnet\` host on PATH to run a
bundled NuGet/MSBuild resolver (SDK-style projects and legacy
\`packages.config\` are both supported). An unresolved dependency is a fatal
error; pass --ignore-unresolved to warn and continue instead.

Unlike the JVM generators there are no configuration filters: .NET
resolution has no equivalent of Gradle/Maven configurations, so
--include-configs / --exclude-configs do not apply.

You can specify --bin to override the path to the \`dotnet\` host to invoke,
and --dotnet-opts to pass extra options through to the bundled tool.

Support is beta. Please report issues or give us feedback on what's missing.

Examples

$ socket manifest dotnet .
$ socket manifest dotnet --bin=/usr/local/share/dotnet/dotnet ."
`)
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
"
_____ _ _ /---------------
| __|___ ___| |_ ___| |_ | CLI: <redacted>
|__ | * | _| '_| -_| _| | token: <redacted>, org: <redacted>
|_____|___|___|_,_|___|_|.dev | Command: \`socket manifest dotnet\`, cwd: <redacted>"
`)

expect(code, 'explicit help should exit with code 0').toBe(0)
expect(stderr, 'banner includes base command').toContain(
'`socket manifest dotnet`',
)
},
)

cmdit(
['manifest', 'dotnet', FLAG_DRY_RUN, FLAG_CONFIG, '{}'],
'should require args with just dry-run',
async cmd => {
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
"
_____ _ _ /---------------
| __|___ ___| |_ ___| |_ | CLI: <redacted>
|__ | * | _| '_| -_| _| | token: <redacted>, org: <redacted>
|_____|___|___|_,_|___|_|.dev | Command: \`socket manifest dotnet\`, cwd: <redacted>"
`)

expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
},
)
})
6 changes: 4 additions & 2 deletions src/commands/manifest/cmd-manifest.mts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { cmdManifestBazel } from './bazel/cmd-manifest-bazel.mts'
import { cmdManifestAuto } from './cmd-manifest-auto.mts'
import { cmdManifestCdxgen } from './cmd-manifest-cdxgen.mts'
import { cmdManifestConda } from './cmd-manifest-conda.mts'
import { cmdManifestDotnet } from './cmd-manifest-dotnet.mts'
import { cmdManifestGradle } from './cmd-manifest-gradle.mts'
import { cmdManifestKotlin } from './cmd-manifest-kotlin.mts'
import { cmdManifestMaven } from './cmd-manifest-maven.mts'
Expand Down Expand Up @@ -39,8 +40,8 @@ const config: CliCommandConfig = {
configurations available. See \`manifest <language> --help\` for usage details
per language.

Currently supported language: bazel [beta], gradle [beta], kotlin (through
gradle) [beta], maven [beta], scala [beta].
Currently supported language: bazel [beta], dotnet [beta], gradle [beta],
kotlin (through gradle) [beta], maven [beta], scala [beta].

Examples

Expand Down Expand Up @@ -73,6 +74,7 @@ async function run(
bazel: cmdManifestBazel,
cdxgen: cmdManifestCdxgen,
conda: cmdManifestConda,
dotnet: cmdManifestDotnet,
gradle: cmdManifestGradle,
kotlin: cmdManifestKotlin,
maven: cmdManifestMaven,
Expand Down
1 change: 1 addition & 0 deletions src/commands/manifest/cmd-manifest.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('socket manifest', async () => {
bazel [beta] Bazel SBOM support \\u2014 generate manifest files for a Bazel project (Maven, PyPI)
cdxgen Run cdxgen for SBOM generation
conda [beta] Convert a Conda environment.yml file to a python requirements.txt
dotnet [beta] Generate a Socket facts file from a .NET project (\`.csproj\`/\`.sln\`/etc)
gradle [beta] Generate a Socket facts file (or \`pom.xml\` with --pom) for a Gradle/Java/Kotlin/etc project
kotlin [beta] Generate a Socket facts file (or \`pom.xml\` with --pom) for a Kotlin project
maven [beta] Generate a Socket facts file from a Maven \`pom.xml\` project
Expand Down
Loading
Loading