Skip to content

Digest values are not validated in sync path and test fixtures use structurally invalid digests #770

Description

@trevor-vaughan

🦾 Written with LLM assistance [claude-opus-4-6]
💪 Reviewed by a human before submission


Describe the bug

The SyncPolicy() and SyncComplypack() functions do not currently validate that digest strings from DefinitionVersion() are structurally valid OCI digests (e.g., sha256: followed by 64 hex characters). This works correctly today because production digests come from oras, which always returns well-formed values.

Because the DefinitionVersion() interface is public, a future PolicySource or ComplypackSource implementation could return malformed digests that would be persisted to state.json, potentially causing subtle cache corruption (e.g., a digest that can never match again, forcing perpetual re-sync).

Separately, test fixtures across the internal/cache package use placeholder digest strings like "sha256:abc123", "sha256:v1digest", and "sha256:fetchandstore111". These were sufficient for the string-equality comparisons the tests exercise, but they would not pass digest.Parse(). If digest validation is added to the sync path, these fixtures would need to be updated first.

To Reproduce

  1. Read internal/cache/sync.go line 111: remoteDigest from DefinitionVersion() is passed through without structural validation
  2. Read internal/cache/complypack_sync.go line 89: same pattern
  3. Read internal/cache/complypack_sync_test.go: seedComplypack calls use placeholder strings like "sha256:v1digest" that would not pass digest.Parse() from opencontainers/go-digest
  4. Read internal/cache/sync_test.go: SeedPolicy calls use "sha256:abc123", a placeholder that is not a structurally valid SHA256 hex string

Expected behavior

  1. SyncPolicy() and SyncComplypack() validate the digest returned by DefinitionVersion() using digest.Parse() and return a wrapped error if invalid. Fails fast with a clear message
  2. Test fixtures use structurally valid digest strings (64-char hex for SHA256) so they pass digest.Parse() and model production inputs

Additional context

Files requiring digest fixture updates (non-exhaustive):

  • internal/cache/complypack_sync_test.go - seedComplypack() calls
  • internal/cache/sync_test.go - SeedPolicy() calls
  • internal/cache/cachetest/mock_source.go - SeedPolicy(), SeedBundlePolicy() callers
  • internal/cache/state_test.go - inline digest strings
  • internal/cache/complypack_pipeline_test.go - inline digest strings
  • internal/cache/verify_test.go - inline digest strings
  • internal/cache/complypack_test.go - inline digest strings
  • internal/cache/complypack_source_test.go - inline digest strings

The brokenUnpackMock in complypack_sync_test.go line 786 already uses a valid 64-char hex digest, that's the correct pattern to follow.

The digest is currently used only for string equality comparison (cache skip) and state persistence/display. It is never used for content integrity verification in the sync path — that's handled by oras.Copy() and sigstore-go independently. This change is defense-in-depth: the current code is correct, but we are protecting against future changes that weaken the invariant.

The opencontainers/go-digest package is already imported in internal/cache/sync.go (used by classifyVersion()), so no new dependency is needed.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Fields

    Priority

    Medium

    Effort

    Low

    Projects

    Status
    Ready 🚀

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions