Skip to content

Add uv fix support to the Python package updater - #883

Merged
orto17 merged 2 commits into
jfrog:devfrom
orto17:feature/uv-fix-support
Sep 17, 2026
Merged

orto17 merged 2 commits into
jfrog:devfrom
orto17:feature/uv-fix-support

Conversation

@orto17

@orto17 orto17 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor
  • The pull request is targeting the dev branch.
  • The code has been validated to compile successfully by running go vet ./....
  • The code has been formatted properly using go fmt ./....
  • All static analysis checks passed.
  • All tests have passed. If this feature is not already covered by the tests, new tests have been added.
  • Updated the Contributing page / ReadMe page / CI Workflow files if needed.
  • All changes are detailed at the description. if not already covered at JFrog Documentation, new documentation have been added.

Stacks on top of jfrog/jfrog-cli-security#880 (Pip/Uv detection ambiguity fix) — uv is now detected correctly, but there was no fix implementation for it: GetCompatiblePackageUpdater had no case for techutils.Uv, so any fix attempt hit the unsupported-technology path.

Adds a handleUv method to PythonPackageUpdater that rewrites the pinned dependency directly in pyproject.toml — wherever it appears (main dependencies, optional extras, or a PEP 735 dependency-groups table) — then runs uv lock --upgrade-package to refresh uv.lock.

Why not uv add

The obvious approach, mirroring Poetry's handler (poetry add pkg==version + poetry update), doesn't generalize: uv add pkg==version requires an explicit --group/--optional flag to target anything outside the main dependency list, and hard-fails with a resolver conflict if you omit it while the package lives in a dependency-groups table. There's no evidence data available (from Xray or the SBOM) telling us which group a dependency came from, so that flag can never be chosen reliably.

Instead this reuses the same mechanism the existing Pip handler already uses (handlePip): a text substitution of the exact pinned line, regardless of which manifest table it's in, followed by a lock refresh. It's also simpler than Poetry's handler — uv add already keeps pyproject.toml and uv.lock in sync in one command, so the equivalent here is one substitution + one scoped lock refresh, no two-step CLI dance.

Verification

Added test fixtures under tests/testdata/projects/package-managers/uv/ (plain project, indirect-dependency, dependency-groups, and a two-package workspace) and four new subtests in TestUpdateDependency:

Indirect dependency → ErrUnsupportedFix, matching pip/poetry/pipenv.
Plain direct dependency → pyjwt==1.7.1 becomes 2.4.0 in pyproject.toml; uv.lock refreshed.
Dependency-groups ([dependency-groups].dev) → same fix succeeds, no --group flag needed, unlike a naive uv add.
Workspace (dependency owned by a member, not the root) → fails safely with "impacted package not found, fix failed", the same error Pip's handler already returns for an unmatched package; root's pyproject.toml is left untouched.

Limitations

  • Workspaces aren't fixed, only safely rejected. If the vulnerable dependency is declared in a workspace member's pyproject.toml rather than the scanned target's own file, this handler can't locate it — it only ever reads/writes the target directory's own manifest. The result is a clean failure (no wrong-file edits, no corruption), not a fix. Solving this needs the vulnerability's evidence to identify which workspace member owns it, which isn't available today.
  • Indirect/transitive dependencies are unsupported, same as pip/poetry/pipenv — uv does have a resolver-driven option (uv lock --upgrade-package without editing the manifest first) that pip fundamentally lacks, but that's a possible future enhancement, not attempted here, to keep behavior consistent with the other Python managers.
  • Requires the uv binary and network access wherever the fix runs (CI/build environment), same precondition Poetry and Pipenv already have.
  • Static, single-pin assumption: like handlePip, this assumes the impacted version is statically pinned with a simple operator (==, >=, etc.) directly in the manifest — it doesn't handle version ranges spanning multiple constraints in unusual ways, dynamic versions, or a dependency declared via path/git/url sources instead of a version pin.

@orto17 orto17 added the new feature Automatically generated release notes label Sep 15, 2026
@coderabbitai

coderabbitai Bot commented Sep 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: b679f033-4ad1-4912-908f-7b8ad8393f21

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

Comment thread remediation/sca/packageupdaters/pythonpackageupdater.go
Comment thread remediation/sca/packageupdaters/pythonpackageupdater.go Outdated
Comment thread remediation/sca/packageupdaters/pythonpackageupdater.go Outdated
Comment thread utils/techutils/techutils.go Outdated

@Jordanh1996 Jordanh1996 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From frogbot side - don't forget to whitelist yarn and uv(and dotnet once its ready)

@orto17
orto17 requested a review from Jordanh1996 September 16, 2026 08:25
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 16, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 16, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 16, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 16, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 17, 2026
orto17 and others added 2 commits September 17, 2026 11:17
uv is detected but had no fix implementation: GetCompatiblePackageUpdater
had no case for it, so a fix attempt hit the unsupported-technology path.

Adds a handleUv method that rewrites the pinned dependency directly in
pyproject.toml (wherever it appears - main dependencies, optional
extras, or a PEP 735 dependency-groups table) and then runs
'uv lock --upgrade-package' to refresh uv.lock. This mirrors how
Renovate fixes uv dependencies today, and avoids needing to know which
--group/--optional flag 'uv add' would require, since that information
isn't available from the vulnerability evidence.

Workspace-owned dependencies aren't targeted: since the handler only
ever edits the scanned target's own pyproject.toml, a dependency
declared by a workspace member fails safely with the same
"impacted package not found" error Pip's handler already returns for
an unmatched package, rather than risking editing the wrong file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…elds

Three issues from review:
- The package name was interpolated into the regex without QuoteMeta
  or a left boundary, so fixing "attrs" also matched inside "cattrs"
  and silently bumped the wrong package.
- FindString + Replace(..., 1) only fixed the first declaration, so a
  package listed in both [project].dependencies and a
  [dependency-groups] table was left half-edited, making the
  subsequent uv lock fail as unsatisfiable.
- No rollback of pyproject.toml when uv lock fails, unlike the other
  package updaters (npm, pnpm, yarn, go) that all restore the
  original descriptor on a failed lock/install step.

Fixes:
- Escape the package name with regexp.QuoteMeta and require a left
  boundary (start of file, or preceded by whitespace/quote/bracket/
  comma) before it, so it can't match as a suffix of another name.
- Use ReplaceAllString instead of FindString+Replace(..., 1), so every
  declaration of the same package gets fixed together.
- Roll pyproject.toml back to its original content if uv lock fails,
  matching the existing convention in npm/pnpm/yarn/go's updaters.
- Removed Uv's packageInstallationCommand/packageVersionOperator from
  techutils.go - unused, since handleUv doesn't go through the generic
  CLI-add path those fields configure.

Added regression tests (TestHandleUvSubstringCollisionSafe,
TestHandleUvFixesAllDeclarations) and their testdata fixtures,
reproducing both bugs from review before the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@orto17
orto17 force-pushed the feature/uv-fix-support branch from 42d870d to a338c59 Compare September 17, 2026 08:19
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@orto17 orto17 added the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@github-actions github-actions Bot removed the safe to test Approve running integration tests on a pull request label Sep 17, 2026
@orto17
orto17 merged commit 628d34c into jfrog:dev Sep 17, 2026
65 of 88 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Automatically generated release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants