Skip to content

Represent validated source archives before persistence - #21246

Merged
zanieb merged 3 commits into
mainfrom
zb/validated-source-archive-refactor
Aug 24, 2026
Merged

zanieb merged 3 commits into
mainfrom
zb/validated-source-archive-refactor

Conversation

@zanieb

@zanieb zanieb commented Aug 20, 2026 •

Copy link
Copy Markdown
Member

Source archive helpers currently extract an archive and save it to the cache in the same call. Separate those steps so the type system records that extraction and its existing checks have completed before persistence.

Introduce a private ValidatedSourceArchive that owns the temporary directory. Its HTTP and local constructors retain their transport setup, diagnostics, and cleanup behavior, while a private reader helper performs the shared extraction and hash collection. The consuming persist method saves the source tree and returns the collected hashes and byte count. This preserves existing behavior; it does not add trusted-hash comparisons or change cache repair.

#21247 separates hash generation from verification, and #21248 checks caller-supplied hashes before persistence.

Comment thread crates/uv-distribution/src/source/validated_archive.rs Outdated
Comment thread crates/uv-distribution/src/source/validated_archive.rs
@astral-sh-bot

astral-sh-bot Bot commented Aug 20, 2026 •

Copy link
Copy Markdown

uv test inventory changes

This PR changes the tests when compared with the main base revision.

  • Added tests: 2
  • Removed tests: 0
  • Changed suites: 1
uv-distribution: +2 / -0

Added:

  • uv-distribution::source::validated_archive::tests::parser_error_precedes_trailing_read
  • uv-distribution::source::validated_archive::tests::reader_is_exhausted_only_for_hashes_or_size

Removed: none

Comment on lines +214 to +216
// Two zero-filled records form an empty, valid tar archive.
const EMPTY_TAR: &[u8] = &[0; 1024];
const TAR: SourceDistExtension = SourceDistExtension::Legacy(LegacySourceDistExtension::Tar);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nitpick: it might be nice to use a slightly more realistic fixture here, particularly since we're considering bare .tar legacy at this point. But minor IMO as we can change going forwards.

algorithms: &[HashAlgorithm],
origin: ArchiveOrigin,
expected_size: Option<u64>,
error_context: impl Fn(&Path) -> String,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Confirming my understanding: this is a closure that produces an appropriate piece of context for an error by transforming a path into a string?

If so, maybe we could save this complexity and perform that transform at the callsite? It looks pretty minimal for our two existing callers; FWICT the only downside would be an eager allocation but that's probably not saving us any perf in either a network or FS pathway.

(Super minor though.)

@woodruffw woodruffw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, two extreme nitpicks.

@zanieb
zanieb merged commit 187300c into main Aug 24, 2026
117 checks passed
@zanieb
zanieb deleted the zb/validated-source-archive-refactor branch August 24, 2026 16:47
zanieb added a commit that referenced this pull request Aug 24, 2026
`HashStrategy` currently represents hash generation and verification as
mutually exclusive choices. A resolution may need both: verify artifacts
whose hashes have been supplied, while generating hashes for new
artifacts.

Store generation separately from `HashVerification` in `HashStrategy`.
Each distribution still receives a `HashPolicy` specifying whether to
generate or check hashes. Update existing call sites to use the separate
settings while preserving their hash inputs and which hashes uv treats
as trusted. Generation remains available for artifacts without supplied
hashes, unless the caller requires a hash for every artifact. This does
not read hashes from `uv.lock` or add new archive comparisons.

Based on #21246. #21248 checks caller-supplied hashes before source
archives enter the cache. #21223 reads hashes from an existing `uv.lock`
and supplies them for verification.
zanieb added a commit that referenced this pull request Sep 8, 2026
`uv.lock` records archive digests, but checking an existing lockfile or
resolving with `--locked` can obtain source metadata without comparing
those digests. A replaced source archive can therefore run its build
backend before a later hash or lockfile check rejects it.

Construct a `HashStrategy` from the existing lockfile's hashes for
registry packages, direct URLs, and local archives. Enforce the recorded
hashes when reusing cached source metadata, resolving with `--locked`,
and installing build dependencies recorded in the lockfile into isolated
build environments. Normalize equivalent archive subdirectories so they
find the same recorded hashes. Keep recorded hashes while checking
whether an existing lockfile is still usable, but allow unlocked fresh
resolution to replace them. Build dependencies without recorded hashes
remain allowed.

Based on #21248, which checks source archives before saving them to the
cache. #21246 contains the structural refactor, and #21247 separates
hash generation from verification.

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
Co-authored-by: Tomasz Kramkowski <tom@astral.sh>
Co-authored-by: Charlie Marsh <charlie.r.marsh@gmail.com>

This branch was previously deployed

1 inactive deployment
automations — 9982ed99 Deployed Aug 21, 2026 by zanieb via review / security review #45169
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants