Skip to content

Add EnsureNotContainerImage to prevent container images in artifact store#9782

Merged
openshift-merge-bot[bot] merged 1 commit into
cri-o:mainfrom
bitoku:artifact-image-mistreat
Feb 27, 2026
Merged

Add EnsureNotContainerImage to prevent container images in artifact store#9782
openshift-merge-bot[bot] merged 1 commit into
cri-o:mainfrom
bitoku:artifact-image-mistreat

Conversation

@bitoku

@bitoku bitoku commented Feb 25, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind bug

What this PR does / why we need it:

Fix cases where regular container images could accidentally be pulled into the OCI artifact store by validating manifest and config media types, including multi-arch manifest resolution and artifact type detection. Includes unit tests and updated mocks.

Which issue(s) this PR fixes:

Special notes for your reviewer:

Does this PR introduce a user-facing change?

Fixed cases where regular container images could accidentally be pulled into the OCI artifact store

Summary by CodeRabbit

  • New Features

    • Rejects container images when pulling OCI artifacts (early validation) and surfaces a clear error
    • Adds access to SystemContext for artifact handling
  • Bug Fixes

    • Pull flow now validates references earlier to avoid misclassifying images as artifacts
  • Tests

    • Added comprehensive tests for manifest parsing and multi-arch resolution; test helpers to inject implementations
  • Chores

    • Expanded mock generation to include OCI artifact interfaces and updated mock surfaces

@bitoku bitoku requested a review from mrunalp as a code owner February 25, 2026 17:14
@openshift-ci openshift-ci Bot added release-note Denotes a PR that will be considered when it comes time to generate release notes. kind/bug Categorizes issue or PR as related to a bug. dco-signoff: no Indicates the PR's author has not DCO signed all their commits. labels Feb 25, 2026
@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an OCI artifact abstraction (Impl) with a default implementation, exposes LibartifactStore.SystemContext, implements Store.EnsureNotContainerImage (multi-arch-aware manifest inspection and rejection of container images), updates mocks and Makefile mockgen target, and adds tests and test-injection for the new behavior.

Changes

Cohort / File(s) Summary
Build System
Makefile
Added mock-ociartifact-types target and .PHONY; wired into aggregate mockgen to generate OCI artifact mocks (Impl, LibartifactStore).
OCI Abstractions & Mocks
internal/ociartifact/impl.go, test/mocks/ociartifact/ociartifact.go
Introduced public Impl interface (ChooseInstance, GetManifestFromRef) and defaultImpl; updated generated mock signatures to match new interface methods.
Libartifact Store Adapter
internal/ociartifact/libartifact_store.go
Added SystemContext() *types.SystemContext to LibartifactStore and artifactStore wrapper exposing embedded ArtifactStore's SystemContext.
Store Logic
internal/ociartifact/store.go
Added ErrIsAnImage and Store.EnsureNotContainerImage(ctx, ref) with manifest/index parsing and multi-arch instance resolution; Store now contains impl.
Tests & Test Helpers
internal/ociartifact/store_test.go, internal/ociartifact/store_test_inject.go
New comprehensive tests for EnsureNotContainerImage covering parsing, multi-arch flows, and errors; added SetFakeImpl for injecting test Impl.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Caller as Caller
participant Store as Store.EnsureNotContainerImage
participant LibStore as LibartifactStore
participant Impl as Impl (GetManifestFromRef / ChooseInstance)
participant Registry as ImageSource/Registry

Caller->>Store: EnsureNotContainerImage(ctx, ref)
Store->>LibStore: SystemContext()
LibStore-->>Store: *types.SystemContext
Store->>Impl: GetManifestFromRef(ctx, ref, sys, nil)
alt manifest is index/list
    Impl-->>Store: manifest list bytes, mimeType
    Store->>Impl: ChooseInstance(list, sys)
    Impl-->>Store: instanceDigest / error
    Store->>Impl: GetManifestFromRef(ctx, ref, sys, instanceDigest)
    Impl-->>Store: instance manifest bytes, mimeType
end
Store->>Store: parse manifest, inspect mediaType & artifactType
alt detected container image
    Store-->>Caller: ErrIsAnImage
else non-container artifact
    Store-->>Caller: nil
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ok-to-test

Suggested reviewers

  • mrunalp
  • hasan4791
  • klihub

Poem

🐰 I hopped through manifests in moonlit code,
sniffed indexes and bytes on the artifact road.
I picked an instance, fetched the manifest fine,
guarded OCI secrets, one digest at a time.
🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding EnsureNotContainerImage method to prevent container images from being pulled into the artifact store.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@openshift-ci openshift-ci Bot requested review from hasan4791 and klihub February 25, 2026 17:14

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
internal/ociartifact/impl.go (1)

34-42: Consider wrapping the error returned by GetManifest.

src.GetManifest errors propagate unwrapped. While the caller currently wraps them, adding a wrap here would provide better context if GetManifestFromRef is reused elsewhere.

Suggested improvement
-	return src.GetManifest(ctx, instanceDigest)
+	manifestBytes, mimeType, err := src.GetManifest(ctx, instanceDigest)
+	if err != nil {
+		return nil, "", fmt.Errorf("get manifest: %w", err)
+	}
+	return manifestBytes, mimeType, nil
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/impl.go` around lines 34 - 42, The GetManifestFromRef
implementation returns src.GetManifest errors unwrapped; update the return to
capture the result and error from src.GetManifest(ctx, instanceDigest) and, if
err != nil, return nil, "", fmt.Errorf("get manifest from source: %w", err) so
callers receive contextualized errors; reference the GetManifestFromRef function
and the src.GetManifest call when making the change.
internal/ociartifact/store.go (1)

148-152: Swallowing the OCI1 parse error is intentional but loses diagnostic info.

When manifest.OCI1FromManifest fails, the error is discarded and ErrIsAnImage is returned directly. This is correct behavior (Docker v2s2 manifests will fail here), but a debug log would help troubleshoot unexpected parse failures without changing the control flow.

Suggested improvement
 		ociManifest, err := manifest.OCI1FromManifest(manifestBytes)
 		// Unable to parse an OCI manifest, assume an image
 		if err != nil {
+			log.Debugf(ctx, "Cannot parse as OCI manifest, treating as container image: %v", err)
 			return ErrIsAnImage
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/store.go` around lines 148 - 152, When
manifest.OCI1FromManifest(manifestBytes) fails we currently return ErrIsAnImage
and discard the original error; update the code to log the parse error at
debug/trace level before returning ErrIsAnImage so diagnostics are preserved.
Locate the call to manifest.OCI1FromManifest in internal/ociartifact/store.go
and add a debug log statement that includes the returned error (using the
existing logger in scope, e.g., s.logger, logger, or ctx logger) while keeping
the control flow intact (still return ErrIsAnImage). Ensure the log message
clearly states it was an OCI manifest parse failure and includes the error
value.
internal/ociartifact/store_test.go (1)

25-44: Consider adding a test case for DockerV2Schema1SignedMediaType.

The imageMimeTypes list includes DockerV2Schema1SignedMediaType, but there's no test exercising that path. A schema 1 manifest has a different structure and would exercise the OCI1FromManifest failure → ErrIsAnImage branch distinctly from the Docker v2s2 test.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/store_test.go` around lines 25 - 44, Add a new unit test
that covers the DockerV2Schema1SignedMediaType path: create a schema1-style
manifest bytes (either by extending makeOCIManifest or adding a small helper
that emits the schema1 structure), feed those bytes through the same code path
that uses imageMimeTypes and the manifest-to-OCI conversion (invoking
OCI1FromManifest via the same processing function used by existing tests), and
assert the code returns the ErrIsAnImage error (or the same failure branch)
rather than succeeding; reference makeOCIManifest,
DockerV2Schema1SignedMediaType, imageMimeTypes, OCI1FromManifest, and
ErrIsAnImage to locate where to add the test and assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/ociartifact/store.go`:
- Around line 90-164: EnsureNotContainerImage fails to recognize unsigned Docker
v2 schema 1 manifests as images; add the unsigned MIME type to imageMimeTypes.
Update the imageMimeTypes slice in EnsureNotContainerImage to include
manifest.DockerV2Schema1MediaType alongside
manifest.DockerV2Schema1SignedMediaType so both signed and unsigned schema1
variants are treated as container images; verify references to imageMimeTypes
and re-run tests that exercise detection in Store.EnsureNotContainerImage and
manifest handling functions.

---

Nitpick comments:
In `@internal/ociartifact/impl.go`:
- Around line 34-42: The GetManifestFromRef implementation returns
src.GetManifest errors unwrapped; update the return to capture the result and
error from src.GetManifest(ctx, instanceDigest) and, if err != nil, return nil,
"", fmt.Errorf("get manifest from source: %w", err) so callers receive
contextualized errors; reference the GetManifestFromRef function and the
src.GetManifest call when making the change.

In `@internal/ociartifact/store_test.go`:
- Around line 25-44: Add a new unit test that covers the
DockerV2Schema1SignedMediaType path: create a schema1-style manifest bytes
(either by extending makeOCIManifest or adding a small helper that emits the
schema1 structure), feed those bytes through the same code path that uses
imageMimeTypes and the manifest-to-OCI conversion (invoking OCI1FromManifest via
the same processing function used by existing tests), and assert the code
returns the ErrIsAnImage error (or the same failure branch) rather than
succeeding; reference makeOCIManifest, DockerV2Schema1SignedMediaType,
imageMimeTypes, OCI1FromManifest, and ErrIsAnImage to locate where to add the
test and assertions.

In `@internal/ociartifact/store.go`:
- Around line 148-152: When manifest.OCI1FromManifest(manifestBytes) fails we
currently return ErrIsAnImage and discard the original error; update the code to
log the parse error at debug/trace level before returning ErrIsAnImage so
diagnostics are preserved. Locate the call to manifest.OCI1FromManifest in
internal/ociartifact/store.go and add a debug log statement that includes the
returned error (using the existing logger in scope, e.g., s.logger, logger, or
ctx logger) while keeping the control flow intact (still return ErrIsAnImage).
Ensure the log message clearly states it was an OCI manifest parse failure and
includes the error value.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 11eaffa and f52ee7b.

📒 Files selected for processing (7)
  • Makefile
  • internal/ociartifact/impl.go
  • internal/ociartifact/libartifact_store.go
  • internal/ociartifact/store.go
  • internal/ociartifact/store_test.go
  • internal/ociartifact/store_test_inject.go
  • test/mocks/ociartifact/ociartifact.go

Comment thread internal/ociartifact/store.go
@codecov

codecov Bot commented Feb 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.57%. Comparing base (7421a8e) to head (7eb2cc1).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9782      +/-   ##
==========================================
+ Coverage   67.45%   67.57%   +0.12%     
==========================================
  Files         210      212       +2     
  Lines       29123    29195      +72     
==========================================
+ Hits        19644    19728      +84     
+ Misses       7790     7782       -8     
+ Partials     1689     1685       -4     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bitoku bitoku force-pushed the artifact-image-mistreat branch from f52ee7b to 0dd1fb3 Compare February 25, 2026 17:24
@openshift-ci openshift-ci Bot added dco-signoff: yes Indicates the PR's author has DCO signed all their commits. and removed dco-signoff: no Indicates the PR's author has not DCO signed all their commits. labels Feb 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/ociartifact/store.go (1)

131-135: Missing unsigned Docker v2 schema 1 media type (DockerV2Schema1MediaType).

imageMimeTypes includes DockerV2Schema1SignedMediaType but not DockerV2Schema1MediaType. If a registry returns the unsigned schema 1 variant (application/vnd.docker.distribution.manifest.v1+json), it will slip through detection and be treated as an artifact. Both legacy variants should be rejected.

Proposed fix
 	imageMimeTypes := []string{
 		specs.MediaTypeImageManifest,
 		manifest.DockerV2Schema2MediaType,
 		manifest.DockerV2Schema1SignedMediaType,
+		manifest.DockerV2Schema1MediaType,
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/store.go` around lines 131 - 135, The imageMimeTypes
slice in internal/ociartifact/store.go currently lists
manifest.DockerV2Schema1SignedMediaType but omits the unsigned variant; update
the imageMimeTypes declaration to include manifest.DockerV2Schema1MediaType
alongside DockerV2Schema1SignedMediaType (so both legacy schema1 media types are
present) to ensure unsigned schema1 manifests are detected and rejected when
processing in the relevant code paths that reference imageMimeTypes.
🧹 Nitpick comments (1)
internal/ociartifact/store.go (1)

148-153: Docker v2 schema 2 detection relies on OCI parse failure — consider adding an explicit comment.

When mimeType is DockerV2Schema2MediaType, the code enters this branch and calls manifest.OCI1FromManifest, which will fail because Docker v2 schema 2 is not a valid OCI manifest. The parse error path then correctly returns ErrIsAnImage. While this produces the right result, it's relying on a non-obvious side effect. A brief comment would prevent future maintainers from "fixing" the parse error handling and inadvertently breaking Docker schema 2 detection.

Suggested clarifying comment
 	if slices.Contains(imageMimeTypes, mimeType) && slices.Contains(configMediaTypes, mediaType) {
 		ociManifest, err := manifest.OCI1FromManifest(manifestBytes)
-		// Unable to parse an OCI manifest, assume an image
+		// Unable to parse as OCI manifest (e.g. Docker v2 schema 2), assume an image
 		if err != nil {
 			return ErrIsAnImage
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/store.go` around lines 148 - 153, The branch that calls
manifest.OCI1FromManifest when mimeType and mediaType match (using
imageMimeTypes and configMediaTypes) intentionally treats a parse error as
Docker v2 schema 2 detection and returns ErrIsAnImage; add an explicit comment
above this block explaining that when mimeType == DockerV2Schema2MediaType
OCI1FromManifest is expected to fail for Docker v2 schema 2, that the error path
is used intentionally to classify the artifact, and that ErrIsAnImage is the
deliberate outcome so future maintainers won't remove or "fix" the parse-failure
logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/ociartifact/store_test.go`:
- Line 205: Update the two test descriptions that currently read "should fail
when manifest bytes are unparseable" and "should fail when config bytes are
unparseable" to use the correct spelling "unparsable"; locate the It(...) calls
in store_test.go (the test cases containing those exact strings) and replace
"unparseable" with "unparsable" in the description arguments.

---

Duplicate comments:
In `@internal/ociartifact/store.go`:
- Around line 131-135: The imageMimeTypes slice in internal/ociartifact/store.go
currently lists manifest.DockerV2Schema1SignedMediaType but omits the unsigned
variant; update the imageMimeTypes declaration to include
manifest.DockerV2Schema1MediaType alongside DockerV2Schema1SignedMediaType (so
both legacy schema1 media types are present) to ensure unsigned schema1
manifests are detected and rejected when processing in the relevant code paths
that reference imageMimeTypes.

---

Nitpick comments:
In `@internal/ociartifact/store.go`:
- Around line 148-153: The branch that calls manifest.OCI1FromManifest when
mimeType and mediaType match (using imageMimeTypes and configMediaTypes)
intentionally treats a parse error as Docker v2 schema 2 detection and returns
ErrIsAnImage; add an explicit comment above this block explaining that when
mimeType == DockerV2Schema2MediaType OCI1FromManifest is expected to fail for
Docker v2 schema 2, that the error path is used intentionally to classify the
artifact, and that ErrIsAnImage is the deliberate outcome so future maintainers
won't remove or "fix" the parse-failure logic.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f52ee7b and 0dd1fb3.

📒 Files selected for processing (7)
  • Makefile
  • internal/ociartifact/impl.go
  • internal/ociartifact/libartifact_store.go
  • internal/ociartifact/store.go
  • internal/ociartifact/store_test.go
  • internal/ociartifact/store_test_inject.go
  • test/mocks/ociartifact/ociartifact.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/ociartifact/store_test_inject.go

Comment thread internal/ociartifact/store_test.go Outdated
@bitoku bitoku force-pushed the artifact-image-mistreat branch from 0dd1fb3 to 06b3cd4 Compare February 25, 2026 17:30
// ErrIsAnImage when the reference points to a regular container image
// rather than an OCI artifact. Multi-architecture manifest lists are
// resolved to the current platform before inspection.
func (s *Store) EnsureNotContainerImage(ctx context.Context, ref types.ImageReference) error {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@bitoku bitoku force-pushed the artifact-image-mistreat branch from 06b3cd4 to a20b4f7 Compare February 25, 2026 17:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
internal/ociartifact/store.go (2)

149-163: Logic gap: manifest.OCI1FromManifest error is silently promoted to ErrIsAnImage.

When manifest.OCI1FromManifest(manifestBytes) fails for a reason other than "not an OCI manifest" (e.g., malformed JSON, I/O error), the original error is silently discarded and ErrIsAnImage is returned. A caller receiving ErrIsAnImage has no way to distinguish a legitimate Docker schema 2 image from a transient parse failure, potentially blocking valid artifact pulls.

Consider logging the parse error at debug level before returning ErrIsAnImage to preserve observability without changing the callers' contract:

💡 Proposed improvement
-		ociManifest, err := manifest.OCI1FromManifest(manifestBytes)
-		// Unable to parse as OCI manifest (e.g. Docker v2 schema 2), assume an image
-		if err != nil {
-			return ErrIsAnImage
-		}
+		ociManifest, ociErr := manifest.OCI1FromManifest(manifestBytes)
+		// Unable to parse as OCI manifest (e.g. Docker v2 schema 2), assume an image.
+		if ociErr != nil {
+			log.Debugf(ctx, "Cannot parse as OCI manifest (assuming container image): %v", ociErr)
+			return ErrIsAnImage
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/store.go` around lines 149 - 163, The call to
manifest.OCI1FromManifest(manifestBytes) currently swallows all parse errors and
returns ErrIsAnImage; update the error path so the original parse error is
logged (e.g., via log.Debugf or similar) before returning ErrIsAnImage to
preserve observability without changing the function contract—change the block
handling err from manifest.OCI1FromManifest to log the error context (including
err and maybe manifest MIME/mediatype) then return ErrIsAnImage, referencing
manifest.OCI1FromManifest, the err variable, ociManifest, and ErrIsAnImage to
locate the code.

63-84: Double manifest fetch on the pull path.

EnsureNotContainerImage opens 1–2 ImageSource connections to fetch the manifest(s), then libartifactStore.Pull internally opens its own connection and re-fetches the same manifest. For large deployments or slow registries this adds measurable latency on every artifact pull.

If the underlying library exposes a way to pass a pre-fetched manifest or if the pull path can be restructured to share the initial fetch, consider doing so. As-is, correctness is unaffected.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/store.go` around lines 63 - 84, EnsureNotContainerImage
currently fetches manifests, then libartifactStore.Pull re-fetches them causing
double manifest fetch; modify EnsureNotContainerImage (or add a new variant
EnsureNotContainerImageWithManifest) to return the already-fetched manifest or
ImageSource/descriptor instead of discarding it, then add/use a matching
libartifactStore.Pull variant that accepts the pre-fetched manifest/ImageSource
(e.g., s.libartifactStore.PullWithManifest or Pull(ctx, artRef, opts, manifest))
and update the call in the pull path in this file to pass the returned manifest
to avoid a second fetch.
internal/ociartifact/impl.go (1)

34-42: src.Close() error silently discarded.

defer src.Close() drops the cleanup error. While benign for read-only manifest fetches (the manifest bytes are already copied), it can hide transport-level teardown failures in edge cases. Consider at least logging it at debug level to aid diagnostics.

💡 Optional improvement
-	defer src.Close()
+	defer func() {
+		if closeErr := src.Close(); closeErr != nil {
+			log.Debugf(ctx, "close image source: %v", closeErr)
+		}
+	}()

(This requires importing "github.com/cri-o/cri-o/internal/log" in this file, or you can simply ignore it if the project convention is to drop close errors from read-only sources.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/impl.go` around lines 34 - 42, GetManifestFromRef
currently defers src.Close() and discards any returned error; change the defer
to capture the Close() error and log it at debug level so transport teardown
failures aren't silently dropped. Update the defer in GetManifestFromRef to call
src.Close(), check its error, and call the project logger (import
"github.com/cri-o/cri-o/internal/log") e.g. log.Debugf("closing image source:
%v", err) when non-nil; keep the original return behavior for manifest and
errors unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/ociartifact/impl.go`:
- Around line 34-42: GetManifestFromRef currently defers src.Close() and
discards any returned error; change the defer to capture the Close() error and
log it at debug level so transport teardown failures aren't silently dropped.
Update the defer in GetManifestFromRef to call src.Close(), check its error, and
call the project logger (import "github.com/cri-o/cri-o/internal/log") e.g.
log.Debugf("closing image source: %v", err) when non-nil; keep the original
return behavior for manifest and errors unchanged.

In `@internal/ociartifact/store.go`:
- Around line 149-163: The call to manifest.OCI1FromManifest(manifestBytes)
currently swallows all parse errors and returns ErrIsAnImage; update the error
path so the original parse error is logged (e.g., via log.Debugf or similar)
before returning ErrIsAnImage to preserve observability without changing the
function contract—change the block handling err from manifest.OCI1FromManifest
to log the error context (including err and maybe manifest MIME/mediatype) then
return ErrIsAnImage, referencing manifest.OCI1FromManifest, the err variable,
ociManifest, and ErrIsAnImage to locate the code.
- Around line 63-84: EnsureNotContainerImage currently fetches manifests, then
libartifactStore.Pull re-fetches them causing double manifest fetch; modify
EnsureNotContainerImage (or add a new variant
EnsureNotContainerImageWithManifest) to return the already-fetched manifest or
ImageSource/descriptor instead of discarding it, then add/use a matching
libartifactStore.Pull variant that accepts the pre-fetched manifest/ImageSource
(e.g., s.libartifactStore.PullWithManifest or Pull(ctx, artRef, opts, manifest))
and update the call in the pull path in this file to pass the returned manifest
to avoid a second fetch.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0dd1fb3 and a20b4f7.

📒 Files selected for processing (7)
  • Makefile
  • internal/ociartifact/impl.go
  • internal/ociartifact/libartifact_store.go
  • internal/ociartifact/store.go
  • internal/ociartifact/store_test.go
  • internal/ociartifact/store_test_inject.go
  • test/mocks/ociartifact/ociartifact.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/ociartifact/libartifact_store.go
  • internal/ociartifact/store_test.go

Comment thread internal/ociartifact/store.go Outdated
Comment thread internal/ociartifact/store.go Outdated
@bitoku bitoku force-pushed the artifact-image-mistreat branch from a20b4f7 to e9a8c3e Compare February 25, 2026 20:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/ociartifact/store.go`:
- Around line 151-155: The manifest parsing branch mislabels the failure as a
Docker v2 schema 2 case and drops the actual parse error; update the inline
comment near the call to manifest.OCI1FromManifest(manifestBytes) to state that
a parsing error here indicates genuinely malformed/unparseable manifest bytes
(not that Docker v2 schema 2 was detected), and before returning ErrIsAnImage
capture and log the parse error (include the err value and useful context such
as manifest length or a brief snippet) using the existing logger in this scope
so diagnostic information is preserved; keep the return of ErrIsAnImage after
logging.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a20b4f7 and e9a8c3e.

📒 Files selected for processing (7)
  • Makefile
  • internal/ociartifact/impl.go
  • internal/ociartifact/libartifact_store.go
  • internal/ociartifact/store.go
  • internal/ociartifact/store_test.go
  • internal/ociartifact/store_test_inject.go
  • test/mocks/ociartifact/ociartifact.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/ociartifact/store_test.go
  • Makefile
  • internal/ociartifact/impl.go

Comment thread internal/ociartifact/store.go
@bitoku

bitoku commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@cri-o/cri-o-maintainers PTAL

ociManifest, err := manifest.OCI1FromManifest(manifestBytes)
// Unable to parse as OCI manifest (e.g. Docker v2 schema 2), assume an image
if err != nil {
return ErrIsAnImage

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.

A log.Debugf before returning ErrIsAnImage here would help with troubleshooting. The parse error is silently discarded, and while that's intentional for Docker v2s2, it could mask unexpected failures.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done!

func (*defaultImpl) GetManifestFromRef(ctx context.Context, ref types.ImageReference, sys *types.SystemContext, instanceDigest *digest.Digest) (manifestBlob []byte, mimeType string, err error) {
src, err := ref.NewImageSource(ctx, sys)
if err != nil {
return nil, "", fmt.Errorf("create image source: %w", err)

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.

nit: the named return values (manifestBlob, mimeType, err) aren't used for naked returns, could simplify to unnamed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is by linter unfortunately.

})

It("should return ErrIsAnImage for Docker v2 schema 2 manifest", func() {
// Given

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.

Worth adding a test case for DockerV2Schema1SignedMediaType as well to exercise the schema 1 → OCI1FromManifest failure → ErrIsAnImage path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@bitoku bitoku force-pushed the artifact-image-mistreat branch from e9a8c3e to d49115c Compare February 26, 2026 17:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
internal/ociartifact/impl.go (1)

34-42: Consider wrapping the error from GetManifest for consistency.

The error from ref.NewImageSource is wrapped with context (line 37), but src.GetManifest errors are returned unwrapped. For consistent error tracing across the codebase, consider wrapping this error as well.

Also, src.Close() error is discarded. While this is a common pattern for read-only sources, logging or propagating a close error can help diagnose resource cleanup issues.

♻️ Proposed fix
 func (*defaultImpl) GetManifestFromRef(ctx context.Context, ref types.ImageReference, sys *types.SystemContext, instanceDigest *digest.Digest) (manifestBlob []byte, mimeType string, err error) {
 	src, err := ref.NewImageSource(ctx, sys)
 	if err != nil {
 		return nil, "", fmt.Errorf("create image source: %w", err)
 	}
-	defer src.Close()
+	defer func() {
+		if closeErr := src.Close(); closeErr != nil && err == nil {
+			err = fmt.Errorf("close image source: %w", closeErr)
+		}
+	}()

-	return src.GetManifest(ctx, instanceDigest)
+	manifestBlob, mimeType, err = src.GetManifest(ctx, instanceDigest)
+	if err != nil {
+		return nil, "", fmt.Errorf("get manifest: %w", err)
+	}
+	return manifestBlob, mimeType, nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/ociartifact/impl.go` around lines 34 - 42, In GetManifestFromRef on
defaultImpl, wrap the error returned by src.GetManifest with context (similar to
how ref.NewImageSource is wrapped) so callers get consistent error traces, and
handle the deferred src.Close() error instead of discarding it — either log it
via the package logger or wrap/append it to the returned error before returning;
update references to ref.NewImageSource, src.GetManifest, and src.Close
accordingly to implement these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/ociartifact/impl.go`:
- Around line 34-42: In GetManifestFromRef on defaultImpl, wrap the error
returned by src.GetManifest with context (similar to how ref.NewImageSource is
wrapped) so callers get consistent error traces, and handle the deferred
src.Close() error instead of discarding it — either log it via the package
logger or wrap/append it to the returned error before returning; update
references to ref.NewImageSource, src.GetManifest, and src.Close accordingly to
implement these changes.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9a8c3e and d49115c.

📒 Files selected for processing (7)
  • Makefile
  • internal/ociartifact/impl.go
  • internal/ociartifact/libartifact_store.go
  • internal/ociartifact/store.go
  • internal/ociartifact/store_test.go
  • internal/ociartifact/store_test_inject.go
  • test/mocks/ociartifact/ociartifact.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/ociartifact/libartifact_store.go
  • Makefile

…tore

Fix cases where regular container images could accidentally be pulled
into the OCI artifact store by validating manifest and config media
types, including multi-arch manifest resolution and artifact type
detection. Includes unit tests and updated mocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Ayato Tokubi <atokubi@redhat.com>
@bitoku bitoku force-pushed the artifact-image-mistreat branch from d49115c to 7eb2cc1 Compare February 26, 2026 17:06

@saschagrunert saschagrunert 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.

/retest

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Feb 27, 2026
@openshift-ci

openshift-ci Bot commented Feb 27, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bitoku, saschagrunert

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Feb 27, 2026
@bitoku

bitoku commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@bitoku

bitoku commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

/cherry-pick release-1.35

@openshift-cherrypick-robot

Copy link
Copy Markdown

@bitoku: once the present PR merges, I will cherry-pick it on top of release-1.35 in a new PR and assign it to you.

Details

In response to this:

/cherry-pick release-1.35

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@bitoku

bitoku commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@openshift-merge-bot openshift-merge-bot Bot merged commit 52e0cfa into cri-o:main Feb 27, 2026
74 checks passed
@openshift-cherrypick-robot

Copy link
Copy Markdown

@bitoku: new pull request created: #9788

Details

In response to this:

/cherry-pick release-1.35

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has DCO signed all their commits. kind/bug Categorizes issue or PR as related to a bug. lgtm Indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants