Skip to content
Merged
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
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Changed
- Updated the Coana CLI to v `15.12.12`.
- JVM manifest generation now fails on dependencies the build tool only warns about or skips, instead of passing them into the scan without code: missing or invalid Maven POMs, missing `system` jars, jars coursier or Ivy could not fetch, failed Gradle project dependencies, artifacts failing Gradle dependency verification, and Gradle variants ambiguous in every configuration.
- To proceed past them, set `ignoreUnresolved`, and for reachability scans also pass `--reach-continue-on-install-errors`.
- An artifact with no checksum in Gradle's verification metadata no longer fails the run; it is left out of reachability analysis.

### Fixed
- With `--dynamic-sbom-inference`, an sbt build that fails before writing its results is reported as failed instead of reusing another build's results.
- `socket scan create --reach` keeps the private libraries and build modules of Socket facts files in the stored SBOM, with their dependencies under them.
- JVM dependencies whose jars cannot be fetched, or are gone from disk, no longer reach reachability analysis looking resolved without code.
- One unresolvable Maven dependency no longer strips the jar paths from every other dependency of its build, which made reachability analysis call their vulnerabilities unreachable. Jars that cannot be fetched are reported with their real cause, Maven 4 included.
- Maven dependencies with missing or invalid POMs are reported in every module that uses them, `system`-scope jars reach reachability analysis, and a failing dependency no longer fails scopes that `excludeConfigs` leaves out or the build when it lost a version conflict.
- Maven `system`-scope jars reach reachability analysis, and a failing Maven dependency no longer fails scopes that `excludeConfigs` leaves out, or the build when it lost a version conflict.
- Reachability scans that hit unresolved JVM dependencies now point out that ignoring them also needs `--reach-continue-on-install-errors`.
- A Gradle dependency whose variant is ambiguous in every configuration of its project now fails the run instead of quietly dropping out of the scan.
- Gradle dependencies that fail dependency verification, or whose jars cannot be fetched, are reported instead of passed on as healthy.
- Failed Gradle project dependencies are reported instead of silently dropping everything they pull in, and failures name the version that actually failed.
- sbt dependencies whose jars coursier skips or Ivy fails to download are reported instead of passed off as POM-only modules, and POM-only, war and aar modules keep their real shape.
- JVM failures name the version that actually failed, and sbt POM-only, war and aar modules keep their real shape.
- A module excluded with `--exclude-paths` is still analysed as part of the projects that depend on it.

