From 145611e0d26a28b36bd61ce9fe43263ff6591294 Mon Sep 17 00:00:00 2001 From: anthonytwh Date: Tue, 1 Sep 2026 17:22:01 -0400 Subject: [PATCH 01/19] docs: add CVE remediation plan for forklift image builds Analysis of the ISS v2.12.7 scan (2026-08-28, 13,548 unique findings across 8 images) identifying five root causes and a prioritized remediation plan. Key findings: - 74% of all findings come from the libguestfs supermin appliance kernel in forklift-virt-v2v, reported grype-only and matched against upstream kernel.org ranges rather than RHEL errata - 35 of 40 Containerfiles never run a package update, so the pinned base digest is the permanent patch level (3.5 to 12 months stale) - weak dependencies pull webkit2gtk3, qt5, gstreamer and libsoup into runtime images - four different Go toolchains across the image set - must-gather and console-plugin are consumed prebuilt and cannot be patched --- docs/security/cve-remediation-plan.md | 194 ++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 docs/security/cve-remediation-plan.md diff --git a/docs/security/cve-remediation-plan.md b/docs/security/cve-remediation-plan.md new file mode 100644 index 0000000000..509303eb1d --- /dev/null +++ b/docs/security/cve-remediation-plan.md @@ -0,0 +1,194 @@ +# Forklift Image CVE Remediation Plan + +Basis: ISS scan `forklift-latest / v2.12.7 / results_image_all_2026-08-28::21:29:57:055` +(`gs://iss-upload/forklift-latest/v2.12.7/.../results/raw_results.csv`), 8 images, +scanners twistcli + trivy + grype. + +## 1. What the scan actually says + +| Metric | Count | +|---|---| +| Raw CSV rows | 14,550 | +| Unique `(image, CVE, package)` findings | 13,548 | +| Critical + High (unique) | 3,043 | +| Findings reported by **grype only** (no corroboration) | 10,306 (76%) | + +Per image (unique findings): + +| Image | Total | Crit | High | Built in this repo? | +|---|---|---|---|---| +| forklift-virt-v2v | 10,681 | 34 | 1,756 | yes | +| forklift-must-gather | 990 | 1 | 277 | **no** | +| forklift-console-plugin | 567 | 0 | 78 | **no** | +| ovirt-populator | 404 | 0 | 32 | yes | +| forklift-validation | 284 | 0 | 66 | yes | +| forklift-controller | 249 | 0 | 49 | yes | +| forklift-operator | 195 | 0 | 29 | yes | +| populator-controller | 178 | 0 | 40 | yes | + +## 2. Root cause A — the appliance kernel is 74% of the entire CVE count + +`linux-kernel` + `kernel-core` inside `forklift-virt-v2v` account for **9,963 of 13,548 +findings (74%)** and **2,354 of 3,043 crit/high (77%)**. Both are grype-only. + +* **`linux-kernel` — 6,241 findings, 100% grype, 100% NVD URLs.** This is grype's + kernel-binary cataloger fingerprinting the raw `vmlinuz` inside the libguestfs supermin + appliance (`/usr/lib64/guestfs/appliance`, built in `build/virt-v2v/Containerfile:15-19`). + It reports version `5.14.0-741.el9.x86_64` and compares it against **upstream kernel.org + ranges** (`fixed_version` values look like `5.10.258, 5.15.209, 6.1.175, 6.6.141, ...`). + RHEL backports fixes without moving the upstream version, so the comparison is structurally + wrong — this set is very close to 100% false positive. +* **`kernel-core` — 3,722 findings**, the real RPM, matched against RHSA data. + **3,127 (84%) are Red Hat "No fix available"** (won't-fix / out of support scope). +* Exploitability context: this kernel never boots as the container's kernel. It is only + executed inside the short-lived qemu/supermin VM that libguestfs starts to inspect guest + disks. Container workload isolation is unaffected by it. + +**The kernel is also hard-pinned by exact NEVRA**, so no automation can ever move it: + +```yaml +# .konflux/virt-v2v/runtime/rpms.in.yaml +packages: + - kernel-6.12.0-211.16.1.el10_2 + - kernel-modules-core-6.12.0-211.16.1.el10_2 + - kernel-core-6.12.0-211.16.1.el10_2 + - kernel-modules-6.12.0-211.16.1.el10_2 +``` + +Renovate's `refresh-rpm-lockfiles` preset re-resolves the lock, but a literal version +constraint pins the result. It cannot advance. + +## 3. Root cause B — nothing is ever patched after the base image + +**35 of 40 Containerfiles never run any package update.** Only `forklift-api`, +`ova-provider-server`, `ova-proxy`, `populator-controller`, `vsphere-copy-offload-populator` +(upstream variants) and the two `virt-v2v` downstream files do. For everything else the +pinned base digest *is* the patch level, permanently. + +Base pin ages at scan date (2026-08-28): + +| Pin | Date | Stale by | +|---|---|---| +| `ubi9-minimal:9.7-1778562320` (most images) | 2026-05-12 | 3.5 months | +| `ubi9:9.6-1760340943` (`virt-v2v-rhel9`) | 2025-10-13 | 10.5 months | +| `ubi9/nginx-126:1-1756959223` (`cli-download` downstream) | 2025-09-04 | ~12 months | + +Signature of the problem: the *same* crit/high findings in `libevent`, `curl`, `openssl`, +`sqlite` and `gnutls` appear in **all 8 images** — they are inherited from a common stale +base, not introduced by any component. + +## 4. Root cause C — weak dependencies drag a desktop stack into runtime images + +`install_weak_deps=False` is used **only** in the `virt-v2v` appliance and builder stages. +No runtime stage in any image sets it (`build/virt-v2v/Containerfile:45`, +`build/virt-v2v/Containerfile:60`). Result — confirmed present in the hermetic runtime +lockfile `.konflux/virt-v2v-rhel9/runtime/rpms.lock.yaml` (298 RPMs): + +`webkit2gtk3-jsc`, `libsoup`, `dhcp-client`, `dnsmasq`, `qt5-srpm-macros`, plus `vim`, +`gdb`, `gstreamer1-plugins-base`, `libtiff` across the other images. + +A browser engine (`webkit2gtk3`) is **93 of the 473 fixable crit/high findings** — the single +largest actionable package in the whole scan — and it is in `forklift-must-gather` (106) and +`forklift-virt-v2v` (30), neither of which renders anything. + +## 5. Root cause D — Go toolchain and module drift + +| Image | Go stdlib seen | Notable module | +|---|---|---| +| forklift-must-gather | **1.22.5** | `golang.org/x/crypto` **v0.21.0**, `containerd` v1.7.11 | +| forklift-validation | 1.24.6 | `golang.org/x/crypto` v0.40.0 | +| forklift-controller / populator-controller | 1.25.9 | `golang.org/x/crypto` v0.50.0 | +| forklift-operator | 1.26.3 | — | + +Four different toolchains across one product. Go accounts for 200 of the 473 fixable +crit/high (103 module + 97 stdlib). + +## 6. Root cause E — two images are not ours to patch + +`forklift-must-gather` and `forklift-console-plugin` are consumed as prebuilt +`quay.io/kubev2v/*` images (`Makefile:145-146`); no Containerfile exists here. Together they +are **1,557 findings / 377 crit+high** — the largest actionable block after the appliance +kernel — and Tigera currently has no way to rebuild them. + +## 7. The actual actionable set + +Excluding the appliance kernel: + +| | Count | +|---|---| +| Findings | 3,585 | +| Crit + High | **689** | +| — fix published upstream (rebuild/bump closes it) | **473** | +| — Red Hat "No fix available" (needs VEX, not a code change) | 216 | + +Of the 473 fixable: 273 RPM, 103 Go module, 97 Go stdlib. + +## 8. Remediation + +Ordered by findings removed per unit of effort. + +### P0 — Scanner truth (removes ~74% of the count, zero build risk) + +* Author a VEX/OpenVEX document for `linux-kernel` + `kernel-core` in `forklift-virt-v2v` + with status `not_affected`, justification `vulnerable_code_not_in_execute_path` — the + appliance kernel only executes inside the guest-inspection VM. +* Disable grype's `linux-kernel` binary cataloger for the appliance path so the vmlinuz + is not matched against upstream kernel.org ranges. +* Feed the same list to ISS via `iss-cli --cve-ignore`, sourced from the VEX doc (single + source of truth, reviewed in-repo — not a hand-maintained ignore list). +* Effect: 13,548 → ~3,585 findings; 3,043 → 689 crit/high. + +### P1 — Make the base image move + +* Re-pin every base to a current digest, and add a Renovate rule that advances base digests + on a weekly cadence (today `dockerfile` updates are grouped but there is no digest-refresh + schedule for stale-but-same-tag rebuilds). +* Remove the four literal `kernel-*-6.12.0-211.16.1.el10_2` pins from + `.konflux/virt-v2v/runtime/rpms.in.yaml`; use unversioned `kernel-core` / `kernel-modules-core` + so `refresh-rpm-lockfiles` can advance the z-stream. +* For the non-hermetic upstream Containerfiles, add `dnf -y update` / `microdnf -y update` as + the first RUN of each runtime stage. For hermetic Konflux builds this is a no-op by design — + there the lever is the lockfile refresh above, not a network `dnf`. +* Effect: clears the cross-image `libevent` / `curl` / `openssl` / `sqlite` / `gnutls` block. + +### P2 — Shrink the runtime surface + +* Add `--setopt=install_weak_deps=False` to every runtime-stage install, matching what the + appliance stage already does. +* Set the equivalent cachi2 option in each `rpms.in.yaml` so the hermetic lock is resolved + without weak deps, then regenerate all `rpms.lock.yaml`. +* Explicitly exclude `webkit2gtk3*`, `qt5*`, `gstreamer1*`, `libsoup`, `dhcp*`, `dnsmasq`, + `vim*`, `gdb` from runtime locks. +* Move `virt-v2v` runtime from full `ubi9`/`ubi10` to `ubi9-minimal` where the RPM set allows. +* Effect: −93 crit/high from `webkit2gtk3` alone, plus the gstreamer/libsoup/vim tail. + +### P3 — Unify Go + +* Single toolchain (1.26.x) across every builder stage; rebuild all binaries so stdlib + findings collapse to one version. +* Bump `golang.org/x/crypto`, `golang.org/x/net`, `containerd` in `go.mod` + `vendor/`. +* Effect: −200 crit/high. + +### P4 — Take ownership of the two external images + +* Either fork and rebuild `must-gather` and `console-plugin` under Tigera build pipelines, + or drop `must-gather` from the Tigera bundle if it is not required for the Calico Enterprise + L2 migration workflow. It is the worst actionable offender in the scan (299 crit/high) and + is a diagnostics-only convenience. + +### P5 — Gate it so it cannot regress + +* Add an `iss-cli` step to the Konflux/Tekton push pipelines: + `--scan-type image --severity high --cve-ignore `, failing the build on new + crit/high that are not VEX-justified. +* Keep `renovate.json` `cve-automerge-high` but extend `matchBaseBranches` to the Tigera + release branches once they exist. + +## 9. Expected end state + +| Stage | Crit + High | +|---|---| +| Today | 3,043 | +| After P0 (VEX appliance kernel) | 689 | +| After P1–P3 (rebuild, trim, Go bump) | ~216 | +| Residual | 216, all Red Hat "No fix available" — VEX-documented, no code change possible | From 1b4ed90dfeb85fb1e4907de9a106ff2246622244 Mon Sep 17 00:00:00 2001 From: anthonytwh Date: Tue, 1 Sep 2026 17:59:38 -0400 Subject: [PATCH 02/19] Address image CVE burden: float bases, trim runtime, unify Go toolchain Acts on P1-P3 of docs/security/cve-remediation-plan.md, driven by the ISS v2.12.7 scan (13,548 unique findings, 3,043 crit/high across 8 images). P1 - base images now move again. Every upstream base went from a frozen build-timestamp tag to a floating tag (ubi9-minimal:9.7-1778562320 -> ubi9-minimal:latest, and equivalents for ubi9, ubi8, nginx-126). The pins were 3.5 to 12 months stale and, since 35 of 40 Containerfiles ran no package update, the pinned digest was the permanent patch level. Also removed the four literal kernel NEVRA pins from .konflux/virt-v2v/runtime/rpms.in.yaml so refresh-rpm-lockfiles can advance the appliance kernel instead of holding it frozen forever. Trade-off: builds are no longer bit-reproducible from the Containerfile alone. The registry.redhat.io bases in the -downstream files are left pinned; those are Konflux/Mintmaker-managed. P2 - runtime trim plus update. All 40 install sites now disable weak dependencies, and the non-hermetic runtime stages update first. Measured on centos:stream9, virt-v2v goes from 324 packages / 2.2 GB to 297 / 1.4 GB with nothing added, dropping webkit2gtk3-jsc, libproxy-webkitgtk4, linux-firmware, nfs-utils, rpcbind and friends. passt and libvirt-daemon-config-network are now installed explicitly, because libguestfs only recommends them and the image runs LIBGUESTFS_BACKEND=libvirt:qemu:///session, which breaks without them. The nbdkit metapackage is dropped safely: virt-v2v hard-requires every plugin it actually uses. Note microdnf accepts --setopt=install_weak_deps=0 but rejects =False, so all sites were normalised to =0. P3 - one Go toolchain. All 32 builder stages move to go-toolset:1.26; the shipped images previously spanned Go 1.22.5, 1.24.6, 1.25.9 and 1.26.3. Module go directives unified on 1.26, and the CVE-bearing dependencies bumped: x/crypto v0.50.0 -> v0.55.0 and x/net v0.53.0 -> v0.58.0 in the main module, x/crypto v0.40.0 -> v0.55.0, x/net v0.42.0 -> v0.58.0 and containerd/v2 v2.1.4 -> v2.3.4 in the validation module. vendor/ regenerated. build/validation/Containerfile no longer curls a prebuilt opa binary from GitHub releases with no checksum. It builds OPA from source with the repo's own toolchain, matching what Containerfile-downstream already did. That curl was what pinned the validation image to Go 1.22.5, the oldest toolchain in the product. Verified: forklift-controller, forklift-api, validation, ova-provider-server and populator-controller all build; the shipped opa binary reports go1.26.7 (Red Hat 1.26.7-1.el9_8) X:strictfipsruntime and links containerd/v2 v2.3.4, x/crypto v0.55.0, x/net v0.58.0; weak-dependency deltas measured in a real container rather than assumed. Not verified locally: virt-v2v and ovirt-populator need a Red Hat subscription, and the Konflux hermetic builds need rpms.lock.yaml regeneration with entitled repos. --- .konflux/forklift-operator-bundle/go.mod | 2 +- .konflux/validation/go.mod | 64 +- .konflux/validation/go.sum | 64 + .konflux/virt-v2v/runtime/rpms.in.yaml | 11 +- build/deep-inspection-rhel9/Containerfile | 5 +- .../Containerfile-downstream | 4 +- build/deep-inspection/Containerfile | 5 +- .../deep-inspection/Containerfile-downstream | 4 +- build/forklift-api/Containerfile | 11 +- build/forklift-api/Containerfile-downstream | 4 +- build/forklift-cli-download/Containerfile | 4 +- .../Containerfile-downstream | 2 +- build/forklift-controller/Containerfile | 8 +- .../Containerfile-downstream | 4 +- build/forklift-operator-index/Containerfile | 6 +- build/hyperv-provider-server/Containerfile | 8 +- .../Containerfile-downstream | 4 +- build/openstack-populator/Containerfile | 8 +- .../Containerfile-downstream | 4 +- build/ova-provider-server/Containerfile | 11 +- .../Containerfile-downstream | 4 +- build/ova-proxy/Containerfile | 8 +- build/ova-proxy/Containerfile-downstream | 2 +- build/ovirt-populator/Containerfile | 8 +- .../ovirt-populator/Containerfile-downstream | 4 +- build/ovirt-populator/Containerfile-upstream | 6 +- build/populator-controller/Containerfile | 11 +- .../Containerfile-downstream | 4 +- build/validation/Containerfile | 25 +- build/validation/Containerfile-downstream | 2 +- build/virt-v2v-rhel9/Containerfile | 22 +- build/virt-v2v-rhel9/Containerfile-downstream | 12 +- build/virt-v2v/Containerfile | 22 +- build/virt-v2v/Containerfile-downstream | 14 +- .../Containerfile-downstream-fssupport | 6 +- build/virt-v2v/Containerfile-upstream | 7 +- build/virt-v2v/Containerfile-upstream-fedora | 7 +- build/virt-v2v/Containerfile-upstream-xfs | 7 +- .../Containerfile | 8 +- .../Containerfile-downstream | 2 +- docs/security/cve-remediation-plan.md | 204 +- go.mod | 18 +- go.sum | 32 +- vendor/golang.org/x/crypto/cryptobyte/asn1.go | 825 +++ .../x/crypto/cryptobyte/asn1/asn1.go | 46 + .../golang.org/x/crypto/cryptobyte/builder.go | 350 ++ .../golang.org/x/crypto/cryptobyte/string.go | 183 + .../x/crypto/internal/poly1305/mac_noasm.go | 2 +- .../x/crypto/internal/poly1305/sum_asm.go | 2 +- .../x/crypto/internal/poly1305/sum_riscv64.s | 158 + vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go | 73 +- vendor/golang.org/x/crypto/ssh/certs.go | 33 +- vendor/golang.org/x/crypto/ssh/channel.go | 137 +- vendor/golang.org/x/crypto/ssh/cipher.go | 2 +- vendor/golang.org/x/crypto/ssh/client.go | 85 + vendor/golang.org/x/crypto/ssh/client_auth.go | 66 +- vendor/golang.org/x/crypto/ssh/common.go | 8 +- vendor/golang.org/x/crypto/ssh/connection.go | 12 +- vendor/golang.org/x/crypto/ssh/control.go | 155 + vendor/golang.org/x/crypto/ssh/handshake.go | 2 +- vendor/golang.org/x/crypto/ssh/kex.go | 75 +- vendor/golang.org/x/crypto/ssh/keys.go | 136 +- vendor/golang.org/x/crypto/ssh/messages.go | 2 +- vendor/golang.org/x/crypto/ssh/mux.go | 52 +- vendor/golang.org/x/crypto/ssh/server.go | 230 +- vendor/golang.org/x/crypto/ssh/session.go | 3 + vendor/golang.org/x/crypto/ssh/ssh_gss.go | 16 +- vendor/golang.org/x/crypto/ssh/streamlocal.go | 2 + vendor/golang.org/x/crypto/ssh/tcpip.go | 2 + vendor/golang.org/x/net/html/entity.go | 5 +- vendor/golang.org/x/net/html/escape.go | 140 +- vendor/golang.org/x/net/html/foreign.go | 2 +- vendor/golang.org/x/net/html/parse.go | 287 +- vendor/golang.org/x/net/html/render.go | 35 +- vendor/golang.org/x/net/html/token.go | 52 +- vendor/golang.org/x/net/http2/README.md | 19 + .../x/net/http2/client_conn_pool.go | 14 +- vendor/golang.org/x/net/http2/clientconn.go | 57 + vendor/golang.org/x/net/http2/config.go | 2 + vendor/golang.org/x/net/http2/h2c/h2c.go | 28 +- vendor/golang.org/x/net/http2/hpack/encode.go | 1 - vendor/golang.org/x/net/http2/hpack/hpack.go | 1 - vendor/golang.org/x/net/http2/hpack/tables.go | 58 +- vendor/golang.org/x/net/http2/http2.go | 2 +- vendor/golang.org/x/net/http2/server.go | 214 +- .../golang.org/x/net/http2/server_common.go | 221 + vendor/golang.org/x/net/http2/server_wrap.go | 217 + vendor/golang.org/x/net/http2/transport.go | 453 +- .../x/net/http2/transport_common.go | 447 ++ .../golang.org/x/net/http2/transport_wrap.go | 407 ++ vendor/golang.org/x/net/http2/writesched.go | 46 +- .../x/net/http2/writesched_common.go | 90 + .../net/http2/writesched_priority_rfc7540.go | 43 +- .../net/http2/writesched_priority_rfc9218.go | 2 + .../x/net/http2/writesched_random.go | 2 + .../x/net/http2/writesched_roundrobin.go | 2 + vendor/golang.org/x/net/idna/go118.go | 13 - .../x/net/idna/{idna10.0.0.go => idna.go} | 185 +- vendor/golang.org/x/net/idna/idna9.0.0.go | 717 --- vendor/golang.org/x/net/idna/pre_go118.go | 11 - vendor/golang.org/x/net/idna/punycode.go | 5 +- vendor/golang.org/x/net/idna/tables10.0.0.go | 4559 -------------- vendor/golang.org/x/net/idna/tables11.0.0.go | 4653 --------------- vendor/golang.org/x/net/idna/tables12.0.0.go | 4733 --------------- vendor/golang.org/x/net/idna/tables13.0.0.go | 4959 --------------- vendor/golang.org/x/net/idna/tables15.0.0.go | 2 +- vendor/golang.org/x/net/idna/tables17.0.0.go | 5302 +++++++++++++++++ vendor/golang.org/x/net/idna/tables9.0.0.go | 4486 -------------- vendor/golang.org/x/net/idna/trie12.0.0.go | 30 - vendor/golang.org/x/net/idna/trie13.0.0.go | 30 - .../x/net/internal/httpcommon/request.go | 8 + vendor/golang.org/x/sync/errgroup/errgroup.go | 2 +- vendor/golang.org/x/sys/cpu/cpu.go | 19 +- .../golang.org/x/sys/cpu/cpu_linux_riscv64.go | 2 + vendor/golang.org/x/sys/cpu/cpu_loong64.go | 16 +- .../golang.org/x/sys/cpu/cpu_other_arm64.go | 2 +- vendor/golang.org/x/sys/cpu/cpu_riscv64.go | 1 + vendor/golang.org/x/sys/cpu/cpu_windows.go | 26 + .../golang.org/x/sys/cpu/cpu_windows_arm64.go | 38 + vendor/golang.org/x/sys/cpu/parse.go | 62 +- vendor/golang.org/x/sys/cpu/zcpu_windows.go | 48 + .../golang.org/x/sys/unix/affinity_linux.go | 128 +- vendor/golang.org/x/sys/unix/mkall.sh | 2 +- vendor/golang.org/x/sys/unix/mkerrors.sh | 3 + vendor/golang.org/x/sys/unix/readv_unix.go | 103 + .../golang.org/x/sys/unix/syscall_darwin.go | 89 - vendor/golang.org/x/sys/unix/syscall_linux.go | 115 +- .../x/sys/unix/syscall_linux_386.go | 1 - .../x/sys/unix/syscall_linux_amd64.go | 1 - .../x/sys/unix/syscall_linux_arm.go | 4 +- .../x/sys/unix/syscall_linux_arm64.go | 4 +- .../x/sys/unix/syscall_linux_loong64.go | 4 +- .../x/sys/unix/syscall_linux_mips64x.go | 1 - .../x/sys/unix/syscall_linux_mipsx.go | 1 - .../x/sys/unix/syscall_linux_ppc.go | 1 - .../x/sys/unix/syscall_linux_ppc64x.go | 1 - .../x/sys/unix/syscall_linux_riscv64.go | 4 +- .../x/sys/unix/syscall_linux_s390x.go | 1 - .../x/sys/unix/syscall_linux_sparc64.go | 1 - .../golang.org/x/sys/unix/syscall_openbsd.go | 4 + vendor/golang.org/x/sys/unix/zerrors_linux.go | 69 +- .../x/sys/unix/zerrors_linux_386.go | 7 +- .../x/sys/unix/zerrors_linux_amd64.go | 7 +- .../x/sys/unix/zerrors_linux_arm.go | 7 +- .../x/sys/unix/zerrors_linux_arm64.go | 7 +- .../x/sys/unix/zerrors_linux_loong64.go | 7 +- .../x/sys/unix/zerrors_linux_mips.go | 7 +- .../x/sys/unix/zerrors_linux_mips64.go | 7 +- .../x/sys/unix/zerrors_linux_mips64le.go | 7 +- .../x/sys/unix/zerrors_linux_mipsle.go | 7 +- .../x/sys/unix/zerrors_linux_ppc.go | 7 +- .../x/sys/unix/zerrors_linux_ppc64.go | 7 +- .../x/sys/unix/zerrors_linux_ppc64le.go | 7 +- .../x/sys/unix/zerrors_linux_riscv64.go | 1114 ++-- .../x/sys/unix/zerrors_linux_s390x.go | 7 +- .../x/sys/unix/zerrors_linux_sparc64.go | 7 +- .../golang.org/x/sys/unix/zsyscall_linux.go | 29 +- .../x/sys/unix/zsyscall_linux_386.go | 17 - .../x/sys/unix/zsyscall_linux_amd64.go | 17 - .../x/sys/unix/zsyscall_linux_arm.go | 17 - .../x/sys/unix/zsyscall_linux_arm64.go | 17 - .../x/sys/unix/zsyscall_linux_loong64.go | 17 - .../x/sys/unix/zsyscall_linux_mips.go | 17 - .../x/sys/unix/zsyscall_linux_mips64.go | 17 - .../x/sys/unix/zsyscall_linux_mips64le.go | 17 - .../x/sys/unix/zsyscall_linux_mipsle.go | 17 - .../x/sys/unix/zsyscall_linux_ppc.go | 17 - .../x/sys/unix/zsyscall_linux_ppc64.go | 17 - .../x/sys/unix/zsyscall_linux_ppc64le.go | 17 - .../x/sys/unix/zsyscall_linux_riscv64.go | 17 - .../x/sys/unix/zsyscall_linux_s390x.go | 17 - .../x/sys/unix/zsyscall_linux_sparc64.go | 17 - .../x/sys/unix/zsyscall_openbsd_386.go | 84 + .../x/sys/unix/zsyscall_openbsd_386.s | 20 + .../x/sys/unix/zsyscall_openbsd_amd64.go | 84 + .../x/sys/unix/zsyscall_openbsd_amd64.s | 20 + .../x/sys/unix/zsyscall_openbsd_arm.go | 84 + .../x/sys/unix/zsyscall_openbsd_arm.s | 20 + .../x/sys/unix/zsyscall_openbsd_arm64.go | 84 + .../x/sys/unix/zsyscall_openbsd_arm64.s | 20 + .../x/sys/unix/zsyscall_openbsd_mips64.go | 84 + .../x/sys/unix/zsyscall_openbsd_mips64.s | 20 + .../x/sys/unix/zsyscall_openbsd_ppc64.go | 84 + .../x/sys/unix/zsyscall_openbsd_ppc64.s | 24 + .../x/sys/unix/zsyscall_openbsd_riscv64.go | 84 + .../x/sys/unix/zsyscall_openbsd_riscv64.s | 20 + .../x/sys/unix/zsysnum_linux_386.go | 4 + .../x/sys/unix/zsysnum_linux_amd64.go | 5 + .../x/sys/unix/zsysnum_linux_arm.go | 4 + .../x/sys/unix/zsysnum_linux_arm64.go | 4 + .../x/sys/unix/zsysnum_linux_loong64.go | 5 + .../x/sys/unix/zsysnum_linux_mips.go | 4 + .../x/sys/unix/zsysnum_linux_mips64.go | 4 + .../x/sys/unix/zsysnum_linux_mips64le.go | 4 + .../x/sys/unix/zsysnum_linux_mipsle.go | 4 + .../x/sys/unix/zsysnum_linux_ppc.go | 4 + .../x/sys/unix/zsysnum_linux_ppc64.go | 4 + .../x/sys/unix/zsysnum_linux_ppc64le.go | 4 + .../x/sys/unix/zsysnum_linux_riscv64.go | 4 + .../x/sys/unix/zsysnum_linux_s390x.go | 4 + .../x/sys/unix/zsysnum_linux_sparc64.go | 5 + vendor/golang.org/x/sys/unix/ztypes_linux.go | 123 +- .../golang.org/x/sys/unix/ztypes_linux_386.go | 12 + .../x/sys/unix/ztypes_linux_amd64.go | 12 + .../golang.org/x/sys/unix/ztypes_linux_arm.go | 12 + .../x/sys/unix/ztypes_linux_arm64.go | 12 + .../x/sys/unix/ztypes_linux_loong64.go | 12 + .../x/sys/unix/ztypes_linux_mips.go | 12 + .../x/sys/unix/ztypes_linux_mips64.go | 12 + .../x/sys/unix/ztypes_linux_mips64le.go | 12 + .../x/sys/unix/ztypes_linux_mipsle.go | 12 + .../golang.org/x/sys/unix/ztypes_linux_ppc.go | 12 + .../x/sys/unix/ztypes_linux_ppc64.go | 12 + .../x/sys/unix/ztypes_linux_ppc64le.go | 12 + .../x/sys/unix/ztypes_linux_riscv64.go | 12 + .../x/sys/unix/ztypes_linux_s390x.go | 12 + .../x/sys/unix/ztypes_linux_sparc64.go | 12 + .../x/sys/windows/security_windows.go | 36 + .../x/sys/windows/syscall_windows.go | 24 +- .../golang.org/x/sys/windows/types_windows.go | 34 +- .../x/sys/windows/zsyscall_windows.go | 71 + .../x/text/unicode/norm/forminfo.go | 9 +- vendor/golang.org/x/text/unicode/norm/iter.go | 8 +- .../x/text/unicode/norm/normalize.go | 20 +- vendor/golang.org/x/tools/go/ast/edge/edge.go | 24 +- .../golang.org/x/tools/go/packages/golist.go | 17 +- .../x/tools/go/packages/packages.go | 30 +- .../x/tools/go/types/objectpath/objectpath.go | 570 +- .../x/tools/internal/gcimporter/iexport.go | 3 + .../x/tools/internal/gcimporter/iimport.go | 26 +- .../x/tools/internal/gcimporter/ureader.go | 42 +- .../x/tools/internal/gocommand/version.go | 5 +- .../x/tools/internal/stdlib/deps.go | 655 +- .../x/tools/internal/stdlib/manifest.go | 289 +- .../x/tools/internal/typesinternal/element.go | 8 +- .../x/tools/internal/typesinternal/types.go | 28 + .../tools/internal/typesinternal/zerovalue.go | 16 +- vendor/modules.txt | 18 +- 238 files changed, 13766 insertions(+), 27379 deletions(-) create mode 100644 vendor/golang.org/x/crypto/cryptobyte/asn1.go create mode 100644 vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go create mode 100644 vendor/golang.org/x/crypto/cryptobyte/builder.go create mode 100644 vendor/golang.org/x/crypto/cryptobyte/string.go create mode 100644 vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s create mode 100644 vendor/golang.org/x/crypto/ssh/control.go create mode 100644 vendor/golang.org/x/net/http2/README.md create mode 100644 vendor/golang.org/x/net/http2/clientconn.go create mode 100644 vendor/golang.org/x/net/http2/server_common.go create mode 100644 vendor/golang.org/x/net/http2/server_wrap.go create mode 100644 vendor/golang.org/x/net/http2/transport_common.go create mode 100644 vendor/golang.org/x/net/http2/transport_wrap.go create mode 100644 vendor/golang.org/x/net/http2/writesched_common.go delete mode 100644 vendor/golang.org/x/net/idna/go118.go rename vendor/golang.org/x/net/idna/{idna10.0.0.go => idna.go} (80%) delete mode 100644 vendor/golang.org/x/net/idna/idna9.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/pre_go118.go delete mode 100644 vendor/golang.org/x/net/idna/tables10.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables11.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables12.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables13.0.0.go create mode 100644 vendor/golang.org/x/net/idna/tables17.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/tables9.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/trie12.0.0.go delete mode 100644 vendor/golang.org/x/net/idna/trie13.0.0.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_windows.go create mode 100644 vendor/golang.org/x/sys/cpu/cpu_windows_arm64.go create mode 100644 vendor/golang.org/x/sys/cpu/zcpu_windows.go create mode 100644 vendor/golang.org/x/sys/unix/readv_unix.go diff --git a/.konflux/forklift-operator-bundle/go.mod b/.konflux/forklift-operator-bundle/go.mod index 25aca49115..97c977a4d7 100644 --- a/.konflux/forklift-operator-bundle/go.mod +++ b/.konflux/forklift-operator-bundle/go.mod @@ -1,6 +1,6 @@ module github.com/kubev2v/forklift -go 1.24.4 +go 1.26.0 require sigs.k8s.io/kustomize/kustomize/v5 v5.4.3 diff --git a/.konflux/validation/go.mod b/.konflux/validation/go.mod index cf1ec30dfb..7b810a3cda 100644 --- a/.konflux/validation/go.mod +++ b/.konflux/validation/go.mod @@ -1,6 +1,6 @@ module github.com/kubev2v/forklift -go 1.24.6 +go 1.26.3 require github.com/open-policy-agent/opa v1.8.0 @@ -8,12 +8,12 @@ require ( github.com/agnivade/levenshtein v1.2.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 // indirect - github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/containerd/containerd/v2 v2.1.4 // indirect + github.com/containerd/containerd/v2 v2.3.4 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v1.0.0-rc.1 // indirect + github.com/containerd/platforms v1.0.0-rc.4 // indirect github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dgraph-io/badger/v4 v4.8.0 // indirect @@ -30,9 +30,9 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.18.5 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc/v3 v3.0.0 // indirect @@ -45,18 +45,18 @@ require ( github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/pelletier/go-toml/v2 v2.3.0 // indirect github.com/peterh/liner v1.2.2 // indirect - github.com/prometheus/client_golang v1.23.0 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/sergi/go-diff v1.4.0 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect @@ -70,30 +70,30 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect - go.opentelemetry.io/otel v1.37.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect - go.opentelemetry.io/otel/metric v1.37.0 // indirect - go.opentelemetry.io/otel/sdk v1.37.0 // indirect - go.opentelemetry.io/otel/trace v1.37.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/sdk v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.9.0 // indirect - go.yaml.in/yaml/v2 v2.4.2 // indirect - golang.org/x/crypto v0.40.0 // indirect - golang.org/x/net v0.42.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.34.0 // indirect - golang.org/x/text v0.27.0 // indirect - golang.org/x/time v0.12.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect - google.golang.org/grpc v1.74.2 // indirect - google.golang.org/protobuf v1.36.6 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/crypto v0.55.0 // indirect + golang.org/x/net v0.58.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.41.0 // indirect + golang.org/x/time v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect + google.golang.org/grpc v1.80.0 // indirect + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/yaml.v3 v3.0.1 // indirect oras.land/oras-go/v2 v2.6.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/.konflux/validation/go.sum b/.konflux/validation/go.sum index f95e50b45c..21d9143575 100644 --- a/.konflux/validation/go.sum +++ b/.konflux/validation/go.sum @@ -6,16 +6,24 @@ github.com/bytecodealliance/wasmtime-go/v3 v3.0.2 h1:3uZCA/BLTIu+DqCfguByNMJa2HV github.com/bytecodealliance/wasmtime-go/v3 v3.0.2/go.mod h1:RnUjnIXxEJcL6BgCvNyzCCRzZcxCgsZCi+RNlvYor5Q= github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/containerd/containerd/v2 v2.1.4 h1:/hXWjiSFd6ftrBOBGfAZ6T30LJcx1dBjdKEeI8xucKQ= github.com/containerd/containerd/v2 v2.1.4/go.mod h1:8C5QV9djwsYDNhxfTCFjWtTBZrqjditQ4/ghHSYjnHM= +github.com/containerd/containerd/v2 v2.3.4 h1:c2PJo/9UGVdiiw8SwrxuLxWGY+9b3jQ6Xp9zntneIvI= +github.com/containerd/containerd/v2 v2.3.4/go.mod h1:a30D8fWZJ1Uzx/2WpjLbLsxBkq9He41pe8ENW+QZ3LY= github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0Q5b3op97T4= +github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A= +github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y= +github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -54,12 +62,16 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -93,17 +105,25 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM= +github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw= github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= @@ -117,6 +137,8 @@ github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= @@ -154,24 +176,44 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= @@ -180,11 +222,15 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -193,11 +239,15 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -205,12 +255,18 @@ golang.org/x/sys v0.0.0-20211117180635-dee7805ff2e1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -221,12 +277,20 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4= google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/.konflux/virt-v2v/runtime/rpms.in.yaml b/.konflux/virt-v2v/runtime/rpms.in.yaml index b734b94659..986a9b2e35 100644 --- a/.konflux/virt-v2v/runtime/rpms.in.yaml +++ b/.konflux/virt-v2v/runtime/rpms.in.yaml @@ -3,10 +3,13 @@ packages: - virtio-win - catatonit - libvirt-libs - - kernel-6.12.0-211.16.1.el10_2 - - kernel-modules-core-6.12.0-211.16.1.el10_2 - - kernel-core-6.12.0-211.16.1.el10_2 - - kernel-modules-6.12.0-211.16.1.el10_2 + # Unversioned so refresh-rpm-lockfiles can advance the appliance kernel to the + # current z-stream. Pinning an exact NEVRA here froze it permanently and was the + # single largest source of CVE findings in the v2.12.7 scan. + - kernel + - kernel-modules-core + - kernel-core + - kernel-modules contentOrigin: repofiles: - ./redhat.repo diff --git a/build/deep-inspection-rhel9/Containerfile b/build/deep-inspection-rhel9/Containerfile index b00af423bc..0c44bd3dd5 100644 --- a/build/deep-inspection-rhel9/Containerfile +++ b/build/deep-inspection-rhel9/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -10,7 +10,8 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go bui FROM quay.io/centos/centos:stream9 # Update packages to address security vulnerabilities (CVE fixes) -RUN dnf install -y --allowerasing \ +RUN dnf update -y && \ + dnf install -y --allowerasing --setopt=install_weak_deps=0 \ libguestfs \ libguestfs-tools \ libguestfs-tools-c \ diff --git a/build/deep-inspection-rhel9/Containerfile-downstream b/build/deep-inspection-rhel9/Containerfile-downstream index 9c672ebc75..9e2447c1fa 100644 --- a/build/deep-inspection-rhel9/Containerfile-downstream +++ b/build/deep-inspection-rhel9/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -8,7 +8,7 @@ ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go build -buildvcs=false -ldflags="-w -s" -o deep-inspection github.com/kubev2v/forklift/cmd/deep-inspection FROM registry.redhat.io/ubi9/ubi-minimal:9.7-1778562320 -RUN microdnf install -y \ +RUN microdnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ virtio-win \ qemu-img \ diff --git a/build/deep-inspection/Containerfile b/build/deep-inspection/Containerfile index c8e41462cf..1663f1a93d 100644 --- a/build/deep-inspection/Containerfile +++ b/build/deep-inspection/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -10,7 +10,8 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go bui FROM quay.io/centos/centos:stream10 # Update packages to address security vulnerabilities (CVE fixes) -RUN dnf install -y --allowerasing \ +RUN dnf update -y && \ + dnf install -y --allowerasing --setopt=install_weak_deps=0 \ libguestfs \ libguestfs-tools \ libguestfs-tools-c \ diff --git a/build/deep-inspection/Containerfile-downstream b/build/deep-inspection/Containerfile-downstream index 887035047a..3667243c99 100644 --- a/build/deep-inspection/Containerfile-downstream +++ b/build/deep-inspection/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi10/go-toolset:1.25.9-1778589469 AS builder +FROM registry.redhat.io/ubi10/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -8,7 +8,7 @@ ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go build -buildvcs=false -ldflags="-w -s" -o deep-inspection github.com/kubev2v/forklift/cmd/deep-inspection FROM registry.redhat.io/ubi10/ubi-minimal:10.1-1778576723 -RUN microdnf install -y \ +RUN microdnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ virtio-win \ qemu-img \ diff --git a/build/forklift-api/Containerfile b/build/forklift-api/Containerfile index d7d6f90674..2af9ee65ff 100644 --- a/build/forklift-api/Containerfile +++ b/build/forklift-api/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,12 +7,11 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o forklift-api github.com/kubev2v/forklift/cmd/forklift-api -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Required to be able to get files from within the pod -RUN microdnf -y install tar && \ - # Update packages to address security vulnerabilities (CVE fixes) - microdnf update -y && \ - microdnf clean all +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 tar && \ + microdnf -y clean all COPY --from=builder /app/forklift-api /usr/local/bin/forklift-api ENTRYPOINT ["/usr/local/bin/forklift-api"] diff --git a/build/forklift-api/Containerfile-downstream b/build/forklift-api/Containerfile-downstream index 3672c6672c..2831d54ec4 100644 --- a/build/forklift-api/Containerfile-downstream +++ b/build/forklift-api/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -10,7 +10,7 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go bui FROM registry.redhat.io/ubi9-minimal:9.7-1778562320 # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y install --setopt=install_weak_deps=0 tar && microdnf -y clean all COPY --from=builder /app/forklift-api /usr/local/bin/forklift-api ENTRYPOINT ["/usr/local/bin/forklift-api"] diff --git a/build/forklift-cli-download/Containerfile b/build/forklift-cli-download/Containerfile index ad44c644d3..40eabc5fa1 100644 --- a/build/forklift-cli-download/Containerfile +++ b/build/forklift-cli-download/Containerfile @@ -1,5 +1,5 @@ # Build stage -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app @@ -15,7 +15,7 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 make build-all RUN --mount=type=cache,target=${GOCACHE},uid=1001 make build-mcp # Nginx + MCP server stage -FROM registry.access.redhat.com/ubi9/nginx-126:1-1772411898 +FROM registry.access.redhat.com/ubi9/nginx-126:latest # Copy cross-compiled binaries for all platforms (linux, darwin, windows) COPY --from=builder /app/cmd/kubectl-mtv/bin /opt/app-root/src/ diff --git a/build/forklift-cli-download/Containerfile-downstream b/build/forklift-cli-download/Containerfile-downstream index b9ac596a79..0926cc6fba 100644 --- a/build/forklift-cli-download/Containerfile-downstream +++ b/build/forklift-cli-download/Containerfile-downstream @@ -1,5 +1,5 @@ # Build stage -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app diff --git a/build/forklift-controller/Containerfile b/build/forklift-controller/Containerfile index d6f93b324a..85163ffc49 100644 --- a/build/forklift-controller/Containerfile +++ b/build/forklift-controller/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,10 +7,12 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o manager github.com/kubev2v/forklift/cmd/forklift-controller -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # RUN microdnf -y update && microdnf -y clean all # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 tar && \ + microdnf -y clean all COPY --from=builder /app/manager /usr/local/bin/forklift-controller ENTRYPOINT ["/usr/local/bin/forklift-controller"] diff --git a/build/forklift-controller/Containerfile-downstream b/build/forklift-controller/Containerfile-downstream index 367e7fa45c..22bc302f1a 100644 --- a/build/forklift-controller/Containerfile-downstream +++ b/build/forklift-controller/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app @@ -12,7 +12,7 @@ FROM registry.redhat.io/ubi9-minimal:9.7-1778562320 # RUN microdnf -y update && microdnf -y clean all # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y install --setopt=install_weak_deps=0 tar && microdnf -y clean all COPY --from=builder /app/manager /usr/local/bin/forklift-controller ENTRYPOINT ["/usr/local/bin/forklift-controller"] diff --git a/build/forklift-operator-index/Containerfile b/build/forklift-operator-index/Containerfile index 910ece0366..0115a4c50f 100644 --- a/build/forklift-operator-index/Containerfile +++ b/build/forklift-operator-index/Containerfile @@ -2,14 +2,16 @@ # https://github.com/operator-framework/operator-registry/pull/1664 # Ref: https://redhat-internal.slack.com/archives/C074JM28DTP/p1746458556603619 FROM quay.io/operator-framework/opm:v1.64.0 AS opm -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 AS builder +FROM registry.access.redhat.com/ubi9-minimal:latest AS builder ARG CHANNELS="development" ARG DEFAULT_CHANNEL="development" ARG VERSION="99.0.0" ARG OPERATOR_BUNDLE_IMAGE ARG OPM_OPTS -RUN microdnf install gettext -y +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 gettext && \ + microdnf -y clean all COPY --from=opm /bin/opm /bin/opm COPY operator /app diff --git a/build/hyperv-provider-server/Containerfile b/build/hyperv-provider-server/Containerfile index d20c3d8c1d..65ea4e6ed5 100644 --- a/build/hyperv-provider-server/Containerfile +++ b/build/hyperv-provider-server/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,9 +7,11 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o hyperv-provider-server github.com/kubev2v/forklift/cmd/hyperv-provider-server -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 tar && \ + microdnf -y clean all COPY --from=builder /app/hyperv-provider-server /usr/local/bin/hyperv-provider-server USER 1001 diff --git a/build/hyperv-provider-server/Containerfile-downstream b/build/hyperv-provider-server/Containerfile-downstream index d00dd2e378..1be237938e 100644 --- a/build/hyperv-provider-server/Containerfile-downstream +++ b/build/hyperv-provider-server/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -11,7 +11,7 @@ FROM registry.redhat.io/ubi9-minimal:9.7-1778562320 # RUN microdnf -y update && microdnf -y clean all # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y install --setopt=install_weak_deps=0 tar && microdnf -y clean all COPY --from=builder /app/hyperv-provider-server /usr/local/bin/hyperv-provider-server USER 1001 diff --git a/build/openstack-populator/Containerfile b/build/openstack-populator/Containerfile index 846fb5a3e8..72fb1969b2 100644 --- a/build/openstack-populator/Containerfile +++ b/build/openstack-populator/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,9 +7,11 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o openstack-populator github.com/kubev2v/forklift/cmd/openstack-populator -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 tar && \ + microdnf -y clean all COPY --from=builder /app/openstack-populator /usr/local/bin/openstack-populator ENTRYPOINT ["/usr/local/bin/openstack-populator"] diff --git a/build/openstack-populator/Containerfile-downstream b/build/openstack-populator/Containerfile-downstream index 6026b84b1a..797e49d0d3 100644 --- a/build/openstack-populator/Containerfile-downstream +++ b/build/openstack-populator/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -10,7 +10,7 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go bui FROM registry.redhat.io/ubi9-minimal:9.7-1778562320 # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y install --setopt=install_weak_deps=0 tar && microdnf -y clean all COPY --from=builder /app/openstack-populator /usr/local/bin/openstack-populator ENTRYPOINT ["/usr/local/bin/openstack-populator"] diff --git a/build/ova-provider-server/Containerfile b/build/ova-provider-server/Containerfile index 10bde7e6aa..d2747bd89d 100644 --- a/build/ova-provider-server/Containerfile +++ b/build/ova-provider-server/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,13 +7,12 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o ova-provider-server github.com/kubev2v/forklift/cmd/ova-provider-server -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Required to be able to get files from within the pod -RUN microdnf -y install tar && \ - # Update packages to address security vulnerabilities (CVE fixes) - microdnf update -y && \ - microdnf clean all +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 tar && \ + microdnf -y clean all COPY --from=builder /app/ova-provider-server /usr/local/bin/ova-provider-server ENTRYPOINT ["/usr/local/bin/ova-provider-server"] diff --git a/build/ova-provider-server/Containerfile-downstream b/build/ova-provider-server/Containerfile-downstream index e55f6b6c83..e2363fa43c 100644 --- a/build/ova-provider-server/Containerfile-downstream +++ b/build/ova-provider-server/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -11,7 +11,7 @@ FROM registry.redhat.io/ubi9-minimal:9.7-1778562320 # RUN microdnf -y update && microdnf -y clean all # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y install --setopt=install_weak_deps=0 tar && microdnf -y clean all COPY --from=builder /app/ova-provider-server /usr/local/bin/ova-provider-server ENTRYPOINT ["/usr/local/bin/ova-provider-server"] diff --git a/build/ova-proxy/Containerfile b/build/ova-proxy/Containerfile index d465de80fa..c9e154acc7 100644 --- a/build/ova-proxy/Containerfile +++ b/build/ova-proxy/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -8,11 +8,11 @@ ENV GOCACHE=/go-build/cache RUN echo $(ls) RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o proxy github.com/kubev2v/forklift/cmd/ova-proxy -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Update packages to address security vulnerabilities (CVE fixes) -RUN microdnf update -y && \ - microdnf clean all +RUN microdnf -y update && \ + microdnf -y clean all COPY --from=builder /app/proxy /usr/local/bin/ova-proxy ENTRYPOINT ["/usr/local/bin/ova-proxy"] diff --git a/build/ova-proxy/Containerfile-downstream b/build/ova-proxy/Containerfile-downstream index e581831795..b72849f246 100644 --- a/build/ova-proxy/Containerfile-downstream +++ b/build/ova-proxy/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ diff --git a/build/ovirt-populator/Containerfile b/build/ovirt-populator/Containerfile index 78ef9173eb..de46ea5a64 100644 --- a/build/ovirt-populator/Containerfile +++ b/build/ovirt-populator/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi8/go-toolset:1.25.9-1778589418 AS builder +FROM registry.access.redhat.com/ubi8/go-toolset:1.26 AS builder ENV GOPATH=$APP_ROOT WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,10 +7,12 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -o ovirt-populator github.com/kubev2v/forklift/cmd/ovirt-populator -FROM registry.access.redhat.com/ubi8/ubi:8.10-1778062799 +FROM registry.access.redhat.com/ubi8/ubi:latest COPY --from=builder /app/ovirt-populator /usr/local/bin/ovirt-populator RUN subscription-manager refresh && \ - dnf install -y python3-ovirt-engine-sdk4 ovirt-imageio-client && dnf clean all + dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 python3-ovirt-engine-sdk4 ovirt-imageio-client && \ + dnf clean all ENTRYPOINT ["/usr/local/bin/ovirt-populator"] LABEL \ diff --git a/build/ovirt-populator/Containerfile-downstream b/build/ovirt-populator/Containerfile-downstream index acb7848e05..e59048fc4c 100644 --- a/build/ovirt-populator/Containerfile-downstream +++ b/build/ovirt-populator/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi8/go-toolset:1.25.9-1778589418 AS builder +FROM registry.redhat.io/ubi8/go-toolset:1.26 AS builder USER 0 ENV GOPATH=$APP_ROOT WORKDIR /app @@ -11,7 +11,7 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 GOOS=linux GOARCH=amd64 go bui FROM registry.redhat.io/ubi8-minimal:8.10-1778576163 COPY --from=builder /app/ovirt-populator /usr/local/bin/ovirt-populator -RUN microdnf install -y python3-ovirt-engine-sdk4 ovirt-imageio-client +RUN microdnf install -y --setopt=install_weak_deps=0 python3-ovirt-engine-sdk4 ovirt-imageio-client ENTRYPOINT ["/usr/local/bin/ovirt-populator"] diff --git a/build/ovirt-populator/Containerfile-upstream b/build/ovirt-populator/Containerfile-upstream index 17ec48efaa..6b01413c61 100644 --- a/build/ovirt-populator/Containerfile-upstream +++ b/build/ovirt-populator/Containerfile-upstream @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi8/go-toolset:1.25.9-1778589418 AS builder +FROM registry.access.redhat.com/ubi8/go-toolset:1.26 AS builder ENV GOPATH=$APP_ROOT WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -10,7 +10,9 @@ RUN go build -o ovirt-populator github.com/kubev2v/forklift/cmd/ovirt-populator FROM quay.io/centos/centos:stream9 COPY --from=builder /app/ovirt-populator /usr/local/bin/ovirt-populator RUN dnf install -y centos-release-ovirt45 -RUN dnf install -y python3-ovirt-engine-sdk4 ovirt-imageio-client && dnf clean all +RUN dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 python3-ovirt-engine-sdk4 ovirt-imageio-client && \ + dnf clean all ENTRYPOINT ["/usr/local/bin/ovirt-populator"] ARG GIT_COMMIT=unknown diff --git a/build/populator-controller/Containerfile b/build/populator-controller/Containerfile index 57956c3b50..7bdfc2a417 100644 --- a/build/populator-controller/Containerfile +++ b/build/populator-controller/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -7,13 +7,12 @@ ENV GOEXPERIMENT=strictfipsruntime ENV GOCACHE=/go-build/cache RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o controller github.com/kubev2v/forklift/cmd/populator-controller -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Required to be able to get files from within the pod -RUN microdnf -y install tar && \ - # Update packages to address security vulnerabilities (CVE fixes) - microdnf update -y && \ - microdnf clean all +RUN microdnf -y update && \ + microdnf -y install --setopt=install_weak_deps=0 tar && \ + microdnf -y clean all COPY --from=builder /app/controller /usr/local/bin/populator-controller ENTRYPOINT ["/usr/local/bin/populator-controller"] diff --git a/build/populator-controller/Containerfile-downstream b/build/populator-controller/Containerfile-downstream index e3e8a5b500..4cb73feb0b 100644 --- a/build/populator-controller/Containerfile-downstream +++ b/build/populator-controller/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -11,7 +11,7 @@ FROM registry.redhat.io/ubi9-minimal:9.7-1778562320 # RUN microdnf -y update && microdnf -y clean all # Required to be able to get files from within the pod -RUN microdnf -y install tar && microdnf clean all +RUN microdnf -y install --setopt=install_weak_deps=0 tar && microdnf -y clean all COPY --from=builder /app/controller /usr/local/bin/populator-controller ENTRYPOINT ["/usr/local/bin/populator-controller"] diff --git a/build/validation/Containerfile b/build/validation/Containerfile index 35c4a31059..77d6706270 100644 --- a/build/validation/Containerfile +++ b/build/validation/Containerfile @@ -1,9 +1,26 @@ -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 -ARG TARGETARCH=amd64 -RUN curl -L https://github.com/open-policy-agent/opa/releases/download/v1.8.0/opa_linux_${TARGETARCH}_static > /usr/bin/opa -RUN chmod +x /usr/bin/opa +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS mtv-opa +USER 0 +WORKDIR /app/opa +ENV GOFLAGS="-tags=strictfipsruntime" +ENV GOEXPERIMENT=strictfipsruntime + +# Build OPA from source with the same Go toolchain as every other image in this +# repo. Previously this image pulled a prebuilt opa binary from GitHub releases, +# which pinned it to whatever Go version upstream happened to build with (1.22.5 +# at last scan) and was fetched over the network with no checksum verification. +COPY .konflux/validation/go.mod go.mod +COPY .konflux/validation/go.sum go.sum + +RUN go build -buildvcs=false -ldflags="-w -s" -o /tmp/opa github.com/open-policy-agent/opa + +FROM registry.access.redhat.com/ubi9-minimal:latest + +RUN microdnf -y update && \ + microdnf -y clean all + COPY validation/policies /usr/share/opa/policies/ COPY validation/entrypoint.sh /usr/bin/ +COPY --from=mtv-opa /tmp/opa /usr/bin/opa ENTRYPOINT ["/usr/bin/entrypoint.sh"] diff --git a/build/validation/Containerfile-downstream b/build/validation/Containerfile-downstream index 9facc5b61d..75d13c26a1 100644 --- a/build/validation/Containerfile-downstream +++ b/build/validation/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS mtv-opa +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS mtv-opa USER 0 WORKDIR /app/opa ENV GOFLAGS="-tags=strictfipsruntime" diff --git a/build/virt-v2v-rhel9/Containerfile b/build/virt-v2v-rhel9/Containerfile index e244fbc87f..18238e1321 100644 --- a/build/virt-v2v-rhel9/Containerfile +++ b/build/virt-v2v-rhel9/Containerfile @@ -1,7 +1,7 @@ -FROM registry.access.redhat.com/ubi9:9.6-1760340943 AS appliance +FROM registry.access.redhat.com/ubi9:latest AS appliance RUN subscription-manager refresh && \ - dnf install -y --setopt=install_weak_deps=False \ + dnf install -y --setopt=install_weak_deps=0 \ qemu-img \ libguestfs-devel \ libguestfs-winsupport \ @@ -18,7 +18,7 @@ RUN mkdir -p /usr/local/lib/guestfs/appliance && \ qemu-img convert -c -O qcow2 root root.qcow2 && \ mv -vf root.qcow2 root -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -31,7 +31,7 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldfl RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o virt-v2v-wrapper github.com/kubev2v/forklift/cmd/virt-v2v RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o forklift-wait-for-reboot github.com/kubev2v/forklift/cmd/forklift-wait-for-reboot -FROM registry.access.redhat.com/ubi9:9.6-1760340943 +FROM registry.access.redhat.com/ubi9:latest # RUN rm /etc/pki/tls/fips_local.cnf && \ # echo -e '[fips_sect]\ntls1-prf-ems-check = 0\nactivate = 1' > /etc/pki/tls/fips_local.cnf && \ @@ -42,9 +42,13 @@ FROM registry.access.redhat.com/ubi9:9.6-1760340943 RUN subscription-manager refresh && \ mkdir /disks && \ source /etc/os-release && \ - dnf install -y \ + dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ - virtio-win && \ + virtio-win \ + libvirt-daemon-config-network \ + passt \ + catatonit && \ dnf clean all # This prevents libvirt from attempting to create files in the root directory, @@ -53,11 +57,9 @@ RUN subscription-manager refresh && \ RUN mkdir /home/qemu && chown qemu:qemu /home/qemu ENV HOME=/home/qemu -# The libvirt can hang when destroying libguestfs domains due to the -# absence of an init process to clean up zombie processes. 'catatonit' acts -# as PID 1 to resolve this. +# 'catatonit' (installed above) acts as PID 1 so zombie processes are reaped; +# without it libvirt can hang when destroying libguestfs domains. # Refer to: https://issues.redhat.com/browse/RHEL-105529 -RUN dnf install -y catatonit # This ensures that the 'libvirt:qemu:///session' connection method, which is # the officially tested and supported flow, is utilized instead of direct connections. diff --git a/build/virt-v2v-rhel9/Containerfile-downstream b/build/virt-v2v-rhel9/Containerfile-downstream index 3fbb830814..8be5cc674d 100644 --- a/build/virt-v2v-rhel9/Containerfile-downstream +++ b/build/virt-v2v-rhel9/Containerfile-downstream @@ -1,12 +1,12 @@ FROM registry.redhat.io/ubi9:9.6-1760340943 AS winlegacyiso RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release -RUN yum install -y --setopt=install_weak_deps=False virtio-win-1.9.12-4.el7 +RUN yum install -y --setopt=install_weak_deps=0 virtio-win-1.9.12-4.el7 FROM registry.redhat.io/ubi9:9.6-1760340943 AS appliance RUN dnf update -y && \ - dnf install -y --setopt=install_weak_deps=False \ + dnf install -y --setopt=install_weak_deps=0 \ qemu-img \ libguestfs-devel \ libguestfs-winsupport \ @@ -23,9 +23,9 @@ RUN mkdir -p /usr/local/lib/guestfs/appliance && \ qemu-img convert -c -O qcow2 root root.qcow2 && \ mv -vf root.qcow2 root -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder USER 0 -RUN dnf install -y libvirt-devel +RUN dnf install -y --setopt=install_weak_deps=0 libvirt-devel WORKDIR /app COPY --chown=1001:0 ./ ./ ENV GOFLAGS="-mod=vendor -tags=strictfipsruntime" @@ -45,7 +45,7 @@ RUN rm /etc/pki/tls/fips_local.cnf && \ RUN mkdir /disks && \ source /etc/os-release && \ dnf update -y --exclude=kernel,kernel-core,kernel-modules,kernel-modules-core && \ - dnf install -y --setopt=install_weak_deps=False \ + dnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ virtio-win \ libvirt-libs @@ -60,7 +60,7 @@ ENV HOME=/home/qemu # absence of an init process to clean up zombie processes. 'catatonit' acts # as PID 1 to resolve this. # Refer to: https://issues.redhat.com/browse/RHEL-105529 -RUN dnf install -y catatonit +RUN dnf install -y --setopt=install_weak_deps=0 catatonit # This ensures that the 'libvirt:qemu:///session' connection method, which is # the officially tested and supported flow, is utilized instead of direct connections. diff --git a/build/virt-v2v/Containerfile b/build/virt-v2v/Containerfile index be75de666b..18238e1321 100644 --- a/build/virt-v2v/Containerfile +++ b/build/virt-v2v/Containerfile @@ -1,7 +1,7 @@ -FROM registry.access.redhat.com/ubi9:9.7-1778576335 AS appliance +FROM registry.access.redhat.com/ubi9:latest AS appliance RUN subscription-manager refresh && \ - dnf install -y --setopt=install_weak_deps=False \ + dnf install -y --setopt=install_weak_deps=0 \ qemu-img \ libguestfs-devel \ libguestfs-winsupport \ @@ -18,7 +18,7 @@ RUN mkdir -p /usr/local/lib/guestfs/appliance && \ qemu-img convert -c -O qcow2 root root.qcow2 && \ mv -vf root.qcow2 root -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER 0 WORKDIR /app COPY --chown=1001:0 ./ ./ @@ -31,7 +31,7 @@ RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldfl RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o virt-v2v-wrapper github.com/kubev2v/forklift/cmd/virt-v2v RUN --mount=type=cache,target=${GOCACHE},uid=1001 go build -buildvcs=false -ldflags="-w -s" -o forklift-wait-for-reboot github.com/kubev2v/forklift/cmd/forklift-wait-for-reboot -FROM registry.access.redhat.com/ubi9:9.7-1778576335 +FROM registry.access.redhat.com/ubi9:latest # RUN rm /etc/pki/tls/fips_local.cnf && \ # echo -e '[fips_sect]\ntls1-prf-ems-check = 0\nactivate = 1' > /etc/pki/tls/fips_local.cnf && \ @@ -42,9 +42,13 @@ FROM registry.access.redhat.com/ubi9:9.7-1778576335 RUN subscription-manager refresh && \ mkdir /disks && \ source /etc/os-release && \ - dnf install -y \ + dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ - virtio-win && \ + virtio-win \ + libvirt-daemon-config-network \ + passt \ + catatonit && \ dnf clean all # This prevents libvirt from attempting to create files in the root directory, @@ -53,11 +57,9 @@ RUN subscription-manager refresh && \ RUN mkdir /home/qemu && chown qemu:qemu /home/qemu ENV HOME=/home/qemu -# The libvirt can hang when destroying libguestfs domains due to the -# absence of an init process to clean up zombie processes. 'catatonit' acts -# as PID 1 to resolve this. +# 'catatonit' (installed above) acts as PID 1 so zombie processes are reaped; +# without it libvirt can hang when destroying libguestfs domains. # Refer to: https://issues.redhat.com/browse/RHEL-105529 -RUN dnf install -y catatonit # This ensures that the 'libvirt:qemu:///session' connection method, which is # the officially tested and supported flow, is utilized instead of direct connections. diff --git a/build/virt-v2v/Containerfile-downstream b/build/virt-v2v/Containerfile-downstream index e727c92cc8..d45d7fdc35 100644 --- a/build/virt-v2v/Containerfile-downstream +++ b/build/virt-v2v/Containerfile-downstream @@ -1,12 +1,12 @@ FROM registry.redhat.io/ubi10:10.2-1777462922 AS winlegacyiso RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release -RUN yum install -y --setopt=install_weak_deps=False virtio-win-1.9.12-4.el7 +RUN yum install -y --setopt=install_weak_deps=0 virtio-win-1.9.12-4.el7 FROM registry.redhat.io/ubi10:10.2-1777462922 AS appliance RUN dnf update -y && \ - dnf install -y --setopt=install_weak_deps=False \ + dnf install -y --setopt=install_weak_deps=0 \ qemu-img \ libguestfs-devel \ libguestfs-winsupport \ @@ -23,9 +23,9 @@ RUN mkdir -p /usr/local/lib/guestfs/appliance && \ qemu-img convert -c -O qcow2 root root.qcow2 && \ mv -vf root.qcow2 root -FROM registry.redhat.io/ubi10/go-toolset:1.25.9-1778589469 AS builder +FROM registry.redhat.io/ubi10/go-toolset:1.26 AS builder USER 0 -RUN dnf install -y libvirt-devel +RUN dnf install -y --setopt=install_weak_deps=0 libvirt-devel WORKDIR /app COPY --chown=1001:0 ./ ./ ENV GOFLAGS="-mod=vendor -tags=strictfipsruntime" @@ -42,7 +42,7 @@ COPY --chown=1001:0 ./libguestfs-fssupport-10.2-4.el10_2.* ./ COPY --chown=1001:0 ./kernel-modules-internal-6.12.0-211.16.1.el10_2.* ./ # RHEL 10.1 custom kernel for libguestfs-fssupport -RUN dnf install -y --setopt=sslverify=false \ +RUN dnf install -y --setopt=sslverify=false --setopt=install_weak_deps=0 \ kernel-6.12.0-211.16.1.el10_2 \ kernel-modules-core-6.12.0-211.16.1.el10_2 \ kernel-core-6.12.0-211.16.1.el10_2 \ @@ -57,7 +57,7 @@ RUN rm /etc/pki/tls/fips_local.cnf && \ RUN mkdir /disks && \ source /etc/os-release && \ dnf update -y --exclude=kernel,kernel-core,kernel-modules,kernel-modules-core && \ - dnf install -y --setopt=install_weak_deps=False \ + dnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ virtio-win \ libvirt-libs @@ -72,7 +72,7 @@ ENV HOME=/home/qemu # absence of an init process to clean up zombie processes. 'catatonit' acts # as PID 1 to resolve this. # Refer to: https://issues.redhat.com/browse/RHEL-105529 -RUN dnf install -y catatonit +RUN dnf install -y --setopt=install_weak_deps=0 catatonit # This ensures that the 'libvirt:qemu:///session' connection method, which is # the officially tested and supported flow, is utilized instead of direct connections. diff --git a/build/virt-v2v/Containerfile-downstream-fssupport b/build/virt-v2v/Containerfile-downstream-fssupport index 795048d647..4f37907bfa 100644 --- a/build/virt-v2v/Containerfile-downstream-fssupport +++ b/build/virt-v2v/Containerfile-downstream-fssupport @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi10/go-toolset:1.25.9-1778589469 AS builder +FROM registry.redhat.io/ubi10/go-toolset:1.26 AS builder WORKDIR /app COPY --chown=1001:0 ./ ./ ENV GOFLAGS="-mod=vendor -tags=strictfipsruntime" @@ -43,7 +43,7 @@ RUN \ libguestfs-fssupport:libguestfs-fssupport-10.1-3.el10 \ ' ; \ set -x ; \ - dnf install -y --setopt=sslverify=false \ + dnf install -y --setopt=sslverify=false --setopt=install_weak_deps=0 \ $( for p in $packages; do \ [[ "$p" =~ ([^:]+):(.+)-([^-]+)-([^-]+) ]] && \ echo $brewroot/${BASH_REMATCH[1]}/${BASH_REMATCH[3]}/${BASH_REMATCH[4]}/$arch/${BASH_REMATCH[2]}-${BASH_REMATCH[3]}-${BASH_REMATCH[4]}.$arch.rpm; \ @@ -72,7 +72,7 @@ ENV HOME=/home/qemu # absence of an init process to clean up zombie processes. 'catatonit' acts # as PID 1 to resolve this. # Refer to: https://issues.redhat.com/browse/RHEL-105529 -RUN dnf install -y catatonit +RUN dnf install -y --setopt=install_weak_deps=0 catatonit # This ensures that the 'libvirt:qemu:///session' connection method, which is # the officially tested and supported flow, is utilized instead of direct connections. diff --git a/build/virt-v2v/Containerfile-upstream b/build/virt-v2v/Containerfile-upstream index ffaaaec976..b9d92a8dc5 100644 --- a/build/virt-v2v/Containerfile-upstream +++ b/build/virt-v2v/Containerfile-upstream @@ -24,8 +24,11 @@ ENV LIBGUESTFS_DEBUG=1 LIBGUESTFS_TRACE=1 RUN mkdir /disks && \ source /etc/os-release && \ - dnf install -y \ - virt-v2v && \ + dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 \ + virt-v2v \ + libvirt-daemon-config-network \ + passt && \ dnf clean all # TODO: This should be replaced with `dnf install -y virtio-win` from centos-stream10 diff --git a/build/virt-v2v/Containerfile-upstream-fedora b/build/virt-v2v/Containerfile-upstream-fedora index 65d919dd2d..ba871f759c 100644 --- a/build/virt-v2v/Containerfile-upstream-fedora +++ b/build/virt-v2v/Containerfile-upstream-fedora @@ -22,8 +22,11 @@ ENV LIBGUESTFS_DEBUG=1 LIBGUESTFS_TRACE=1 RUN mkdir /disks && \ source /etc/os-release && \ - dnf install -y \ - virt-v2v && \ + dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 \ + virt-v2v \ + libvirt-daemon-config-network \ + passt && \ dnf clean all RUN dnf install -y https://kojihub.stream.centos.org/kojifiles/packages/virtio-win/1.9.40/1.el9/noarch/virtio-win-1.9.40-1.el9.noarch.rpm diff --git a/build/virt-v2v/Containerfile-upstream-xfs b/build/virt-v2v/Containerfile-upstream-xfs index 8bbae1ab14..133a7826e9 100644 --- a/build/virt-v2v/Containerfile-upstream-xfs +++ b/build/virt-v2v/Containerfile-upstream-xfs @@ -24,9 +24,12 @@ ENV LIBGUESTFS_DEBUG=1 LIBGUESTFS_TRACE=1 RUN mkdir /disks && \ source /etc/os-release && \ - dnf install -y \ + dnf update -y && \ + dnf install -y --setopt=install_weak_deps=0 \ virt-v2v \ - kernel && \ + kernel \ + libvirt-daemon-config-network \ + passt && \ dnf clean all # TODO: This should be replaced with `dnf install -y virtio-win` from centos-stream10 diff --git a/build/vsphere-copy-offload-populator/Containerfile b/build/vsphere-copy-offload-populator/Containerfile index 27e1a71c50..1d3a2d7ddc 100644 --- a/build/vsphere-copy-offload-populator/Containerfile +++ b/build/vsphere-copy-offload-populator/Containerfile @@ -1,4 +1,4 @@ -FROM registry.access.redhat.com/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.access.redhat.com/ubi9/go-toolset:1.26 AS builder USER root WORKDIR /usr/src/app @@ -14,11 +14,11 @@ RUN --mount=type=cache,target=/go/pkg/mod \ RUN make vmkfstools-wrapper -FROM registry.access.redhat.com/ubi9-minimal:9.7-1778562320 +FROM registry.access.redhat.com/ubi9-minimal:latest # Update packages to address security vulnerabilities (CVE fixes) -RUN microdnf update -y && \ - microdnf clean all +RUN microdnf -y update && \ + microdnf -y clean all COPY --from=builder /usr/src/app/cmd/vsphere-copy-offload-populator/bin/vsphere-copy-offload-populator \ /bin/vsphere-copy-offload-populator diff --git a/build/vsphere-copy-offload-populator/Containerfile-downstream b/build/vsphere-copy-offload-populator/Containerfile-downstream index 67718600fc..a9d0a9effe 100644 --- a/build/vsphere-copy-offload-populator/Containerfile-downstream +++ b/build/vsphere-copy-offload-populator/Containerfile-downstream @@ -1,4 +1,4 @@ -FROM registry.redhat.io/ubi9/go-toolset:1.25.9-1778604137 AS builder +FROM registry.redhat.io/ubi9/go-toolset:1.26 AS builder COPY --chown=1001:0 . /src WORKDIR /src/cmd/vsphere-copy-offload-populator diff --git a/docs/security/cve-remediation-plan.md b/docs/security/cve-remediation-plan.md index 509303eb1d..d03368545d 100644 --- a/docs/security/cve-remediation-plan.md +++ b/docs/security/cve-remediation-plan.md @@ -123,72 +123,174 @@ Excluding the appliance kernel: Of the 473 fixable: 273 RPM, 103 Go module, 97 Go stdlib. -## 8. Remediation +## 8. Remediation status -Ordered by findings removed per unit of effort. +### P0 — VEX the appliance kernel — **DEFERRED** -### P0 — Scanner truth (removes ~74% of the count, zero build risk) +Not actioned here. Grype produces a large share of uncorroborated findings across *all* +ISS reports, not just Forklift, so suppression is being handled as a scanner-level review +rather than per-product. Analysis and options are recorded in the image-scan-service repo +at `docs/scanner-tuning/grype-result-inflation.md`. -* Author a VEX/OpenVEX document for `linux-kernel` + `kernel-core` in `forklift-virt-v2v` - with status `not_affected`, justification `vulnerable_code_not_in_execute_path` — the - appliance kernel only executes inside the guest-inspection VM. -* Disable grype's `linux-kernel` binary cataloger for the appliance path so the vmlinuz - is not matched against upstream kernel.org ranges. -* Feed the same list to ISS via `iss-cli --cve-ignore`, sourced from the VEX doc (single - source of truth, reviewed in-repo — not a hand-maintained ignore list). -* Effect: 13,548 → ~3,585 findings; 3,043 → 689 crit/high. +Until that lands, the ~9,963 appliance-kernel findings remain in the report. They are not +remediable from this repo. -### P1 — Make the base image move +### P1 — Base images now float — **DONE** -* Re-pin every base to a current digest, and add a Renovate rule that advances base digests - on a weekly cadence (today `dockerfile` updates are grouped but there is no digest-refresh - schedule for stale-but-same-tag rebuilds). -* Remove the four literal `kernel-*-6.12.0-211.16.1.el10_2` pins from - `.konflux/virt-v2v/runtime/rpms.in.yaml`; use unversioned `kernel-core` / `kernel-modules-core` - so `refresh-rpm-lockfiles` can advance the z-stream. -* For the non-hermetic upstream Containerfiles, add `dnf -y update` / `microdnf -y update` as - the first RUN of each runtime stage. For hermetic Konflux builds this is a no-op by design — - there the lever is the lockfile refresh above, not a network `dnf`. -* Effect: clears the cross-image `libevent` / `curl` / `openssl` / `sqlite` / `gnutls` block. +Every base in the upstream Containerfiles moved from a frozen build-timestamp tag to a +floating tag, so each rebuild picks up current Red Hat content: -### P2 — Shrink the runtime surface +| Was | Now | +|---|---| +| `ubi9-minimal:9.7-1778562320` | `ubi9-minimal:latest` | +| `ubi9:9.7-1778576335`, `ubi9:9.6-1760340943` | `ubi9:latest` | +| `ubi8/ubi:8.10-1778062799` | `ubi8/ubi:latest` | +| `ubi9/nginx-126:1-1772411898` | `ubi9/nginx-126:latest` | +| `ubi9/go-toolset:1.25.9-1778604137` | `ubi9/go-toolset:1.26` | + +Also removed the four literal kernel NEVRA pins from +`.konflux/virt-v2v/runtime/rpms.in.yaml` (`kernel-6.12.0-211.16.1.el10_2` and friends), so +`refresh-rpm-lockfiles` can advance the appliance kernel to the current z-stream instead of +being frozen forever. + +**Trade-off, stated explicitly:** floating tags mean builds are no longer bit-reproducible +from the Containerfile alone. Two builds a week apart can differ. This is the right default +for a security-driven rebuild with no Mintmaker/Renovate bot moving digests, but if Tigera +later needs reproducible builds, the alternative is a digest pin plus a scheduled bot that +actually advances it — the failure mode to avoid is the current one, a pin that never moves. +The `registry.redhat.io` bases in the `-downstream` files were left pinned; those are +Konflux/Mintmaker-managed and need a Red Hat entitlement to build. + +### P2 — Runtime trim + package update — **DONE** + +Every runtime-stage install now disables weak dependencies and runs an update first. + +Note `microdnf` accepts `--setopt=install_weak_deps=0` but **rejects** `=False`, which the +existing `dnf` lines use. All new lines use `=0`, which both accept. + +Measured on `quay.io/centos/centos:stream9` (`dnf install --assumeno virt-v2v virtio-win`): + +| | Packages | Download | Installed | +|---|---|---|---| +| Before | 324 | 1.0 GB | 2.2 GB | +| After | 297 | 338 MB | 1.4 GB | -* Add `--setopt=install_weak_deps=False` to every runtime-stage install, matching what the - appliance stage already does. -* Set the equivalent cachi2 option in each `rpms.in.yaml` so the hermetic lock is resolved - without weak deps, then regenerate all `rpms.lock.yaml`. -* Explicitly exclude `webkit2gtk3*`, `qt5*`, `gstreamer1*`, `libsoup`, `dhcp*`, `dnsmasq`, - `vim*`, `gdb` from runtime locks. -* Move `virt-v2v` runtime from full `ubi9`/`ubi10` to `ubi9-minimal` where the RPM set allows. -* Effect: −93 crit/high from `webkit2gtk3` alone, plus the gstreamer/libsoup/vim tail. +Nothing is added relative to today's set. Dropped includes `webkit2gtk3-jsc`, +`libproxy-webkitgtk4`, `linux-firmware`, `nfs-utils`, `rpcbind`, `gssproxy`, `quota`, +`abattis-cantarell-fonts`, `geolite2-*`. -### P3 — Unify Go +Two former weak dependencies are now installed **explicitly**, because dropping them would +have been a functional regression: -* Single toolchain (1.26.x) across every builder stage; rebuild all binaries so stdlib - findings collapse to one version. -* Bump `golang.org/x/crypto`, `golang.org/x/net`, `containerd` in `go.mod` + `vendor/`. -* Effect: −200 crit/high. +* `passt` and `libvirt-daemon-config-network` — recommended by `libguestfs`, and required + because the image sets `LIBGUESTFS_BACKEND=libvirt:qemu:///session`. Session-mode + networking breaks without them. +* The `nbdkit` *metapackage* is dropped, which is safe: `virt-v2v` hard-requires every + plugin it actually uses (`nbdkit-server`, `-basic-filters`, `-basic-plugins`, + `-curl-plugin`, `-nbd-plugin`, `-python-plugin`, `-ssh-plugin`, `-vddk-plugin`), and + those are all retained. The metapackage was only pulled in as a recommendation of + `libvirt-daemon-driver-qemu`. -### P4 — Take ownership of the two external images +Unlike Red Hat's downstream file, the upstream virt-v2v runtime `dnf update` deliberately +does **not** exclude the kernel, so the appliance kernel advances with the base. -* Either fork and rebuild `must-gather` and `console-plugin` under Tigera build pipelines, - or drop `must-gather` from the Tigera bundle if it is not required for the Calico Enterprise - L2 migration workflow. It is the worst actionable offender in the scan (299 crit/high) and - is a diagnostics-only convenience. +### P3 — One Go toolchain — **DONE** -### P5 — Gate it so it cannot regress +All 32 builder stages across every Containerfile, upstream and downstream, now use +`go-toolset:1.26`. Previously the shipped images spanned Go 1.22.5, 1.24.6, 1.25.9 and +1.26.3. -* Add an `iss-cli` step to the Konflux/Tekton push pipelines: - `--scan-type image --severity high --cve-ignore `, failing the build on new - crit/high that are not VEX-justified. -* Keep `renovate.json` `cve-automerge-high` but extend `matchBaseBranches` to the Tigera - release branches once they exist. +Module `go` directives unified on 1.26 (`go.mod`, `.konflux/validation/go.mod`, +`.konflux/forklift-operator-bundle/go.mod`). + +Dependency bumps for the CVEs the scan attributed to Go: + +| Module | Package | Was | Now | +|---|---|---|---| +| main | `golang.org/x/crypto` | v0.50.0 | v0.55.0 | +| main | `golang.org/x/net` | v0.53.0 | v0.58.0 | +| main | `golang.org/x/text` | v0.36.0 | v0.41.0 | +| validation | `golang.org/x/crypto` | v0.40.0 | v0.55.0 | +| validation | `golang.org/x/net` | v0.42.0 | v0.58.0 | +| validation | `containerd/v2` | v2.1.4 | v2.3.4 | + +`vendor/` regenerated. `containerd/v2 v2.3.4` requires a `go 1.26.3` directive, which is +what settled the toolchain on 1.26 rather than 1.25. + +**`build/validation/Containerfile` rewritten.** It previously did: + +``` +RUN curl -L https://github.com/open-policy-agent/opa/releases/download/v1.8.0/opa_linux_${TARGETARCH}_static > /usr/bin/opa +``` -## 9. Expected end state +That is an unverified network fetch with no checksum, and it pinned the validation image to +whatever Go version the OPA project happened to release with — Go 1.22.5 at scan time, the +oldest toolchain in the product. It now builds OPA from source with the repo's own +toolchain, mirroring what `Containerfile-downstream` already did. + +Verified: `opa version` in the built image reports +`Go Version: go1.26.7 (Red Hat 1.26.7-1.el9_8) X:strictfipsruntime`. The `el9_8` also +confirms the floating base tag worked — it resolved to 9.8 content, a full minor ahead of +the 9.7 the pins were frozen at. + +OPA itself was left at v1.8.0. It is 12 minors behind (v1.20.1 is current) but upgrading it +changes validation-service behaviour and belongs in its own change. + +### Spotted while doing P2 — not changed, needs a decision + +`build/virt-v2v/Containerfile-downstream:45` and `Containerfile-downstream-fssupport:46` +install RPMs with `--setopt=sslverify=false`, disabling TLS certificate verification for the +package download. In a Konflux hermetic build the packages come from a local cachi2 repo, so +the practical exposure is limited, but it is a bad default to carry and it would be a real +supply-chain hole if either file were ever built non-hermetically. Left alone here because +changing it needs a hermetic build to verify; worth removing when the Tigera build path is +settled. + +### P4 — must-gather / console-plugin — **NOT ACTIONED** + +Pending a decision on whether `must-gather` is used in the Calico Enterprise L2 migration +workflow. It remains the largest actionable block: 299 crit/high, including 106 from +`webkit2gtk3`. `console-plugin` adds a further 78. Neither is buildable from this repo. + +### P5 — CI gating — **DOCUMENTED, NOT WIRED** + +Pattern recorded in image-scan-service at `docs/scanner-tuning/ci-scan-gating.md`. Wiring it +into `.tekton/*-push.yaml` needs the Tigera build pipeline to exist first. + +## 9. Verification performed + +* `docker build` of `forklift-validation`, `forklift-controller` and `forklift-api` on the + new bases and Go 1.26 — all succeed. +* `opa version` executed inside the built validation image to confirm the source-built + binary works and reports the expected toolchain. +* `go build` of the controller, api, apis and ova-provider-server package trees against the + regenerated `vendor/` — clean. +* Weak-dependency package deltas measured in a real `centos:stream9` container rather than + assumed, including a check that the proposed install line adds nothing versus today. +* `microdnf` `--setopt` syntax verified against `ubi9-minimal:latest`. + +Not verified locally, and needing CI: + +* `virt-v2v` and `ovirt-populator` builds — both need a Red Hat subscription + (`subscription-manager refresh`); `virt-v2v` is not in the UBI repositories at all. + **This matters for the Tigera rebuild**: virt-v2v cannot be built from UBI without RHEL + entitlements, which is why the `Containerfile-upstream` variants use CentOS Stream. The + redistribution path needs deciding. +* The Konflux hermetic `-downstream` builds — `rpms.lock.yaml` regeneration needs + `rpm-lockfile-prototype` plus entitled repos. +* `.konflux/forklift-operator-bundle/go.mod` at `go 1.26.0` — consumed only by cachi2's + gomod prefetch (the operator-sdk-builder image ships no Go at all), and nothing it + produces is shipped in a runtime image, so it carries no CVE surface either way. + +## 10. Expected effect | Stage | Crit + High | |---|---| | Today | 3,043 | -| After P0 (VEX appliance kernel) | 689 | -| After P1–P3 (rebuild, trim, Go bump) | ~216 | -| Residual | 216, all Red Hat "No fix available" — VEX-documented, no code change possible | +| Appliance kernel (P0, deferred to ISS-level review) | 2,354 of that total | +| Addressable here | 689 | +| — closed by P1–P3 | up to 473 | +| — Red Hat "No fix available", needs VEX | 216 | + +Re-scan after the first rebuild to get the real number; the 473 is the count with a +published fix, not a guarantee every one is picked up in a single rebuild. diff --git a/go.mod b/go.mod index 7329147c6b..588339515b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/kubev2v/forklift -go 1.25.0 +go 1.26.0 require ( github.com/Masterminds/sprig/v3 v3.3.0 @@ -39,7 +39,7 @@ require ( github.com/vmware/govmomi v0.50.0 go.uber.org/mock v0.4.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.50.0 + golang.org/x/crypto v0.55.0 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.32.5 k8s.io/apiextensions-apiserver v0.32.5 @@ -151,15 +151,15 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.15.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect - golang.org/x/mod v0.35.0 // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/mod v0.38.0 // indirect + golang.org/x/net v0.58.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/term v0.45.0 // indirect + golang.org/x/text v0.41.0 // indirect golang.org/x/time v0.7.0 // indirect - golang.org/x/tools v0.44.0 // indirect + golang.org/x/tools v0.48.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/protobuf v1.36.7 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect diff --git a/go.sum b/go.sum index 8241b7a0d7..3a17ce70b0 100644 --- a/go.sum +++ b/go.sum @@ -665,8 +665,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= +golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= @@ -679,8 +679,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk= +golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -715,8 +715,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= +golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -731,8 +731,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= +golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -777,13 +777,13 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -794,8 +794,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= +golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -823,8 +823,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE= +golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go new file mode 100644 index 0000000000..d25979d9f5 --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -0,0 +1,825 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cryptobyte + +import ( + encoding_asn1 "encoding/asn1" + "fmt" + "math/big" + "reflect" + "time" + + "golang.org/x/crypto/cryptobyte/asn1" +) + +// This file contains ASN.1-related methods for String and Builder. + +// Builder + +// AddASN1Int64 appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1Int64(v int64) { + b.addASN1Signed(asn1.INTEGER, v) +} + +// AddASN1Int64WithTag appends a DER-encoded ASN.1 INTEGER with the +// given tag. +func (b *Builder) AddASN1Int64WithTag(v int64, tag asn1.Tag) { + b.addASN1Signed(tag, v) +} + +// AddASN1Enum appends a DER-encoded ASN.1 ENUMERATION. +func (b *Builder) AddASN1Enum(v int64) { + b.addASN1Signed(asn1.ENUM, v) +} + +func (b *Builder) addASN1Signed(tag asn1.Tag, v int64) { + b.AddASN1(tag, func(c *Builder) { + length := 1 + for i := v; i >= 0x80 || i < -0x80; i >>= 8 { + length++ + } + + for ; length > 0; length-- { + i := v >> uint((length-1)*8) & 0xff + c.AddUint8(uint8(i)) + } + }) +} + +// AddASN1Uint64 appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1Uint64(v uint64) { + b.AddASN1(asn1.INTEGER, func(c *Builder) { + length := 1 + for i := v; i >= 0x80; i >>= 8 { + length++ + } + + for ; length > 0; length-- { + i := v >> uint((length-1)*8) & 0xff + c.AddUint8(uint8(i)) + } + }) +} + +// AddASN1BigInt appends a DER-encoded ASN.1 INTEGER. +func (b *Builder) AddASN1BigInt(n *big.Int) { + if b.err != nil { + return + } + + b.AddASN1(asn1.INTEGER, func(c *Builder) { + if n.Sign() < 0 { + // A negative number has to be converted to two's-complement form. So we + // invert and subtract 1. If the most-significant-bit isn't set then + // we'll need to pad the beginning with 0xff in order to keep the number + // negative. + nMinus1 := new(big.Int).Neg(n) + nMinus1.Sub(nMinus1, bigOne) + bytes := nMinus1.Bytes() + for i := range bytes { + bytes[i] ^= 0xff + } + if len(bytes) == 0 || bytes[0]&0x80 == 0 { + c.add(0xff) + } + c.add(bytes...) + } else if n.Sign() == 0 { + c.add(0) + } else { + bytes := n.Bytes() + if bytes[0]&0x80 != 0 { + c.add(0) + } + c.add(bytes...) + } + }) +} + +// AddASN1OctetString appends a DER-encoded ASN.1 OCTET STRING. +func (b *Builder) AddASN1OctetString(bytes []byte) { + b.AddASN1(asn1.OCTET_STRING, func(c *Builder) { + c.AddBytes(bytes) + }) +} + +const generalizedTimeFormatStr = "20060102150405Z0700" + +// AddASN1GeneralizedTime appends a DER-encoded ASN.1 GENERALIZEDTIME. +func (b *Builder) AddASN1GeneralizedTime(t time.Time) { + if t.Year() < 0 || t.Year() > 9999 { + b.err = fmt.Errorf("cryptobyte: cannot represent %v as a GeneralizedTime", t) + return + } + b.AddASN1(asn1.GeneralizedTime, func(c *Builder) { + c.AddBytes([]byte(t.Format(generalizedTimeFormatStr))) + }) +} + +// AddASN1UTCTime appends a DER-encoded ASN.1 UTCTime. +func (b *Builder) AddASN1UTCTime(t time.Time) { + b.AddASN1(asn1.UTCTime, func(c *Builder) { + // As utilized by the X.509 profile, UTCTime can only + // represent the years 1950 through 2049. + if t.Year() < 1950 || t.Year() >= 2050 { + b.err = fmt.Errorf("cryptobyte: cannot represent %v as a UTCTime", t) + return + } + c.AddBytes([]byte(t.Format(defaultUTCTimeFormatStr))) + }) +} + +// AddASN1BitString appends a DER-encoded ASN.1 BIT STRING. This does not +// support BIT STRINGs that are not a whole number of bytes. +func (b *Builder) AddASN1BitString(data []byte) { + b.AddASN1(asn1.BIT_STRING, func(b *Builder) { + b.AddUint8(0) + b.AddBytes(data) + }) +} + +func (b *Builder) addBase128Int(n int64) { + var length int + if n == 0 { + length = 1 + } else { + for i := n; i > 0; i >>= 7 { + length++ + } + } + + for i := length - 1; i >= 0; i-- { + o := byte(n >> uint(i*7)) + o &= 0x7f + if i != 0 { + o |= 0x80 + } + + b.add(o) + } +} + +func isValidOID(oid encoding_asn1.ObjectIdentifier) bool { + if len(oid) < 2 { + return false + } + + if oid[0] > 2 || (oid[0] <= 1 && oid[1] >= 40) { + return false + } + + for _, v := range oid { + if v < 0 { + return false + } + } + + return true +} + +func (b *Builder) AddASN1ObjectIdentifier(oid encoding_asn1.ObjectIdentifier) { + b.AddASN1(asn1.OBJECT_IDENTIFIER, func(b *Builder) { + if !isValidOID(oid) { + b.err = fmt.Errorf("cryptobyte: invalid OID: %v", oid) + return + } + + b.addBase128Int(int64(oid[0])*40 + int64(oid[1])) + for _, v := range oid[2:] { + b.addBase128Int(int64(v)) + } + }) +} + +func (b *Builder) AddASN1Boolean(v bool) { + b.AddASN1(asn1.BOOLEAN, func(b *Builder) { + if v { + b.AddUint8(0xff) + } else { + b.AddUint8(0) + } + }) +} + +func (b *Builder) AddASN1NULL() { + b.add(uint8(asn1.NULL), 0) +} + +// MarshalASN1 calls encoding_asn1.Marshal on its input and appends the result if +// successful or records an error if one occurred. +func (b *Builder) MarshalASN1(v interface{}) { + // NOTE(martinkr): This is somewhat of a hack to allow propagation of + // encoding_asn1.Marshal errors into Builder.err. N.B. if you call MarshalASN1 with a + // value embedded into a struct, its tag information is lost. + if b.err != nil { + return + } + bytes, err := encoding_asn1.Marshal(v) + if err != nil { + b.err = err + return + } + b.AddBytes(bytes) +} + +// AddASN1 appends an ASN.1 object. The object is prefixed with the given tag. +// Tags greater than 30 are not supported and result in an error (i.e. +// low-tag-number form only). The child builder passed to the +// BuilderContinuation can be used to build the content of the ASN.1 object. +func (b *Builder) AddASN1(tag asn1.Tag, f BuilderContinuation) { + if b.err != nil { + return + } + // Identifiers with the low five bits set indicate high-tag-number format + // (two or more octets), which we don't support. + if tag&0x1f == 0x1f { + b.err = fmt.Errorf("cryptobyte: high-tag number identifier octets not supported: 0x%x", tag) + return + } + b.AddUint8(uint8(tag)) + b.addLengthPrefixed(1, true, f) +} + +// String + +// ReadASN1Boolean decodes an ASN.1 BOOLEAN and converts it to a boolean +// representation into out and advances. It reports whether the read +// was successful. +func (s *String) ReadASN1Boolean(out *bool) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.BOOLEAN) || len(bytes) != 1 { + return false + } + + switch bytes[0] { + case 0: + *out = false + case 0xff: + *out = true + default: + return false + } + + return true +} + +// ReadASN1Integer decodes an ASN.1 INTEGER into out and advances. If out does +// not point to an integer, to a big.Int, or to a []byte it panics. Only +// positive and zero values can be decoded into []byte, and they are returned as +// big-endian binary values that share memory with s. Positive values will have +// no leading zeroes, and zero will be returned as a single zero byte. +// ReadASN1Integer reports whether the read was successful. +func (s *String) ReadASN1Integer(out interface{}) bool { + switch out := out.(type) { + case *int, *int8, *int16, *int32, *int64: + var i int64 + if !s.readASN1Int64(&i) || reflect.ValueOf(out).Elem().OverflowInt(i) { + return false + } + reflect.ValueOf(out).Elem().SetInt(i) + return true + case *uint, *uint8, *uint16, *uint32, *uint64: + var u uint64 + if !s.readASN1Uint64(&u) || reflect.ValueOf(out).Elem().OverflowUint(u) { + return false + } + reflect.ValueOf(out).Elem().SetUint(u) + return true + case *big.Int: + return s.readASN1BigInt(out) + case *[]byte: + return s.readASN1Bytes(out) + default: + panic("out does not point to an integer type") + } +} + +func checkASN1Integer(bytes []byte) bool { + if len(bytes) == 0 { + // An INTEGER is encoded with at least one octet. + return false + } + if len(bytes) == 1 { + return true + } + if bytes[0] == 0 && bytes[1]&0x80 == 0 || bytes[0] == 0xff && bytes[1]&0x80 == 0x80 { + // Value is not minimally encoded. + return false + } + return true +} + +var bigOne = big.NewInt(1) + +func (s *String) readASN1BigInt(out *big.Int) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) { + return false + } + if bytes[0]&0x80 == 0x80 { + // Negative number. + neg := make([]byte, len(bytes)) + for i, b := range bytes { + neg[i] = ^b + } + out.SetBytes(neg) + out.Add(out, bigOne) + out.Neg(out) + } else { + out.SetBytes(bytes) + } + return true +} + +func (s *String) readASN1Bytes(out *[]byte) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) { + return false + } + if bytes[0]&0x80 == 0x80 { + return false + } + for len(bytes) > 1 && bytes[0] == 0 { + bytes = bytes[1:] + } + *out = bytes + return true +} + +func (s *String) readASN1Int64(out *int64) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Signed(out, bytes) { + return false + } + return true +} + +func asn1Signed(out *int64, n []byte) bool { + length := len(n) + if length > 8 { + return false + } + for i := 0; i < length; i++ { + *out <<= 8 + *out |= int64(n[i]) + } + // Shift up and down in order to sign extend the result. + *out <<= 64 - uint8(length)*8 + *out >>= 64 - uint8(length)*8 + return true +} + +func (s *String) readASN1Uint64(out *uint64) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.INTEGER) || !checkASN1Integer(bytes) || !asn1Unsigned(out, bytes) { + return false + } + return true +} + +func asn1Unsigned(out *uint64, n []byte) bool { + length := len(n) + if length > 9 || length == 9 && n[0] != 0 { + // Too large for uint64. + return false + } + if n[0]&0x80 != 0 { + // Negative number. + return false + } + for i := 0; i < length; i++ { + *out <<= 8 + *out |= uint64(n[i]) + } + return true +} + +// ReadASN1Int64WithTag decodes an ASN.1 INTEGER with the given tag into out +// and advances. It reports whether the read was successful and resulted in a +// value that can be represented in an int64. +func (s *String) ReadASN1Int64WithTag(out *int64, tag asn1.Tag) bool { + var bytes String + return s.ReadASN1(&bytes, tag) && checkASN1Integer(bytes) && asn1Signed(out, bytes) +} + +// ReadASN1Enum decodes an ASN.1 ENUMERATION into out and advances. It reports +// whether the read was successful. +func (s *String) ReadASN1Enum(out *int) bool { + var bytes String + var i int64 + if !s.ReadASN1(&bytes, asn1.ENUM) || !checkASN1Integer(bytes) || !asn1Signed(&i, bytes) { + return false + } + if int64(int(i)) != i { + return false + } + *out = int(i) + return true +} + +func (s *String) readBase128Int(out *int) bool { + ret := 0 + for i := 0; len(*s) > 0; i++ { + if i == 5 { + return false + } + // Avoid overflowing int on a 32-bit platform. + // We don't want different behavior based on the architecture. + if ret >= 1<<(31-7) { + return false + } + ret <<= 7 + b := s.read(1)[0] + + // ITU-T X.690, section 8.19.2: + // The subidentifier shall be encoded in the fewest possible octets, + // that is, the leading octet of the subidentifier shall not have the value 0x80. + if i == 0 && b == 0x80 { + return false + } + + ret |= int(b & 0x7f) + if b&0x80 == 0 { + *out = ret + return true + } + } + return false // truncated +} + +// ReadASN1ObjectIdentifier decodes an ASN.1 OBJECT IDENTIFIER into out and +// advances. It reports whether the read was successful. +func (s *String) ReadASN1ObjectIdentifier(out *encoding_asn1.ObjectIdentifier) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.OBJECT_IDENTIFIER) || len(bytes) == 0 { + return false + } + + // In the worst case, we get two elements from the first byte (which is + // encoded differently) and then every varint is a single byte long. + components := make([]int, len(bytes)+1) + + // The first varint is 40*value1 + value2: + // According to this packing, value1 can take the values 0, 1 and 2 only. + // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, + // then there are no restrictions on value2. + var v int + if !bytes.readBase128Int(&v) { + return false + } + if v < 80 { + components[0] = v / 40 + components[1] = v % 40 + } else { + components[0] = 2 + components[1] = v - 80 + } + + i := 2 + for ; len(bytes) > 0; i++ { + if !bytes.readBase128Int(&v) { + return false + } + components[i] = v + } + *out = components[:i] + return true +} + +// ReadASN1GeneralizedTime decodes an ASN.1 GENERALIZEDTIME into out and +// advances. It reports whether the read was successful. +func (s *String) ReadASN1GeneralizedTime(out *time.Time) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.GeneralizedTime) { + return false + } + t := string(bytes) + res, err := time.Parse(generalizedTimeFormatStr, t) + if err != nil { + return false + } + if serialized := res.Format(generalizedTimeFormatStr); serialized != t { + return false + } + *out = res + return true +} + +const defaultUTCTimeFormatStr = "060102150405Z0700" + +// ReadASN1UTCTime decodes an ASN.1 UTCTime into out and advances. +// It reports whether the read was successful. +func (s *String) ReadASN1UTCTime(out *time.Time) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.UTCTime) { + return false + } + t := string(bytes) + + formatStr := defaultUTCTimeFormatStr + var err error + res, err := time.Parse(formatStr, t) + if err != nil { + // Fallback to minute precision if we can't parse second + // precision. If we are following X.509 or X.690 we shouldn't + // support this, but we do. + formatStr = "0601021504Z0700" + res, err = time.Parse(formatStr, t) + } + if err != nil { + return false + } + + if serialized := res.Format(formatStr); serialized != t { + return false + } + + if res.Year() >= 2050 { + // UTCTime interprets the low order digits 50-99 as 1950-99. + // This only applies to its use in the X.509 profile. + // See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 + res = res.AddDate(-100, 0, 0) + } + *out = res + return true +} + +// ReadASN1BitString decodes an ASN.1 BIT STRING into out and advances. +// It reports whether the read was successful. +func (s *String) ReadASN1BitString(out *encoding_asn1.BitString) bool { + var bytes String + if !s.ReadASN1(&bytes, asn1.BIT_STRING) || len(bytes) == 0 || + len(bytes)*8/8 != len(bytes) { + return false + } + + paddingBits := bytes[0] + bytes = bytes[1:] + if paddingBits > 7 || + len(bytes) == 0 && paddingBits != 0 || + len(bytes) > 0 && bytes[len(bytes)-1]&(1< 4 || len(*s) < int(2+lenLen) { + return false + } + + lenBytes := String((*s)[2 : 2+lenLen]) + if !lenBytes.readUnsigned(&len32, int(lenLen)) { + return false + } + + // ITU-T X.690 section 10.1 (DER length forms) requires encoding the length + // with the minimum number of octets. + if len32 < 128 { + // Length should have used short-form encoding. + return false + } + if len32>>((lenLen-1)*8) == 0 { + // Leading octet is 0. Length should have been at least one byte shorter. + return false + } + + headerLen = 2 + uint32(lenLen) + if headerLen+len32 < len32 { + // Overflow. + return false + } + length = headerLen + len32 + } + + if int(length) < 0 || !s.ReadBytes((*[]byte)(out), int(length)) { + return false + } + if skipHeader && !out.Skip(int(headerLen)) { + panic("cryptobyte: internal error") + } + + return true +} diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go new file mode 100644 index 0000000000..90ef6a241d --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1/asn1.go @@ -0,0 +1,46 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package asn1 contains supporting types for parsing and building ASN.1 +// messages with the cryptobyte package. +package asn1 + +// Tag represents an ASN.1 identifier octet, consisting of a tag number +// (indicating a type) and class (such as context-specific or constructed). +// +// Methods in the cryptobyte package only support the low-tag-number form, i.e. +// a single identifier octet with bits 7-8 encoding the class and bits 1-6 +// encoding the tag number. +type Tag uint8 + +const ( + classConstructed = 0x20 + classContextSpecific = 0x80 +) + +// Constructed returns t with the constructed class bit set. +func (t Tag) Constructed() Tag { return t | classConstructed } + +// ContextSpecific returns t with the context-specific class bit set. +func (t Tag) ContextSpecific() Tag { return t | classContextSpecific } + +// The following is a list of standard tag and class combinations. +const ( + BOOLEAN = Tag(1) + INTEGER = Tag(2) + BIT_STRING = Tag(3) + OCTET_STRING = Tag(4) + NULL = Tag(5) + OBJECT_IDENTIFIER = Tag(6) + ENUM = Tag(10) + UTF8String = Tag(12) + SEQUENCE = Tag(16 | classConstructed) + SET = Tag(17 | classConstructed) + PrintableString = Tag(19) + T61String = Tag(20) + IA5String = Tag(22) + UTCTime = Tag(23) + GeneralizedTime = Tag(24) + GeneralString = Tag(27) +) diff --git a/vendor/golang.org/x/crypto/cryptobyte/builder.go b/vendor/golang.org/x/crypto/cryptobyte/builder.go new file mode 100644 index 0000000000..cf254f5f1e --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/builder.go @@ -0,0 +1,350 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cryptobyte + +import ( + "errors" + "fmt" +) + +// A Builder builds byte strings from fixed-length and length-prefixed values. +// Builders either allocate space as needed, or are ‘fixed’, which means that +// they write into a given buffer and produce an error if it's exhausted. +// +// The zero value is a usable Builder that allocates space as needed. +// +// Simple values are marshaled and appended to a Builder using methods on the +// Builder. Length-prefixed values are marshaled by providing a +// BuilderContinuation, which is a function that writes the inner contents of +// the value to a given Builder. See the documentation for BuilderContinuation +// for details. +type Builder struct { + err error + result []byte + fixedSize bool + child *Builder + offset int + pendingLenLen int + pendingIsASN1 bool + inContinuation *bool +} + +// NewBuilder creates a Builder that appends its output to the given buffer. +// Like append(), the slice will be reallocated if its capacity is exceeded. +// Use Bytes to get the final buffer. +func NewBuilder(buffer []byte) *Builder { + return &Builder{ + result: buffer, + } +} + +// NewFixedBuilder creates a Builder that appends its output into the given +// buffer. This builder does not reallocate the output buffer. Writes that +// would exceed the buffer's capacity are treated as an error. +func NewFixedBuilder(buffer []byte) *Builder { + return &Builder{ + result: buffer, + fixedSize: true, + } +} + +// SetError sets the value to be returned as the error from Bytes. Writes +// performed after calling SetError are ignored. +func (b *Builder) SetError(err error) { + b.err = err +} + +// Bytes returns the bytes written by the builder or an error if one has +// occurred during building. +func (b *Builder) Bytes() ([]byte, error) { + if b.err != nil { + return nil, b.err + } + return b.result[b.offset:], nil +} + +// BytesOrPanic returns the bytes written by the builder or panics if an error +// has occurred during building. +func (b *Builder) BytesOrPanic() []byte { + if b.err != nil { + panic(b.err) + } + return b.result[b.offset:] +} + +// AddUint8 appends an 8-bit value to the byte string. +func (b *Builder) AddUint8(v uint8) { + b.add(byte(v)) +} + +// AddUint16 appends a big-endian, 16-bit value to the byte string. +func (b *Builder) AddUint16(v uint16) { + b.add(byte(v>>8), byte(v)) +} + +// AddUint24 appends a big-endian, 24-bit value to the byte string. The highest +// byte of the 32-bit input value is silently truncated. +func (b *Builder) AddUint24(v uint32) { + b.add(byte(v>>16), byte(v>>8), byte(v)) +} + +// AddUint32 appends a big-endian, 32-bit value to the byte string. +func (b *Builder) AddUint32(v uint32) { + b.add(byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + +// AddUint48 appends a big-endian, 48-bit value to the byte string. +func (b *Builder) AddUint48(v uint64) { + b.add(byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + +// AddUint64 appends a big-endian, 64-bit value to the byte string. +func (b *Builder) AddUint64(v uint64) { + b.add(byte(v>>56), byte(v>>48), byte(v>>40), byte(v>>32), byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) +} + +// AddBytes appends a sequence of bytes to the byte string. +func (b *Builder) AddBytes(v []byte) { + b.add(v...) +} + +// BuilderContinuation is a continuation-passing interface for building +// length-prefixed byte sequences. Builder methods for length-prefixed +// sequences (AddUint8LengthPrefixed etc) will invoke the BuilderContinuation +// supplied to them. The child builder passed to the continuation can be used +// to build the content of the length-prefixed sequence. For example: +// +// parent := cryptobyte.NewBuilder() +// parent.AddUint8LengthPrefixed(func (child *Builder) { +// child.AddUint8(42) +// child.AddUint8LengthPrefixed(func (grandchild *Builder) { +// grandchild.AddUint8(5) +// }) +// }) +// +// It is an error to write more bytes to the child than allowed by the reserved +// length prefix. After the continuation returns, the child must be considered +// invalid, i.e. users must not store any copies or references of the child +// that outlive the continuation. +// +// If the continuation panics with a value of type BuildError then the inner +// error will be returned as the error from Bytes. If the child panics +// otherwise then Bytes will repanic with the same value. +type BuilderContinuation func(child *Builder) + +// BuildError wraps an error. If a BuilderContinuation panics with this value, +// the panic will be recovered and the inner error will be returned from +// Builder.Bytes. +type BuildError struct { + Err error +} + +// AddUint8LengthPrefixed adds a 8-bit length-prefixed byte sequence. +func (b *Builder) AddUint8LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(1, false, f) +} + +// AddUint16LengthPrefixed adds a big-endian, 16-bit length-prefixed byte sequence. +func (b *Builder) AddUint16LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(2, false, f) +} + +// AddUint24LengthPrefixed adds a big-endian, 24-bit length-prefixed byte sequence. +func (b *Builder) AddUint24LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(3, false, f) +} + +// AddUint32LengthPrefixed adds a big-endian, 32-bit length-prefixed byte sequence. +func (b *Builder) AddUint32LengthPrefixed(f BuilderContinuation) { + b.addLengthPrefixed(4, false, f) +} + +func (b *Builder) callContinuation(f BuilderContinuation, arg *Builder) { + if !*b.inContinuation { + *b.inContinuation = true + + defer func() { + *b.inContinuation = false + + r := recover() + if r == nil { + return + } + + if buildError, ok := r.(BuildError); ok { + b.err = buildError.Err + } else { + panic(r) + } + }() + } + + f(arg) +} + +func (b *Builder) addLengthPrefixed(lenLen int, isASN1 bool, f BuilderContinuation) { + // Subsequent writes can be ignored if the builder has encountered an error. + if b.err != nil { + return + } + + offset := len(b.result) + b.add(make([]byte, lenLen)...) + + if b.inContinuation == nil { + b.inContinuation = new(bool) + } + + b.child = &Builder{ + result: b.result, + fixedSize: b.fixedSize, + offset: offset, + pendingLenLen: lenLen, + pendingIsASN1: isASN1, + inContinuation: b.inContinuation, + } + + b.callContinuation(f, b.child) + b.flushChild() + if b.child != nil { + panic("cryptobyte: internal error") + } +} + +func (b *Builder) flushChild() { + if b.child == nil { + return + } + b.child.flushChild() + child := b.child + b.child = nil + + if child.err != nil { + b.err = child.err + return + } + + length := len(child.result) - child.pendingLenLen - child.offset + + if length < 0 { + panic("cryptobyte: internal error") // result unexpectedly shrunk + } + + if child.pendingIsASN1 { + // For ASN.1, we reserved a single byte for the length. If that turned out + // to be incorrect, we have to move the contents along in order to make + // space. + if child.pendingLenLen != 1 { + panic("cryptobyte: internal error") + } + var lenLen, lenByte uint8 + if int64(length) > 0xfffffffe { + b.err = errors.New("pending ASN.1 child too long") + return + } else if length > 0xffffff { + lenLen = 5 + lenByte = 0x80 | 4 + } else if length > 0xffff { + lenLen = 4 + lenByte = 0x80 | 3 + } else if length > 0xff { + lenLen = 3 + lenByte = 0x80 | 2 + } else if length > 0x7f { + lenLen = 2 + lenByte = 0x80 | 1 + } else { + lenLen = 1 + lenByte = uint8(length) + length = 0 + } + + // Insert the initial length byte, make space for successive length bytes, + // and adjust the offset. + child.result[child.offset] = lenByte + extraBytes := int(lenLen - 1) + if extraBytes != 0 { + child.add(make([]byte, extraBytes)...) + childStart := child.offset + child.pendingLenLen + copy(child.result[childStart+extraBytes:], child.result[childStart:]) + } + child.offset++ + child.pendingLenLen = extraBytes + } + + l := length + for i := child.pendingLenLen - 1; i >= 0; i-- { + child.result[child.offset+i] = uint8(l) + l >>= 8 + } + if l != 0 { + b.err = fmt.Errorf("cryptobyte: pending child length %d exceeds %d-byte length prefix", length, child.pendingLenLen) + return + } + + if b.fixedSize && &b.result[0] != &child.result[0] { + panic("cryptobyte: BuilderContinuation reallocated a fixed-size buffer") + } + + b.result = child.result +} + +func (b *Builder) add(bytes ...byte) { + if b.err != nil { + return + } + if b.child != nil { + panic("cryptobyte: attempted write while child is pending") + } + if len(b.result)+len(bytes) < len(bytes) { + b.err = errors.New("cryptobyte: length overflow") + } + if b.fixedSize && len(b.result)+len(bytes) > cap(b.result) { + b.err = errors.New("cryptobyte: Builder is exceeding its fixed-size buffer") + return + } + b.result = append(b.result, bytes...) +} + +// Unwrite rolls back non-negative n bytes written directly to the Builder. +// An attempt by a child builder passed to a continuation to unwrite bytes +// from its parent will panic. +func (b *Builder) Unwrite(n int) { + if b.err != nil { + return + } + if b.child != nil { + panic("cryptobyte: attempted unwrite while child is pending") + } + length := len(b.result) - b.pendingLenLen - b.offset + if length < 0 { + panic("cryptobyte: internal error") + } + if n < 0 { + panic("cryptobyte: attempted to unwrite negative number of bytes") + } + if n > length { + panic("cryptobyte: attempted to unwrite more than was written") + } + b.result = b.result[:len(b.result)-n] +} + +// A MarshalingValue marshals itself into a Builder. +type MarshalingValue interface { + // Marshal is called by Builder.AddValue. It receives a pointer to a builder + // to marshal itself into. It may return an error that occurred during + // marshaling, such as unset or invalid values. + Marshal(b *Builder) error +} + +// AddValue calls Marshal on v, passing a pointer to the builder to append to. +// If Marshal returns an error, it is set on the Builder so that subsequent +// appends don't have an effect. +func (b *Builder) AddValue(v MarshalingValue) { + err := v.Marshal(b) + if err != nil { + b.err = err + } +} diff --git a/vendor/golang.org/x/crypto/cryptobyte/string.go b/vendor/golang.org/x/crypto/cryptobyte/string.go new file mode 100644 index 0000000000..4b0f8097f9 --- /dev/null +++ b/vendor/golang.org/x/crypto/cryptobyte/string.go @@ -0,0 +1,183 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cryptobyte contains types that help with parsing and constructing +// length-prefixed, binary messages, including ASN.1 DER. (The asn1 subpackage +// contains useful ASN.1 constants.) +// +// The String type is for parsing. It wraps a []byte slice and provides helper +// functions for consuming structures, value by value. +// +// The Builder type is for constructing messages. It providers helper functions +// for appending values and also for appending length-prefixed submessages – +// without having to worry about calculating the length prefix ahead of time. +// +// See the documentation and examples for the Builder and String types to get +// started. +package cryptobyte + +// String represents a string of bytes. It provides methods for parsing +// fixed-length and length-prefixed values from it. +type String []byte + +// read advances a String by n bytes and returns them. If less than n bytes +// remain, it returns nil. +func (s *String) read(n int) []byte { + if len(*s) < n || n < 0 { + return nil + } + v := (*s)[:n] + *s = (*s)[n:] + return v +} + +// Skip advances the String by n byte and reports whether it was successful. +func (s *String) Skip(n int) bool { + return s.read(n) != nil +} + +// ReadUint8 decodes an 8-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint8(out *uint8) bool { + v := s.read(1) + if v == nil { + return false + } + *out = uint8(v[0]) + return true +} + +// ReadUint16 decodes a big-endian, 16-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint16(out *uint16) bool { + v := s.read(2) + if v == nil { + return false + } + *out = uint16(v[0])<<8 | uint16(v[1]) + return true +} + +// ReadUint24 decodes a big-endian, 24-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint24(out *uint32) bool { + v := s.read(3) + if v == nil { + return false + } + *out = uint32(v[0])<<16 | uint32(v[1])<<8 | uint32(v[2]) + return true +} + +// ReadUint32 decodes a big-endian, 32-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint32(out *uint32) bool { + v := s.read(4) + if v == nil { + return false + } + *out = uint32(v[0])<<24 | uint32(v[1])<<16 | uint32(v[2])<<8 | uint32(v[3]) + return true +} + +// ReadUint48 decodes a big-endian, 48-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint48(out *uint64) bool { + v := s.read(6) + if v == nil { + return false + } + *out = uint64(v[0])<<40 | uint64(v[1])<<32 | uint64(v[2])<<24 | uint64(v[3])<<16 | uint64(v[4])<<8 | uint64(v[5]) + return true +} + +// ReadUint64 decodes a big-endian, 64-bit value into out and advances over it. +// It reports whether the read was successful. +func (s *String) ReadUint64(out *uint64) bool { + v := s.read(8) + if v == nil { + return false + } + *out = uint64(v[0])<<56 | uint64(v[1])<<48 | uint64(v[2])<<40 | uint64(v[3])<<32 | uint64(v[4])<<24 | uint64(v[5])<<16 | uint64(v[6])<<8 | uint64(v[7]) + return true +} + +func (s *String) readUnsigned(out *uint32, length int) bool { + v := s.read(length) + if v == nil { + return false + } + var result uint32 + for i := 0; i < length; i++ { + result <<= 8 + result |= uint32(v[i]) + } + *out = result + return true +} + +func (s *String) readLengthPrefixed(lenLen int, outChild *String) bool { + lenBytes := s.read(lenLen) + if lenBytes == nil { + return false + } + var length uint32 + for _, b := range lenBytes { + length = length << 8 + length = length | uint32(b) + } + v := s.read(int(length)) + if v == nil { + return false + } + *outChild = v + return true +} + +// ReadUint8LengthPrefixed reads the content of an 8-bit length-prefixed value +// into out and advances over it. It reports whether the read was successful. +func (s *String) ReadUint8LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(1, out) +} + +// ReadUint16LengthPrefixed reads the content of a big-endian, 16-bit +// length-prefixed value into out and advances over it. It reports whether the +// read was successful. +func (s *String) ReadUint16LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(2, out) +} + +// ReadUint24LengthPrefixed reads the content of a big-endian, 24-bit +// length-prefixed value into out and advances over it. It reports whether +// the read was successful. +func (s *String) ReadUint24LengthPrefixed(out *String) bool { + return s.readLengthPrefixed(3, out) +} + +// ReadBytes reads n bytes into out and advances over them. It reports +// whether the read was successful. +func (s *String) ReadBytes(out *[]byte, n int) bool { + v := s.read(n) + if v == nil { + return false + } + *out = v + return true +} + +// CopyBytes copies len(out) bytes into out and advances over them. It reports +// whether the copy operation was successful +func (s *String) CopyBytes(out []byte) bool { + n := len(out) + v := s.read(n) + if v == nil { + return false + } + return copy(out, v) == n +} + +// Empty reports whether the string does not contain any bytes. +func (s String) Empty() bool { + return len(s) == 0 +} diff --git a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go index 8d99551fee..b1da45687c 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/mac_noasm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !s390x) || !gc || purego +//go:build (!amd64 && !loong64 && !ppc64le && !ppc64 && !riscv64 && !s390x) || !gc || purego package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go b/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go index 315b84ac39..55041bf51d 100644 --- a/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_asm.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le) +//go:build gc && !purego && (amd64 || loong64 || ppc64 || ppc64le || riscv64) package poly1305 diff --git a/vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s b/vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s new file mode 100644 index 0000000000..ce5eb3d43d --- /dev/null +++ b/vendor/golang.org/x/crypto/internal/poly1305/sum_riscv64.s @@ -0,0 +1,158 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build gc && !purego + +#define LOAD64U(base, offset, t0, t1, t2, t3, dst) \ + MOVBU (offset+0*1)(base), t0; \ + MOVBU (offset+1*1)(base), t1; \ + MOVBU (offset+2*1)(base), t2; \ + MOVBU (offset+3*1)(base), t3; \ + SLL $8, t1; \ + SLL $16, t2; \ + SLL $24, t3; \ + OR t1, t0; \ + OR t3, t2; \ + OR t2, t0, dst; \ + MOVBU (offset+4*1)(base), t0; \ + MOVBU (offset+5*1)(base), t1; \ + MOVBU (offset+6*1)(base), t2; \ + MOVBU (offset+7*1)(base), t3; \ + SLL $32, t0; \ + SLL $40, t1; \ + SLL $48, t2; \ + SLL $56, t3; \ + OR t1, t0; \ + OR t3, t2; \ + OR t2, t0; \ + OR t0, dst + +// func update(state *macState, msg []byte) +TEXT ·update(SB), $0-32 + MOV state+0(FP), X5 + MOV msg_base+8(FP), X6 + MOV msg_len+16(FP), X7 + + MOV $16, X8 + + AND $7, X6, X28 + + MOV (0*8)(X5), X9 // h0 + MOV (1*8)(X5), X10 // h1 + MOV (2*8)(X5), X11 // h2 + MOV (3*8)(X5), X12 // r0 + MOV (4*8)(X5), X13 // r1 + + BLT X7, X8, tail + +loop: + BEQZ X28, aligned_load + + LOAD64U(X6, 0*8, X16, X18, X19, X20, X15) // msg[0:8] + LOAD64U(X6, 1*8, X16, X18, X19, X20, X17) // msg[8:16] + JMP block + +aligned_load: + MOV (0*8)(X6), X15 // msg[0:8] + MOV (1*8)(X6), X17 // msg[8:16] + +block: + ADD X15, X9 // h0 (x1 + y1 = z1', if z1' < x1 then z1' overflow) + SLTU X15, X9, X19 // h0.carry + ADD X17, X10, X22 + SLTU X17, X22, X23 + ADD X22, X19, X10 // h1 + SLTU X22, X10, X19 + OR X23, X19 // h1.carry + ADD $1, X19 + ADD X19, X11 // h2 + + ADD $16, X6 // msg = msg[16:] + +multiply: + MULHU X9, X12, X16 // h0r0.hi + MUL X9, X12, X15 // h0r0.lo + MULHU X10, X12, X17 // h1r0.hi + MUL X10, X12, X14 // h1r0.lo + ADD X14, X16 + SLTU X14, X16, X19 + ADD X19, X17 + MUL X11, X12, X20 + ADD X17, X20 + MULHU X9, X13, X17 // h0r1.hi + MUL X9, X13, X14 // h0r1.lo + ADD X14, X16 + SLTU X14, X16, X19 + ADD X19, X17 + MOV X17, X9 + MUL X11, X13, X21 // h2r1 + MULHU X10, X13, X17 // h1r1.hi + MUL X10, X13, X14 // h1r1.lo + ADD X14, X20 + ADD X17, X21, X22 + SLTU X14, X20, X19 + ADD X22, X19, X21 + ADD X9, X20 + SLTU X9, X20, X19 + ADD X19, X21 + AND $3, X20, X11 + AND $-4, X20, X18 + ADD X18, X15, X9 + ADD X21, X16, X22 + SLTU X18, X9, X19 + SLTU X21, X22, X23 + ADD X22, X19, X10 + SLTU X22, X10, X19 + OR X19, X23, X19 + ADD X19, X11 + SLL $62, X21, X22 + SRL $2, X20, X23 + SRL $2, X21, X21 + OR X22, X23, X20 + ADD X20, X9, X9 + ADD X21, X10, X22 + SLTU X20, X9, X19 + SLTU X21, X22, X23 + ADD X22, X19, X10 + SLTU X22, X10, X19 + OR X19, X23, X19 + ADD X19, X11, X11 + + SUB $16, X7, X7 + BGE X7, X8, loop + +tail: + BEQ X7, X0, done + MOV $1, X15 + MOV $0, X16 + ADD X7, X6, X6 + +flush_buffer: + MOVBU -1(X6), X20 + SRL $56, X15, X19 + SLL $8, X16, X23 + SLL $8, X15, X15 + OR X19, X23, X16 + XOR X20, X15 + SUB $1, X7, X7 + SUB $1, X6, X6 + BNE X7, X0, flush_buffer + + ADD X15, X9 + SLTU X15, X9, X19 + ADD X16, X10, X22 + SLTU X16, X22, X23 + ADD X22, X19, X10 + SLTU X22, X10, X19 + OR X23, X19 + ADD X19, X11 + + MOV $16, X7 + JMP multiply + +done: + MOV X9, (0*8)(X5) // h0 + MOV X10, (1*8)(X5) + MOV X11, (2*8)(X5) + RET diff --git a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go index 28cd99c7f3..b332122033 100644 --- a/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go +++ b/vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go @@ -2,24 +2,17 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* -Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC -2898 / PKCS #5 v2.0. - -A key derivation function is useful when encrypting data based on a password -or any other not-fully-random data. It uses a pseudorandom function to derive -a secure encryption key based on the password. - -While v2.0 of the standard defines only one pseudorandom function to use, -HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved -Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To -choose, you can pass the `New` functions from the different SHA packages to -pbkdf2.Key. -*/ +// Package pbkdf2 implements the key derivation function PBKDF2 as defined in +// RFC 8018 (PKCS #5 v2.1). +// +// This package is a wrapper for the PBKDF2 implementation in the +// [crypto/pbkdf2] package. It is [frozen] and is not accepting new features. +// +// [frozen]: https://go.dev/wiki/Frozen package pbkdf2 import ( - "crypto/hmac" + "crypto/pbkdf2" "hash" ) @@ -27,51 +20,11 @@ import ( // []byte of length keylen that can be used as cryptographic key. The key is // derived based on the method described as PBKDF2 with the HMAC variant using // the supplied hash function. -// -// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you -// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by -// doing: -// -// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) -// -// Remember to get a good random salt. At least 8 bytes is recommended by the -// RFC. -// -// Using a higher iteration count will increase the cost of an exhaustive -// search but will also make derivation proportionally slower. func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { - prf := hmac.New(h, password) - hashLen := prf.Size() - numBlocks := (keyLen + hashLen - 1) / hashLen - - var buf [4]byte - dk := make([]byte, 0, numBlocks*hashLen) - U := make([]byte, hashLen) - for block := 1; block <= numBlocks; block++ { - // N.B.: || means concatenation, ^ means XOR - // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter - // U_1 = PRF(password, salt || uint(i)) - prf.Reset() - prf.Write(salt) - buf[0] = byte(block >> 24) - buf[1] = byte(block >> 16) - buf[2] = byte(block >> 8) - buf[3] = byte(block) - prf.Write(buf[:4]) - dk = prf.Sum(dk) - T := dk[len(dk)-hashLen:] - copy(U, T) - - // U_n = PRF(password, U_(n-1)) - for n := 2; n <= iter; n++ { - prf.Reset() - prf.Write(U) - U = U[:0] - U = prf.Sum(U) - for x := range U { - T[x] ^= U[x] - } - } + out, err := pbkdf2.Key(h, string(password), salt, iter, keyLen) + if err != nil { + // FIPS 140 enforcement, or an invalid key length. + panic(err) } - return dk[:keyLen] + return out } diff --git a/vendor/golang.org/x/crypto/ssh/certs.go b/vendor/golang.org/x/crypto/ssh/certs.go index 139fa31e1b..fa848f51a5 100644 --- a/vendor/golang.org/x/crypto/ssh/certs.go +++ b/vendor/golang.org/x/crypto/ssh/certs.go @@ -229,15 +229,20 @@ func parseCert(in []byte, privAlgo string) (*Certificate, error) { return nil, err } c.Reserved = g.Reserved + // Reject a certificate whose signature key is itself a certificate before + // parsing it. Certificates signed by certificates are not supported (see + // PROTOCOL.certkeys), and rejecting after ParsePublicKey returns would allow + // a chain of nested certificates to recurse once per level, exhausting the + // goroutine stack. + if sigAlgo, _, ok := parseString(g.SignatureKey); !ok { + return nil, errShortRead + } else if _, ok := certKeyAlgoNames[string(sigAlgo)]; ok { + return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", sigAlgo) + } k, err := ParsePublicKey(g.SignatureKey) if err != nil { return nil, err } - // The Type() function is intended to return only certificate key types, but - // we use certKeyAlgoNames anyway for safety, to match [Certificate.Type]. - if _, ok := certKeyAlgoNames[k.Type()]; ok { - return nil, fmt.Errorf("ssh: the signature key type %q is invalid for certificates", k.Type()) - } c.SignatureKey = k c.Signature, rest, ok = parseSignatureBody(g.Signature) if !ok || len(rest) > 0 { @@ -348,6 +353,9 @@ func (c *CertChecker) CheckHostKey(addr string, remote net.Addr, key PublicKey) if cert.CertType != HostCert { return fmt.Errorf("ssh: certificate presented as a host key has type %d", cert.CertType) } + if c.IsHostAuthority == nil { + return errors.New("ssh: cannot verify certificate, IsHostAuthority not set") + } if !c.IsHostAuthority(cert.SignatureKey, addr) { return fmt.Errorf("ssh: no authorities for hostname: %v", addr) } @@ -375,6 +383,9 @@ func (c *CertChecker) Authenticate(conn ConnMetadata, pubKey PublicKey) (*Permis if cert.CertType != UserCert { return nil, fmt.Errorf("ssh: cert has type %d", cert.CertType) } + if c.IsUserAuthority == nil { + return nil, errors.New("ssh: cannot verify certificate, IsUserAuthority not set") + } if !c.IsUserAuthority(cert.SignatureKey) { return nil, fmt.Errorf("ssh: certificate signed by unrecognized authority") } @@ -438,7 +449,17 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error { if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { return fmt.Errorf("ssh: cert has expired") } - if err := cert.SignatureKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { + // Match OpenSSH: the SK user-presence flag is never enforced on a + // certificate's CA signature. OpenSSH calls sshkey_verify with + // detailsp==NULL in sshkey.c:cert_parse, so the UP/UV flags are + // not even extracted. The UP bit on a CA signature reflects the + // CA operator's presence at signing time, which has no bearing on + // whether the user being authenticated is present now; enforcing + // it here would only break interop with certificates issued by + // non-interactive SK CAs. skKeyWithoutUP is a no-op for non-SK + // keys (the common case). + caKey := skKeyWithoutUP(cert.SignatureKey) + if err := caKey.Verify(cert.bytesForSigning(), cert.Signature); err != nil { return fmt.Errorf("ssh: certificate signature does not verify") } diff --git a/vendor/golang.org/x/crypto/ssh/channel.go b/vendor/golang.org/x/crypto/ssh/channel.go index cc0bb7ab64..ba3279e91d 100644 --- a/vendor/golang.org/x/crypto/ssh/channel.go +++ b/vendor/golang.org/x/crypto/ssh/channel.go @@ -11,6 +11,7 @@ import ( "io" "log" "sync" + "sync/atomic" ) const ( @@ -131,11 +132,17 @@ func (r RejectionReason) String() string { return fmt.Sprintf("unknown reason %d", int(r)) } -func min(a uint32, b int) uint32 { - if a < uint32(b) { - return a +// minPayloadSize returns min(limit, length) clamped to a uint32. It is used +// to compute the size of the next channel data packet from the remaining +// payload. The comparison is done in int64 because length is an int — on +// 64-bit systems len(data) can exceed 2^32, and a direct uint32(length) +// cast would silently truncate to 0 at every multiple of 2^32, causing +// WriteExtended's loop to spin without making progress. +func minPayloadSize(limit uint32, length int) uint32 { + if int64(length) > int64(limit) { + return limit } - return uint32(b) + return uint32(length) } type channelDirection uint8 @@ -177,6 +184,12 @@ type channel struct { // with WantReply=true outstanding. This lock is held by a // goroutine that has such an outgoing request pending. sentRequestMu sync.Mutex + // sentRequestPending is set to true while a SendRequest call with + // WantReply=true is in flight. handlePacket uses it as a gate: responses + // arriving while no request is pending are dropped to prevent a + // misbehaving peer from stalling the mux read loop by filling ch.msg + // with unsolicited channelRequestSuccess/Failure messages. + sentRequestPending atomic.Bool incomingRequests chan *Request @@ -203,6 +216,10 @@ type channel struct { // packetPool has a buffer for each extended channel ID to // save allocations during writes. packetPool map[uint32][]byte + + // closeOnce guards close so it is idempotent: closing the internal Go + // channels (msg, incomingRequests) more than once would panic. + closeOnce sync.Once } // writePacket sends a packet. If the packet is a channel close, it updates @@ -251,7 +268,7 @@ func (ch *channel) WriteExtended(data []byte, extendedCode uint32) (n int, err e ch.writeMu.Unlock() for len(data) > 0 { - space := min(ch.maxRemotePayload, len(data)) + space := minPayloadSize(ch.maxRemotePayload, len(data)) if space, err = ch.remoteWin.reserve(space); err != nil { return n, err } @@ -327,7 +344,18 @@ func (ch *channel) handleData(packet []byte) error { if extended == 1 { ch.extPending.write(data) } else if extended > 0 { - // discard other extended data. + // RFC 4254, Section 5.2 defines no extended data types other + // than stderr (type 1, handled above) and this package provides + // no API to read them, so the data is discarded. Credit its + // window back immediately: it can never be read, so the + // deduction above would otherwise shrink the window permanently. + // adjustWindow returns io.EOF if the local side has already + // sent a channel close; ignore it like ReadExtended does, since + // an error returned here would terminate the mux read loop and + // tear down the whole connection. + if err := ch.adjustWindow(length); err != nil && err != io.EOF { + return err + } } else { ch.pending.write(data) } @@ -380,17 +408,19 @@ func (c *channel) ReadExtended(data []byte, extended uint32) (n int, err error) } func (c *channel) close() { - c.pending.eof() - c.extPending.eof() - close(c.msg) - close(c.incomingRequests) - c.writeMu.Lock() - // This is not necessary for a normal channel teardown, but if - // there was another error, it is. - c.sentClose = true - c.writeMu.Unlock() - // Unblock writers. - c.remoteWin.close() + c.closeOnce.Do(func() { + c.pending.eof() + c.extPending.eof() + close(c.msg) + close(c.incomingRequests) + c.writeMu.Lock() + // This is not necessary for a normal channel teardown, but if + // there was another error, it is. + c.sentClose = true + c.writeMu.Unlock() + // Unblock writers. + c.remoteWin.close() + }) } // responseMessageReceived is called when a success or failure message is @@ -460,6 +490,18 @@ func (ch *channel) handlePacket(packet []byte) error { } ch.incomingRequests <- &req + case *channelRequestSuccessMsg, *channelRequestFailureMsg: + // Drop responses that arrive when no SendRequest is waiting, to + // prevent a malicious peer from filling ch.msg and stalling the + // mux read loop. The non-blocking send additionally protects the + // loop if a well-behaved caller is slow to read. + if !ch.sentRequestPending.Load() { + return nil + } + select { + case ch.msg <- msg: + default: + } default: ch.msg <- msg } @@ -468,19 +510,20 @@ func (ch *channel) handlePacket(packet []byte) error { func (m *mux) newChannel(chanType string, direction channelDirection, extraData []byte) *channel { ch := &channel{ - remoteWin: window{Cond: newCond()}, - myWindow: channelWindowSize, - pending: newBuffer(), - extPending: newBuffer(), - direction: direction, - incomingRequests: make(chan *Request, chanSize), - msg: make(chan interface{}, chanSize), - chanType: chanType, - extraData: extraData, - mux: m, - packetPool: make(map[uint32][]byte), - } - ch.localId = m.chanList.add(ch) + remoteWin: window{Cond: newCond()}, + myWindow: channelWindowSize, + maxIncomingPayload: channelMaxPacket, + pending: newBuffer(), + extPending: newBuffer(), + direction: direction, + incomingRequests: make(chan *Request, chanSize), + msg: make(chan interface{}, chanSize), + chanType: chanType, + extraData: extraData, + mux: m, + packetPool: make(map[uint32][]byte), + } + m.chanList.add(ch) return ch } @@ -504,7 +547,6 @@ func (ch *channel) Accept() (Channel, <-chan *Request, error) { if ch.decided { return nil, nil, errDecidedAlready } - ch.maxIncomingPayload = channelMaxPacket confirm := channelOpenConfirmMsg{ PeersID: ch.remoteId, MyID: ch.localId, @@ -530,7 +572,17 @@ func (ch *channel) Reject(reason RejectionReason, message string) error { Language: "en", } ch.decided = true - return ch.sendMessage(reject) + err := ch.sendMessage(reject) + + // Remove the channel from the mux to prevent memory leaks. + // Do not call ch.close() here: no goroutine holds a reference to a + // rejected channel's internal channels (msg, incomingRequests), so + // removing it from chanList is sufficient for GC. Calling close() + // would race with the mux loop goroutine (handlePacket or dropAll), + // causing a panic from closing an already-closed channel. + ch.mux.chanList.remove(ch.localId) + + return err } func (ch *channel) Read(data []byte) (int, error) { @@ -586,6 +638,27 @@ func (ch *channel) SendRequest(name string, wantReply bool, payload []byte) (boo if wantReply { ch.sentRequestMu.Lock() defer ch.sentRequestMu.Unlock() + + // Open the gate so that responses arriving while this request is in + // flight are allowed to reach ch.msg. Responses arriving while no + // request is pending are dropped by handlePacket. + ch.sentRequestPending.Store(true) + defer ch.sentRequestPending.Store(false) + + // Drain any spurious responses that may have been buffered. This + // prevents a previously buffered unexpected response from being + // consumed instead of the actual response for this request. + drain: + for { + select { + case _, ok := <-ch.msg: + if !ok { + break drain + } + default: + break drain + } + } } msg := channelRequestMsg{ diff --git a/vendor/golang.org/x/crypto/ssh/cipher.go b/vendor/golang.org/x/crypto/ssh/cipher.go index ad2b370578..48d0199545 100644 --- a/vendor/golang.org/x/crypto/ssh/cipher.go +++ b/vendor/golang.org/x/crypto/ssh/cipher.go @@ -407,7 +407,7 @@ func (c *gcmCipher) readCipherPacket(seqNum uint32, r io.Reader) ([]byte, error) return nil, fmt.Errorf("ssh: illegal padding %d", padding) } - if int(padding+1) >= len(plain) { + if int(padding)+1 >= len(plain) { return nil, fmt.Errorf("ssh: padding %d too large", padding) } plain = plain[1 : length-uint32(padding)] diff --git a/vendor/golang.org/x/crypto/ssh/client.go b/vendor/golang.org/x/crypto/ssh/client.go index 33079789bc..89f0def9f6 100644 --- a/vendor/golang.org/x/crypto/ssh/client.go +++ b/vendor/golang.org/x/crypto/ssh/client.go @@ -88,6 +88,32 @@ func NewClientConn(c net.Conn, addr string, config *ClientConfig) (Conn, <-chan return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil } +// NewControlClientConn establishes an SSH connection over an OpenSSH +// ControlMaster socket c in proxy mode. +// +// Note that this package only implements the client side of the multiplexing +// protocol. The provided net.Conn must be a local, secure connection (such as a +// Unix domain socket) connected to an already-running OpenSSH process acting as +// the ControlMaster. +// +// WARNING: Because proxy mode bypasses the standard cryptographic handshake +// passing a standard network connection (e.g., TCP) will result in plaintext +// data leakage. +// +// The Request and NewChannel channels must be serviced or the connection +// will hang. +func NewControlClientConn(c net.Conn) (Conn, <-chan NewChannel, <-chan *Request, error) { + conn := &connection{ + sshConn: sshConn{conn: c}, + } + var err error + if conn.transport, err = handshakeControlProxy(c); err != nil { + return nil, nil, nil, fmt.Errorf("ssh: control proxy handshake failed: %w", err) + } + conn.mux = newMux(conn.transport) + return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil +} + // clientHandshake performs the client side key exchange. See RFC 4253 Section // 7. func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) error { @@ -197,6 +223,59 @@ type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error // the server. A BannerCallback receives the message sent by the remote server. type BannerCallback func(message string) error +// ClientAuthContext contains information about the current state of the +// authentication process, passed to [ClientAuthCallback]. +type ClientAuthContext struct { + // Metadata contains the connection metadata. + Metadata ConnMetadata + + // Algorithms contains the negotiated algorithms. + Algorithms NegotiatedAlgorithms + + // AllowedMethods lists the authentication methods currently accepted + // by the server. These are the protocol-level names defined in RFC 4252 + // such as "publickey", "password". + AllowedMethods []string + + // PartialSuccessMethods lists the authentication methods that have already + // succeeded, indicating a multi-step authentication flow. This list + // represents the exact sequence of partial successes and may contain + // duplicates if the same method succeeded multiple times. + PartialSuccessMethods []string + + // TriedMethods lists the methods that have already been attempted and + // failed during this session. This list represents the exact sequence of + // failures and may contain duplicates. This allows the callback to also + // track the number of failed attempts for a specific method. + TriedMethods []string +} + +// ClientAuthCallback is a hook invoked before each authentication attempt. It +// allows the client to dynamically select an authentication method based on the +// current context, server capabilities, or previous failures. +// +// The callback is invoked after the initial "none" authentication method, once +// the server's supported authentication methods are known. +// +// Return values: +// - (AuthMethod, nil): The client will attempt this specific method next. +// The returned method does NOT need to be present in [ClientConfig.Auth]. +// This allows for dynamic authentication strategies (e.g., prompting +// for a password only if public key auth fails). Callers should inspect +// [ClientAuthContext.TriedMethods] to avoid repeatedly returning the +// same failing method. +// - (nil, nil): The client selects from [ClientConfig.Auth] the first +// instance of a method that has not been tried yet, or aborts if none +// are left. If authentication is not successful, the callback is invoked +// again before the following attempt. +// - (nil, error): The authentication process is aborted immediately, +// causing the ongoing SSH handshake to fail with the provided error. +// +// To bound resource use, the client caps the total number of authentication +// attempts (failures and partial successes combined) at 64. If the cap is +// exceeded the handshake aborts with an error. +type ClientAuthCallback func(ctx *ClientAuthContext) (AuthMethod, error) + // A ClientConfig structure is used to configure a Client. It must not be // modified after having been passed to an SSH function. type ClientConfig struct { @@ -210,6 +289,9 @@ type ClientConfig struct { // Auth contains possible authentication methods to use with the // server. Only the first instance of a particular RFC 4252 method will // be used during authentication. + // + // If AuthCallback is set, these AuthMethod are only used if the + // callback returns nil. Auth []AuthMethod // HostKeyCallback is called during the cryptographic @@ -240,6 +322,9 @@ type ClientConfig struct { // // A Timeout of zero means no timeout. Timeout time.Duration + + // AuthCallback, if non-nil, is invoked before each authentication attempt. + AuthCallback ClientAuthCallback } // InsecureIgnoreHostKey returns a function that can be used for diff --git a/vendor/golang.org/x/crypto/ssh/client_auth.go b/vendor/golang.org/x/crypto/ssh/client_auth.go index 4f2f75c367..138cc9003a 100644 --- a/vendor/golang.org/x/crypto/ssh/client_auth.go +++ b/vendor/golang.org/x/crypto/ssh/client_auth.go @@ -21,6 +21,12 @@ const ( authSuccess ) +// maxAuthClientTried bounds the total number of authentication attempts +// (failures and partial successes combined) the client makes before +// aborting the loop, to prevent unbounded growth when an AuthCallback +// keeps supplying methods. +const maxAuthClientTried = 64 + // clientAuthenticate authenticates with the remote server. See RFC 4252. func (c *connection) clientAuthenticate(config *ClientConfig) error { // initiate user auth session @@ -67,32 +73,62 @@ func (c *connection) clientAuthenticate(config *ClientConfig) error { // then any untried methods suggested by the server. var tried []string var lastMethods []string + var partialSuccess []string sessionID := c.transport.getSessionID() for auth := AuthMethod(new(noneAuth)); auth != nil; { ok, methods, err := auth.auth(sessionID, config.User, c.transport, config.Rand, extensions) if err != nil { // On disconnect, return error immediately - if _, ok := err.(*disconnectMsg); ok { + if _, isDisconnect := err.(*disconnectMsg); isDisconnect { return err } - // We return the error later if there is no other method left to - // try. + // We return the error later if there is no other method + // left to try. ok = authFailure } - if ok == authSuccess { - // success + + switch ok { + case authSuccess: return nil - } else if ok == authFailure { - if m := auth.method(); !slices.Contains(tried, m) { - tried = append(tried, m) - } + case authPartialSuccess: + partialSuccess = append(partialSuccess, auth.method()) + case authFailure: + tried = append(tried, auth.method()) } + if len(partialSuccess)+len(tried) > maxAuthClientTried { + return fmt.Errorf("ssh: too many authentication attempts (%d), aborting", + len(partialSuccess)+len(tried)) + } + if methods == nil { methods = lastMethods } lastMethods = methods + // If AuthCallback is set it takes precedence: it picks the next + // AuthMethod dynamically. The returned method need not be in + // config.Auth. If the callback returns (nil, nil) we fall back to + // selecting the next untried method from config.Auth below; on + // (nil, error) the handshake aborts. + if config.AuthCallback != nil { + ctx := &ClientAuthContext{ + Metadata: c, + Algorithms: c.Algorithms(), + AllowedMethods: slices.Clone(methods), + PartialSuccessMethods: slices.Clone(partialSuccess), + TriedMethods: slices.Clone(tried), + } + altAuth, cbErr := config.AuthCallback(ctx) + if cbErr != nil { + return cbErr + } + if altAuth != nil { + auth = altAuth + continue + } + } + auth = nil findNext: @@ -377,11 +413,11 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand return authFailure, nil, err } - // If authentication succeeds or the list of available methods does not - // contain the "publickey" method, do not attempt to authenticate with any - // other keys. According to RFC 4252 Section 7, the latter can occur when - // additional authentication methods are required. - if success == authSuccess || !slices.Contains(methods, cb.method()) { + // If authentication succeeds or partially succeeds, return immediately + // so the caller can select the next auth method. According to RFC 4252 + // Section 7, if the server no longer lists "publickey" among its + // allowed methods, do not attempt to authenticate with any other keys. + if success == authSuccess || success == authPartialSuccess || !slices.Contains(methods, cb.method()) { return success, methods, err } } @@ -762,7 +798,7 @@ func (g *gssAPIWithMICCallback) auth(session []byte, user string, c packetConn, return authFailure, nil, fmt.Errorf("GSS-API Error:\n"+ "Major Status: %d\n"+ "Minor Status: %d\n"+ - "Error Message: %s\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus, + "Error Message: %q\n", userAuthGSSAPIErrorResp.MajorStatus, userAuthGSSAPIErrorResp.MinorStatus, userAuthGSSAPIErrorResp.Message) case msgUserAuthGSSAPIToken: userAuthGSSAPITokenReq := &userAuthGSSAPIToken{} diff --git a/vendor/golang.org/x/crypto/ssh/common.go b/vendor/golang.org/x/crypto/ssh/common.go index 2e44e9c9ec..aed0fd926a 100644 --- a/vendor/golang.org/x/crypto/ssh/common.go +++ b/vendor/golang.org/x/crypto/ssh/common.go @@ -419,7 +419,7 @@ type AlgorithmNegotiationError struct { } func (a *AlgorithmNegotiationError) Error() string { - return fmt.Sprintf("ssh: no common algorithm for %s; we offered: %v, peer offered: %v", + return fmt.Sprintf("ssh: no common algorithm for %s; we offered: %q, peer offered: %q", a.What, a.SupportedAlgorithms, a.RequestedAlgorithms) } @@ -544,7 +544,7 @@ func (c *Config) SetDefaults() { if c.Rand == nil { c.Rand = rand.Reader } - if c.Ciphers == nil { + if len(c.Ciphers) == 0 { c.Ciphers = defaultCiphers } var ciphers []string @@ -556,7 +556,7 @@ func (c *Config) SetDefaults() { } c.Ciphers = ciphers - if c.KeyExchanges == nil { + if len(c.KeyExchanges) == 0 { c.KeyExchanges = defaultKexAlgos } var kexs []string @@ -571,7 +571,7 @@ func (c *Config) SetDefaults() { } c.KeyExchanges = kexs - if c.MACs == nil { + if len(c.MACs) == 0 { c.MACs = defaultMACs } var macs []string diff --git a/vendor/golang.org/x/crypto/ssh/connection.go b/vendor/golang.org/x/crypto/ssh/connection.go index 613a71a7b3..9e0ed089c4 100644 --- a/vendor/golang.org/x/crypto/ssh/connection.go +++ b/vendor/golang.org/x/crypto/ssh/connection.go @@ -17,7 +17,7 @@ type OpenChannelError struct { } func (e *OpenChannelError) Error() string { - return fmt.Sprintf("ssh: rejected: %s (%s)", e.Reason, e.Message) + return fmt.Sprintf("ssh: rejected: %s (%q)", e.Reason, e.Message) } // ConnMetadata holds metadata for the connection. @@ -91,9 +91,17 @@ func DiscardRequests(in <-chan *Request) { } } +// A connTransport represents the transport for a connection. +type connTransport interface { + packetConn + getAlgorithms() NegotiatedAlgorithms + getSessionID() []byte + waitSession() error +} + // A connection represents an incoming connection. type connection struct { - transport *handshakeTransport + transport connTransport sshConn // The connection protocol. diff --git a/vendor/golang.org/x/crypto/ssh/control.go b/vendor/golang.org/x/crypto/ssh/control.go new file mode 100644 index 0000000000..9b14e4cafa --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/control.go @@ -0,0 +1,155 @@ +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ssh + +import ( + "encoding/binary" + "errors" + "fmt" + "io" + + "golang.org/x/crypto/cryptobyte" +) + +const ( + muxProtocolVersion = 4 + + muxMsgHello = 0x00000001 + muxCProxy = 0x1000000f + muxSProxy = 0x8000000f +) + +const controlProxyRequestID = 0 + +// handshakeControlProxy attempts to establish a transport connection with an +// OpenSSH ControlMaster socket in proxy mode. For details see: +// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.mux +func handshakeControlProxy(rw io.ReadWriteCloser) (connTransport, error) { + if err := controlProxyWritePacket(rw, func(b *cryptobyte.Builder) { + b.AddUint32(muxMsgHello) + b.AddUint32(muxProtocolVersion) + }); err != nil { + return nil, fmt.Errorf("mux hello write failed: %w", err) + } + if err := controlProxyWritePacket(rw, func(b *cryptobyte.Builder) { + b.AddUint32(muxCProxy) + b.AddUint32(controlProxyRequestID) + }); err != nil { + return nil, fmt.Errorf("mux client proxy write failed: %w", err) + } + + messageType, body, err := controlProxyReadMessage(rw) + if err != nil { + return nil, fmt.Errorf("mux hello read failed: %w", err) + } + if messageType != muxMsgHello { + return nil, fmt.Errorf("expected hello response, got %v", messageType) + } + var v uint32 + if !body.ReadUint32(&v) { + return nil, errors.New("EOF reading mux protocol version") + } + if v != muxProtocolVersion { + return nil, fmt.Errorf("mux server has unsupported version %v", v) + } + messageType, body, err = controlProxyReadMessage(rw) + if err != nil { + return nil, fmt.Errorf("mux server proxy read failed: %w", err) + } + if messageType != muxSProxy { + return nil, fmt.Errorf("expected server proxy response, got %v", messageType) + } + var reqID uint32 + if !body.ReadUint32(&reqID) { + return nil, errors.New("EOF reading request id") + } + if reqID != controlProxyRequestID { + return nil, fmt.Errorf("expected request id %v, got %v", controlProxyRequestID, reqID) + } + return &controlProxyTransport{rw}, nil +} + +// controlProxyTransport implements the connTransport interface for +// ControlMaster connections. Each controlMessage has zero length padding and +// no MAC. +type controlProxyTransport struct { + rw io.ReadWriteCloser +} + +func (p *controlProxyTransport) Close() error { + return p.rw.Close() +} + +func (p *controlProxyTransport) writePacket(controlMessage []byte) error { + return controlProxyWritePacket(p.rw, func(b *cryptobyte.Builder) { + b.AddUint8(0) // Padding length. + b.AddBytes(controlMessage) + }) +} + +func (p *controlProxyTransport) readPacket() ([]byte, error) { + buf, err := controlProxyReadPacket(p.rw) + if err != nil { + return nil, fmt.Errorf("ssh: error reading control message: %w", err) + } + // Discard the padding length. + if len(buf) < 1 { + return nil, errors.New("ssh: EOF reading padding length") + } + if buf[0] != 0 { + return nil, errors.New("ssh: unexpected non-zero padding in control message") + } + return buf[1:], nil +} + +func (p *controlProxyTransport) getAlgorithms() NegotiatedAlgorithms { + return NegotiatedAlgorithms{} +} + +func (p *controlProxyTransport) getSessionID() []byte { + return nil +} + +func (p *controlProxyTransport) waitSession() error { + return nil +} + +func controlProxyWritePacket(w io.Writer, f cryptobyte.BuilderContinuation) error { + var buf []byte + b := cryptobyte.NewBuilder(buf) + b.AddUint32LengthPrefixed(f) + out, err := b.Bytes() + if err != nil { + return err + } + _, err = w.Write(out) + return err +} + +func controlProxyReadPacket(r io.Reader) (cryptobyte.String, error) { + var l uint32 + if err := binary.Read(r, binary.BigEndian, &l); err != nil { + return nil, err + } + if l > maxPacket { + return nil, fmt.Errorf("message length %v exceeds maximum %v", l, maxPacket) + } + buf := make([]byte, l) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, err + } + return buf, nil +} + +func controlProxyReadMessage(r io.Reader) (messageType uint32, body cryptobyte.String, err error) { + body, err = controlProxyReadPacket(r) + if err != nil { + return 0, nil, fmt.Errorf("error reading message body: %w", err) + } + if !body.ReadUint32(&messageType) { + return 0, nil, errors.New("EOF reading message type") + } + return messageType, body, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/handshake.go b/vendor/golang.org/x/crypto/ssh/handshake.go index 4be3cbb6de..711a7f7c47 100644 --- a/vendor/golang.org/x/crypto/ssh/handshake.go +++ b/vendor/golang.org/x/crypto/ssh/handshake.go @@ -162,7 +162,7 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt t.remoteAddr = addr t.hostKeyCallback = config.HostKeyCallback t.bannerCallback = config.BannerCallback - if config.HostKeyAlgorithms != nil { + if len(config.HostKeyAlgorithms) > 0 { t.hostKeyAlgorithms = config.HostKeyAlgorithms } else { t.hostKeyAlgorithms = defaultHostKeyAlgos diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index 5f7fdd8514..91b771c4ae 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -16,6 +16,7 @@ import ( "io" "math/big" "slices" + "sync" "golang.org/x/crypto/curve25519" ) @@ -718,15 +719,9 @@ func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshak kexDHGexRequest.MaxBits, kexDHGexRequest.PreferredBits) } - var p *big.Int - // We hardcode sending Oakley Group 14 (2048 bits), Oakley Group 15 (3072 - // bits) or Oakley Group 16 (4096 bits), based on the requested max size. - if kexDHGexRequest.MaxBits < 3072 { - p, _ = new(big.Int).SetString(oakleyGroup14, 16) - } else if kexDHGexRequest.MaxBits < 4096 { - p, _ = new(big.Int).SetString(oakleyGroup15, 16) - } else { - p, _ = new(big.Int).SetString(oakleyGroup16, 16) + p, err := chooseDH(kexDHGexRequest) + if err != nil { + return nil, err } g := big.NewInt(2) @@ -805,3 +800,65 @@ func (gex *dhGEXSHA) Server(c packetConn, randSource io.Reader, magics *handshak Hash: gex.hashFunc, }, err } + +type dhKEXGroup struct { + size int + p *big.Int +} + +// supportedDHKEXGroups returns the DH groups the server is willing to offer +// for diffie-hellman-group-exchange-* key exchanges. The list is built lazily +// on first use to keep the hex-to-big.Int parse out of package initialization. +var supportedDHKEXGroups = sync.OnceValue(func() []dhKEXGroup { + specs := []struct { + size int + hex string + }{ + {2048, oakleyGroup14}, + {3072, oakleyGroup15}, + {4096, oakleyGroup16}, + } + out := make([]dhKEXGroup, 0, len(specs)) + for _, s := range specs { + p, _ := new(big.Int).SetString(s.hex, 16) + out = append(out, dhKEXGroup{size: s.size, p: p}) + } + return out +}) + +// chooseDH picks a DH group for the given client request, mirroring the +// algorithm used by OpenSSH's choose_dh in dh.c: prefer the smallest known +// group larger than or equal to the client's PreferredBits, and otherwise pick +// the largest group within the accepted [MinBits, MaxBits] range. +func chooseDH(req kexDHGexRequestMsg) (*big.Int, error) { + var best *big.Int + bestSize := 0 + wantBits := int(req.PreferredBits) + + for _, group := range supportedDHKEXGroups() { + if uint32(group.size) < req.MinBits || uint32(group.size) > req.MaxBits { + continue + } + + if bestSize == 0 { + best = group.p + bestSize = group.size + continue + } + + closerFromAbove := group.size >= wantBits && group.size < bestSize + closerFromBelow := group.size > bestSize && bestSize < wantBits + + if closerFromAbove || closerFromBelow { + best = group.p + bestSize = group.size + } + } + + if bestSize == 0 { + return nil, fmt.Errorf("ssh: no suitable DH group found for request min: %d, preferred: %d, max: %d", + req.MinBits, req.PreferredBits, req.MaxBits) + } + + return best, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/keys.go b/vendor/golang.org/x/crypto/ssh/keys.go index 47a07539d9..fbbfb6cf42 100644 --- a/vendor/golang.org/x/crypto/ssh/keys.go +++ b/vendor/golang.org/x/crypto/ssh/keys.go @@ -76,7 +76,7 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err case InsecureKeyAlgoDSA: return parseDSA(in) case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521: - return parseECDSA(in) + return parseECDSA(in, algo) case KeyAlgoSKECDSA256: return parseSKECDSA(in) case KeyAlgoED25519: @@ -182,14 +182,19 @@ func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey } hosts := string(keyFields[0]) - // keyFields[1] contains the key type (e.g. “ssh-rsa”). - // However, that information is duplicated inside the - // base64-encoded key and so is ignored here. + // keyFields[1] contains the key type (e.g. "ssh-rsa"). This information + // is duplicated within the base64-encoded key blob. As OpenSSH's + // sshkey_read does, we verify that the declared key type matches the + // type embedded in the key blob. + wantType := string(keyFields[1]) key := bytes.Join(keyFields[2:], []byte(" ")) if pubKey, comment, err = parseAuthorizedKey(key); err != nil { return "", nil, nil, "", nil, err } + if pubKey.Type() != wantType { + return "", nil, nil, "", nil, fmt.Errorf("ssh: known hosts key type mismatch: human-readable type %q, encoded type %q", wantType, pubKey.Type()) + } return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil } @@ -228,10 +233,17 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str } if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { - return out, comment, options, rest, nil - } else { - lastErr = err + // The first field contains the declared key type. As OpenSSH's + // sshkey_read does, we verify that it matches the type embedded in + // the key blob. Without this check, a single-token option (e.g. + // "restrict") appearing in the key type position could be silently + // discarded along with its intended effect. + if string(in[:i]) == out.Type() { + return out, comment, options, rest, nil + } + err = fmt.Errorf("ssh: authorized keys key type mismatch: human-readable type %q, encoded type %q", in[:i], out.Type()) } + lastErr = err // No key type recognised. Maybe there's an options field at // the beginning. @@ -271,11 +283,15 @@ func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []str } if out, comment, err = parseAuthorizedKey(in[i:]); err == nil { - options = candidateOptions - return out, comment, options, rest, nil - } else { - lastErr = err + // As above, the declared key type (here following the options + // field) must match the type embedded in the key blob. + if string(in[:i]) == out.Type() { + options = candidateOptions + return out, comment, options, rest, nil + } + err = fmt.Errorf("ssh: authorized keys key type mismatch: human-readable type %q, encoded type %q", in[:i], out.Type()) } + lastErr = err in = rest continue @@ -469,6 +485,13 @@ func parseRSA(in []byte) (out PublicKey, rest []byte, err error) { return nil, nil, err } + // 16384 bits is the largest RSA key OpenSSH will generate (ssh-keygen + // caps -b at 16384), so it is the practical upper bound for keys seen on + // the wire. Rejecting anything larger bounds the CPU spent verifying an + // attacker-supplied key and signature, mitigating a denial of service. + if w.N.BitLen() > 16384 { + return nil, nil, errors.New("ssh: rsa modulus too large") + } if w.E.BitLen() > 24 { return nil, nil, errors.New("ssh: exponent too large") } @@ -574,6 +597,24 @@ func checkDSAParams(param *dsa.Parameters) error { return fmt.Errorf("ssh: unsupported DSA key size %d", l) } + // FIPS 186-2 specifies that Q must be exactly 160 bits. We must enforce + // this to prevent DoS attacks where an attacker sends a huge Q which makes + // verification slow. + if l := param.Q.BitLen(); l != 160 { + return fmt.Errorf("ssh: unsupported DSA sub-prime size %d", l) + } + + // The generator G is an element of the group, so it must be strictly less + // than the modulus P. + if param.G.Cmp(param.P) >= 0 { + return errors.New("ssh: DSA generator larger than modulus") + } + + // G must be positive. + if param.G.Sign() <= 0 { + return errors.New("ssh: DSA generator must be positive") + } + return nil } @@ -596,6 +637,14 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) { return nil, nil, err } + // The public value Y must be a non-zero element of the group, i.e. + // strictly between 0 and P. crypto/dsa.Verify does not range-check Y, + // so we reject out-of-range values here to prevent a maliciously + // oversized Y from slowing verification. + if w.Y.Sign() <= 0 || w.Y.Cmp(w.P) >= 0 { + return nil, nil, errors.New("ssh: DSA public value Y out of range") + } + key := &dsaPublicKey{ Parameters: param, Y: w.Y, @@ -774,7 +823,7 @@ func supportedEllipticCurve(curve elliptic.Curve) bool { } // parseECDSA parses an ECDSA key according to RFC 5656, section 3.1. -func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { +func parseECDSA(in []byte, expectedType string) (out PublicKey, rest []byte, err error) { var w struct { Curve string KeyBytes []byte @@ -785,6 +834,12 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) { return nil, nil, err } + actualType := "ecdsa-sha2-" + w.Curve + if expectedType != actualType { + return nil, nil, fmt.Errorf("ssh: algorithm type mismatch: expected %q, found curve %q (type %q)", + expectedType, w.Curve, actualType) + } + key := new(ecdsa.PublicKey) switch w.Curve { @@ -869,11 +924,25 @@ type skFields struct { Counter uint32 } +// flagUserPresence is the "user present" bit (UP) in the SK signature +// flags, matching the FIDO CTAP2 authenticatorData UP flag. See +// openssh/PROTOCOL.u2f. +const flagUserPresence = 0x01 + +// errSKMissingUserPresence is returned by SK key Verify methods when +// the signature does not assert user presence and the key was not +// marked as no-touch-required. +var errSKMissingUserPresence = errors.New("ssh: signature missing required user presence flag") + type skECDSAPublicKey struct { // application is a URL-like string, typically "ssh:" for SSH. // see openssh/PROTOCOL.u2f for details. application string ecdsa.PublicKey + // noTouchRequired, when true, disables the default user-presence + // check in Verify. It is set by skKeyWithoutUP on a clone of the + // key, never on an instance shared across authentication attempts. + noTouchRequired bool } func (k *skECDSAPublicKey) Type() string { @@ -959,6 +1028,10 @@ func (k *skECDSAPublicKey) Verify(data []byte, sig *Signature) error { return err } + if skf.Flags&flagUserPresence == 0 && !k.noTouchRequired { + return errSKMissingUserPresence + } + blob := struct { ApplicationDigest []byte `ssh:"rest"` Flags byte @@ -992,6 +1065,10 @@ type skEd25519PublicKey struct { // see openssh/PROTOCOL.u2f for details. application string ed25519.PublicKey + // noTouchRequired, when true, disables the default user-presence + // check in Verify. It is set by skKeyWithoutUP on a clone of the + // key, never on an instance shared across authentication attempts. + noTouchRequired bool } func (k *skEd25519PublicKey) Type() string { @@ -1066,6 +1143,10 @@ func (k *skEd25519PublicKey) Verify(data []byte, sig *Signature) error { return err } + if skf.Flags&flagUserPresence == 0 && !k.noTouchRequired { + return errSKMissingUserPresence + } + blob := struct { ApplicationDigest []byte `ssh:"rest"` Flags byte @@ -1408,6 +1489,17 @@ func passphraseProtectedOpenSSHKey(passphrase []byte) openSSHDecryptFunc { return nil, err } + // OpenSSH does not impose an upper bound on the bcrypt round count + // stored in the key file, but bcrypt_pbkdf cost is linear in rounds: + // the default is 16, ssh-keygen lets users pick anything up to + // INT_MAX. Cap at 2048 (128x the default, a few seconds of CPU) so + // that an oversized value in the file cannot tie up the caller for + // months. + const maxRounds = 1 << 11 + if opts.Rounds > maxRounds { + return nil, fmt.Errorf("ssh: bcrypt KDF rounds %d exceed maximum %d", opts.Rounds, maxRounds) + } + k, err := bcrypt_pbkdf.Key(passphrase, []byte(opts.Salt), int(opts.Rounds), 32+16) if err != nil { return nil, err @@ -1577,10 +1669,28 @@ func parseOpenSSHPrivateKey(key []byte, decrypt openSSHDecryptFunc) (crypto.Priv return nil, err } + // Mirror the validation done in parseRSA for public keys: cap the + // modulus at the OpenSSH-generated maximum, reject oversized or + // invalid exponents, and additionally bound the prime factors to + // avoid the expensive CRT coefficient recomputation in pk.Precompute. + if key.N.BitLen() > 16384 { + return nil, errors.New("ssh: rsa modulus too large") + } + if key.P.BitLen() > 8192 || key.Q.BitLen() > 8192 { + return nil, errors.New("ssh: rsa prime too large") + } + if key.E.BitLen() > 24 { + return nil, errors.New("ssh: exponent too large") + } + e := key.E.Int64() + if e < 3 || e&1 == 0 { + return nil, errors.New("ssh: incorrect exponent") + } + pk := &rsa.PrivateKey{ PublicKey: rsa.PublicKey{ N: key.N, - E: int(key.E.Int64()), + E: int(e), }, D: key.D, Primes: []*big.Int{key.P, key.Q}, diff --git a/vendor/golang.org/x/crypto/ssh/messages.go b/vendor/golang.org/x/crypto/ssh/messages.go index ab22c3d38d..1d23dde259 100644 --- a/vendor/golang.org/x/crypto/ssh/messages.go +++ b/vendor/golang.org/x/crypto/ssh/messages.go @@ -44,7 +44,7 @@ type disconnectMsg struct { } func (d *disconnectMsg) Error() string { - return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message) + return fmt.Sprintf("ssh: disconnect, reason %d: %q", d.Reason, d.Message) } // See RFC 4253, section 7.1. diff --git a/vendor/golang.org/x/crypto/ssh/mux.go b/vendor/golang.org/x/crypto/ssh/mux.go index d2d24c635d..955560bdae 100644 --- a/vendor/golang.org/x/crypto/ssh/mux.go +++ b/vendor/golang.org/x/crypto/ssh/mux.go @@ -32,18 +32,21 @@ type chanList struct { offset uint32 } -// Assigns a channel ID to the given channel. -func (c *chanList) add(ch *channel) uint32 { +// add stores the given channel and assigns its localId while holding the +// lock, so that getChan can never return a channel whose localId is not yet +// initialized. +func (c *chanList) add(ch *channel) { c.Lock() defer c.Unlock() for i := range c.chans { if c.chans[i] == nil { c.chans[i] = ch - return uint32(i) + c.offset + ch.localId = uint32(i) + c.offset + return } } c.chans = append(c.chans, ch) - return uint32(len(c.chans)-1) + c.offset + ch.localId = uint32(len(c.chans)-1) + c.offset } // getChan returns the channel for the given ID. @@ -91,9 +94,10 @@ type mux struct { incomingChannels chan NewChannel - globalSentMu sync.Mutex - globalResponses chan interface{} - incomingRequests chan *Request + globalSentMu sync.Mutex + globalSentPending atomic.Bool + globalResponses chan interface{} + incomingRequests chan *Request errCond *sync.Cond err error @@ -141,6 +145,27 @@ func (m *mux) SendRequest(name string, wantReply bool, payload []byte) (bool, [] if wantReply { m.globalSentMu.Lock() defer m.globalSentMu.Unlock() + + // Open the gate so that responses arriving while this request is in + // flight are allowed to reach globalResponses. Any response arriving + // while no request is pending is dropped by handleGlobalPacket. + m.globalSentPending.Store(true) + defer m.globalSentPending.Store(false) + + // Drain any spurious responses that may have been buffered. This prevents + // a previously buffered unexpected response from being consumed instead + // of the actual response for this request. + drain: + for { + select { + case _, ok := <-m.globalResponses: + if !ok { + break drain + } + default: + break drain + } + } } if err := m.sendMessage(globalRequestMsg{ @@ -267,7 +292,16 @@ func (m *mux) handleGlobalPacket(packet []byte) error { mux: m, } case *globalRequestSuccessMsg, *globalRequestFailureMsg: - m.globalResponses <- msg + // Drop responses that arrive when no SendRequest is waiting, to + // prevent a malicious peer from staging responses for a future + // caller. + if !m.globalSentPending.Load() { + return nil + } + select { + case m.globalResponses <- msg: + default: + } default: panic(fmt.Sprintf("not a global message %#v", msg)) } @@ -312,8 +346,6 @@ func (m *mux) OpenChannel(chanType string, extra []byte) (Channel, <-chan *Reque func (m *mux) openChannel(chanType string, extra []byte) (*channel, error) { ch := m.newChannel(chanType, channelOutbound, extra) - ch.maxIncomingPayload = channelMaxPacket - open := channelOpenMsg{ ChanType: chanType, PeersWindow: ch.myWindow, diff --git a/vendor/golang.org/x/crypto/ssh/server.go b/vendor/golang.org/x/crypto/ssh/server.go index 064dcbaf5a..e090262682 100644 --- a/vendor/golang.org/x/crypto/ssh/server.go +++ b/vendor/golang.org/x/crypto/ssh/server.go @@ -26,29 +26,43 @@ type Permissions struct { // defines "force-command" (only allow the given command to // execute) and "source-address" (only allow connections from // the given address). The SSH package currently only enforces - // the "source-address" critical option. It is up to server - // implementations to enforce other critical options, such as - // "force-command", by checking them after the SSH handshake - // is successful. In general, SSH servers should reject + // the "source-address" critical option: it is validated against + // the client's remote address whenever it is present in the + // Permissions returned by any authentication callback. Its value + // is a comma-separated list of IP addresses and CIDR blocks; + // consistently with OpenSSH, a connection whose remote address is + // not an IP address, such as a Unix domain socket, never matches + // the list and is rejected when the option is present. It is up + // to server implementations to enforce other critical options, + // such as "force-command", by checking them after the SSH + // handshake is successful. In general, SSH servers should reject // connections that specify critical options that are unknown // or not supported. CriticalOptions map[string]string - // Extensions are extra functionality that the server may - // offer on authenticated connections. Lack of support for an - // extension does not preclude authenticating a user. Common - // extensions are "permit-agent-forwarding", - // "permit-X11-forwarding". The Go SSH library currently does - // not act on any extension, and it is up to server - // implementations to honor them. Extensions can be used to - // pass data from the authentication callbacks to the server - // application layer. + // Extensions are extra functionality that the server may offer on + // authenticated connections. Lack of support for an extension does not + // preclude authenticating a user. Common extensions are + // "permit-agent-forwarding", "permit-X11-forwarding". In general the Go + // SSH library does not act on extensions and it is up to server + // implementations to honor them; extensions can also be used to pass data + // from the authentication callbacks to the server application layer. + // + // The one extension acted upon by this library is "no-touch-required", + // which applies only to security-key public keys + // (sk-ecdsa-sha2-nistp256@openssh.com and sk-ssh-ed25519@openssh.com). + // When present, it waives the default requirement that SK signatures + // assert user presence (i.e. a physical touch of the authenticator) + // during signature verification. Extensions map[string]string // ExtraData allows to store user defined data. ExtraData map[any]any } +// GSSAPIWithMICConfig includes the server callbacks for gssapi-with-mic +// authentication. If either field is nil, gssapi-with-mic is considered not +// configured. type GSSAPIWithMICConfig struct { // AllowLogin, must be set, is called when gssapi-with-mic // authentication is selected (RFC 4462 section 3). The srcName is from the @@ -63,6 +77,10 @@ type GSSAPIWithMICConfig struct { Server GSSAPIServer } +func gssapiWithMICConfigured(config *GSSAPIWithMICConfig) bool { + return config != nil && config.AllowLogin != nil && config.Server != nil +} + // SendAuthBanner implements [ServerPreAuthConn]. func (s *connection) SendAuthBanner(msg string) error { return s.transport.writePacket(Marshal(&userAuthBannerMsg{ @@ -84,6 +102,79 @@ type ServerPreAuthConn interface { SendAuthBanner(string) error } +// noTouchRequiredExtension is the extension name used by OpenSSH in +// authorized_keys options and certificate extensions to mark keys +// whose signatures do not need to assert user presence (touch). See +// ssh-keygen(1) and sshd(8). +const noTouchRequiredExtension = "no-touch-required" + +// noTouchAllowed reports whether the user presence requirement on +// SK signatures should be waived for this authentication attempt. The +// requirement is waived when the "no-touch-required" extension is +// present either in the Permissions returned by the auth callback +// (authorized_keys-level opt-out) or in the certificate's own +// Extensions (CA-level opt-out), matching OpenSSH behavior. OpenSSH +// reads the per-key opt-out only from cert Extensions and +// authorized_keys options (never from CriticalOptions); we follow the +// same rule. +func noTouchAllowed(pubKey PublicKey, perms *Permissions) bool { + if perms != nil { + if _, ok := perms.Extensions[noTouchRequiredExtension]; ok { + return true + } + } + if cert, ok := pubKey.(*Certificate); ok { + if _, ok := cert.Extensions[noTouchRequiredExtension]; ok { + return true + } + } + return false +} + +// skKeyWithoutUP returns a PublicKey equivalent to pubKey but whose +// Verify accepts SK signatures with the user-presence flag clear. If +// pubKey is not (and does not wrap) an SK key, pubKey is returned +// unchanged. The returned value never mutates pubKey: for SK keys a +// shallow copy is made so that the noTouchRequired flag is set only on +// the clone. +// +// The implementation is iterative rather than recursive. When pubKey +// is a *Certificate we unwrap exactly one level to look at the inner +// key. The SSH cert format forbids Certificate.Key from being another +// Certificate (parseCert rejects it), but nothing stops callers from +// constructing such a value directly in Go; a recursive descent could +// otherwise be driven to unbounded depth by a hand-crafted or cyclic +// Certificate. A malformed input of that shape simply returns +// unchanged here. +func skKeyWithoutUP(pubKey PublicKey) PublicKey { + cert, isCert := pubKey.(*Certificate) + target := pubKey + if isCert { + target = cert.Key + } + var cloned PublicKey + switch k := target.(type) { + case *skECDSAPublicKey: + c := *k + c.noTouchRequired = true + cloned = &c + case *skEd25519PublicKey: + c := *k + c.noTouchRequired = true + cloned = &c + default: + // Not an SK key (or a pathological *Certificate wrapping + // another *Certificate): pubKey is already usable for Verify. + return pubKey + } + if !isCert { + return cloned + } + c := *cert + c.Key = cloned + return &c +} + // ServerConfig holds server specific configuration data. type ServerConfig struct { // Config contains configuration shared between client and server. @@ -138,7 +229,9 @@ type ServerConfig struct { // Permissions object can be the same object, optionally modified, or a // completely new object. If VerifiedPublicKeyCallback is non-nil, // PublicKeyCallback is not allowed to return a PartialSuccessError, which - // can instead be returned by VerifiedPublicKeyCallback. + // can instead be returned by VerifiedPublicKeyCallback. The + // signatureAlgorithm argument is the format of the signature that was + // successfully verified. // // VerifiedPublicKeyCallback does not affect which authentication methods // are included in the list of methods that can be attempted by the client. @@ -242,8 +335,10 @@ func (c *pubKeyCache) add(candidate cachedPubKey) { type ServerConn struct { Conn - // If the succeeding authentication callback returned a - // non-nil Permissions pointer, it is stored here. + // If the succeeding authentication callback returned a non-nil Permissions + // pointer, it is stored here. These are the permissions from the final, + // successful authentication method. Permissions returned by callbacks that + // return PartialSuccessError are not preserved and must be nil. Permissions *Permissions } @@ -302,8 +397,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) } if !config.NoClientAuth && config.PasswordCallback == nil && config.PublicKeyCallback == nil && - config.KeyboardInteractiveCallback == nil && (config.GSSAPIWithMICConfig == nil || - config.GSSAPIWithMICConfig.AllowLogin == nil || config.GSSAPIWithMICConfig.Server == nil) { + config.KeyboardInteractiveCallback == nil && !gssapiWithMICConfigured(config.GSSAPIWithMICConfig) { return nil, errors.New("ssh: no authentication methods configured but NoClientAuth is also false") } @@ -356,6 +450,10 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error) return perms, err } +// checkSourceAddress matches addr against sourceAddrs, a comma-separated list +// of IP addresses and CIDR blocks. Consistently with OpenSSH, a remote address +// that is not IP-based, such as a Unix domain socket, never matches the list +// and is rejected. func checkSourceAddress(addr net.Addr, sourceAddrs string) error { if addr == nil { return errors.New("ssh: no address known for client, but source-address match required") @@ -363,7 +461,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error { tcpAddr, ok := addr.(*net.TCPAddr) if !ok { - return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr) + return fmt.Errorf("ssh: remote address %v is not a TCP address when checking source-address match", addr) } for _, sourceAddr := range strings.Split(sourceAddrs, ",") { @@ -386,6 +484,21 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error { return fmt.Errorf("ssh: remote address %v is not allowed because of source-address restriction", addr) } +// checkSourceAddressCriticalOption enforces the source-address critical +// option, if present in perms, as documented in Permissions.CriticalOptions. +// A present but empty value matches no address, so it denies authentication, +// consistently with OpenSSH, rather than being treated as absent. +func checkSourceAddressCriticalOption(addr net.Addr, perms *Permissions) error { + if perms == nil { + return nil + } + saco, ok := perms.CriticalOptions[sourceAddressCriticalOption] + if !ok { + return nil + } + return checkSourceAddress(addr, saco) +} + func gssExchangeToken(gssapiConfig *GSSAPIWithMICConfig, token []byte, s *connection, sessionID []byte, userAuthReq userAuthRequestMsg) (authErr error, perms *Permissions, err error) { gssAPIServer := gssapiConfig.Server @@ -527,6 +640,15 @@ func (b *BannerError) Error() string { return b.Err.Error() } +// maxAuthServerAttempts caps the total number of SSH_MSG_USERAUTH_REQUEST +// messages the server will process on a single connection, regardless of +// outcome (failure, partial success, public key query, or none). It is a +// backstop against clients that drive the authentication loop indefinitely +// without ever incurring a real failure — for example by repeatedly +// triggering PartialSuccessError or by spamming public key offer queries — +// neither of which increment the MaxAuthTries failure counter. +const maxAuthServerAttempts = 128 + func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) { if config.PreAuthConnCallback != nil { config.PreAuthConnCallback(s) @@ -537,6 +659,7 @@ func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, err var perms *Permissions authFailures := 0 + authAttempts := 0 noneAuthCount := 0 var authErrs []error var calledBannerCallback bool @@ -565,6 +688,19 @@ userAuthLoop: return nil, &ServerAuthError{Errors: authErrs} } + if authAttempts >= maxAuthServerAttempts { + discMsg := &disconnectMsg{ + Reason: 2, + Message: "too many authentication attempts", + } + if err := s.transport.writePacket(Marshal(discMsg)); err != nil { + return nil, err + } + authErrs = append(authErrs, discMsg) + return nil, &ServerAuthError{Errors: authErrs} + } + authAttempts++ + var userAuthReq userAuthRequestMsg if packet, err := s.transport.readPacket(); err != nil { if err == io.EOF { @@ -576,7 +712,7 @@ userAuthLoop: } if userAuthReq.Service != serviceSSH { - return nil, errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service) + return nil, fmt.Errorf("ssh: client attempted to negotiate for unknown service: %q", userAuthReq.Service) } if s.user != userAuthReq.User && partialSuccessReturned { @@ -662,7 +798,8 @@ userAuthLoop: pubKey, err := ParsePublicKey(pubKeyData) if err != nil { - return nil, err + authErr = err + break } candidate, ok := cache.get(s.user, pubKeyData) @@ -675,13 +812,14 @@ userAuthLoop: return nil, errors.New("ssh: invalid library usage: PublicKeyCallback must not return partial success when VerifiedPublicKeyCallback is defined") } - if (candidate.result == nil || isPartialSuccessError) && - candidate.perms != nil && - candidate.perms.CriticalOptions != nil && - candidate.perms.CriticalOptions[sourceAddressCriticalOption] != "" { - if err := checkSourceAddress( - s.RemoteAddr(), - candidate.perms.CriticalOptions[sourceAddressCriticalOption]); err != nil { + // This check is authoritative for the Permissions returned by + // PublicKeyCallback: the check at the end of the auth loop sees + // the final Permissions, which VerifiedPublicKeyCallback may + // have replaced, and is skipped on partial success. It also + // makes public key queries fail before the client signs when + // PublicKeyCallback supplies the restriction. + if candidate.result == nil || isPartialSuccessError { + if err := checkSourceAddressCriticalOption(s.RemoteAddr(), candidate.perms); err != nil { candidate.result = err } } @@ -737,8 +875,15 @@ userAuthLoop: } signedData := buildDataSignedForAuth(sessionID, userAuthReq, algo, pubKeyData) - - if err := pubKey.Verify(signedData, sig); err != nil { + // pubKey is reused below for VerifiedPublicKeyCallback and + // must remain the key as presented by the client; derive a + // separate value for Verify that carries any applicable + // no-touch-required opt-out. + pubKeyForVerify := pubKey + if noTouchAllowed(pubKey, candidate.perms) { + pubKeyForVerify = skKeyWithoutUP(pubKey) + } + if err := pubKeyForVerify.Verify(signedData, sig); err != nil { return nil, err } @@ -748,11 +893,11 @@ userAuthLoop: // Only call VerifiedPublicKeyCallback after the key has been accepted // and successfully verified. If authErr is non-nil, the key is not // considered verified and the callback must not run. - perms, authErr = config.VerifiedPublicKeyCallback(s, pubKey, perms, algo) + perms, authErr = config.VerifiedPublicKeyCallback(s, pubKey, perms, sig.Format) } } case "gssapi-with-mic": - if authConfig.GSSAPIWithMICConfig == nil { + if !gssapiWithMICConfigured(authConfig.GSSAPIWithMICConfig) { authErr = errors.New("ssh: gssapi-with-mic auth not configured") break } @@ -802,6 +947,17 @@ userAuthLoop: authErr = fmt.Errorf("ssh: unknown method %q", userAuthReq.Method) } + // The source-address critical option is enforced on the Permissions + // returned by any authentication callback. Permissions returned + // together with a PartialSuccessError skip this check: that is safe + // because they are required to be nil, as enforced in the partial + // success handling below. + if authErr == nil { + if err := checkSourceAddressCriticalOption(s.RemoteAddr(), perms); err != nil { + authErr = err + } + } + authErrs = append(authErrs, authErr) if config.AuthLogCallback != nil { @@ -824,6 +980,13 @@ userAuthLoop: var failureMsg userAuthFailureMsg if partialSuccess, ok := authErr.(*PartialSuccessError); ok { + // Permissions are not preserved between authentication steps. To + // avoid confusion about the final state of the connection, we + // disallow returning non-nil Permissions combined with + // PartialSuccessError. + if perms != nil { + return nil, errors.New("ssh: permissions must be nil when returning PartialSuccessError") + } // After a partial success error we don't allow changing the user // name and execute the NoClientAuthCallback. partialSuccessReturned = true @@ -878,8 +1041,7 @@ userAuthLoop: if authConfig.KeyboardInteractiveCallback != nil { failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive") } - if authConfig.GSSAPIWithMICConfig != nil && authConfig.GSSAPIWithMICConfig.Server != nil && - authConfig.GSSAPIWithMICConfig.AllowLogin != nil { + if gssapiWithMICConfigured(authConfig.GSSAPIWithMICConfig) { failureMsg.Methods = append(failureMsg.Methods, "gssapi-with-mic") } diff --git a/vendor/golang.org/x/crypto/ssh/session.go b/vendor/golang.org/x/crypto/ssh/session.go index acef62259f..ac62955788 100644 --- a/vendor/golang.org/x/crypto/ssh/session.go +++ b/vendor/golang.org/x/crypto/ssh/session.go @@ -423,6 +423,9 @@ func (s *Session) wait(reqs <-chan *Request) error { for msg := range reqs { switch msg.Type { case "exit-status": + if len(msg.Payload) < 4 { + return errors.New("ssh: malformed exit-status request") + } wm.status = int(binary.BigEndian.Uint32(msg.Payload)) case "exit-signal": var sigval struct { diff --git a/vendor/golang.org/x/crypto/ssh/ssh_gss.go b/vendor/golang.org/x/crypto/ssh/ssh_gss.go index a6249a1227..a7a099754f 100644 --- a/vendor/golang.org/x/crypto/ssh/ssh_gss.go +++ b/vendor/golang.org/x/crypto/ssh/ssh_gss.go @@ -118,24 +118,28 @@ func parseGSSAPIPayload(payload []byte) (*userAuthRequestGSSAPI, error) { OIDS: make([]asn1.ObjectIdentifier, n), } for i := 0; i < int(n); i++ { - var ( - desiredMech []byte - err error - ) + var desiredMech []byte desiredMech, rest, ok = parseString(rest) if !ok { return nil, errors.New("parse string failed") } - if rest, err = asn1.Unmarshal(desiredMech, &s.OIDS[i]); err != nil { + trailing, err := asn1.Unmarshal(desiredMech, &s.OIDS[i]) + if err != nil { return nil, err } + if len(trailing) != 0 { + return nil, errors.New("trailing bytes after OID") + } + } + if len(rest) != 0 { + return nil, errors.New("trailing bytes after mechanisms") } return s, nil } // See RFC 4462 section 3.6. func buildMIC(sessionID string, username string, service string, authMethod string) []byte { - out := make([]byte, 0, 0) + out := make([]byte, 0) out = appendString(out, sessionID) out = append(out, msgUserAuthRequest) out = appendString(out, username) diff --git a/vendor/golang.org/x/crypto/ssh/streamlocal.go b/vendor/golang.org/x/crypto/ssh/streamlocal.go index 152470fcb7..8e997da3db 100644 --- a/vendor/golang.org/x/crypto/ssh/streamlocal.go +++ b/vendor/golang.org/x/crypto/ssh/streamlocal.go @@ -58,6 +58,7 @@ func (c *Client) dialStreamLocal(socketPath string) (Channel, error) { return nil, err } go DiscardRequests(in) + go io.Copy(io.Discard, ch.Stderr()) return ch, err } @@ -79,6 +80,7 @@ func (l *unixListener) Accept() (net.Conn, error) { return nil, err } go DiscardRequests(incoming) + go io.Copy(io.Discard, ch.Stderr()) return &chanConn{ Channel: ch, diff --git a/vendor/golang.org/x/crypto/ssh/tcpip.go b/vendor/golang.org/x/crypto/ssh/tcpip.go index 78c41fe5a1..213d8a614a 100644 --- a/vendor/golang.org/x/crypto/ssh/tcpip.go +++ b/vendor/golang.org/x/crypto/ssh/tcpip.go @@ -332,6 +332,7 @@ func (l *tcpListener) Accept() (net.Conn, error) { return nil, err } go DiscardRequests(incoming) + go io.Copy(io.Discard, ch.Stderr()) return &chanConn{ Channel: ch, @@ -495,6 +496,7 @@ func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel return nil, err } go DiscardRequests(in) + go io.Copy(io.Discard, ch.Stderr()) return ch, nil } diff --git a/vendor/golang.org/x/net/html/entity.go b/vendor/golang.org/x/net/html/entity.go index b628880a01..4e8d5d55f2 100644 --- a/vendor/golang.org/x/net/html/entity.go +++ b/vendor/golang.org/x/net/html/entity.go @@ -2156,9 +2156,8 @@ var entity = map[string]rune{ // HTML entities that are two unicode codepoints. var entity2 = map[string][2]rune{ - // TODO(nigeltao): Handle replacements that are wider than their names. - // "nLt;": {'\u226A', '\u20D2'}, - // "nGt;": {'\u226B', '\u20D2'}, + "nLt;": {'\u226A', '\u20D2'}, + "nGt;": {'\u226B', '\u20D2'}, "NotEqualTilde;": {'\u2242', '\u0338'}, "NotGreaterFullEqual;": {'\u2267', '\u0338'}, "NotGreaterGreater;": {'\u226B', '\u0338'}, diff --git a/vendor/golang.org/x/net/html/escape.go b/vendor/golang.org/x/net/html/escape.go index 12f2273706..df3edc5b12 100644 --- a/vendor/golang.org/x/net/html/escape.go +++ b/vendor/golang.org/x/net/html/escape.go @@ -6,6 +6,7 @@ package html import ( "bytes" + "slices" "strings" "unicode/utf8" ) @@ -50,25 +51,24 @@ var replacementTable = [...]rune{ // 0x0D->'\u000D' is a no-op. } -// unescapeEntity reads an entity like "<" from b[src:] and writes the -// corresponding "<" to b[dst:], returning the incremented dst and src cursors. -// Precondition: b[src] == '&' && dst <= src. -// attribute should be true if parsing an attribute value. -func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { +// unescapeEntity attempts to consume a character reference from s[src:], +// returning the rune, potential second rune, and number of bytes consumed +// (which indicates the length of the character reference). It is assumed that +// the first byte of s is '&'. attribute should be true if parsing an attribute +// value. +func unescapeEntity(s []byte, attribute bool) (rune, rune, int) { // https://html.spec.whatwg.org/multipage/syntax.html#consume-a-character-reference // i starts at 1 because we already know that s[0] == '&'. - i, s := 1, b[src:] + i := 1 if len(s) <= 1 { - b[dst] = b[src] - return dst + 1, src + 1 + return '&', 0, 1 } if s[i] == '#' { - if len(s) <= 3 { // We need to have at least "&#.". - b[dst] = b[src] - return dst + 1, src + 1 + if len(s) <= 2 { // We need to have at least "&#". + return '&', 0, 1 } i++ c := s[i] @@ -78,34 +78,43 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { i++ } + i0 := i x := '\x00' for i < len(s) { c = s[i] - i++ + var d rune + var mult rune if hex { + mult = 16 if '0' <= c && c <= '9' { - x = 16*x + rune(c) - '0' - continue + d = rune(c) - '0' } else if 'a' <= c && c <= 'f' { - x = 16*x + rune(c) - 'a' + 10 - continue + d = rune(c) - 'a' + 10 } else if 'A' <= c && c <= 'F' { - x = 16*x + rune(c) - 'A' + 10 - continue + d = rune(c) - 'A' + 10 + } else { + break + } + } else { + mult = 10 + if '0' <= c && c <= '9' { + d = rune(c) - '0' + } else { + break } - } else if '0' <= c && c <= '9' { - x = 10*x + rune(c) - '0' - continue } - if c != ';' { - i-- + if x <= 0x10FFFF { + x = mult*x + d } - break + i++ + } + + if i == i0 { // No characters matched. + return '&', 0, 1 } - if i <= 3 { // No characters matched. - b[dst] = b[src] - return dst + 1, src + 1 + if i < len(s) && s[i] == ';' { + i++ } if 0x80 <= x && x <= 0x9F { @@ -116,7 +125,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { x = '\uFFFD' } - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } // Consume the maximum number of characters possible, with the @@ -141,10 +150,9 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' { // No-op. } else if x := entity[entityName]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + i + return x, 0, i } else if x := entity2[entityName]; x[0] != 0 { - dst1 := dst + utf8.EncodeRune(b[dst:], x[0]) - return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i + return x[0], x[1], i } else if !attribute { maxLen := len(entityName) - 1 if maxLen > longestEntityWithoutSemicolon { @@ -152,35 +160,67 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) { } for j := maxLen; j > 1; j-- { if x := entity[entityName[:j]]; x != 0 { - return dst + utf8.EncodeRune(b[dst:], x), src + j + 1 + return x, 0, j + 1 } } } - dst1, src1 = dst+i, src+i - copy(b[dst:dst1], b[src:src1]) - return dst1, src1 + return '&', 0, 1 } -// unescape unescapes b's entities in-place, so that "a<b" becomes "a entityNameLen { + if reusingB { + out = slices.Clone(out) + reusingB = false } - return b[0:dst] + out = slices.Grow(out, replLen) + } + out = utf8.AppendRune(out, r1) + if r2 != 0 { + out = utf8.AppendRune(out, r2) } + + src += entityNameLen } - return b + + return out } // lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". diff --git a/vendor/golang.org/x/net/html/foreign.go b/vendor/golang.org/x/net/html/foreign.go index e8515d8e88..65d01d1ed9 100644 --- a/vendor/golang.org/x/net/html/foreign.go +++ b/vendor/golang.org/x/net/html/foreign.go @@ -23,7 +23,7 @@ func adjustForeignAttributes(aa []Attribute) { } switch a.Key { case "xlink:actuate", "xlink:arcrole", "xlink:href", "xlink:role", "xlink:show", - "xlink:title", "xlink:type", "xml:base", "xml:lang", "xml:space", "xmlns:xlink": + "xlink:title", "xlink:type", "xml:lang", "xml:space", "xmlns:xlink": j := strings.Index(a.Key, ":") aa[i].Namespace = a.Key[:j] aa[i].Key = a.Key[j+1:] diff --git a/vendor/golang.org/x/net/html/parse.go b/vendor/golang.org/x/net/html/parse.go index 88fc0056a3..165b6108d4 100644 --- a/vendor/golang.org/x/net/html/parse.go +++ b/vendor/golang.org/x/net/html/parse.go @@ -5,9 +5,11 @@ package html import ( + "cmp" "errors" "fmt" "io" + "slices" "strings" a "golang.org/x/net/html/atom" @@ -61,7 +63,7 @@ func (p *parser) top() *Node { // Stop tags for use in popUntil. These come from section 12.2.4.2. var ( defaultScopeStopTags = map[string][]a.Atom{ - "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template}, + "": {a.Applet, a.Caption, a.Html, a.Table, a.Td, a.Th, a.Marquee, a.Object, a.Template, a.Select}, "math": {a.AnnotationXml, a.Mi, a.Mn, a.Mo, a.Ms, a.Mtext}, "svg": {a.Desc, a.ForeignObject, a.Title}, } @@ -76,7 +78,6 @@ const ( tableScope tableRowScope tableBodyScope - selectScope ) // popUntil pops the stack of open elements at the highest element whose tag @@ -131,10 +132,6 @@ func (p *parser) indexOfElementInScope(s scope, matchTags ...a.Atom) int { if tagAtom == a.Html || tagAtom == a.Table || tagAtom == a.Template { return -1 } - case selectScope: - if tagAtom != a.Optgroup && tagAtom != a.Option { - return -1 - } default: panic(fmt.Sprintf("html: internal error: indexOfElementInScope unknown scope: %d", s)) } @@ -328,6 +325,14 @@ func (p *parser) addText(text string) { }) } +func attrCompare(a, b Attribute) int { + return cmp.Or( + cmp.Compare(a.Namespace, b.Namespace), + cmp.Compare(a.Key, b.Key), + cmp.Compare(a.Val, b.Val), + ) +} + // addElement adds a child element based on the current token. func (p *parser) addElement() { p.addChild(&Node{ @@ -343,6 +348,10 @@ func (p *parser) addFormattingElement() { tagAtom, attr := p.tok.DataAtom, p.tok.Attr p.addElement() + // In order to optimize the search, we need the attributes to be sorted, so we + // can just use slices.Equal. + slices.SortFunc(attr, attrCompare) + // Implement the Noah's Ark clause, but with three per family instead of two. identicalElements := 0 findIdenticalElements: @@ -360,19 +369,7 @@ findIdenticalElements: if n.DataAtom != tagAtom { continue } - if len(n.Attr) != len(attr) { - continue - } - compareAttributes: - for _, t0 := range n.Attr { - for _, t1 := range attr { - if t0.Key == t1.Key && t0.Namespace == t1.Namespace && t0.Val == t1.Val { - // Found a match for this attribute, continue with the next attribute. - continue compareAttributes - } - } - // If we get here, there is no attribute that matches a. - // Therefore the element is not identical to the new one. + if !slices.Equal(n.Attr, attr) { continue findIdenticalElements } @@ -382,7 +379,11 @@ findIdenticalElements: } } - p.afe = append(p.afe, p.top()) + // Sort the attributes to optimize future identical-element searches. + top := p.top() + slices.SortFunc(top.Attr, attrCompare) + + p.afe = append(p.afe, top) } // Section 12.2.4.3. @@ -454,21 +455,6 @@ func (p *parser) resetInsertionMode() { } switch n.DataAtom { - case a.Select: - if !last { - for ancestor, first := n, p.oe[0]; ancestor != first; { - ancestor = p.oe[p.oe.index(ancestor)-1] - switch ancestor.DataAtom { - case a.Template: - p.im = inSelectIM - return - case a.Table: - p.im = inSelectInTableIM - return - } - } - } - p.im = inSelectIM case a.Td, a.Th: // TODO: remove this divergence from the HTML5 spec. // @@ -996,7 +982,10 @@ func inBodyIM(p *parser) bool { p.popUntil(buttonScope, a.P) p.addElement() case a.Button: - p.popUntil(defaultScope, a.Button) + if p.elementInScope(defaultScope, a.Button) { + p.generateImpliedEndTags() + p.popUntil(defaultScope, a.Button) + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false @@ -1034,7 +1023,18 @@ func inBodyIM(p *parser) bool { p.framesetOK = false p.im = inTableIM return true - case a.Area, a.Br, a.Embed, a.Img, a.Input, a.Keygen, a.Wbr: + case a.Area, a.Br, a.Embed, a.Img, a.Keygen, a.Wbr: + p.reconstructActiveFormattingElements() + p.addElement() + p.oe.pop() + p.acknowledgeSelfClosingTag() + p.framesetOK = false + case a.Input: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } + p.popUntil(defaultScope, a.Select) p.reconstructActiveFormattingElements() p.addElement() p.oe.pop() @@ -1055,7 +1055,13 @@ func inBodyIM(p *parser) bool { p.oe.pop() p.acknowledgeSelfClosingTag() case a.Hr: - p.popUntil(buttonScope, a.P) + if p.elementInScope(buttonScope, a.P) { + p.generateImpliedEndTags("p") + p.popUntil(defaultScope, a.P) + } + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + } p.addElement() p.oe.pop() p.acknowledgeSelfClosingTag() @@ -1089,13 +1095,30 @@ func inBodyIM(p *parser) bool { // Don't let the tokenizer go into raw text mode when scripting is disabled. p.tokenizer.NextIsNotRawText() case a.Select: + if p.fragment && p.context.DataAtom == a.Select { + // Ignore the token. + return true + } else if p.popUntil(defaultScope, a.Select) { + return true + } p.reconstructActiveFormattingElements() p.addElement() p.framesetOK = false - p.im = inSelectIM return true - case a.Optgroup, a.Option: - if p.top().DataAtom == a.Option { + case a.Option: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags("optgroup") + // If oe has option element in scope, parse error? + } else if p.top().DataAtom == a.Option { + p.oe.pop() + } + p.reconstructActiveFormattingElements() + p.addElement() + case a.Optgroup: + if p.elementInScope(defaultScope, a.Select) { + p.generateImpliedEndTags() + // If oe has option or optgroup element in scope, parse error? + } else if p.top().DataAtom == a.Option { p.oe.pop() } p.reconstructActiveFormattingElements() @@ -1143,7 +1166,12 @@ func inBodyIM(p *parser) bool { return false } return true - case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Summary, a.Ul: + case a.Address, a.Article, a.Aside, a.Blockquote, a.Button, a.Center, a.Details, a.Dialog, a.Dir, a.Div, a.Dl, a.Fieldset, a.Figcaption, a.Figure, a.Footer, a.Header, a.Hgroup, a.Listing, a.Main, a.Menu, a.Nav, a.Ol, a.Pre, a.Search, a.Section, a.Select, a.Summary, a.Ul: + if !p.elementInScope(defaultScope, p.tok.DataAtom) { + // Ignore the token. + return true + } + p.generateImpliedEndTags() p.popUntil(defaultScope, p.tok.DataAtom) case a.Form: if p.oe.contains(a.Template) { @@ -1372,8 +1400,6 @@ func (p *parser) inBodyEndTagFormatting(tagAtom a.Atom, tagName string) { } // inBodyEndTagOther performs the "any other end tag" algorithm for inBodyIM. -// "Any other end tag" handling from 12.2.6.5 The rules for parsing tokens in foreign content -// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inforeign func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { for i := len(p.oe) - 1; i >= 0; i-- { // Two element nodes have the same tag if they have the same Data (a @@ -1383,7 +1409,7 @@ func (p *parser) inBodyEndTagOther(tagAtom a.Atom, tagName string) { // Uncommon (custom) tags get a zero DataAtom. // // The if condition here is equivalent to (p.oe[i].Data == tagName). - if (p.oe[i].DataAtom == tagAtom) && + if p.oe[i].Namespace == "" && (p.oe[i].DataAtom == tagAtom) && ((tagAtom != 0) || (p.oe[i].Data == tagName)) { p.oe = p.oe[:i] break @@ -1484,17 +1510,6 @@ func inTableIM(p *parser) bool { } p.addElement() p.form = p.oe.pop() - case a.Select: - p.reconstructActiveFormattingElements() - switch p.top().DataAtom { - case a.Table, a.Tbody, a.Tfoot, a.Thead, a.Tr: - p.fosterParenting = true - } - p.addElement() - p.fosterParenting = false - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1543,12 +1558,6 @@ func inCaptionIM(p *parser) bool { p.clearActiveFormattingElements() p.im = inTableIM return false - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1758,12 +1767,6 @@ func inCellIM(p *parser) bool { } // Ignore the token. return true - case a.Select: - p.reconstructActiveFormattingElements() - p.addElement() - p.framesetOK = false - p.im = inSelectInTableIM - return true } case EndTagToken: switch p.tok.DataAtom { @@ -1794,118 +1797,6 @@ func inCellIM(p *parser) bool { return inBodyIM(p) } -// Section 12.2.6.4.16. -func inSelectIM(p *parser) bool { - switch p.tok.Type { - case TextToken: - p.addText(strings.Replace(p.tok.Data, "\x00", "", -1)) - case StartTagToken: - switch p.tok.DataAtom { - case a.Html: - return inBodyIM(p) - case a.Option: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - p.addElement() - case a.Optgroup: - if p.top().DataAtom == a.Option { - p.oe.pop() - } - if p.top().DataAtom == a.Optgroup { - p.oe.pop() - } - p.addElement() - case a.Select: - if !p.popUntil(selectScope, a.Select) { - // Ignore the token. - return true - } - p.resetInsertionMode() - case a.Input, a.Keygen, a.Textarea: - if p.elementInScope(selectScope, a.Select) { - p.parseImpliedToken(EndTagToken, a.Select, a.Select.String()) - return false - } - // In order to properly ignore