Validate the multi-repo map file before initializing any client - #760
Merged
Conversation
rdimitrov
force-pushed
the
rdimitrov/multirepo-map-validation
branch
from
August 7, 2026 11:25
12bc099 to
2a99deb
Compare
rdimitrov
marked this pull request as ready for review
August 7, 2026 11:26
There was a problem hiding this comment.
Pull request overview
This PR consolidates multi-repo map validation into a single validateRepoMap helper that runs before any repository initialization, preventing partial initialization side effects and converting several panic cases into upfront configuration errors.
Changes:
- Added
validateRepoMapto validate repository names, repository URLs, and mapping-referenced repositories before callinginitTUFClients. - Introduced exported sentinel errors (
ErrMissingRepoURL,ErrUnknownMappingRepo) to supporterrors.Ischecks by callers. - Added regression tests covering missing/empty URLs, unknown mapping repositories, null mapping entries, and missing repo map config.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| metadata/multirepo/multirepo.go | Adds centralized pre-init map validation and new sentinel errors; removes URL validation from initialization loop. |
| metadata/multirepo/multirepo_test.go | Adds tests to ensure invalid maps/configs fail fast with the expected sentinel errors or non-nil errors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
rdimitrov
force-pushed
the
rdimitrov/multirepo-map-validation
branch
from
August 7, 2026 11:39
6fc5422 to
e024e5f
Compare
Follow-up to #750. That PR fixed the out-of-bounds access on a repository with no URL, but left the check inside initTUFClients, where it runs interleaved with client initialization. Move all map file validation into a single validateRepoMap helper that runs before initTUFClients, and extend it: - Repository names and URLs are now validated in one place, so New either returns a fully initialized client or fails without having created cache directories for an arbitrary subset of the repositories. - Repository names are checked in sorted order, so a map file with more than one defect reports the same error on every run instead of depending on map iteration order. - A mapping that references a repository absent from the top-level repositories object is now rejected. Previously it left no TUF client for that name and GetTargetInfo dereferenced a nil *updater.Updater, panicking once a target path matched the mapping. - A null mapping entry and a config with no repository map are rejected for the same reason, rather than panicking. Empty-URL and empty-slice rejection now wraps the exported ErrMissingRepoURL sentinel, matching the existing ErrInvalidRepoName convention so callers can use errors.Is. Adds regression tests for each case; #750 shipped without any. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
New dereferenced config.RepoMap without checking the config pointer, so New(nil) panicked. NewConfig returns a nil config alongside its error, so a caller that ignores the error reaches this path. Also move client construction after validation, so the invalid-input paths no longer allocate a client they immediately discard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Radoslav Dimitrov <radoslav@stacklok.com>
rdimitrov
force-pushed
the
rdimitrov/multirepo-map-validation
branch
from
August 7, 2026 11:42
e024e5f to
5ff859f
Compare
rdimitrov
enabled auto-merge (squash)
August 7, 2026 12:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #750, addressing the review findings on that PR.
#750 correctly fixed the out-of-bounds access on
repoURL[0]when a repository is declared with no URL. This follow-up relocates and extends that check.What changes
Validation moves into a single
validateRepoMaphelper that runs beforeinitTUFClients.New()already had a pre-flight loop callingvalidateRepoName; #750 put the URL check in a second place, inside the initialization loop. Consolidating them means:Newbecomes atomic. Map iteration order is randomized, so with the check insideinitTUFClientsan arbitrary number of repositories getEnsurePathsExist()andupdater.New()run before the error surfaces — leaving cache directories on disk for a call that returnsnil, errand gives the caller no handle to clean up.This ordering problem is not hypothetical. Before the move, a map file whose mapping was malformed failed with:
because initialization began before validation finished, masking the real defect.
Bugs fixed beyond #750
A mapping referencing an undeclared repository panicked.
Mapping.repositorieswas never cross-checked against the top-levelrepositoriesobject. An unknown name leaves no entry inTUFClients, andGetTargetInfoindexes it without anokcheck, unlikeDownloadTargetwhich does guard this. Confirmed against the pre-fix code:It needs both a matching path pattern and a typo'd repository name in the same mapping to trigger, which is likely why it went unnoticed.
"mapping": [null]panicked. It unmarshals into a[]*Mappingholding a nil element, dereferenced while walking mappings.A
MultiRepoConfigwith noRepoMappanicked. The struct has exported fields, so callers can build one directly without going throughNewConfig.Other
ErrMissingRepoURLsentinel, matching the existingErrInvalidRepoNameconvention so callers can useerrors.Is. Unknown mapping repositories getErrUnknownMappingRepo..golangci.ymlenables no formatters, so CI does not currently catchgofmtdrift).Compatibility
A map file with a mapping pointing at an undeclared repository is now rejected by
New()rather than accepted and panicking later on a matching target lookup. That is a behaviour change, but it converts a latent runtime panic into an upfront configuration error. The shippedexamples/multirepo/repository/targets/map.jsonwas verified to pass validation unchanged.Tests
#750 shipped without tests; this adds regression coverage for every case above — empty/null/empty-string URL lists, unknown mapping repository, null mapping entry, and missing repository map. Each was written first and watched fail before the fix.
🤖 Generated with Claude Code