Skip to content

Commit 65ac25b

Browse files
authored
feat(manifest): add socket manifest maven (1.1.125, Coana 15.5.5) (#1373)
* 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. * fix(manifest): correct 1.1.125 changelog label; quote-aware build-tool opts Address review feedback on the maven PR: - CHANGELOG: the new section is the 1.1.125 release (per package.json after the v1.x merge), so relabel its heading `1.1.124` -> `1.1.125` (the link target was already v1.1.125). - `--gradle-opts` / `--sbt-opts` / `--maven-opts` were split on every space, shredding a value with a spaced path (e.g. `-s "my settings.xml"`) into separate tokens. Introduce a shared quote-aware tokenizer (`parseBuildToolOpts`) honoring single/double quotes and use it across all manifest opts sites (gradle/kotlin/scala/maven + auto-manifest) so the fix is consistent rather than a maven-only divergence. Unquoted input tokenizes exactly as before. * fix changelog date
1 parent 55c80a6 commit 65ac25b

19 files changed

Lines changed: 589 additions & 37 deletions

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,16 @@ 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
7+
## [1.1.125](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.125) - 2026-06-22
88

99
### 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+
15+
## [1.1.124](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.124) - 2026-06-19
16+
1017
- `socket scan create --reach` accepts a new `--reach-retain-facts-file` flag. By default the CLI deletes the `.socket.facts.json` reachability report from the scan directory after a successful scan; pass this flag to keep it (e.g. for inspection or debugging). **Important:** you must delete the retained `.socket.facts.json` before running a fresh tier 1 reachability scan — a stale file 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.
1118

1219
### Changed

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.124",
3+
"version": "1.1.125",
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.4",
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.

src/commands/manifest/cmd-manifest-gradle.mts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { logger } from '@socketsecurity/registry/lib/logger'
55

66
import { convertGradleToFacts } from './convert-gradle-to-facts.mts'
77
import { convertGradleToMaven } from './convert_gradle_to_maven.mts'
8+
import { parseBuildToolOpts } from './parse-build-tool-opts.mts'
89
import constants, { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.mts'
910
import { commonFlags } from '../../flags.mts'
1011
import { checkCommandInput } from '../../utils/check-input.mts'
@@ -280,10 +281,7 @@ async function run(
280281
return
281282
}
282283

283-
const parsedGradleOpts = String(gradleOpts || '')
284-
.split(' ')
285-
.map(s => s.trim())
286-
.filter(Boolean)
284+
const parsedGradleOpts = parseBuildToolOpts(String(gradleOpts || ''))
287285

288286
if (facts) {
289287
await convertGradleToFacts({

src/commands/manifest/cmd-manifest-kotlin.mts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { logger } from '@socketsecurity/registry/lib/logger'
55

66
import { convertGradleToFacts } from './convert-gradle-to-facts.mts'
77
import { convertGradleToMaven } from './convert_gradle_to_maven.mts'
8+
import { parseBuildToolOpts } from './parse-build-tool-opts.mts'
89
import constants, { REQUIREMENTS_TXT, SOCKET_JSON } from '../../constants.mts'
910
import { commonFlags } from '../../flags.mts'
1011
import { checkCommandInput } from '../../utils/check-input.mts'
@@ -283,10 +284,7 @@ async function run(
283284
return
284285
}
285286

286-
const parsedGradleOpts = String(gradleOpts || '')
287-
.split(' ')
288-
.map(s => s.trim())
289-
.filter(Boolean)
287+
const parsedGradleOpts = parseBuildToolOpts(String(gradleOpts || ''))
290288

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

0 commit comments

Comments
 (0)