Bug 2522941 (CVE-2026-101295)

Summary: CVE-2026-101295 oc-mirror: oc-mirror: Path traversal / arbitrary file write in operator catalog image extraction
Product: [Other] Security Response Reporter: OSIDB Bzimport <bzimport>
Component: vulnerabilityAssignee: Product Security <prodsec-ir-bot>
Status: NEW --- QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: akhatavk, aos-team-art-private, asdas, dpaolell, jdelft, jupierce, lgamliel, lgarciaa, mbiarnes, ppalepu, ppostler, prdhamdh, sbratsla, security-response-team, sghai, sidsharm, suppawar, vlaad
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: ---
Doc Text:
Path traversal / arbitrary file write in oc-mirror's operator catalog image extraction. When mirroring operator catalogs using either the legacy v1 path (--v1) or the OCI feature path (--use-oci-feature), oc-mirror extracts tar entries from catalog image layers without validating that file paths resolve within the intended destination directory.
Story Points: ---
Clone Of: Environment:
Last Closed: Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description OSIDB Bzimport 2026-08-24 19:19:32 UTC
## Zip-slip and symlink traversal in v1 catalog-image extractors

**CWEs:** CWE-22, CWE-59
**CVSS:** 7.3 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:L)
**Component:** openshift-4.18-payload/oc-mirror__release-4.18

### Description
The legacy v1 engine extracts the full operator-catalog image filesystem (catalog_images.go writeToFs) and the FBC configs directory (fbc_operators.go) by iterating tar entries from mutate.Extract(img). In catalog_images.go the destination is computed as descriptor := filepath.Join(destFolder, header.Name) for TypeDir and TypeReg with no traversal guard, and for TypeSymlink it stores symLinks[descriptor] = filepath.Join(destFolder, header.Linkname), letting both the link path and target escape destFolder. fbc_operators.go gates on strings.HasPrefix(header.Name, configsPrefix) but a name such as 'configs/../../etc/x' still has that prefix after which targetFileName := filepath.Join(extractedImageDir, header.Name) escapes. As with FIND-001 the tar stream is the layer content of a catalog image the user asked to mirror.

### Affected Locations
- `:`
- `:`

### Evidence
**pkg/cli/mirror/catalog_images.go:680-716 — unsanitised Name and Linkname**
```go
// catalog_images.go
descriptor := filepath.Join(destFolder, header.Name)
...
f, err := os.OpenFile(descriptor, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)|0755)
...
} else if header.Typeflag == tar.TypeSymlink {
    symLinks[descriptor] = filepath.Join(destFolder, header.Linkname)
}
```

**pkg/cli/mirror/fbc_operators.go:359-378 — HasPrefix does not block 'configs/../..'**
```go
if !strings.HasPrefix(header.Name, configsPrefix) || header.FileInfo().IsDir() {
    continue
}
...
targetFileName := filepath.Join(extractedImageDir, header.Name)
...
f, err := os.Create(targetFileName)
```

### Attack Pattern
Compromised or malicious catalog image mirrored with v1 oc-mirror yields arbitrary file create/overwrite (and arbitrary symlink placement) under the privileges of the mirror operator.

---
*Source: Ex-Wing security assessment, finding FIND-002*