Skip to content

Commit dec47bf

Browse files
committed
feat(manifest): add socket manifest maven (1.1.124, Coana 15.5.5)
Add a `socket manifest maven` command that generates a Socket facts file (`.socket.facts.json`) from a Maven `pom.xml` project by delegating to the Coana CLI's `manifest maven` command, mirroring the existing gradle/sbt facts flows. Includes pom.xml auto-detection, `socket manifest auto` wiring, the `socket manifest setup` configurator, socket.json defaults, and `--maven-opts` / `--bin` pass-through. Bump Coana CLI to 15.5.5, which adds the `manifest maven` command this delegates to.
1 parent 5675a51 commit dec47bf

14 files changed

Lines changed: 488 additions & 14 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44

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

7+
## [1.1.124](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.124) - 2026-06-19
8+
9+
### Added
10+
- New `socket manifest maven` command generates a Socket facts file (`.socket.facts.json`) directly from a Maven `pom.xml` project. Like the Gradle and sbt generators, it auto-detects your project, plugs into `socket manifest auto` and the `socket manifest setup` configurator, and accepts `--maven-opts` to pass options through to Maven (e.g. `--maven-opts="-P release -s settings.xml"`), plus `--bin` to point at a wrapper such as `./mvnw`.
11+
12+
### Changed
13+
- Updated the Coana CLI to v `15.5.5`.
14+
715
## [1.1.123](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.123) - 2026-06-18
816

917
### Added

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "socket",
3-
"version": "1.1.123",
3+
"version": "1.1.124",
44
"description": "CLI for Socket.dev",
55
"homepage": "https://github.com/SocketDev/socket-cli",
66
"license": "MIT",
@@ -96,7 +96,7 @@
9696
"@babel/preset-typescript": "7.27.1",
9797
"@babel/runtime": "7.28.4",
9898
"@biomejs/biome": "2.2.4",
99-
"@coana-tech/cli": "15.5.0",
99+
"@coana-tech/cli": "15.5.5",
100100
"@cyclonedx/cdxgen": "12.1.2",
101101
"@dotenvx/dotenvx": "1.49.0",
102102
"@eslint/compat": "1.3.2",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/manifest/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,13 @@ scala flows.
151151
Uses Gradle to generate a manifest file (`pom.xml`) for a Kotlin project; the
152152
underlying flow is identical to the gradle subcommand.
153153

154+
## socket manifest maven [beta]
155+
156+
Generates a Socket facts file (`.socket.facts.json`) from a Maven `pom.xml`
157+
project, using `mvn` (override with `--bin`, e.g. a project `./mvnw` wrapper).
158+
Pass extra options through to maven with `--maven-opts` (e.g.
159+
`--maven-opts="-P release -s settings.xml"`).
160+
154161
## socket manifest scala [beta]
155162

156163
Generates a manifest file (`pom.xml`) from Scala's `build.sbt` file.
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import path from 'node:path'
2+
3+
import { debugFn } from '@socketsecurity/registry/lib/debug'
4+
import { logger } from '@socketsecurity/registry/lib/logger'
5+
6+
import { convertMavenToFacts } from './convert-maven-to-facts.mts'
7+
import constants, { SOCKET_JSON } from '../../constants.mts'
8+
import { commonFlags } from '../../flags.mts'
9+
import { checkCommandInput } from '../../utils/check-input.mts'
10+
import { getOutputKind } from '../../utils/get-output-kind.mts'
11+
import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
12+
import { getFlagListOutput } from '../../utils/output-formatting.mts'
13+
import { readOrDefaultSocketJson } from '../../utils/socket-json.mts'
14+
15+
import type {
16+
CliCommandConfig,
17+
CliCommandContext,
18+
} from '../../utils/meow-with-subcommands.mts'
19+
20+
const config: CliCommandConfig = {
21+
commandName: 'maven',
22+
description:
23+
'[beta] Generate a Socket facts file from a Maven `pom.xml` project',
24+
hidden: false,
25+
flags: {
26+
...commonFlags,
27+
bin: {
28+
type: 'string',
29+
description: 'Location of the maven binary to use, default: mvn on PATH',
30+
},
31+
includeConfigs: {
32+
type: 'string',
33+
description:
34+
'Comma-separated glob patterns matched against Maven dependency scopes (case-sensitive, `*` and `?` wildcards). Only scopes matching at least one pattern are resolved. e.g. `compile,runtime`. Default: every scope',
35+
},
36+
excludeConfigs: {
37+
type: 'string',
38+
description:
39+
'Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)',
40+
},
41+
ignoreUnresolved: {
42+
type: 'boolean',
43+
description:
44+
'Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)',
45+
},
46+
mavenOpts: {
47+
type: 'string',
48+
description:
49+
'Additional options to pass on to maven, e.g. `-P <profile> -s <settings.xml>`',
50+
},
51+
verbose: {
52+
type: 'boolean',
53+
description: 'Print debug messages',
54+
},
55+
},
56+
help: (command, config) => `
57+
Usage
58+
$ ${command} [options] [CWD=.]
59+
60+
Options
61+
${getFlagListOutput(config.flags)}
62+
63+
Emits a single \`.socket.facts.json\` describing the resolved dependency
64+
graph of your Maven project, using maven (\`mvn\` on PATH by default). It
65+
reads dependency metadata only and never downloads artifacts; an unresolved
66+
dependency is a fatal error. You can pass --include-configs /
67+
--exclude-configs (comma-separated glob patterns) to control which Maven
68+
scopes are resolved (e.g. --include-configs=\`compile,runtime\`), and
69+
--ignore-unresolved to warn on unresolved dependencies instead of failing.
70+
71+
You can specify --bin to override the path to the \`mvn\` binary to invoke
72+
(e.g. a project \`./mvnw\` wrapper), and --maven-opts to pass extra options
73+
through to maven (e.g. \`-P <profile> -s <settings.xml>\`).
74+
75+
Support is beta. Please report issues or give us feedback on what's missing.
76+
77+
Examples
78+
79+
$ ${command} .
80+
$ ${command} --bin=./mvnw .
81+
$ ${command} --maven-opts="-P release" .
82+
`,
83+
}
84+
85+
export const cmdManifestMaven = {
86+
description: config.description,
87+
hidden: config.hidden,
88+
run,
89+
}
90+
91+
async function run(
92+
argv: string[] | readonly string[],
93+
importMeta: ImportMeta,
94+
{ parentName }: CliCommandContext,
95+
): Promise<void> {
96+
const cli = meowOrExit({
97+
argv,
98+
config,
99+
importMeta,
100+
parentName,
101+
})
102+
103+
const { json = false, markdown = false } = cli.flags
104+
105+
const dryRun = !!cli.flags['dryRun']
106+
107+
// TODO: Implement json/md further.
108+
const outputKind = getOutputKind(json, markdown)
109+
110+
let [cwd = '.'] = cli.input
111+
// Note: path.resolve vs .join:
112+
// If given path is absolute then cwd should not affect it.
113+
cwd = path.resolve(process.cwd(), cwd)
114+
115+
const sockJson = readOrDefaultSocketJson(cwd)
116+
117+
debugFn(
118+
'inspect',
119+
`override: ${SOCKET_JSON} maven`,
120+
sockJson?.defaults?.manifest?.maven,
121+
)
122+
123+
let {
124+
bin,
125+
excludeConfigs,
126+
ignoreUnresolved,
127+
includeConfigs,
128+
mavenOpts,
129+
verbose,
130+
} = cli.flags
131+
132+
// Set defaults for any flag/arg that is not given. Check socket.json first.
133+
if (!bin) {
134+
if (sockJson.defaults?.manifest?.maven?.bin) {
135+
bin = sockJson.defaults?.manifest?.maven?.bin
136+
logger.info(`Using default --bin from ${SOCKET_JSON}:`, bin)
137+
} else {
138+
bin = 'mvn'
139+
}
140+
}
141+
if (!mavenOpts) {
142+
if (sockJson.defaults?.manifest?.maven?.mavenOpts) {
143+
mavenOpts = sockJson.defaults?.manifest?.maven?.mavenOpts
144+
logger.info(`Using default --maven-opts from ${SOCKET_JSON}:`, mavenOpts)
145+
} else {
146+
mavenOpts = ''
147+
}
148+
}
149+
if (includeConfigs === undefined) {
150+
if (sockJson.defaults?.manifest?.maven?.includeConfigs !== undefined) {
151+
includeConfigs = sockJson.defaults?.manifest?.maven?.includeConfigs
152+
logger.info(
153+
`Using default --include-configs from ${SOCKET_JSON}:`,
154+
includeConfigs,
155+
)
156+
} else {
157+
includeConfigs = ''
158+
}
159+
}
160+
if (excludeConfigs === undefined) {
161+
if (sockJson.defaults?.manifest?.maven?.excludeConfigs !== undefined) {
162+
excludeConfigs = sockJson.defaults?.manifest?.maven?.excludeConfigs
163+
logger.info(
164+
`Using default --exclude-configs from ${SOCKET_JSON}:`,
165+
excludeConfigs,
166+
)
167+
} else {
168+
excludeConfigs = ''
169+
}
170+
}
171+
if (ignoreUnresolved === undefined) {
172+
if (sockJson.defaults?.manifest?.maven?.ignoreUnresolved !== undefined) {
173+
ignoreUnresolved = sockJson.defaults?.manifest?.maven?.ignoreUnresolved
174+
logger.info(
175+
`Using default --ignore-unresolved from ${SOCKET_JSON}:`,
176+
ignoreUnresolved,
177+
)
178+
} else {
179+
ignoreUnresolved = false
180+
}
181+
}
182+
if (verbose === undefined) {
183+
if (sockJson.defaults?.manifest?.maven?.verbose !== undefined) {
184+
verbose = sockJson.defaults?.manifest?.maven?.verbose
185+
logger.info(`Using default --verbose from ${SOCKET_JSON}:`, verbose)
186+
} else {
187+
verbose = false
188+
}
189+
}
190+
191+
if (verbose) {
192+
logger.group('- ', parentName, config.commandName, ':')
193+
logger.group('- flags:', cli.flags)
194+
logger.groupEnd()
195+
logger.log('- input:', cli.input)
196+
logger.groupEnd()
197+
}
198+
199+
const wasValidInput = checkCommandInput(outputKind, {
200+
nook: true,
201+
test: cli.input.length <= 1,
202+
message: 'Can only accept one DIR (make sure to escape spaces!)',
203+
fail: 'received ' + cli.input.length,
204+
})
205+
if (!wasValidInput) {
206+
return
207+
}
208+
209+
if (verbose) {
210+
logger.group()
211+
logger.info('- cwd:', cwd)
212+
logger.info('- maven bin:', bin)
213+
logger.groupEnd()
214+
}
215+
216+
if (dryRun) {
217+
logger.log(constants.DRY_RUN_BAILING_NOW)
218+
return
219+
}
220+
221+
const parsedMavenOpts = String(mavenOpts || '')
222+
.split(' ')
223+
.map(s => s.trim())
224+
.filter(Boolean)
225+
226+
await convertMavenToFacts({
227+
bin: String(bin),
228+
cwd,
229+
excludeConfigs: String(excludeConfigs || ''),
230+
ignoreUnresolved: Boolean(ignoreUnresolved),
231+
includeConfigs: String(includeConfigs || ''),
232+
mavenOpts: parsedMavenOpts,
233+
verbose: Boolean(verbose),
234+
})
235+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { describe, expect } from 'vitest'
2+
3+
import constants, {
4+
FLAG_CONFIG,
5+
FLAG_DRY_RUN,
6+
FLAG_HELP,
7+
} from '../../../src/constants.mts'
8+
import { cmdit, spawnSocketCli } from '../../../test/utils.mts'
9+
10+
describe('socket manifest maven', async () => {
11+
const { binCliPath } = constants
12+
13+
cmdit(
14+
['manifest', 'maven', FLAG_HELP, FLAG_CONFIG, '{}'],
15+
`should support ${FLAG_HELP}`,
16+
async cmd => {
17+
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
18+
expect(stdout).toMatchInlineSnapshot(`
19+
"[beta] Generate a Socket facts file from a Maven \`pom.xml\` project
20+
21+
Usage
22+
$ socket manifest maven [options] [CWD=.]
23+
24+
Options
25+
--bin Location of the maven binary to use, default: mvn on PATH
26+
--exclude-configs Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)
27+
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
28+
--include-configs Comma-separated glob patterns matched against Maven dependency scopes (case-sensitive, \`*\` and \`?\` wildcards). Only scopes matching at least one pattern are resolved. e.g. \`compile,runtime\`. Default: every scope
29+
--maven-opts Additional options to pass on to maven, e.g. \`-P <profile> -s <settings.xml>\`
30+
--verbose Print debug messages
31+
32+
Emits a single \`.socket.facts.json\` describing the resolved dependency
33+
graph of your Maven project, using maven (\`mvn\` on PATH by default). It
34+
reads dependency metadata only and never downloads artifacts; an unresolved
35+
dependency is a fatal error. You can pass --include-configs /
36+
--exclude-configs (comma-separated glob patterns) to control which Maven
37+
scopes are resolved (e.g. --include-configs=\`compile,runtime\`), and
38+
--ignore-unresolved to warn on unresolved dependencies instead of failing.
39+
40+
You can specify --bin to override the path to the \`mvn\` binary to invoke
41+
(e.g. a project \`./mvnw\` wrapper), and --maven-opts to pass extra options
42+
through to maven (e.g. \`-P <profile> -s <settings.xml>\`).
43+
44+
Support is beta. Please report issues or give us feedback on what's missing.
45+
46+
Examples
47+
48+
$ socket manifest maven .
49+
$ socket manifest maven --bin=./mvnw .
50+
$ socket manifest maven --maven-opts="-P release" ."
51+
`)
52+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
53+
"
54+
_____ _ _ /---------------
55+
| __|___ ___| |_ ___| |_ | CLI: <redacted>
56+
|__ | * | _| '_| -_| _| | token: <redacted>, org: <redacted>
57+
|_____|___|___|_,_|___|_|.dev | Command: \`socket manifest maven\`, cwd: <redacted>"
58+
`)
59+
60+
expect(code, 'explicit help should exit with code 0').toBe(0)
61+
expect(stderr, 'banner includes base command').toContain(
62+
'`socket manifest maven`',
63+
)
64+
},
65+
)
66+
67+
cmdit(
68+
['manifest', 'maven', FLAG_DRY_RUN, FLAG_CONFIG, '{}'],
69+
'should require args with just dry-run',
70+
async cmd => {
71+
const { code, stderr, stdout } = await spawnSocketCli(binCliPath, cmd)
72+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
73+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
74+
"
75+
_____ _ _ /---------------
76+
| __|___ ___| |_ ___| |_ | CLI: <redacted>
77+
|__ | * | _| '_| -_| _| | token: <redacted>, org: <redacted>
78+
|_____|___|___|_,_|___|_|.dev | Command: \`socket manifest maven\`, cwd: <redacted>"
79+
`)
80+
81+
expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
82+
},
83+
)
84+
})

0 commit comments

Comments
 (0)