Skip to content

feat(scan): scan selected uv workspace members with dependency graphs - #1565

Merged
Martin Torp (mtorp) merged 8 commits into
v1.xfrom
martin/ask-440-scanning-uv-subdirectories
Sep 28, 2026
Merged

Martin Torp (mtorp) merged 8 commits into
v1.xfrom
martin/ask-440-scanning-uv-subdirectories

Conversation

@mtorp

@mtorp Martin Torp (mtorp) commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

A shared uv.lock covers the whole workspace, so scanning it directly also pulls in unrelated members. Scanning a member's pyproject.toml on its own loses the lockfile's pinned versions.

This adds a --uv-members flag. Each TARGET is a member directory:

socket scan create --uv-members ./packages/api ./packages/worker

For each target, the CLI runs uv export --project <dir> in frozen, offline mode. uv finds the uv.lock in that directory or in its workspace root, so one scan can cover members of several workspaces. The CycloneDX SBOM keeps pinned versions and dependency relationships, including transitive workspace dependencies, all extras, and all dependency groups. Dependencies used only by dependency groups are marked as development dependencies by comparing an export with groups against one without. This needs uv with CycloneDX export support, which uv still treats as a preview feature.

Each SBOM is written as socket-uv-cdx.json beside the member's pyproject.toml, uploaded in place of manifest discovery, and removed after the scan, including on failure, Ctrl-C, or SIGTERM. The CLI stops if that file already exists. Because the file sits where the member's own manifest would, the regular upload roots and the reachability target work unchanged. handleCreateNewScan takes a generic generateScanFiles hook and has no uv-specific code. With --reach, which takes a single target, Coana analyzes only that member's directory.

Invalid targets and export failures stop the scan. --uv-members can't be combined with --auto-manifest or --dynamic-sbom-inference. The README and command help describe the option.

Validation:

  • Source build, type checks, and lint on the changed files. Lint reports only the warnings the repo already has.
  • 105 unit and CLI tests in the four touched test files.
  • Six e2e tests against the real uv binary, run on both uv 0.10.9 and CI's uv 0.12.15. They cover pinned versions, dependency edges, extras, groups, development scopes, markers, exclusion of unrelated members, upload inputs, and cleanup. uv 0.12 leaves out dependsOn on leaf nodes and adds workspace-root dependency groups to members that don't define their own. The tests accept both.
  • Live --reach scans against the Socket API with Coana 15.11.3, on a two-member workspace where app depends on requests 2.31.0 without calling it and other calls requests.get. --uv-members packages/app reported all 3 requests advisories as unreachable, and --uv-members packages/other reported all 3 as reachable. A regular scan of the whole workspace gave the same per-member results.
  • Sending SIGINT or SIGTERM to the scan's process group mid-analysis removes the generated SBOM and keeps exit codes 130 and 143.

The depscan importer drops local workspace nodes without a purl. uv's uv:workspace:path values stay relative to the workspace root.

@mtorp
Martin Torp (mtorp) marked this pull request as ready for review September 28, 2026 10:47
…ng-uv-subdirectories

# Conflicts:
#	CHANGELOG.md
These cases spawn the built CLI like the cmdit tests do, but ran with vitest's 5s default and timed out under parallel load.
Replace --uv-package <project.name> with a boolean --uv-members flag. Each
TARGET is a member directory, and uv resolves it from the uv.lock in that
directory or its workspace root, so one scan can cover members of several
workspaces.

Each member's CycloneDX SBOM is written beside its pyproject.toml and removed
after the scan. That keeps the existing upload roots and reachability target
unchanged, so the scan handler takes a generic generateScanFiles hook and no
longer has uv-specific branches. With --reach, analysis runs on the member
directory only.

The e2e assertions now accept uv 0.12, which omits dependsOn on leaf nodes and
adds workspace-root dependency groups to members without their own.
Completes the previous commit, which only picked up the renames and the reachability revert.
The launcher SIGKILLs a signalled scan after a short grace period, and a
signal or process.exit() skips finally blocks, so Ctrl-C or a CI cancel left
socket-uv-cdx.json in the member directory. A later regular scan would then
pick it up as a pre-generated SBOM. Written SBOMs are now also removed
synchronously on exit, SIGHUP, SIGINT and SIGTERM.

The real-uv e2e tests get a 30s timeout, since each one runs uv twice per
member and the handler test sometimes passed 5s.
@mtorp Martin Torp (mtorp) changed the title feat(scan): scan selected uv packages with dependency graphs feat(scan): scan selected uv workspace members with dependency graphs Sep 28, 2026
@mtorp
Martin Torp (mtorp) merged commit 9d721ef into v1.x Sep 28, 2026
10 checks passed
@mtorp
Martin Torp (mtorp) deleted the martin/ask-440-scanning-uv-subdirectories branch September 28, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants