Skip to content

Commit f9f82aa

Browse files
Merge branch 'main' into upgradable-bypass
2 parents 38ed542 + 1190f66 commit f9f82aa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+4797
-494
lines changed

.github/copilot-instructions.md

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ Copacetic (Copa) is a CLI tool that patches container image vulnerabilities usin
99
## Folder Structure
1010
- `pkg/patch/`: CLI commands and core patching logic
1111
- `pkg/buildkit/`: BuildKit integration and platform discovery
12-
- `pkg/pkgmgr/`: Package manager adapters (dpkg, rpm, apk)
12+
- `pkg/pkgmgr/`: OS package manager adapters (dpkg, rpm, apk)
13+
- `pkg/langmgr/`: Language (application/library) package managers (e.g. Python/pip)
1314
- `pkg/report/`: Vulnerability report parsing and scanner plugin interface
1415
- `pkg/imageloader/`: Container engine integration (Docker, Podman)
1516
- `pkg/types/`: Type definitions and configurations
1617
- `website/docs/`: Project documentation and user guides
17-
- `integration/`: Integration tests for multi-arch and single-arch scenarios
18+
- `integration/`: Integration tests for multi-arch and single-arch scenarios (OS + language flows)
1819
- `main.go`: Root CLI setup with Cobra framework
1920

2021
## Libraries and Frameworks
@@ -36,19 +37,63 @@ Copacetic (Copa) is a CLI tool that patches container image vulnerabilities usin
3637
## Key Architecture Concepts
3738
- **Patching modes**: Targeted (with vulnerability reports) or comprehensive (all available updates)
3839
- **Multi-platform support**: Handles amd64, arm64, and other architectures with QEMU emulation
39-
- **Package managers**: Debian (apt/dpkg), RHEL (yum/rpm), Alpine (apk), Azure Linux (tdnf)
40+
- **OS package managers**: Debian (apt/dpkg), RHEL family (yum/rpm/dnf/microdnf/tdnf), Alpine (apk), Azure Linux / CBL-Mariner (tdnf)
41+
- **Language (application/library) patching**: Experimental support for upgrading vulnerable application dependencies (currently Python via pip) with semantic version / patch-level controls.
4042
- **BuildKit integration**: Uses LLB operations for image building and manipulation
4143
- **Scanner plugins**: Supports custom vulnerability scanners via `customParseScanReport` interface
44+
- **Selective package types**: User can choose to patch only OS, only library, or both via `--pkg-types`.
4245

43-
## Supported Operating Systems & Package Managers
44-
- **Debian/Ubuntu**: Uses `dpkg` and `apt`
45-
- **RHEL/CentOS/Rocky/Alma/Oracle/Amazon**: Uses `rpm`, `yum`, and `dnf`
46-
- **Alpine**: Uses `apk`
47-
- **CBL-Mariner/Azure Linux**: Uses `rpm` and `tdnf`
46+
## Supported Targets
47+
### Operating Systems (OS package layer)
48+
- **Debian/Ubuntu**: `dpkg` + `apt`
49+
- **RHEL/CentOS/Rocky/Alma/Oracle/Amazon**: `rpm`, `yum`, `dnf`, `microdnf`, `tdnf` (as available)
50+
- **Alpine**: `apk`
51+
- **CBL-Mariner/Azure Linux**: `rpm` + `tdnf`
52+
53+
### Language / Application Dependencies (Experimental)
54+
- **Python**: pip-based site-packages upgrades with version validation and patch-level selection.
55+
- Controlled via `--pkg-types library` (or `os,library`) and `--library-patch-level`.
56+
- Patch levels: `patch` (default), `minor`, `major`; influences chosen fixed version when multiple are available.
57+
- Special per-package overrides supported (see `getSpecialPackagePatchLevels()` inside Python manager for curated exceptions).
4858

4959
## Key Functions
60+
61+
### CLI / Orchestration
5062
- `Patch()`: Main entry point for patching operations
5163
- `patchSingleArchImage()` / `patchMultiPlatformImage()`: Core patching logic
5264
- `DiscoverPlatformsFromReference()` / `DiscoverPlatformsFromReport()`: Platform discovery
53-
- `InstallUpdates()`: Package manager interface for applying updates
5465
- `InitializeBuildkitConfig()`: Initializes BuildKit configuration for patching operations
66+
67+
### OS Package Layer
68+
- `pkgmgr.GetPackageManager()` and concrete managers' `InstallUpdates()` methods
69+
- `GetUniqueLatestUpdates()` (OS) for deduplicating + selecting latest OS package versions
70+
71+
### Language / Library Layer
72+
- `langmgr.GetLanguageManagers()` returns appropriate language managers based on manifest content
73+
- `pythonManager.InstallUpdates()` coordinates: selecting versions, performing pip upgrades, validating results
74+
- `langmgr.GetUniqueLatestUpdates()` (libraries) similar to OS but tolerant of empty sets & patch-level filtering
75+
- `FindOptimalFixedVersionWithPatchLevel()` (Trivy parsing path) chooses best fixed version under patch-level constraint
76+
77+
### Report Parsing & Filtering
78+
- `report.TryParseScanReport()` / Trivy parser: builds unified UpdateManifest (OS + Lang)
79+
- Filtering by `--pkg-types` occurs early and again before build execution for safety.
80+
81+
### Validation & VEX
82+
- `vex.TryOutputVexDocument()` generates optional VEX documents (only if report + updates applied)
83+
84+
## Language Patching
85+
Language/library patching is gated behind `COPA_EXPERIMENTAL=1`.
86+
87+
- `--pkg-types`: comma list of `os`, `library` (default `os`). Determines which sections of the UpdateManifest are acted upon.
88+
- `--library-patch-level`: one of `patch|minor|major` (default `patch`). Sets semantic version boundary for chosen upgrade version.
89+
- Behavior when report only has library vulns:
90+
- If `--pkg-types` includes `library`, proceed (even if no OS updates). Empty OS set no longer triggers an error.
91+
- If `--pkg-types os` only, library updates are ignored (manifest language section cleared early).
92+
93+
## Library Patching Flow (Python)
94+
1. Trivy report parsed -> vulnerable Python packages aggregated with all candidate fixed versions.
95+
2. Patch level rule applied to select optimal fixed version per package (with per-package override map for exceptions).
96+
3. Upgrade executed in ephemeral tooling container (derives base Python image tag when possible; fallback tag `3-slim`).
97+
4. Post-upgrade validation via `pip freeze` subset matching: ensures requested versions actually installed.
98+
5. Failed installs or mismatches collected; errors either propagate or are logged based on `--ignore-errors`.
99+
6. Validated updates merged into final manifest fed to VEX generation.

.github/workflows/build.yml

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ permissions:
3333
jobs:
3434
unit-test:
3535
name: Unit Test
36-
runs-on: ubuntu-latest
36+
runs-on: ubuntu-latest-16-cores
3737
timeout-minutes: 5
3838
steps:
3939
- name: Harden Runner
40-
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.3.1
40+
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
4141
with:
4242
egress-policy: audit
4343
- name: Check out code
4444
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
45-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
45+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
4646
with:
4747
go-version: "1.24"
4848
check-latest: true
@@ -56,26 +56,23 @@ jobs:
5656
CODECOV_OPTS: "-coverprofile=coverage.txt -covermode=atomic"
5757
run: make test
5858
- name: Upload coverage to Codecov
59-
uses: codecov/codecov-action@fdcc8476540edceab3de004e990f80d881c6cc00 # v5.5.0
59+
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
6060
env:
6161
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
6262
build:
6363
name: Build
64-
runs-on: ${{ matrix.os }}
64+
runs-on: ubuntu-latest-16-cores
6565
timeout-minutes: 5
6666
permissions:
6767
packages: write
6868
contents: read
69-
strategy:
70-
matrix:
71-
os: [ubuntu-latest]
7269
steps:
7370
- name: Harden Runner
74-
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.3.1
71+
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
7572
with:
7673
egress-policy: audit
7774
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
78-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
75+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
7976
with:
8077
go-version: "1.24"
8178
check-latest: true
@@ -104,7 +101,7 @@ jobs:
104101
test-patch-trivy:
105102
needs: build
106103
name: Test patch with trivy ${{ matrix.buildkit_mode }}
107-
runs-on: ubuntu-latest
104+
runs-on: ubuntu-latest-16-cores
108105
timeout-minutes: 30
109106
strategy:
110107
fail-fast: false
@@ -118,7 +115,7 @@ jobs:
118115
- run: docker system prune -a -f --volumes
119116
- name: Check out code
120117
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
121-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
118+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
122119
with:
123120
go-version: "1.24"
124121
check-latest: true
@@ -150,7 +147,7 @@ jobs:
150147
test-patch-no-report:
151148
needs: build
152149
name: Test patch no report ${{ matrix.buildkit_mode }}
153-
runs-on: ubuntu-latest
150+
runs-on: ubuntu-latest-16-cores
154151
timeout-minutes: 30
155152
strategy:
156153
fail-fast: false
@@ -164,7 +161,7 @@ jobs:
164161
- run: docker system prune -a -f --volumes
165162
- name: Check out code
166163
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
167-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
164+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
168165
with:
169166
go-version: "1.24"
170167
check-latest: true
@@ -202,11 +199,11 @@ jobs:
202199
if [[ -n "${COPA_BUILDKIT_ADDR}" && "${COPA_BUILDKIT_ADDR}" == docker://* ]]; then
203200
export DOCKER_HOST="${COPA_BUILDKIT_ADDR#docker://}"
204201
fi
205-
202+
206203
docker create --name test ghcr.io/project-copacetic/copacetic/test/openssl:test-rpm-patched /bin/sh
207204
tmp="$(mktemp)"
208205
docker cp test:/etc/pki/tls/openssl.cnf "${tmp}"
209-
206+
210207
if ! grep -q foo "${tmp}"; then
211208
echo "Error: openssl.cnf content replaced" >&2
212209
rm "${tmp}"
@@ -256,11 +253,11 @@ jobs:
256253
export DOCKER_HOST="${COPA_BUILDKIT_ADDR#docker://}"
257254
trap '_cleanup' EXIT
258255
fi
259-
256+
260257
docker create --name test ghcr.io/project-copacetic/copacetic/test/openssl:test-debian-patched /bin/sh
261258
tmp="$(mktemp)"
262259
docker cp test:/etc/ssl/openssl.cnf "${tmp}"
263-
260+
264261
if ! grep -q foo "${tmp}"; then
265262
echo "Error: openssl.cnf content replaced" >&2
266263
rm "${tmp}"
@@ -274,16 +271,16 @@ jobs:
274271
test-plugin:
275272
needs: build
276273
name: Test plugin
277-
runs-on: ubuntu-latest
274+
runs-on: ubuntu-latest-16-cores
278275
timeout-minutes: 5
279276
steps:
280277
- name: Harden Runner
281-
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.3.1
278+
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
282279
with:
283280
egress-policy: audit
284281
- name: Check out code
285282
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
286-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
283+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
287284
with:
288285
go-version: "1.24"
289286
check-latest: true
@@ -321,12 +318,12 @@ jobs:
321318
test-push:
322319
needs: build
323320
name: Test push
324-
runs-on: ubuntu-latest
321+
runs-on: ubuntu-latest-16-cores
325322
timeout-minutes: 5
326323
steps:
327324
- name: Check out code
328325
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
329-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
326+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
330327
with:
331328
go-version: "1.24"
332329
check-latest: true
@@ -339,7 +336,7 @@ jobs:
339336
with:
340337
install: true
341338
- name: Install oras CLI
342-
uses: oras-project/setup-oras@8d34698a59f5ffe24821f0b48ab62a3de8b64b20 # v1.2.3
339+
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
343340
- name: Install required tools
344341
shell: bash
345342
run: .github/workflows/scripts/download-tooling.sh
@@ -364,12 +361,12 @@ jobs:
364361
with:
365362
paths: "test-results.xml"
366363

367-
364+
368365
test-patch-multiplatform:
369366
needs: build
370367
name: Test patch with multiplatform ${{ matrix.buildkit_mode }}
371-
runs-on: ubuntu-latest
372-
timeout-minutes: 30
368+
runs-on: ubuntu-latest-16-cores
369+
timeout-minutes: 40
373370
strategy:
374371
fail-fast: false
375372
matrix:
@@ -385,7 +382,7 @@ jobs:
385382
install: true
386383
platforms: linux/amd64,linux/arm/v5,linux/arm64,linux/386,linux/mips64le
387384
- name: Install oras CLI
388-
uses: oras-project/setup-oras@8d34698a59f5ffe24821f0b48ab62a3de8b64b20 # v1.2.3
385+
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
389386
- name: Set up local registry
390387
run: |
391388
docker run -d -p 5000:5000 --restart=always --name registry registry:2
@@ -409,7 +406,7 @@ jobs:
409406
- run: docker system prune -a -f --volumes
410407
- name: Check out code
411408
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
412-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
409+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
413410
with:
414411
go-version: "1.24"
415412
check-latest: true
@@ -434,15 +431,15 @@ jobs:
434431
shell: bash
435432
run: |
436433
set -eu -o pipefail
437-
434+
438435
if [[ "${{ matrix.buildkit_mode }}" == "docker" ]]; then
439436
# For docker mode, use the default docker daemon
440437
export COPA_BUILDKIT_ADDR="docker://"
441438
else
442439
# For other modes, source the corresponding script in the same shell session
443440
. .github/workflows/scripts/buildkitenvs/${{ matrix.buildkit_mode }}
444441
fi
445-
442+
446443
gotestsum --format testname --junitfile test-results.xml -- ./integration/multiarch --addr="${COPA_BUILDKIT_ADDR}" --copa="$(pwd)/copa" -timeout 0
447444
- name: Test Summary
448445
uses: test-summary/action@v2
@@ -452,8 +449,8 @@ jobs:
452449
test-patch-multiplatform-plugin:
453450
needs: build
454451
name: Test multiplatform with plugin
455-
runs-on: ubuntu-latest
456-
timeout-minutes: 30
452+
runs-on: ubuntu-latest-16-cores
453+
timeout-minutes: 50
457454
steps:
458455
- name: Change docker daemon config
459456
run: |
@@ -466,7 +463,7 @@ jobs:
466463
install: true
467464
platforms: linux/amd64,linux/arm64
468465
- name: Install oras CLI
469-
uses: oras-project/setup-oras@8d34698a59f5ffe24821f0b48ab62a3de8b64b20 # v1.2.3
466+
uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
470467
- name: Set up local registry
471468
run: |
472469
docker run -d -p 5000:5000 --restart=always --name registry registry:2
@@ -485,7 +482,7 @@ jobs:
485482
docker push localhost:5000/nginx:latest
486483
- name: Check out code
487484
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
488-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
485+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
489486
with:
490487
go-version: "1.24"
491488
check-latest: true

.github/workflows/check-deps.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ jobs:
1818
timeout-minutes: 10
1919
steps:
2020
- name: Harden the runner (Audit all outbound calls)
21-
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
21+
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
2222
with:
2323
egress-policy: audit
2424

2525
- name: Check out code into the Go module directory
2626
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
27-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
27+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
2828
with:
2929
go-version: "1.24"
3030
check-latest: true

.github/workflows/codeql.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,21 @@ jobs:
4444

4545
steps:
4646
- name: Harden Runner
47-
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.3.1
47+
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.3.1
4848
with:
4949
egress-policy: audit
5050

5151
- name: Checkout repository
5252
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
5353

54-
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
54+
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
5555
with:
5656
go-version: "1.24"
5757
check-latest: true
5858

5959
# Initializes the CodeQL tools for scanning.
6060
- name: Initialize CodeQL
61-
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5
61+
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
6262
with:
6363
languages: ${{ matrix.language }}
6464
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -72,7 +72,7 @@ jobs:
7272
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
7373
# If this step fails, then you should remove it and run the build manually (see below)
7474
- name: Autobuild
75-
uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5
75+
uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
7676

7777
# ℹ️ Command-line programs to run using the OS shell.
7878
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -85,6 +85,6 @@ jobs:
8585
# ./location_of_script_within_repo/buildscript.sh
8686

8787
- name: Perform CodeQL Analysis
88-
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.5
88+
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.29.5
8989
with:
9090
category: "/language:${{matrix.language}}"

0 commit comments

Comments
 (0)