Skip to content

Commit 4250323

Browse files
committed
fix(xpkg): pin Fetch to the verified digest
The CachedClient.Get path was: 1. fetcher.Head(parsedResolvedRef) -> resolves tag -> digest D 2. validator.Validate(ref=D, vc) -> cosign verifies D 3. fetcher.Fetch(parsedResolvedRef) -> remote.Image(tag) 4. img.Manifest() (called by ExtractPackageYAML) -> GET tag again Steps 1 and 4 hit the registry independently. A registry that serves different content between the two requests can have step 2 verify a benign signed digest while step 4 returns an unsigned malicious manifest, and the consumer (Crossplane) installs the malicious package despite the ImageConfig signature verification succeeding. Pin the Fetch (and the validator call) to digestRef so the digest we verify is the digest we pull. Signed-off-by: tonghuaroot <tonghuaroot@gmail.com>
1 parent d331401 commit 4250323

1 file changed

Lines changed: 9 additions & 3 deletions

File tree

pkg/xpkg/client.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,15 +317,21 @@ func (c *CachedClient) Get(ctx context.Context, ref string, opts ...GetOption) (
317317
if err != nil {
318318
return nil, errors.Wrap(err, "cannot get image verification config")
319319
}
320+
// Pin all subsequent registry interactions to the digest resolved by
321+
// Head. Pulling by the original tag would let a registry that serves
322+
// different content between Head and Fetch slip an unsigned image past
323+
// the verifier (the verifier above attests `digest`, not whatever bytes
324+
// the tag happens to resolve to a few milliseconds later).
325+
digestRef := parsedResolvedRef.Context().Digest(digest)
326+
320327
if vc != nil {
321-
ref := parsedResolvedRef.Context().Digest(digest)
322-
if err := c.validator.Validate(ctx, ref, vc, secrets...); err != nil {
328+
if err := c.validator.Validate(ctx, digestRef, vc, secrets...); err != nil {
323329
return nil, errors.Wrap(err, "signature verification failed")
324330
}
325331
applied = append(applied, ImageConfig{Name: name, Reason: ImageConfigReasonVerify})
326332
}
327333

328-
img, err := c.fetcher.Fetch(ctx, parsedResolvedRef, secrets...)
334+
img, err := c.fetcher.Fetch(ctx, digestRef, secrets...)
329335
if err != nil {
330336
return nil, errors.Wrapf(err, "cannot fetch package %s", resolvedRef)
331337
}

0 commit comments

Comments
 (0)