## [1.10.0](https://github.com/SocketDev/socket-cli/releases/tag/v1.10.0) - 2026-10-22

Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-auto.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('socket manifest auto', async () => {
$ socket manifest auto [options] [CWD=.]

Options
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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.
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--verbose Enable debug output (only for auto itself; sub-steps need to have it pre-configured), may help when running into errors

Tries to figure out what language your target repo uses. If it finds a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('socket manifest dynamic-sbom-inference', async () => {
CWD itself.

Options
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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.
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts files); overrides each build's socket.json setting
--json Output as JSON
--markdown Output as Markdown
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-gradle.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('socket manifest gradle', async () => {
Options
--bin Location of the gradle binary to use, default: ./gradlew if present, else gradle on PATH
--exclude-configs When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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.
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--facts Emit a Socket facts JSON file (\`gradle.socket.facts.json\`) describing the resolved dependency graph. This is the default; pass \`--pom\` to generate \`pom.xml\` files instead
--gradle-opts Additional options to pass on to ./gradlew, see \`./gradlew --help\`
--ignore-unresolved When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-kotlin.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('socket manifest kotlin', async () => {
Options
--bin Location of the gradle binary to use, default: ./gradlew if present, else gradle on PATH
--exclude-configs When generating facts: comma-separated glob patterns; Gradle configurations matching any pattern are skipped (applied after --include-configs)
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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.
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--facts Emit a Socket facts JSON file (\`gradle.socket.facts.json\`) describing the resolved dependency graph. This is the default; pass \`--pom\` to generate \`pom.xml\` files instead
--gradle-opts Additional options to pass on to ./gradlew, see \`./gradlew --help\`
--ignore-unresolved When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-maven.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe('socket manifest maven', async () => {
Options
--bin Location of the maven binary to use, default: ./mvnw if present, else mvn on PATH
--exclude-configs Comma-separated glob patterns; Maven scopes matching any pattern are skipped (applied after --include-configs)
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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.
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--ignore-unresolved Warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
--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
--maven-opts Additional options to pass on to maven, e.g. \`-P <profile> -s <settings.xml>\`
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/cmd-manifest-scala.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('socket manifest scala', async () => {
Options
--bin Location of sbt binary to use
--exclude-configs When generating facts: comma-separated glob patterns; sbt configurations matching any pattern are skipped (applied after --include-configs)
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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.
--exclude-paths List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (\`--cwd\` if set): \`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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.
--facts Emit a Socket facts JSON file (\`sbt.socket.facts.json\`) describing the resolved dependency graph. This is the default; pass \`--pom\` to generate \`pom.xml\` files instead
--ignore-unresolved When generating facts: warn on unresolved dependencies instead of failing the run (unresolved deps are not emitted to the facts file)
--include-configs When generating facts: comma-separated glob patterns matched against sbt configuration names (case-sensitive; \`*\`, \`?\`, and \`[...]\` wildcards). Only configurations matching at least one pattern are resolved. e.g. \`compile,test\`. Default: compile,optional,provided,runtime,test
Expand Down
2 changes: 1 addition & 1 deletion src/commands/manifest/manifest-flags.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ export const excludePathsFlag: MeowFlags = {
type: 'string',
isMultiple: true,
description:
'List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (`--cwd` if set): `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.',
'List of glob patterns to exclude from manifest/facts generation. Patterns are anchored micromatch globs matched relative to CWD (`--cwd` if set): `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. An excluded JVM build module is not analysed as a project of its own, but where a project that is not excluded depends on it, its code is analysed as part of that project.',
},
}
4 changes: 3 additions & 1 deletion src/commands/manifest/scripts/assemble.mts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ export function assembleFacts(
artifactPaths: buildArtifactPaths(
finalNodes,
[...parsed.projects.values()],
[...parsed.excludedProjects.values()],
graphs,
fileExists,
),
Expand Down Expand Up @@ -410,6 +411,7 @@ function byProject(
function buildArtifactPaths(
finalNodes: Map<string, MergedNode>,
projects: RawProject[],
excludedProjects: RawProject[],
graphs: Map<string, SubprojectGraph>,
fileExists: (path: string) => boolean,
): ResolvedArtifactPaths {
Expand Down Expand Up @@ -444,7 +446,7 @@ function buildArtifactPaths(
}
// A project's own component shares its id, so this also covers dependency
// edges onto a sibling project.
for (const p of projects) {
for (const p of [...projects, ...excludedProjects]) {
pathsById.set(p.projectKey, {
sources: [...new Set(p.sources)].filter(fileExists).sort(),
targets: [...new Set(p.targets)].filter(fileExists).sort(),
Expand Down
23 changes: 23 additions & 0 deletions src/commands/manifest/scripts/assemble.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,29 @@ describe('records → assemble → sidecar', () => {
expect(bom?.targets).toEqual([])
expect(bom?.sources).toEqual([])
})
it("carries an excluded module's paths into the kept project that depends on it", () => {
const records = [
'meta\tgradle\t8.10\t17',
'project\t:app\tg\tapp\t1\tapp',
'projectSrc\t:app\t/abs/app/src',
'projectTgt\t:app\t/abs/app/classes',
'excludedProject\t:lib',
'projectSrc\t:lib\t/abs/lib/src',
'projectTgt\t:lib\t/abs/lib/classes',
'root\t0\t:app\tcompileClasspath\t1',
'node\t0\t:lib\tg\tlib\t1\tjar\t\t1\t:lib',
].join('\n')
const { artifactPaths, facts } = assembleFacts(parseRecords(records), {
fileExists: () => true,
})

expect(facts.projects?.map(p => p.id)).toEqual([':app'])
expect(artifactPaths.pathsById.get(':lib')).toEqual({
sources: ['/abs/lib/src'],
targets: ['/abs/lib/classes'],
})
expect([...artifactPaths.classpathByProject.keys()]).toEqual([':app'])
})
it('states no targets for an artifact that failed to resolve or is gone from disk', () => {
const records = [
'meta\tmaven\t3.9.6\t17',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,16 @@ public void run(MavenSession session, List<MavenProject> reactor, File rootDir,
// own "no POM in this directory" error surface instead of an NPE.
if (module.getBasedir() == null) continue;
String ws = SocketSupport.workspace(rootDir.toPath(), module.getBasedir().toPath());
if (SocketSupport.isExcludedPath(ws, excludes)) continue;
String key = projectKey(module);
rec(lines, "project", key, module.getGroupId(), module.getArtifactId(), module.getVersion(), ws);
File pom = module.getFile();
if (pom != null && pom.isFile()) rec(lines, "projectBuild", key, SocketSupport.relativePath(rootDir.toPath(), pom.toPath()));
boolean excluded = SocketSupport.isExcludedPath(ws, excludes);
if (excluded) {
// Not analysed on its own, but a kept module depending on it is analysed with its code.
if (opts.withFiles) rec(lines, "excludedProject", key);
} else {
rec(lines, "project", key, module.getGroupId(), module.getArtifactId(), module.getVersion(), ws);
File pom = module.getFile();
if (pom != null && pom.isFile()) rec(lines, "projectBuild", key, SocketSupport.relativePath(rootDir.toPath(), pom.toPath()));
}
if (opts.withFiles) {
for (String s : collectSources(module)) rec(lines, "projectSrc", key, s);
for (String t : collectTargets(module)) rec(lines, "projectTgt", key, t);
Expand Down
16 changes: 16 additions & 0 deletions src/commands/manifest/scripts/records.mts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { ResolutionFailure, Unscannable } from './resolution-report.mts'
// projectSrc projectKey path (--with-files only)
// projectTgt projectKey path (--with-files only)
// projectBuild projectKey path (build-root-relative; may be absent on disk)
// excludedProject projectKey (--with-files only; paths without a project)
// root rootId projectKey config prod(0|1)
// node rootId coordId group name version ext classifier direct(0|1) project
// edge rootId parentCoordId childCoordId
Expand Down Expand Up @@ -80,6 +81,9 @@ export type ParsedRecords = {
// directory-addressed build.
entry: string
projects: Map<string, RawProject>
// Excluded from the scan: not projects of their own, but their paths serve
// the kept projects depending on them.
excludedProjects: Map<string, RawProject>
roots: Map<string, RawRoot>
scannedConfigs: string[]
failures: ResolutionFailure[]
Expand Down Expand Up @@ -115,12 +119,14 @@ export function parseRecords(text: string): ParsedRecords {
buildRoot: '',
entry: '',
projects: new Map(),
excludedProjects: new Map(),
roots: new Map(),
scannedConfigs: [],
failures: [],
unscannable: [],
}
const scanned = new Set<string>()
const excluded = new Set<string>()

const root = (id: string): RawRoot => {
let r = result.roots.get(id)
Expand Down Expand Up @@ -190,6 +196,9 @@ export function parseRecords(text: string): ParsedRecords {
project(f[1] ?? '').targets.push(f[2])
}
break
case 'excludedProject':
Comment thread
jfblaa marked this conversation as resolved.
excluded.add(f[1] ?? '')
break
case 'projectBuild':
if (f[2]) {
project(f[1] ?? '').buildFiles.push(f[2])
Expand Down Expand Up @@ -271,6 +280,13 @@ export function parseRecords(text: string): ParsedRecords {
break
}
}
for (const key of excluded) {
const p = result.projects.get(key)
if (p) {
result.projects.delete(key)
result.excludedProjects.set(key, p)
}
}
result.scannedConfigs = [...scanned].sort()
return result
}
Loading
Loading