diff --git a/authenticator/Dockerfile b/authenticator/Dockerfile
index ff68f2d22..aaa508f7a 100644
--- a/authenticator/Dockerfile
+++ b/authenticator/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.24-alpine3.21 AS build-env
+FROM golang:1.25-alpine AS build-env
USER root
RUN apk add --no-cache git gcc musl-dev
RUN apk add --update make
diff --git a/common-lib/go.mod b/common-lib/go.mod
index a6a8a410c..af1136048 100644
--- a/common-lib/go.mod
+++ b/common-lib/go.mod
@@ -27,7 +27,7 @@ require (
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/posthog/posthog-go v1.5.9
- github.com/prometheus/client_golang v1.22.0
+ github.com/prometheus/client_golang v1.23.2
github.com/robfig/cron/v3 v3.0.1
github.com/stretchr/testify v1.11.1
github.com/vmware-tanzu/velero v1.14.1
@@ -37,18 +37,18 @@ require (
google.golang.org/api v0.234.0
gopkg.in/go-playground/validator.v9 v9.31.0
gopkg.in/yaml.v2 v2.4.0
- helm.sh/helm/v3 v3.18.6
- k8s.io/api v0.33.3
- k8s.io/apiextensions-apiserver v0.33.3
- k8s.io/apimachinery v0.33.3
- k8s.io/cli-runtime v0.33.3
- k8s.io/client-go v0.33.3
+ helm.sh/helm/v3 v3.20.2
+ k8s.io/api v0.35.1
+ k8s.io/apiextensions-apiserver v0.35.1
+ k8s.io/apimachinery v0.35.1
+ k8s.io/cli-runtime v0.35.1
+ k8s.io/client-go v0.35.1
k8s.io/kube-aggregator v0.33.0
- k8s.io/kubectl v0.33.3
+ k8s.io/kubectl v0.35.1
k8s.io/kubernetes v1.33.4
- k8s.io/metrics v0.33.3
- k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979
- sigs.k8s.io/yaml v1.5.0
+ k8s.io/metrics v0.35.1
+ k8s.io/utils v0.0.0-20251002143259-bc988d571ff4
+ sigs.k8s.io/yaml v1.6.0
)
require (
@@ -80,14 +80,11 @@ require (
github.com/distribution/reference v0.6.0 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
- github.com/fsnotify/fsnotify v1.9.0 // indirect
- github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-errors/errors v1.4.2 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/google/go-cmp v0.7.0 // indirect
- github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@@ -109,15 +106,16 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
- go.yaml.in/yaml/v2 v2.4.2 // indirect
- go.yaml.in/yaml/v3 v3.0.3 // indirect
- gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+ go.yaml.in/yaml/v2 v2.4.3 // indirect
+ go.yaml.in/yaml/v3 v3.0.4 // indirect
+ gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
mellium.im/sasl v0.3.2 // indirect
oras.land/oras-go/v2 v2.6.1 // indirect
- sigs.k8s.io/kustomize/api v0.19.0 // indirect
- sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
+ sigs.k8s.io/kustomize/api v0.20.1 // indirect
+ sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
)
require (
@@ -129,10 +127,10 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.2 // indirect
github.com/Azure/go-autorest/tracing v0.6.1 // indirect
- github.com/Masterminds/semver/v3 v3.3.0 // indirect
+ github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/containerd/containerd v1.7.27 // indirect
+ github.com/containerd/containerd v1.7.30 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/docker/cli v29.2.0+incompatible
@@ -148,7 +146,7 @@ require (
github.com/go-openapi/swag v0.23.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
- github.com/google/gnostic-models v0.6.9 // indirect
+ github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
@@ -161,16 +159,16 @@ require (
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-ieproxy v0.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
- github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
- github.com/prometheus/common v0.64.0 // indirect
+ github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/sirupsen/logrus v1.9.3
- github.com/spf13/pflag v1.0.7 // indirect
+ github.com/spf13/pflag v1.0.10 // indirect
go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
@@ -188,9 +186,8 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
- k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
- sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
- sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
+ k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
+ sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
)
require (
diff --git a/common-lib/go.sum b/common-lib/go.sum
index 2f7ccf904..00c192a4a 100644
--- a/common-lib/go.sum
+++ b/common-lib/go.sum
@@ -56,8 +56,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0=
-github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
-github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
+github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE=
@@ -118,8 +118,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
-github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE=
-github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
+github.com/containerd/containerd v1.7.30 h1:/2vezDpLDVGGmkUXmlNPLCCNKHJ5BbC5tJB5JNzQhqE=
+github.com/containerd/containerd v1.7.30/go.mod h1:fek494vwJClULlTpExsmOyKCMUAbuVjlFsJQc4/j44M=
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=
@@ -167,12 +167,12 @@ github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
-github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
+github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0=
+github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
-github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
+github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gammazero/deque v1.0.0 h1:LTmimT8H7bXkkCy6gZX7zNLtkbz4NdS2z8LZuor3j34=
github.com/gammazero/deque v1.0.0/go.mod h1:iflpYvtGfM3U8S8j+sZEKIak3SAKYpA5/SQewgfXDKo=
github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q=
@@ -208,21 +208,18 @@ github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXe
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
-github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
-github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
+github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
+github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
-github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
-github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
+github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
+github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -294,8 +291,9 @@ github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFL
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
+github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
@@ -312,10 +310,10 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
-github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
-github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
-github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
+github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
+github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
+github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
+github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@@ -333,12 +331,12 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posthog/posthog-go v1.5.9 h1:ZtHUyabbtWTmLzC7qtkCm+ne8Byj8X2mtopEGgsIB90=
github.com/posthog/posthog-go v1.5.9/go.mod h1:ZPCind3bz8xDLK0Zhvpv1fQav6WfRcQDqTMfMXmna98=
-github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
-github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
+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.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
-github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
+github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
+github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho=
@@ -355,10 +353,10 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
-github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
-github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
-github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
+github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
+github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -438,10 +436,10 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-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/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
-go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
+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=
+go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
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=
@@ -560,8 +558,8 @@ google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
-gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
+gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M=
@@ -578,47 +576,45 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
-helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY=
-helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg=
-k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
-k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
-k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs=
-k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8=
-k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
-k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
-k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA=
-k8s.io/cli-runtime v0.33.3/go.mod h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo=
-k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
-k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
+helm.sh/helm/v3 v3.20.2 h1:binM4rvPx5DcNsa1sIt7UZi55lRbu3pZUFmQkSoRh48=
+helm.sh/helm/v3 v3.20.2/go.mod h1:Fl1kBaWCpkUrM6IYXPjQ3bdZQfFrogKArqptvueZ6Ww=
+k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q=
+k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM=
+k8s.io/apiextensions-apiserver v0.35.1 h1:p5vvALkknlOcAqARwjS20kJffgzHqwyQRM8vHLwgU7w=
+k8s.io/apiextensions-apiserver v0.35.1/go.mod h1:2CN4fe1GZ3HMe4wBr25qXyJnJyZaquy4nNlNmb3R7AQ=
+k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU=
+k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
+k8s.io/cli-runtime v0.35.1 h1:uKcXFe8J7AMAM4Gm2JDK4mp198dBEq2nyeYtO+JfGJE=
+k8s.io/cli-runtime v0.35.1/go.mod h1:55/hiXIq1C8qIJ3WBrWxEwDLdHQYhBNRdZOz9f7yvTw=
+k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM=
+k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-aggregator v0.33.0 h1:jTjEe/DqpJcaPp4x1CjNaMb1XPD+H8SSf/yVpC8coFg=
k8s.io/kube-aggregator v0.33.0/go.mod h1:6BRnSnWzh6nWUxjQhNwGP9gMnPfSW0WsFeOZGMHtvZw=
-k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
-k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
-k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac=
-k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0=
+k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
+k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
+k8s.io/kubectl v0.35.1 h1:zP3Er8C5i1dcAFUMh9Eva0kVvZHptXIn/+8NtRWMxwg=
+k8s.io/kubectl v0.35.1/go.mod h1:cQ2uAPs5IO/kx8R5s5J3Ihv3VCYwrx0obCXum0CvnXo=
k8s.io/kubernetes v1.33.4 h1:T1d5FLUYm3/KyUeV7YJhKTR980zHCHb7K2xhCSo3lE8=
k8s.io/kubernetes v1.33.4/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00=
-k8s.io/metrics v0.33.3 h1:9CcqBz15JZfISqwca33gdHS8I6XfsK1vA8WUdEnG70g=
-k8s.io/metrics v0.33.3/go.mod h1:Aw+cdg4AYHw0HvUY+lCyq40FOO84awrqvJRTw0cmXDs=
-k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg=
-k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/metrics v0.35.1 h1:MUcrUcWlq81XiripkydzCGsY9zQawDXfP9IICNNcVVw=
+k8s.io/metrics v0.35.1/go.mod h1:9x7xWOAOiWzHA0vaqLgSE4PXF3vyT5ts5XIbx8OSjiI=
+k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
+k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0=
mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY=
oras.land/oras-go/v2 v2.6.1 h1:bonOEkjLfp8tt6qXWRRWP6p1F+9octchOf2EqnWB4Zs=
oras.land/oras-go/v2 v2.6.1/go.mod h1:dhtFrFOuZuDtAVeZ9FUnaa5zfzplG3ZnFX9/uH1J/Yk=
-sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
-sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
-sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
-sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
-sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
-sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
-sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
+sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=
+sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM=
+sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78=
+sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
-sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
-sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
-sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
-sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
-sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
+sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
+sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
+sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
diff --git a/common-lib/securestore/rollback/Dockerfile b/common-lib/securestore/rollback/Dockerfile
index d354cee26..75f5323dc 100644
--- a/common-lib/securestore/rollback/Dockerfile
+++ b/common-lib/securestore/rollback/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.24-alpine AS builder
+FROM golang:1.25-alpine AS builder
# Install git for go mod dependencies
RUN apk add --no-cache git
diff --git a/common-lib/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md b/common-lib/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md
index f95a504fe..fabe5e43d 100644
--- a/common-lib/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md
+++ b/common-lib/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md
@@ -1,5 +1,31 @@
# Changelog
+## 3.4.0 (2025-06-27)
+
+### Added
+
+- #268: Added property to Constraints to include prereleases for Check and Validate
+
+### Changed
+
+- #263: Updated Go testing for 1.24, 1.23, and 1.22
+- #269: Updated the error message handling for message case and wrapping errors
+- #266: Restore the ability to have leading 0's when parsing with NewVersion.
+ Opt-out of this by setting CoerceNewVersion to false.
+
+### Fixed
+
+- #257: Fixed the CodeQL link (thanks @dmitris)
+- #262: Restored detailed errors when failed to parse with NewVersion. Opt-out
+ of this by setting DetailedNewVersionErrors to false for faster performance.
+- #267: Handle pre-releases for an "and" group if one constraint includes them
+
+## 3.3.1 (2024-11-19)
+
+### Fixed
+
+- #253: Fix for allowing some version that were invalid
+
## 3.3.0 (2024-08-27)
### Added
@@ -137,7 +163,7 @@ functions. These are described in the added and changed sections below.
- #78: Fix unchecked error in example code (thanks @ravron)
- #70: Fix the handling of pre-releases and the 0.0.0 release edge case
- #97: Fixed copyright file for proper display on GitHub
-- #107: Fix handling prerelease when sorting alphanum and num
+- #107: Fix handling prerelease when sorting alphanum and num
- #109: Fixed where Validate sometimes returns wrong message on error
## 1.4.2 (2018-04-10)
diff --git a/common-lib/vendor/github.com/Masterminds/semver/v3/README.md b/common-lib/vendor/github.com/Masterminds/semver/v3/README.md
index ed5693608..2f56c676a 100644
--- a/common-lib/vendor/github.com/Masterminds/semver/v3/README.md
+++ b/common-lib/vendor/github.com/Masterminds/semver/v3/README.md
@@ -50,6 +50,18 @@ other versions, convert the version back into a string, and get the original
string. Getting the original string is useful if the semantic version was coerced
into a valid form.
+There are package level variables that affect how `NewVersion` handles parsing.
+
+- `CoerceNewVersion` is `true` by default. When set to `true` it coerces non-compliant
+ versions into SemVer. For example, allowing a leading 0 in a major, minor, or patch
+ part. This enables the use of CalVer in versions even when not compliant with SemVer.
+ When set to `false` less coercion work is done.
+- `DetailedNewVersionErrors` provides more detailed errors. It only has an affect when
+ `CoerceNewVersion` is set to `false`. When `DetailedNewVersionErrors` is set to `true`
+ it can provide some more insight into why a version is invalid. Setting
+ `DetailedNewVersionErrors` to `false` is faster on performance but provides less
+ detailed error messages if a version fails to parse.
+
## Sorting Semantic Versions
A set of versions can be sorted using the `sort` package from the standard library.
@@ -160,6 +172,10 @@ means `>=1.2.3-BETA` will return `1.2.3-alpha`. What you might expect from case
sensitivity doesn't apply here. This is due to ASCII sort ordering which is what
the spec specifies.
+The `Constraints` instance returned from `semver.NewConstraint()` has a property
+`IncludePrerelease` that, when set to true, will return prerelease versions when calls
+to `Check()` and `Validate()` are made.
+
### Hyphen Range Comparisons
There are multiple methods to handle ranges and the first is hyphens ranges.
@@ -250,7 +266,7 @@ or [create a pull request](https://github.com/Masterminds/semver/pulls).
Security is an important consideration for this project. The project currently
uses the following tools to help discover security issues:
-* [CodeQL](https://github.com/Masterminds/semver)
+* [CodeQL](https://codeql.github.com)
* [gosec](https://github.com/securego/gosec)
* Daily Fuzz testing
diff --git a/common-lib/vendor/github.com/Masterminds/semver/v3/constraints.go b/common-lib/vendor/github.com/Masterminds/semver/v3/constraints.go
index 8461c7ed9..8b7a10f83 100644
--- a/common-lib/vendor/github.com/Masterminds/semver/v3/constraints.go
+++ b/common-lib/vendor/github.com/Masterminds/semver/v3/constraints.go
@@ -12,6 +12,13 @@ import (
// checked against.
type Constraints struct {
constraints [][]*constraint
+ containsPre []bool
+
+ // IncludePrerelease specifies if pre-releases should be included in
+ // the results. Note, if a constraint range has a prerelease than
+ // prereleases will be included for that AND group even if this is
+ // set to false.
+ IncludePrerelease bool
}
// NewConstraint returns a Constraints instance that a Version instance can
@@ -22,11 +29,10 @@ func NewConstraint(c string) (*Constraints, error) {
c = rewriteRange(c)
ors := strings.Split(c, "||")
- or := make([][]*constraint, len(ors))
+ lenors := len(ors)
+ or := make([][]*constraint, lenors)
+ hasPre := make([]bool, lenors)
for k, v := range ors {
-
- // TODO: Find a way to validate and fetch all the constraints in a simpler form
-
// Validate the segment
if !validConstraintRegex.MatchString(v) {
return nil, fmt.Errorf("improper constraint: %s", v)
@@ -43,12 +49,22 @@ func NewConstraint(c string) (*Constraints, error) {
return nil, err
}
+ // If one of the constraints has a prerelease record this.
+ // This information is used when checking all in an "and"
+ // group to ensure they all check for prereleases.
+ if pc.con.pre != "" {
+ hasPre[k] = true
+ }
+
result[i] = pc
}
or[k] = result
}
- o := &Constraints{constraints: or}
+ o := &Constraints{
+ constraints: or,
+ containsPre: hasPre,
+ }
return o, nil
}
@@ -57,10 +73,10 @@ func (cs Constraints) Check(v *Version) bool {
// TODO(mattfarina): For v4 of this library consolidate the Check and Validate
// functions as the underlying functions make that possible now.
// loop over the ORs and check the inner ANDs
- for _, o := range cs.constraints {
+ for i, o := range cs.constraints {
joy := true
for _, c := range o {
- if check, _ := c.check(v); !check {
+ if check, _ := c.check(v, (cs.IncludePrerelease || cs.containsPre[i])); !check {
joy = false
break
}
@@ -83,12 +99,12 @@ func (cs Constraints) Validate(v *Version) (bool, []error) {
// Capture the prerelease message only once. When it happens the first time
// this var is marked
var prerelesase bool
- for _, o := range cs.constraints {
+ for i, o := range cs.constraints {
joy := true
for _, c := range o {
// Before running the check handle the case there the version is
// a prerelease and the check is not searching for prereleases.
- if c.con.pre == "" && v.pre != "" {
+ if !(cs.IncludePrerelease || cs.containsPre[i]) && v.pre != "" {
if !prerelesase {
em := fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
e = append(e, em)
@@ -98,7 +114,7 @@ func (cs Constraints) Validate(v *Version) (bool, []error) {
} else {
- if _, err := c.check(v); err != nil {
+ if _, err := c.check(v, (cs.IncludePrerelease || cs.containsPre[i])); err != nil {
e = append(e, err)
joy = false
}
@@ -227,8 +243,8 @@ type constraint struct {
}
// Check if a version meets the constraint
-func (c *constraint) check(v *Version) (bool, error) {
- return constraintOps[c.origfunc](v, c)
+func (c *constraint) check(v *Version, includePre bool) (bool, error) {
+ return constraintOps[c.origfunc](v, c, includePre)
}
// String prints an individual constraint into a string
@@ -236,7 +252,7 @@ func (c *constraint) string() string {
return c.origfunc + c.orig
}
-type cfunc func(v *Version, c *constraint) (bool, error)
+type cfunc func(v *Version, c *constraint, includePre bool) (bool, error)
func parseConstraint(c string) (*constraint, error) {
if len(c) > 0 {
@@ -272,7 +288,7 @@ func parseConstraint(c string) (*constraint, error) {
// The constraintRegex should catch any regex parsing errors. So,
// we should never get here.
- return nil, errors.New("constraint Parser Error")
+ return nil, errors.New("constraint parser error")
}
cs.con = con
@@ -290,7 +306,7 @@ func parseConstraint(c string) (*constraint, error) {
// The constraintRegex should catch any regex parsing errors. So,
// we should never get here.
- return nil, errors.New("constraint Parser Error")
+ return nil, errors.New("constraint parser error")
}
cs := &constraint{
@@ -305,16 +321,14 @@ func parseConstraint(c string) (*constraint, error) {
}
// Constraint functions
-func constraintNotEqual(v *Version, c *constraint) (bool, error) {
- if c.dirty {
-
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
- return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
- }
+func constraintNotEqual(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
+ return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
+ }
+ if c.dirty {
if c.con.Major() != v.Major() {
return true, nil
}
@@ -345,12 +359,11 @@ func constraintNotEqual(v *Version, c *constraint) (bool, error) {
return true, nil
}
-func constraintGreaterThan(v *Version, c *constraint) (bool, error) {
+func constraintGreaterThan(v *Version, c *constraint, includePre bool) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
@@ -391,11 +404,10 @@ func constraintGreaterThan(v *Version, c *constraint) (bool, error) {
return false, fmt.Errorf("%s is less than or equal to %s", v, c.orig)
}
-func constraintLessThan(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintLessThan(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
@@ -406,12 +418,11 @@ func constraintLessThan(v *Version, c *constraint) (bool, error) {
return false, fmt.Errorf("%s is greater than or equal to %s", v, c.orig)
}
-func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) {
+func constraintGreaterThanEqual(v *Version, c *constraint, includePre bool) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
@@ -422,11 +433,10 @@ func constraintGreaterThanEqual(v *Version, c *constraint) (bool, error) {
return false, fmt.Errorf("%s is less than %s", v, c.orig)
}
-func constraintLessThanEqual(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintLessThanEqual(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
@@ -455,11 +465,10 @@ func constraintLessThanEqual(v *Version, c *constraint) (bool, error) {
// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0, <1.3.0
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
-func constraintTilde(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintTilde(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
@@ -487,16 +496,15 @@ func constraintTilde(v *Version, c *constraint) (bool, error) {
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
// it's a straight =
-func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintTildeOrEqual(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
if c.dirty {
- return constraintTilde(v, c)
+ return constraintTilde(v, c, includePre)
}
eq := v.Equal(c.con)
@@ -516,11 +524,10 @@ func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
// ^0.0.3 --> >=0.0.3 <0.0.4
// ^0.0 --> >=0.0.0 <0.1.0
// ^0 --> >=0.0.0 <1.0.0
-func constraintCaret(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
diff --git a/common-lib/vendor/github.com/Masterminds/semver/v3/version.go b/common-lib/vendor/github.com/Masterminds/semver/v3/version.go
index ff499fb66..7a3ba7388 100644
--- a/common-lib/vendor/github.com/Masterminds/semver/v3/version.go
+++ b/common-lib/vendor/github.com/Masterminds/semver/v3/version.go
@@ -14,32 +14,52 @@ import (
// The compiled version of the regex created at init() is cached here so it
// only needs to be created once.
var versionRegex *regexp.Regexp
+var looseVersionRegex *regexp.Regexp
+
+// CoerceNewVersion sets if leading 0's are allowd in the version part. Leading 0's are
+// not allowed in a valid semantic version. When set to true, NewVersion will coerce
+// leading 0's into a valid version.
+var CoerceNewVersion = true
+
+// DetailedNewVersionErrors specifies if detailed errors are returned from the NewVersion
+// function. This is used when CoerceNewVersion is set to false. If set to false
+// ErrInvalidSemVer is returned for an invalid version. This does not apply to
+// StrictNewVersion. Setting this function to false returns errors more quickly.
+var DetailedNewVersionErrors = true
var (
// ErrInvalidSemVer is returned a version is found to be invalid when
// being parsed.
- ErrInvalidSemVer = errors.New("Invalid Semantic Version")
+ ErrInvalidSemVer = errors.New("invalid semantic version")
// ErrEmptyString is returned when an empty string is passed in for parsing.
- ErrEmptyString = errors.New("Version string empty")
+ ErrEmptyString = errors.New("version string empty")
// ErrInvalidCharacters is returned when invalid characters are found as
// part of a version
- ErrInvalidCharacters = errors.New("Invalid characters in version")
+ ErrInvalidCharacters = errors.New("invalid characters in version")
// ErrSegmentStartsZero is returned when a version segment starts with 0.
// This is invalid in SemVer.
- ErrSegmentStartsZero = errors.New("Version segment starts with 0")
+ ErrSegmentStartsZero = errors.New("version segment starts with 0")
// ErrInvalidMetadata is returned when the metadata is an invalid format
- ErrInvalidMetadata = errors.New("Invalid Metadata string")
+ ErrInvalidMetadata = errors.New("invalid metadata string")
// ErrInvalidPrerelease is returned when the pre-release is an invalid format
- ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
+ ErrInvalidPrerelease = errors.New("invalid prerelease string")
)
// semVerRegex is the regular expression used to parse a semantic version.
-const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
+// This is not the official regex from the semver spec. It has been modified to allow for loose handling
+// where versions like 2.1 are detected.
+const semVerRegex string = `v?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?` +
+ `(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?` +
+ `(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`
+
+// looseSemVerRegex is a regular expression that lets invalid semver expressions through
+// with enough detail that certain errors can be checked for.
+const looseSemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
@@ -53,6 +73,7 @@ type Version struct {
func init() {
versionRegex = regexp.MustCompile("^" + semVerRegex + "$")
+ looseVersionRegex = regexp.MustCompile("^" + looseSemVerRegex + "$")
}
const (
@@ -140,7 +161,80 @@ func StrictNewVersion(v string) (*Version, error) {
// attempts to convert it to SemVer. If you want to validate it was a strict
// semantic version at parse time see StrictNewVersion().
func NewVersion(v string) (*Version, error) {
+ if CoerceNewVersion {
+ return coerceNewVersion(v)
+ }
m := versionRegex.FindStringSubmatch(v)
+ if m == nil {
+
+ // Disabling detailed errors is first so that it is in the fast path.
+ if !DetailedNewVersionErrors {
+ return nil, ErrInvalidSemVer
+ }
+
+ // Check for specific errors with the semver string and return a more detailed
+ // error.
+ m = looseVersionRegex.FindStringSubmatch(v)
+ if m == nil {
+ return nil, ErrInvalidSemVer
+ }
+ err := validateVersion(m)
+ if err != nil {
+ return nil, err
+ }
+ return nil, ErrInvalidSemVer
+ }
+
+ sv := &Version{
+ metadata: m[5],
+ pre: m[4],
+ original: v,
+ }
+
+ var err error
+ sv.major, err = strconv.ParseUint(m[1], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
+ }
+
+ if m[2] != "" {
+ sv.minor, err = strconv.ParseUint(m[2], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
+ }
+ } else {
+ sv.minor = 0
+ }
+
+ if m[3] != "" {
+ sv.patch, err = strconv.ParseUint(m[3], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
+ }
+ } else {
+ sv.patch = 0
+ }
+
+ // Perform some basic due diligence on the extra parts to ensure they are
+ // valid.
+
+ if sv.pre != "" {
+ if err = validatePrerelease(sv.pre); err != nil {
+ return nil, err
+ }
+ }
+
+ if sv.metadata != "" {
+ if err = validateMetadata(sv.metadata); err != nil {
+ return nil, err
+ }
+ }
+
+ return sv, nil
+}
+
+func coerceNewVersion(v string) (*Version, error) {
+ m := looseVersionRegex.FindStringSubmatch(v)
if m == nil {
return nil, ErrInvalidSemVer
}
@@ -154,13 +248,13 @@ func NewVersion(v string) (*Version, error) {
var err error
sv.major, err = strconv.ParseUint(m[1], 10, 64)
if err != nil {
- return nil, fmt.Errorf("Error parsing version segment: %s", err)
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
}
if m[2] != "" {
sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64)
if err != nil {
- return nil, fmt.Errorf("Error parsing version segment: %s", err)
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
}
} else {
sv.minor = 0
@@ -169,7 +263,7 @@ func NewVersion(v string) (*Version, error) {
if m[3] != "" {
sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64)
if err != nil {
- return nil, fmt.Errorf("Error parsing version segment: %s", err)
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
}
} else {
sv.patch = 0
@@ -612,7 +706,9 @@ func containsOnly(s string, comp string) bool {
func validatePrerelease(p string) error {
eparts := strings.Split(p, ".")
for _, p := range eparts {
- if containsOnly(p, num) {
+ if p == "" {
+ return ErrInvalidPrerelease
+ } else if containsOnly(p, num) {
if len(p) > 1 && p[0] == '0' {
return ErrSegmentStartsZero
}
@@ -631,9 +727,62 @@ func validatePrerelease(p string) error {
func validateMetadata(m string) error {
eparts := strings.Split(m, ".")
for _, p := range eparts {
- if !containsOnly(p, allowed) {
+ if p == "" {
return ErrInvalidMetadata
+ } else if !containsOnly(p, allowed) {
+ return ErrInvalidMetadata
+ }
+ }
+ return nil
+}
+
+// validateVersion checks for common validation issues but may not catch all errors
+func validateVersion(m []string) error {
+ var err error
+ var v string
+ if m[1] != "" {
+ if len(m[1]) > 1 && m[1][0] == '0' {
+ return ErrSegmentStartsZero
+ }
+ _, err = strconv.ParseUint(m[1], 10, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing version segment: %w", err)
}
}
+
+ if m[2] != "" {
+ v = strings.TrimPrefix(m[2], ".")
+ if len(v) > 1 && v[0] == '0' {
+ return ErrSegmentStartsZero
+ }
+ _, err = strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing version segment: %w", err)
+ }
+ }
+
+ if m[3] != "" {
+ v = strings.TrimPrefix(m[3], ".")
+ if len(v) > 1 && v[0] == '0' {
+ return ErrSegmentStartsZero
+ }
+ _, err = strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing version segment: %w", err)
+ }
+ }
+
+ if m[5] != "" {
+ if err = validatePrerelease(m[5]); err != nil {
+ return err
+ }
+ }
+
+ if m[8] != "" {
+ if err = validateMetadata(m[8]); err != nil {
+ return err
+ }
+ }
+
return nil
}
diff --git a/common-lib/vendor/github.com/fxamacker/cbor/v2/README.md b/common-lib/vendor/github.com/fxamacker/cbor/v2/README.md
index af0a79507..d072b81c7 100644
--- a/common-lib/vendor/github.com/fxamacker/cbor/v2/README.md
+++ b/common-lib/vendor/github.com/fxamacker/cbor/v2/README.md
@@ -1,30 +1,31 @@
-# CBOR Codec in Go
-
-
+
CBOR Codec
[fxamacker/cbor](https://github.com/fxamacker/cbor) is a library for encoding and decoding [CBOR](https://www.rfc-editor.org/info/std94) and [CBOR Sequences](https://www.rfc-editor.org/rfc/rfc8742.html).
CBOR is a [trusted alternative](https://www.rfc-editor.org/rfc/rfc8949.html#name-comparison-of-other-binary-) to JSON, MessagePack, Protocol Buffers, etc. CBOR is an Internet Standard defined by [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94) and is designed to be relevant for decades.
-`fxamacker/cbor` is used in projects by Arm Ltd., Cisco, EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, Kubernetes, Let's Encrypt (ISRG), Linux Foundation, Microsoft, Mozilla, Oasis Protocol, Tailscale, Teleport, [etc](https://github.com/fxamacker/cbor#who-uses-fxamackercbor).
+`fxamacker/cbor` is used in projects by Arm Ltd., EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, IBM, Kubernetes[*](https://github.com/search?q=org%3Akubernetes%20fxamacker%2Fcbor&type=code), Let's Encrypt, Linux Foundation, Microsoft, Oasis Protocol, Red Hat[*](https://github.com/search?q=org%3Aopenshift+fxamacker%2Fcbor&type=code), Tailscale[*](https://github.com/search?q=org%3Atailscale+fxamacker%2Fcbor&type=code), Veraison[*](https://github.com/search?q=org%3Averaison+fxamacker%2Fcbor&type=code), [etc](https://github.com/fxamacker/cbor#who-uses-fxamackercbor).
-See [Quick Start](#quick-start) and [Releases](https://github.com/fxamacker/cbor/releases/). 🆕 `UnmarshalFirst` and `DiagnoseFirst` can decode CBOR Sequences. `cbor.MarshalToBuffer()` and `UserBufferEncMode` accepts user-specified buffer.
+See [Quick Start](#quick-start) and [Releases](https://github.com/fxamacker/cbor/releases/). 🆕 `UnmarshalFirst` and `DiagnoseFirst` can decode CBOR Sequences. `MarshalToBuffer` and `UserBufferEncMode` accepts user-specified buffer.
## fxamacker/cbor
[](https://github.com/fxamacker/cbor/actions?query=workflow%3Aci)
-[](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A596%25%22)
+[](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A597%25%22)
[](https://github.com/fxamacker/cbor/actions/workflows/codeql-analysis.yml)
[](#fuzzing-and-code-coverage)
[](https://goreportcard.com/report/github.com/fxamacker/cbor)
+[](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage)
`fxamacker/cbor` is a CBOR codec in full conformance with [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94). It also supports CBOR Sequences ([RFC 8742](https://www.rfc-editor.org/rfc/rfc8742.html)) and Extended Diagnostic Notation ([Appendix G of RFC 8610](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G)).
Features include full support for CBOR tags, [Core Deterministic Encoding](https://www.rfc-editor.org/rfc/rfc8949.html#name-core-deterministic-encoding), duplicate map key detection, etc.
+API is mostly same as `encoding/json`, plus interfaces that simplify concurrency and CBOR options.
+
Design balances trade-offs between security, speed, concurrency, encoded data size, usability, etc.
-Highlights
+ 🔎 Highlights
__🚀 Speed__
@@ -38,7 +39,7 @@ Codec passed multiple confidential security assessments in 2022. No vulnerabili
__🗜️ Data Size__
-Struct tags (`toarray`, `keyasint`, `omitempty`) automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
+Struct tag options (`toarray`, `keyasint`, `omitempty`, `omitzero`) and field tag "-" automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
__:jigsaw: Usability__
@@ -58,164 +59,205 @@ Features include CBOR [extension points](https://www.rfc-editor.org/rfc/rfc8949.
`fxamacker/cbor` has configurable limits, etc. that defend against malicious CBOR data.
-By contrast, `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security).
-
-Example decoding with encoding/gob 💥 fatal error (out of memory)
-
-```Go
-// Example of encoding/gob having "fatal error: runtime: out of memory"
-// while decoding 181 bytes.
-package main
-import (
- "bytes"
- "encoding/gob"
- "encoding/hex"
- "fmt"
-)
-
-// Example data is from https://github.com/golang/go/issues/24446
-// (shortened to 181 bytes).
-const data = "4dffb503010102303001ff30000109010130010800010130010800010130" +
- "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" +
- "860001013001ff860001013001ffb80000001eff850401010e3030303030" +
- "30303030303030303001ff3000010c0104000016ffb70201010830303030" +
- "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" +
- "303030303030303030303030303030303030303030303030303030303030" +
- "30"
-
-type X struct {
- J *X
- K map[string]int
-}
-
-func main() {
- raw, _ := hex.DecodeString(data)
- decoder := gob.NewDecoder(bytes.NewReader(raw))
-
- var x X
- decoder.Decode(&x) // fatal error: runtime: out of memory
- fmt.Println("Decoding finished.")
-}
-```
-
-
-
-
-
-`fxamacker/cbor` is fast at rejecting malformed CBOR data. E.g. attempts to
-decode 10 bytes of malicious CBOR data to `[]byte` (with default settings):
-
-| Codec | Speed (ns/op) | Memory | Allocs |
-| :---- | ------------: | -----: | -----: |
-| fxamacker/cbor 2.5.0 | 44 ± 5% | 32 B/op | 2 allocs/op |
-| ugorji/go 1.2.11 | 5353261 ± 4% | 67111321 B/op | 13 allocs/op |
-
-Benchmark details
-
-Latest comparison used:
-- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
-- go1.19.10, linux/amd64, i5-13600K (disabled all e-cores, DDR4 @2933)
-- go test -bench=. -benchmem -count=20
-
-#### Prior comparisons
-
-| Codec | Speed (ns/op) | Memory | Allocs |
-| :---- | ------------: | -----: | -----: |
-| fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
-| fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
-| ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
-| ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate |
-
-- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
-- go1.19.6, linux/amd64, i5-13600K (DDR4)
-- go test -bench=. -benchmem -count=20
-
-
-
-
-
-### Smaller Encodings with Struct Tags
-
-Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs.
-
-Example encoding 3-level nested Go struct to 1 byte CBOR
-
-https://go.dev/play/p/YxwvfPdFQG2
-
-```Go
-// Example encoding nested struct (with omitempty tag)
-// - encoding/json: 18 byte JSON
-// - fxamacker/cbor: 1 byte CBOR
-package main
-
-import (
- "encoding/hex"
- "encoding/json"
- "fmt"
-
- "github.com/fxamacker/cbor/v2"
-)
-
-type GrandChild struct {
- Quux int `json:",omitempty"`
-}
-
-type Child struct {
- Baz int `json:",omitempty"`
- Qux GrandChild `json:",omitempty"`
-}
-
-type Parent struct {
- Foo Child `json:",omitempty"`
- Bar int `json:",omitempty"`
-}
-
-func cb() {
- results, _ := cbor.Marshal(Parent{})
- fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
-
- text, _ := cbor.Diagnose(results) // Diagnostic Notation
- fmt.Println("DN: " + text)
-}
-
-func js() {
- results, _ := json.Marshal(Parent{})
- fmt.Println("hex(JSON): " + hex.EncodeToString(results))
-
- text := string(results) // JSON
- fmt.Println("JSON: " + text)
-}
-
-func main() {
- cb()
- fmt.Println("-------------")
- js()
-}
-```
-
-Output (DN is Diagnostic Notation):
-```
-hex(CBOR): a0
-DN: {}
--------------
-hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
-JSON: {"Foo":{"Qux":{}}}
-```
-
-
-
-
-
-Example using different struct tags together:
+Notably, `fxamacker/cbor` is fast at rejecting malformed CBOR data.
+
+> [!NOTE]
+> Benchmarks rejecting 10 bytes of malicious CBOR data decoding to `[]byte`:
+>
+> | Codec | Speed (ns/op) | Memory | Allocs |
+> | :---- | ------------: | -----: | -----: |
+> | fxamacker/cbor 2.7.0 | 47 ± 7% | 32 B/op | 2 allocs/op |
+> | ugorji/go 1.2.12 | 5878187 ± 3% | 67111556 B/op | 13 allocs/op |
+>
+> Faster hardware (overclocked DDR4 or DDR5) can reduce speed difference.
+>
+> 🔎 Benchmark details
+>
+> Latest comparison for decoding CBOR data to Go `[]byte`:
+> - Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
+> - go1.22.7, linux/amd64, i5-13600K (DDR4-2933, disabled e-cores)
+> - go test -bench=. -benchmem -count=20
+>
+> #### Prior comparisons
+>
+> | Codec | Speed (ns/op) | Memory | Allocs |
+> | :---- | ------------: | -----: | -----: |
+> | fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
+> | fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
+> | ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
+> | ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate |
+>
+> - Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
+> - go1.19.6, linux/amd64, i5-13600K (DDR4)
+> - go test -bench=. -benchmem -count=20
+>
+>
+
+In contrast, some codecs can crash or use excessive resources while decoding bad data.
+
+> [!WARNING]
+> Go's `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security).
+>
+> 🔎 gob fatal error (out of memory) 💥 decoding 181 bytes
+>
+> ```Go
+> // Example of encoding/gob having "fatal error: runtime: out of memory"
+> // while decoding 181 bytes (all Go versions as of Dec. 8, 2024).
+> package main
+> import (
+> "bytes"
+> "encoding/gob"
+> "encoding/hex"
+> "fmt"
+> )
+>
+> // Example data is from https://github.com/golang/go/issues/24446
+> // (shortened to 181 bytes).
+> const data = "4dffb503010102303001ff30000109010130010800010130010800010130" +
+> "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" +
+> "860001013001ff860001013001ffb80000001eff850401010e3030303030" +
+> "30303030303030303001ff3000010c0104000016ffb70201010830303030" +
+> "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" +
+> "303030303030303030303030303030303030303030303030303030303030" +
+> "30"
+>
+> type X struct {
+> J *X
+> K map[string]int
+> }
+>
+> func main() {
+> raw, _ := hex.DecodeString(data)
+> decoder := gob.NewDecoder(bytes.NewReader(raw))
+>
+> var x X
+> decoder.Decode(&x) // fatal error: runtime: out of memory
+> fmt.Println("Decoding finished.")
+> }
+> ```
+>
+>
+>
+
+### Smaller Encodings with Struct Tag Options
+
+Struct tags automatically reduce encoded size of structs and improve speed.
+
+We can write less code by using struct tag options:
+- `toarray`: encode without field names (decode back to original struct)
+- `keyasint`: encode field names as integers (decode back to original struct)
+- `omitempty`: omit empty field when encoding
+- `omitzero`: omit zero-value field when encoding
+
+As a special case, struct field tag "-" omits the field.
+
+NOTE: When a struct uses `toarray`, the encoder will ignore `omitempty` and `omitzero` to prevent position of encoded array elements from changing. This allows decoder to match encoded elements to their Go struct field.

-API is mostly same as `encoding/json`, plus interfaces that simplify concurrency for CBOR options.
+> [!NOTE]
+> `fxamacker/cbor` can encode a 3-level nested Go struct to 1 byte!
+> - `encoding/json`: 18 bytes of JSON
+> - `fxamacker/cbor`: 1 byte of CBOR
+>
+> 🔎 Encoding 3-level nested Go struct with omitempty
+>
+> https://go.dev/play/p/YxwvfPdFQG2
+>
+> ```Go
+> // Example encoding nested struct (with omitempty tag)
+> // - encoding/json: 18 byte JSON
+> // - fxamacker/cbor: 1 byte CBOR
+>
+> package main
+>
+> import (
+> "encoding/hex"
+> "encoding/json"
+> "fmt"
+>
+> "github.com/fxamacker/cbor/v2"
+> )
+>
+> type GrandChild struct {
+> Quux int `json:",omitempty"`
+> }
+>
+> type Child struct {
+> Baz int `json:",omitempty"`
+> Qux GrandChild `json:",omitempty"`
+> }
+>
+> type Parent struct {
+> Foo Child `json:",omitempty"`
+> Bar int `json:",omitempty"`
+> }
+>
+> func cb() {
+> results, _ := cbor.Marshal(Parent{})
+> fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
+>
+> text, _ := cbor.Diagnose(results) // Diagnostic Notation
+> fmt.Println("DN: " + text)
+> }
+>
+> func js() {
+> results, _ := json.Marshal(Parent{})
+> fmt.Println("hex(JSON): " + hex.EncodeToString(results))
+>
+> text := string(results) // JSON
+> fmt.Println("JSON: " + text)
+> }
+>
+> func main() {
+> cb()
+> fmt.Println("-------------")
+> js()
+> }
+> ```
+>
+> Output (DN is Diagnostic Notation):
+> ```
+> hex(CBOR): a0
+> DN: {}
+> -------------
+> hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
+> JSON: {"Foo":{"Qux":{}}}
+> ```
+>
+>
+
## Quick Start
__Install__: `go get github.com/fxamacker/cbor/v2` and `import "github.com/fxamacker/cbor/v2"`.
+> [!TIP]
+>
+> Tinygo users can try beta/experimental branch [feature/cbor-tinygo-beta](https://github.com/fxamacker/cbor/tree/feature/cbor-tinygo-beta).
+>
+> 🔎 More about tinygo feature branch
+>
+> ### Tinygo
+>
+> Branch [feature/cbor-tinygo-beta](https://github.com/fxamacker/cbor/tree/feature/cbor-tinygo-beta) is based on fxamacker/cbor v2.7.0 and it can be compiled using tinygo v0.33 (also compiles with golang/go).
+>
+> It passes unit tests (with both go1.22 and tinygo v0.33) and is considered beta/experimental for tinygo.
+>
+> :warning: The `feature/cbor-tinygo-beta` branch does not get fuzz tested yet.
+>
+> Changes in this feature branch only affect tinygo compiled software. Summary of changes:
+> - default `DecOptions.MaxNestedLevels` is reduced to 16 (was 32). User can specify higher limit but 24+ crashes tests when compiled with tinygo v0.33.
+> - disabled decoding CBOR tag data to Go interface because tinygo v0.33 is missing needed feature.
+> - encoding error message can be different when encoding function type.
+>
+> Related tinygo issues:
+> - https://github.com/tinygo-org/tinygo/issues/4277
+> - https://github.com/tinygo-org/tinygo/issues/4458
+>
+>
+
+
### Key Points
This library can encode and decode CBOR (RFC 8949) and CBOR Sequences (RFC 8742).
@@ -252,16 +294,17 @@ rest, err = cbor.UnmarshalFirst(b, &v) // decode []byte b to v
// DiagnoseFirst translates first CBOR data item to text and returns remaining bytes.
text, rest, err = cbor.DiagnoseFirst(b) // decode []byte b to Diagnostic Notation text
-// NOTE: Unmarshal returns ExtraneousDataError if there are remaining bytes,
-// but new funcs UnmarshalFirst and DiagnoseFirst do not.
+// NOTE: Unmarshal() returns ExtraneousDataError if there are remaining bytes, but
+// UnmarshalFirst() and DiagnoseFirst() allow trailing bytes.
```
-__IMPORTANT__: 👉 CBOR settings allow trade-offs between speed, security, encoding size, etc.
-
-- Different CBOR libraries may use different default settings.
-- CBOR-based formats or protocols usually require specific settings.
-
-For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
+> [!IMPORTANT]
+> CBOR settings allow trade-offs between speed, security, encoding size, etc.
+>
+> - Different CBOR libraries may use different default settings.
+> - CBOR-based formats or protocols usually require specific settings.
+>
+> For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
### Presets
@@ -312,9 +355,63 @@ err = em.MarshalToBuffer(v, &buf) // encode v to provided buf
### Struct Tags
-Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs.
+Struct tag options (`toarray`, `keyasint`, `omitempty`, `omitzero`) reduce encoded size of structs.
+
+As a special case, struct field tag "-" omits the field.
+
+ 🔎 Example encoding with struct field tag "-"
+
+https://go.dev/play/p/aWEIFxd7InX
+
+```Go
+// https://github.com/fxamacker/cbor/issues/652
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/fxamacker/cbor/v2"
+)
+
+// The `cbor:"-"` tag omits the Type field when encoding to CBOR.
+type Entity struct {
+ _ struct{} `cbor:",toarray"`
+ ID uint64 `json:"id"`
+ Type string `cbor:"-" json:"typeOf"`
+ Name string `json:"name"`
+}
+
+func main() {
+ entity := Entity{
+ ID: 1,
+ Type: "int64",
+ Name: "Identifier",
+ }
+
+ c, _ := cbor.Marshal(entity)
+ diag, _ := cbor.Diagnose(c)
+ fmt.Printf("CBOR in hex: %x\n", c)
+ fmt.Printf("CBOR in edn: %s\n", diag)
+
+ j, _ := json.Marshal(entity)
+ fmt.Printf("JSON: %s\n", string(j))
+
+ fmt.Printf("JSON encoding is %d bytes\n", len(j))
+ fmt.Printf("CBOR encoding is %d bytes\n", len(c))
+
+ // Output:
+ // CBOR in hex: 82016a4964656e746966696572
+ // CBOR in edn: [1, "Identifier"]
+ // JSON: {"id":1,"typeOf":"int64","name":"Identifier"}
+ // JSON encoding is 45 bytes
+ // CBOR encoding is 13 bytes
+}
+```
+
+
-Example encoding 3-level nested Go struct to 1 byte CBOR
+ 🔎 Example encoding 3-level nested Go struct to 1 byte CBOR
https://go.dev/play/p/YxwvfPdFQG2
@@ -382,13 +479,13 @@ JSON: {"Foo":{"Qux":{}}}
-Example using several struct tags
+ 🔎 Example using struct tag options

-Struct tags simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys.
+Struct tag options simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys.
### CBOR Tags
@@ -404,7 +501,7 @@ em, err := opts.EncModeWithSharedTags(ts) // mutable shared CBOR tags
`TagSet` and modes using it are safe for concurrent use. Equivalent API is available for `DecMode`.
-Example using TagSet and TagOptions
+ 🔎 Example using TagSet and TagOptions
```go
// Use signedCWT struct defined in "Decoding CWT" example.
@@ -430,16 +527,149 @@ if err := dm.Unmarshal(data, &v); err != nil {
em, _ := cbor.EncOptions{}.EncModeWithTags(tags)
// Marshal signedCWT with tag number.
-if data, err := cbor.Marshal(v); err != nil {
+if data, err := em.Marshal(v); err != nil {
return err
}
```
+👉 `fxamacker/cbor` allows user apps to use almost any current or future CBOR tag number by implementing `cbor.Marshaler` and `cbor.Unmarshaler` interfaces.
+
+Basically, `MarshalCBOR` and `UnmarshalCBOR` functions can be implemented by user apps and those functions will automatically be called by this CBOR codec's `Marshal`, `Unmarshal`, etc.
+
+The following [example](https://github.com/fxamacker/cbor/blob/master/example_embedded_json_tag_for_cbor_test.go) shows how to encode and decode a tagged CBOR data item with tag number 262. The tag content is a JSON object "embedded" as a CBOR byte string (major type 2).
+
+ 🔎 Example using Embedded JSON Tag for CBOR (tag 262)
+
+```go
+// https://github.com/fxamacker/cbor/issues/657
+
+package cbor_test
+
+// NOTE: RFC 8949 does not mention tag number 262. IANA assigned
+// CBOR tag number 262 as "Embedded JSON Object" specified by the
+// document Embedded JSON Tag for CBOR:
+//
+// "Tag 262 can be applied to a byte string (major type 2) to indicate
+// that the byte string is a JSON Object. The length of the byte string
+// indicates the content."
+//
+// For more info, see Embedded JSON Tag for CBOR at:
+// https://github.com/toravir/CBOR-Tag-Specs/blob/master/embeddedJSON.md
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+
+ "github.com/fxamacker/cbor/v2"
+)
+
+// cborTagNumForEmbeddedJSON is the CBOR tag number 262.
+const cborTagNumForEmbeddedJSON = 262
+
+// EmbeddedJSON represents a Go value to be encoded as a tagged CBOR data item
+// with tag number 262 and the tag content is a JSON object "embedded" as a
+// CBOR byte string (major type 2).
+type EmbeddedJSON struct {
+ any
+}
+
+func NewEmbeddedJSON(val any) EmbeddedJSON {
+ return EmbeddedJSON{val}
+}
+
+// MarshalCBOR encodes EmbeddedJSON to a tagged CBOR data item with the
+// tag number 262 and the tag content is a JSON object that is
+// "embedded" as a CBOR byte string.
+func (v EmbeddedJSON) MarshalCBOR() ([]byte, error) {
+ // Encode v to JSON object.
+ data, err := json.Marshal(v)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create cbor.Tag representing a tagged CBOR data item.
+ tag := cbor.Tag{
+ Number: cborTagNumForEmbeddedJSON,
+ Content: data,
+ }
+
+ // Marshal to a tagged CBOR data item.
+ return cbor.Marshal(tag)
+}
+
+// UnmarshalCBOR decodes a tagged CBOR data item to EmbeddedJSON.
+// The byte slice provided to this function must contain a single
+// tagged CBOR data item with the tag number 262 and tag content
+// must be a JSON object "embedded" as a CBOR byte string.
+func (v *EmbeddedJSON) UnmarshalCBOR(b []byte) error {
+ // Unmarshal tagged CBOR data item.
+ var tag cbor.Tag
+ if err := cbor.Unmarshal(b, &tag); err != nil {
+ return err
+ }
+
+ // Check tag number.
+ if tag.Number != cborTagNumForEmbeddedJSON {
+ return fmt.Errorf("got tag number %d, expect tag number %d", tag.Number, cborTagNumForEmbeddedJSON)
+ }
+
+ // Check tag content.
+ jsonData, isByteString := tag.Content.([]byte)
+ if !isByteString {
+ return fmt.Errorf("got tag content type %T, expect tag content []byte", tag.Content)
+ }
+
+ // Unmarshal JSON object.
+ return json.Unmarshal(jsonData, v)
+}
+
+// MarshalJSON encodes EmbeddedJSON to a JSON object.
+func (v EmbeddedJSON) MarshalJSON() ([]byte, error) {
+ return json.Marshal(v.any)
+}
+
+// UnmarshalJSON decodes a JSON object.
+func (v *EmbeddedJSON) UnmarshalJSON(b []byte) error {
+ dec := json.NewDecoder(bytes.NewReader(b))
+ dec.UseNumber()
+ return dec.Decode(&v.any)
+}
+
+func Example_embeddedJSONTagForCBOR() {
+ value := NewEmbeddedJSON(map[string]any{
+ "name": "gopher",
+ "id": json.Number("42"),
+ })
+
+ data, err := cbor.Marshal(value)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("cbor: %x\n", data)
+
+ var v EmbeddedJSON
+ err = cbor.Unmarshal(data, &v)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("%+v\n", v.any)
+ for k, v := range v.any.(map[string]any) {
+ fmt.Printf(" %s: %v (%T)\n", k, v, v)
+ }
+}
+```
+
+
+
+
### Functions and Interfaces
-Functions and interfaces at a glance
+ 🔎 Functions and interfaces at a glance
Common functions with same API as `encoding/json`:
- `Marshal`, `Unmarshal`
@@ -453,7 +683,7 @@ because RFC 8949 treats CBOR data item with remaining bytes as malformed.
Other useful functions:
- `Diagnose`, `DiagnoseFirst` produce human-readable [Extended Diagnostic Notation](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G) from CBOR data.
- `UnmarshalFirst` decodes first CBOR data item and return any remaining bytes.
-- `Wellformed` returns true if the the CBOR data item is well-formed.
+- `Wellformed` returns true if the CBOR data item is well-formed.
Interfaces identical or comparable to Go `encoding` packages include:
`Marshaler`, `Unmarshaler`, `BinaryMarshaler`, and `BinaryUnmarshaler`.
@@ -472,15 +702,28 @@ Default limits may need to be increased for systems handling very large data (e.
## Status
-v2.7.0 (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality.
+[v2.9.0](https://github.com/fxamacker/cbor/releases/tag/v2.9.0) (Jul 13, 2025) improved interoperability/transcoding between CBOR & JSON, refactored tests, and improved docs.
+- Add opt-in support for `encoding.TextMarshaler` and `encoding.TextUnmarshaler` to encode and decode from CBOR text string.
+- Add opt-in support for `json.Marshaler` and `json.Unmarshaler` via user-provided transcoding function.
+- Update docs for TimeMode, Tag, RawTag, and add example for Embedded JSON Tag for CBOR.
+
+v2.9.0 passed fuzz tests and is production quality.
+
+The minimum version of Go required to build:
+- v2.8.0 and newer releases require go 1.20+.
+- v2.7.1 and older releases require go 1.17+.
For more details, see [release notes](https://github.com/fxamacker/cbor/releases).
-### Prior Release
+### Prior Releases
+
+[v2.8.0](https://github.com/fxamacker/cbor/releases/tag/v2.8.0) (March 30, 2025) is a small release primarily to add `omitzero` option to struct field tags and fix bugs. It passed fuzz tests (billions of executions) and is production quality.
+
+[v2.7.0](https://github.com/fxamacker/cbor/releases/tag/v2.7.0) (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality.
[v2.6.0](https://github.com/fxamacker/cbor/releases/tag/v2.6.0) (February 2024) adds important new features, optimizations, and bug fixes. It is especially useful to systems that need to convert data between CBOR and JSON. New options and optimizations improve handling of bignum, integers, maps, and strings.
-v2.5.0 was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023).
+[v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023).
__IMPORTANT__: 👉 Before upgrading from v2.4 or older release, please read the notable changes highlighted in the release notes. v2.5.0 is a large release with bug fixes to error handling for extraneous data in `Unmarshal`, etc. that should be reviewed before upgrading.
@@ -489,7 +732,7 @@ See [v2.5.0 release notes](https://github.com/fxamacker/cbor/releases/tag/v2.5.0
See ["Version and API Changes"](https://github.com/fxamacker/cbor#versions-and-api-changes) section for more info about version numbering, etc.
>=1.2.0, <1.3.0
// ~1.2.3, ~>1.2.3 --> >=1.2.3, <1.3.0
// ~1.2.0, ~>1.2.0 --> >=1.2.0, <1.3.0
-func constraintTilde(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintTilde(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
@@ -487,16 +496,15 @@ func constraintTilde(v *Version, c *constraint) (bool, error) {
// When there is a .x (dirty) status it automatically opts in to ~. Otherwise
// it's a straight =
-func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintTildeOrEqual(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
if c.dirty {
- return constraintTilde(v, c)
+ return constraintTilde(v, c, includePre)
}
eq := v.Equal(c.con)
@@ -516,11 +524,10 @@ func constraintTildeOrEqual(v *Version, c *constraint) (bool, error) {
// ^0.0.3 --> >=0.0.3 <0.0.4
// ^0.0 --> >=0.0.0 <0.1.0
// ^0 --> >=0.0.0 <1.0.0
-func constraintCaret(v *Version, c *constraint) (bool, error) {
- // If there is a pre-release on the version but the constraint isn't looking
- // for them assume that pre-releases are not compatible. See issue 21 for
- // more details.
- if v.Prerelease() != "" && c.con.Prerelease() == "" {
+func constraintCaret(v *Version, c *constraint, includePre bool) (bool, error) {
+ // The existence of prereleases is checked at the group level and passed in.
+ // Exit early if the version has a prerelease but those are to be ignored.
+ if v.Prerelease() != "" && !includePre {
return false, fmt.Errorf("%s is a prerelease version and the constraint is only looking for release versions", v)
}
diff --git a/kubelink/vendor/github.com/Masterminds/semver/v3/version.go b/kubelink/vendor/github.com/Masterminds/semver/v3/version.go
index 304edc342..7a3ba7388 100644
--- a/kubelink/vendor/github.com/Masterminds/semver/v3/version.go
+++ b/kubelink/vendor/github.com/Masterminds/semver/v3/version.go
@@ -14,28 +14,40 @@ import (
// The compiled version of the regex created at init() is cached here so it
// only needs to be created once.
var versionRegex *regexp.Regexp
+var looseVersionRegex *regexp.Regexp
+
+// CoerceNewVersion sets if leading 0's are allowd in the version part. Leading 0's are
+// not allowed in a valid semantic version. When set to true, NewVersion will coerce
+// leading 0's into a valid version.
+var CoerceNewVersion = true
+
+// DetailedNewVersionErrors specifies if detailed errors are returned from the NewVersion
+// function. This is used when CoerceNewVersion is set to false. If set to false
+// ErrInvalidSemVer is returned for an invalid version. This does not apply to
+// StrictNewVersion. Setting this function to false returns errors more quickly.
+var DetailedNewVersionErrors = true
var (
// ErrInvalidSemVer is returned a version is found to be invalid when
// being parsed.
- ErrInvalidSemVer = errors.New("Invalid Semantic Version")
+ ErrInvalidSemVer = errors.New("invalid semantic version")
// ErrEmptyString is returned when an empty string is passed in for parsing.
- ErrEmptyString = errors.New("Version string empty")
+ ErrEmptyString = errors.New("version string empty")
// ErrInvalidCharacters is returned when invalid characters are found as
// part of a version
- ErrInvalidCharacters = errors.New("Invalid characters in version")
+ ErrInvalidCharacters = errors.New("invalid characters in version")
// ErrSegmentStartsZero is returned when a version segment starts with 0.
// This is invalid in SemVer.
- ErrSegmentStartsZero = errors.New("Version segment starts with 0")
+ ErrSegmentStartsZero = errors.New("version segment starts with 0")
// ErrInvalidMetadata is returned when the metadata is an invalid format
- ErrInvalidMetadata = errors.New("Invalid Metadata string")
+ ErrInvalidMetadata = errors.New("invalid metadata string")
// ErrInvalidPrerelease is returned when the pre-release is an invalid format
- ErrInvalidPrerelease = errors.New("Invalid Prerelease string")
+ ErrInvalidPrerelease = errors.New("invalid prerelease string")
)
// semVerRegex is the regular expression used to parse a semantic version.
@@ -45,6 +57,12 @@ const semVerRegex string = `v?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?
`(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?` +
`(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`
+// looseSemVerRegex is a regular expression that lets invalid semver expressions through
+// with enough detail that certain errors can be checked for.
+const looseSemVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
+ `(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
+ `(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
+
// Version represents a single semantic version.
type Version struct {
major, minor, patch uint64
@@ -55,6 +73,7 @@ type Version struct {
func init() {
versionRegex = regexp.MustCompile("^" + semVerRegex + "$")
+ looseVersionRegex = regexp.MustCompile("^" + looseSemVerRegex + "$")
}
const (
@@ -142,8 +161,27 @@ func StrictNewVersion(v string) (*Version, error) {
// attempts to convert it to SemVer. If you want to validate it was a strict
// semantic version at parse time see StrictNewVersion().
func NewVersion(v string) (*Version, error) {
+ if CoerceNewVersion {
+ return coerceNewVersion(v)
+ }
m := versionRegex.FindStringSubmatch(v)
if m == nil {
+
+ // Disabling detailed errors is first so that it is in the fast path.
+ if !DetailedNewVersionErrors {
+ return nil, ErrInvalidSemVer
+ }
+
+ // Check for specific errors with the semver string and return a more detailed
+ // error.
+ m = looseVersionRegex.FindStringSubmatch(v)
+ if m == nil {
+ return nil, ErrInvalidSemVer
+ }
+ err := validateVersion(m)
+ if err != nil {
+ return nil, err
+ }
return nil, ErrInvalidSemVer
}
@@ -156,13 +194,13 @@ func NewVersion(v string) (*Version, error) {
var err error
sv.major, err = strconv.ParseUint(m[1], 10, 64)
if err != nil {
- return nil, fmt.Errorf("Error parsing version segment: %s", err)
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
}
if m[2] != "" {
sv.minor, err = strconv.ParseUint(m[2], 10, 64)
if err != nil {
- return nil, fmt.Errorf("Error parsing version segment: %s", err)
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
}
} else {
sv.minor = 0
@@ -171,7 +209,61 @@ func NewVersion(v string) (*Version, error) {
if m[3] != "" {
sv.patch, err = strconv.ParseUint(m[3], 10, 64)
if err != nil {
- return nil, fmt.Errorf("Error parsing version segment: %s", err)
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
+ }
+ } else {
+ sv.patch = 0
+ }
+
+ // Perform some basic due diligence on the extra parts to ensure they are
+ // valid.
+
+ if sv.pre != "" {
+ if err = validatePrerelease(sv.pre); err != nil {
+ return nil, err
+ }
+ }
+
+ if sv.metadata != "" {
+ if err = validateMetadata(sv.metadata); err != nil {
+ return nil, err
+ }
+ }
+
+ return sv, nil
+}
+
+func coerceNewVersion(v string) (*Version, error) {
+ m := looseVersionRegex.FindStringSubmatch(v)
+ if m == nil {
+ return nil, ErrInvalidSemVer
+ }
+
+ sv := &Version{
+ metadata: m[8],
+ pre: m[5],
+ original: v,
+ }
+
+ var err error
+ sv.major, err = strconv.ParseUint(m[1], 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
+ }
+
+ if m[2] != "" {
+ sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
+ }
+ } else {
+ sv.minor = 0
+ }
+
+ if m[3] != "" {
+ sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("error parsing version segment: %w", err)
}
} else {
sv.patch = 0
@@ -615,7 +707,7 @@ func validatePrerelease(p string) error {
eparts := strings.Split(p, ".")
for _, p := range eparts {
if p == "" {
- return ErrInvalidMetadata
+ return ErrInvalidPrerelease
} else if containsOnly(p, num) {
if len(p) > 1 && p[0] == '0' {
return ErrSegmentStartsZero
@@ -643,3 +735,54 @@ func validateMetadata(m string) error {
}
return nil
}
+
+// validateVersion checks for common validation issues but may not catch all errors
+func validateVersion(m []string) error {
+ var err error
+ var v string
+ if m[1] != "" {
+ if len(m[1]) > 1 && m[1][0] == '0' {
+ return ErrSegmentStartsZero
+ }
+ _, err = strconv.ParseUint(m[1], 10, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing version segment: %w", err)
+ }
+ }
+
+ if m[2] != "" {
+ v = strings.TrimPrefix(m[2], ".")
+ if len(v) > 1 && v[0] == '0' {
+ return ErrSegmentStartsZero
+ }
+ _, err = strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing version segment: %w", err)
+ }
+ }
+
+ if m[3] != "" {
+ v = strings.TrimPrefix(m[3], ".")
+ if len(v) > 1 && v[0] == '0' {
+ return ErrSegmentStartsZero
+ }
+ _, err = strconv.ParseUint(v, 10, 64)
+ if err != nil {
+ return fmt.Errorf("error parsing version segment: %w", err)
+ }
+ }
+
+ if m[5] != "" {
+ if err = validatePrerelease(m[5]); err != nil {
+ return err
+ }
+ }
+
+ if m[8] != "" {
+ if err = validateMetadata(m[8]); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/.golangci.yml b/kubelink/vendor/github.com/cyphar/filepath-securejoin/.golangci.yml
index e965034ed..3e8dd99bd 100644
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/.golangci.yml
+++ b/kubelink/vendor/github.com/cyphar/filepath-securejoin/.golangci.yml
@@ -9,6 +9,10 @@
version: "2"
+run:
+ build-tags:
+ - libpathrs
+
linters:
enable:
- asasalint
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md b/kubelink/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
index 3faee0bc5..6d016d05c 100644
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
+++ b/kubelink/vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
@@ -4,7 +4,54 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
-## [Unreleased 0.5.z] ##
+## [Unreleased] ##
+
+## [0.6.1] - 2025-11-19 ##
+
+> At last up jumped the cunning spider, and fiercely held her fast.
+
+### Fixed ###
+- Our logic for deciding whether to use `openat2(2)` or fallback to an `O_PATH`
+ resolver would cache the result to avoid doing needless test runs of
+ `openat2(2)`. However, this causes issues when `pathrs-lite` is being used by
+ a program that applies new seccomp-bpf filters onto itself -- if the filter
+ denies `openat2(2)` then we would return that error rather than falling back
+ to the `O_PATH` resolver. To resolve this issue, we no longer cache the
+ result if `openat2(2)` was successful, only if there was an error.
+- A file descriptor leak in our `openat2` wrapper (when doing the necessary
+ `dup` for `RESOLVE_IN_ROOT`) has been removed.
+
+## [0.5.2] - 2025-11-19 ##
+
+> "Will you walk into my parlour?" said a spider to a fly.
+
+### Fixed ###
+- Our logic for deciding whether to use `openat2(2)` or fallback to an `O_PATH`
+ resolver would cache the result to avoid doing needless test runs of
+ `openat2(2)`. However, this causes issues when `pathrs-lite` is being used by
+ a program that applies new seccomp-bpf filters onto itself -- if the filter
+ denies `openat2(2)` then we would return that error rather than falling back
+ to the `O_PATH` resolver. To resolve this issue, we no longer cache the
+ result if `openat2(2)` was successful, only if there was an error.
+- A file descriptor leak in our `openat2` wrapper (when doing the necessary
+ `dup` for `RESOLVE_IN_ROOT`) has been removed.
+
+## [0.6.0] - 2025-11-03 ##
+
+> By the Power of Greyskull!
+
+### Breaking ###
+- The deprecated `MkdirAll`, `MkdirAllHandle`, `OpenInRoot`, `OpenatInRoot` and
+ `Reopen` wrappers have been removed. Please switch to using `pathrs-lite`
+ directly.
+
+### Added ###
+- `pathrs-lite` now has support for using libpathrs as a backend. This is
+ opt-in and can be enabled at build time with the `libpathrs` build tag. The
+ intention is to allow for downstream libraries and other projects to make use
+ of the pure-Go `github.com/cyphar/filepath-securejoin/pathrs-lite` package
+ and distributors can then opt-in to using `libpathrs` for the entire binary
+ if they wish.
## [0.5.1] - 2025-10-31 ##
@@ -383,7 +430,10 @@ This is our first release of `github.com/cyphar/filepath-securejoin`,
containing a full implementation with a coverage of 93.5% (the only missing
cases are the error cases, which are hard to mocktest at the moment).
-[Unreleased 0.5.z]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.1...release-0.5
+[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.6.1...HEAD
+[0.6.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.6.0...v0.6.1
+[0.6.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.0...v0.6.0
+[0.5.2]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.1...v0.5.2
[0.5.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.5.0...v0.5.1
[0.5.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...v0.5.0
[0.4.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.0...v0.4.1
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/VERSION b/kubelink/vendor/github.com/cyphar/filepath-securejoin/VERSION
index 4b9fcbec1..ee6cdce3c 100644
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/VERSION
+++ b/kubelink/vendor/github.com/cyphar/filepath-securejoin/VERSION
@@ -1 +1 @@
-0.5.1
+0.6.1
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/deprecated_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/deprecated_linux.go
deleted file mode 100644
index 3e427b164..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/deprecated_linux.go
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package securejoin
-
-import (
- "github.com/cyphar/filepath-securejoin/pathrs-lite"
-)
-
-var (
- // MkdirAll is a wrapper around [pathrs.MkdirAll].
- //
- // Deprecated: You should use [pathrs.MkdirAll] directly instead. This
- // wrapper will be removed in filepath-securejoin v0.6.
- MkdirAll = pathrs.MkdirAll
-
- // MkdirAllHandle is a wrapper around [pathrs.MkdirAllHandle].
- //
- // Deprecated: You should use [pathrs.MkdirAllHandle] directly instead.
- // This wrapper will be removed in filepath-securejoin v0.6.
- MkdirAllHandle = pathrs.MkdirAllHandle
-
- // OpenInRoot is a wrapper around [pathrs.OpenInRoot].
- //
- // Deprecated: You should use [pathrs.OpenInRoot] directly instead. This
- // wrapper will be removed in filepath-securejoin v0.6.
- OpenInRoot = pathrs.OpenInRoot
-
- // OpenatInRoot is a wrapper around [pathrs.OpenatInRoot].
- //
- // Deprecated: You should use [pathrs.OpenatInRoot] directly instead. This
- // wrapper will be removed in filepath-securejoin v0.6.
- OpenatInRoot = pathrs.OpenatInRoot
-
- // Reopen is a wrapper around [pathrs.Reopen].
- //
- // Deprecated: You should use [pathrs.Reopen] directly instead. This
- // wrapper will be removed in filepath-securejoin v0.6.
- Reopen = pathrs.Reopen
-)
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/README.md b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/README.md
deleted file mode 100644
index 1be727e75..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/README.md
+++ /dev/null
@@ -1,33 +0,0 @@
-## `pathrs-lite` ##
-
-`github.com/cyphar/filepath-securejoin/pathrs-lite` provides a minimal **pure
-Go** implementation of the core bits of [libpathrs][]. This is not intended to
-be a complete replacement for libpathrs, instead it is mainly intended to be
-useful as a transition tool for existing Go projects.
-
-The long-term plan for `pathrs-lite` is to provide a build tag that will cause
-all `pathrs-lite` operations to call into libpathrs directly, thus removing
-code duplication for projects that wish to make use of libpathrs (and providing
-the ability for software packagers to opt-in to libpathrs support without
-needing to patch upstream).
-
-[libpathrs]: https://github.com/cyphar/libpathrs
-
-### License ###
-
-Most of this subpackage is licensed under the Mozilla Public License (version
-2.0). For more information, see the top-level [COPYING.md][] and
-[LICENSE.MPL-2.0][] files, as well as the individual license headers for each
-file.
-
-```
-Copyright (C) 2024-2025 Aleksa Sarai
-Copyright (C) 2024-2025 SUSE LLC
-
-This Source Code Form is subject to the terms of the Mozilla Public
-License, v. 2.0. If a copy of the MPL was not distributed with this
-file, You can obtain one at https://mozilla.org/MPL/2.0/.
-```
-
-[COPYING.md]: ../COPYING.md
-[LICENSE.MPL-2.0]: ../LICENSE.MPL-2.0
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/doc.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/doc.go
deleted file mode 100644
index d3d745175..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/doc.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package pathrs (pathrs-lite) is a less complete pure Go implementation of
-// some of the APIs provided by [libpathrs].
-package pathrs
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert/assert.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert/assert.go
deleted file mode 100644
index 595dfbf1a..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert/assert.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-// Copyright (C) 2025 Aleksa Sarai
-// Copyright (C) 2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package assert provides some basic assertion helpers for Go.
-package assert
-
-import (
- "fmt"
-)
-
-// Assert panics if the predicate is false with the provided argument.
-func Assert(predicate bool, msg any) {
- if !predicate {
- panic(msg)
- }
-}
-
-// Assertf panics if the predicate is false and formats the message using the
-// same formatting as [fmt.Printf].
-//
-// [fmt.Printf]: https://pkg.go.dev/fmt#Printf
-func Assertf(predicate bool, fmtMsg string, args ...any) {
- Assert(predicate, fmt.Sprintf(fmtMsg, args...))
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/errors_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/errors_linux.go
deleted file mode 100644
index d0b200f4f..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/errors_linux.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package internal contains unexported common code for filepath-securejoin.
-package internal
-
-import (
- "errors"
-
- "golang.org/x/sys/unix"
-)
-
-type xdevErrorish struct {
- description string
-}
-
-func (err xdevErrorish) Error() string { return err.description }
-func (err xdevErrorish) Is(target error) bool { return target == unix.EXDEV }
-
-var (
- // ErrPossibleAttack indicates that some attack was detected.
- ErrPossibleAttack error = xdevErrorish{"possible attack detected"}
-
- // ErrPossibleBreakout indicates that during an operation we ended up in a
- // state that could be a breakout but we detected it.
- ErrPossibleBreakout error = xdevErrorish{"possible breakout detected"}
-
- // ErrInvalidDirectory indicates an unlinked directory.
- ErrInvalidDirectory = errors.New("wandered into deleted directory")
-
- // ErrDeletedInode indicates an unlinked file (non-directory).
- ErrDeletedInode = errors.New("cannot verify path of deleted inode")
-)
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/at_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/at_linux.go
deleted file mode 100644
index 091054913..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/at_linux.go
+++ /dev/null
@@ -1,148 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package fd
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "runtime"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
-)
-
-// prepareAtWith returns -EBADF (an invalid fd) if dir is nil, otherwise using
-// the dir.Fd(). We use -EBADF because in filepath-securejoin we generally
-// don't want to allow relative-to-cwd paths. The returned path is an
-// *informational* string that describes a reasonable pathname for the given
-// *at(2) arguments. You must not use the full path for any actual filesystem
-// operations.
-func prepareAt(dir Fd, path string) (dirFd int, unsafeUnmaskedPath string) {
- dirFd, dirPath := -int(unix.EBADF), "."
- if dir != nil {
- dirFd, dirPath = int(dir.Fd()), dir.Name()
- }
- if !filepath.IsAbs(path) {
- // only prepend the dirfd path for relative paths
- path = dirPath + "/" + path
- }
- // NOTE: If path is "." or "", the returned path won't be filepath.Clean,
- // but that's okay since this path is either used for errors (in which case
- // a trailing "/" or "/." is important information) or will be
- // filepath.Clean'd later (in the case of fd.Openat).
- return dirFd, path
-}
-
-// Openat is an [Fd]-based wrapper around unix.Openat.
-func Openat(dir Fd, path string, flags int, mode int) (*os.File, error) { //nolint:unparam // wrapper func
- dirFd, fullPath := prepareAt(dir, path)
- // Make sure we always set O_CLOEXEC.
- flags |= unix.O_CLOEXEC
- fd, err := unix.Openat(dirFd, path, flags, uint32(mode))
- if err != nil {
- return nil, &os.PathError{Op: "openat", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- // openat is only used with lexically-safe paths so we can use
- // filepath.Clean here, and also the path itself is not going to be used
- // for actual path operations.
- fullPath = filepath.Clean(fullPath)
- return os.NewFile(uintptr(fd), fullPath), nil
-}
-
-// Fstatat is an [Fd]-based wrapper around unix.Fstatat.
-func Fstatat(dir Fd, path string, flags int) (unix.Stat_t, error) {
- dirFd, fullPath := prepareAt(dir, path)
- var stat unix.Stat_t
- if err := unix.Fstatat(dirFd, path, &stat, flags); err != nil {
- return stat, &os.PathError{Op: "fstatat", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- return stat, nil
-}
-
-// Faccessat is an [Fd]-based wrapper around unix.Faccessat.
-func Faccessat(dir Fd, path string, mode uint32, flags int) error {
- dirFd, fullPath := prepareAt(dir, path)
- err := unix.Faccessat(dirFd, path, mode, flags)
- if err != nil {
- err = &os.PathError{Op: "faccessat", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- return err
-}
-
-// Readlinkat is an [Fd]-based wrapper around unix.Readlinkat.
-func Readlinkat(dir Fd, path string) (string, error) {
- dirFd, fullPath := prepareAt(dir, path)
- size := 4096
- for {
- linkBuf := make([]byte, size)
- n, err := unix.Readlinkat(dirFd, path, linkBuf)
- if err != nil {
- return "", &os.PathError{Op: "readlinkat", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- if n != size {
- return string(linkBuf[:n]), nil
- }
- // Possible truncation, resize the buffer.
- size *= 2
- }
-}
-
-const (
- // STATX_MNT_ID_UNIQUE is provided in golang.org/x/sys@v0.20.0, but in order to
- // avoid bumping the requirement for a single constant we can just define it
- // ourselves.
- _STATX_MNT_ID_UNIQUE = 0x4000 //nolint:revive // unix.* name
-
- // We don't care which mount ID we get. The kernel will give us the unique
- // one if it is supported. If the kernel doesn't support
- // STATX_MNT_ID_UNIQUE, the bit is ignored and the returned request mask
- // will only contain STATX_MNT_ID (if supported).
- wantStatxMntMask = _STATX_MNT_ID_UNIQUE | unix.STATX_MNT_ID
-)
-
-var hasStatxMountID = gocompat.SyncOnceValue(func() bool {
- var stx unix.Statx_t
- err := unix.Statx(-int(unix.EBADF), "/", 0, wantStatxMntMask, &stx)
- return err == nil && stx.Mask&wantStatxMntMask != 0
-})
-
-// GetMountID gets the mount identifier associated with the fd and path
-// combination. It is effectively a wrapper around fetching
-// STATX_MNT_ID{,_UNIQUE} with unix.Statx, but with a fallback to 0 if the
-// kernel doesn't support the feature.
-func GetMountID(dir Fd, path string) (uint64, error) {
- // If we don't have statx(STATX_MNT_ID*) support, we can't do anything.
- if !hasStatxMountID() {
- return 0, nil
- }
-
- dirFd, fullPath := prepareAt(dir, path)
-
- var stx unix.Statx_t
- err := unix.Statx(dirFd, path, unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW, wantStatxMntMask, &stx)
- if stx.Mask&wantStatxMntMask == 0 {
- // It's not a kernel limitation, for some reason we couldn't get a
- // mount ID. Assume it's some kind of attack.
- err = fmt.Errorf("could not get mount id: %w", err)
- }
- if err != nil {
- return 0, &os.PathError{Op: "statx(STATX_MNT_ID_...)", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- return stx.Mnt_id, nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd.go
deleted file mode 100644
index d2206a386..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd.go
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-// Copyright (C) 2025 Aleksa Sarai
-// Copyright (C) 2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package fd provides a drop-in interface-based replacement of [*os.File] that
-// allows for things like noop-Close wrappers to be used.
-//
-// [*os.File]: https://pkg.go.dev/os#File
-package fd
-
-import (
- "io"
- "os"
-)
-
-// Fd is an interface that mirrors most of the API of [*os.File], allowing you
-// to create wrappers that can be used in place of [*os.File].
-//
-// [*os.File]: https://pkg.go.dev/os#File
-type Fd interface {
- io.Closer
- Name() string
- Fd() uintptr
-}
-
-// Compile-time interface checks.
-var (
- _ Fd = (*os.File)(nil)
- _ Fd = noClose{}
-)
-
-type noClose struct{ inner Fd }
-
-func (f noClose) Name() string { return f.inner.Name() }
-func (f noClose) Fd() uintptr { return f.inner.Fd() }
-
-func (f noClose) Close() error { return nil }
-
-// NopCloser returns an [*os.File]-like object where the [Close] method is now
-// a no-op.
-//
-// Note that for [*os.File] and similar objects, the Go garbage collector will
-// still call [Close] on the underlying file unless you use
-// [runtime.SetFinalizer] to disable this behaviour. This is up to the caller
-// to do (if necessary).
-//
-// [*os.File]: https://pkg.go.dev/os#File
-// [Close]: https://pkg.go.dev/io#Closer
-// [runtime.SetFinalizer]: https://pkg.go.dev/runtime#SetFinalizer
-func NopCloser(f Fd) Fd { return noClose{inner: f} }
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd_linux.go
deleted file mode 100644
index e1ec3c0b8..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/fd_linux.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package fd
-
-import (
- "fmt"
- "os"
- "runtime"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
-)
-
-// DupWithName creates a new file descriptor referencing the same underlying
-// file, but with the provided name instead of fd.Name().
-func DupWithName(fd Fd, name string) (*os.File, error) {
- fd2, err := unix.FcntlInt(fd.Fd(), unix.F_DUPFD_CLOEXEC, 0)
- if err != nil {
- return nil, os.NewSyscallError("fcntl(F_DUPFD_CLOEXEC)", err)
- }
- runtime.KeepAlive(fd)
- return os.NewFile(uintptr(fd2), name), nil
-}
-
-// Dup creates a new file description referencing the same underlying file.
-func Dup(fd Fd) (*os.File, error) {
- return DupWithName(fd, fd.Name())
-}
-
-// Fstat is an [Fd]-based wrapper around unix.Fstat.
-func Fstat(fd Fd) (unix.Stat_t, error) {
- var stat unix.Stat_t
- if err := unix.Fstat(int(fd.Fd()), &stat); err != nil {
- return stat, &os.PathError{Op: "fstat", Path: fd.Name(), Err: err}
- }
- runtime.KeepAlive(fd)
- return stat, nil
-}
-
-// Fstatfs is an [Fd]-based wrapper around unix.Fstatfs.
-func Fstatfs(fd Fd) (unix.Statfs_t, error) {
- var statfs unix.Statfs_t
- if err := unix.Fstatfs(int(fd.Fd()), &statfs); err != nil {
- return statfs, &os.PathError{Op: "fstatfs", Path: fd.Name(), Err: err}
- }
- runtime.KeepAlive(fd)
- return statfs, nil
-}
-
-// IsDeadInode detects whether the file has been unlinked from a filesystem and
-// is thus a "dead inode" from the kernel's perspective.
-func IsDeadInode(file Fd) error {
- // If the nlink of a file drops to 0, there is an attacker deleting
- // directories during our walk, which could result in weird /proc values.
- // It's better to error out in this case.
- stat, err := Fstat(file)
- if err != nil {
- return fmt.Errorf("check for dead inode: %w", err)
- }
- if stat.Nlink == 0 {
- err := internal.ErrDeletedInode
- if stat.Mode&unix.S_IFMT == unix.S_IFDIR {
- err = internal.ErrInvalidDirectory
- }
- return fmt.Errorf("%w %q", err, file.Name())
- }
- return nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/mount_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/mount_linux.go
deleted file mode 100644
index 77549c7a9..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/mount_linux.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package fd
-
-import (
- "os"
- "runtime"
-
- "golang.org/x/sys/unix"
-)
-
-// Fsopen is an [Fd]-based wrapper around unix.Fsopen.
-func Fsopen(fsName string, flags int) (*os.File, error) {
- // Make sure we always set O_CLOEXEC.
- flags |= unix.FSOPEN_CLOEXEC
- fd, err := unix.Fsopen(fsName, flags)
- if err != nil {
- return nil, os.NewSyscallError("fsopen "+fsName, err)
- }
- return os.NewFile(uintptr(fd), "fscontext:"+fsName), nil
-}
-
-// Fsmount is an [Fd]-based wrapper around unix.Fsmount.
-func Fsmount(ctx Fd, flags, mountAttrs int) (*os.File, error) {
- // Make sure we always set O_CLOEXEC.
- flags |= unix.FSMOUNT_CLOEXEC
- fd, err := unix.Fsmount(int(ctx.Fd()), flags, mountAttrs)
- if err != nil {
- return nil, os.NewSyscallError("fsmount "+ctx.Name(), err)
- }
- return os.NewFile(uintptr(fd), "fsmount:"+ctx.Name()), nil
-}
-
-// OpenTree is an [Fd]-based wrapper around unix.OpenTree.
-func OpenTree(dir Fd, path string, flags uint) (*os.File, error) {
- dirFd, fullPath := prepareAt(dir, path)
- // Make sure we always set O_CLOEXEC.
- flags |= unix.OPEN_TREE_CLOEXEC
- fd, err := unix.OpenTree(dirFd, path, flags)
- if err != nil {
- return nil, &os.PathError{Op: "open_tree", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- return os.NewFile(uintptr(fd), fullPath), nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/openat2_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/openat2_linux.go
deleted file mode 100644
index 3e937fe3c..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd/openat2_linux.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package fd
-
-import (
- "errors"
- "os"
- "runtime"
-
- "golang.org/x/sys/unix"
-)
-
-func scopedLookupShouldRetry(how *unix.OpenHow, err error) bool {
- // RESOLVE_IN_ROOT (and RESOLVE_BENEATH) can return -EAGAIN if we resolve
- // ".." while a mount or rename occurs anywhere on the system. This could
- // happen spuriously, or as the result of an attacker trying to mess with
- // us during lookup.
- //
- // In addition, scoped lookups have a "safety check" at the end of
- // complete_walk which will return -EXDEV if the final path is not in the
- // root.
- return how.Resolve&(unix.RESOLVE_IN_ROOT|unix.RESOLVE_BENEATH) != 0 &&
- (errors.Is(err, unix.EAGAIN) || errors.Is(err, unix.EXDEV))
-}
-
-// This is a fairly arbitrary limit we have just to avoid an attacker being
-// able to make us spin in an infinite retry loop -- callers can choose to
-// retry on EAGAIN if they prefer.
-const scopedLookupMaxRetries = 128
-
-// Openat2 is an [Fd]-based wrapper around unix.Openat2, but with some retry
-// logic in case of EAGAIN errors.
-func Openat2(dir Fd, path string, how *unix.OpenHow) (*os.File, error) {
- dirFd, fullPath := prepareAt(dir, path)
- // Make sure we always set O_CLOEXEC.
- how.Flags |= unix.O_CLOEXEC
- var tries int
- for {
- fd, err := unix.Openat2(dirFd, path, how)
- if err != nil {
- if scopedLookupShouldRetry(how, err) && tries < scopedLookupMaxRetries {
- // We retry a couple of times to avoid the spurious errors, and
- // if we are being attacked then returning -EAGAIN is the best
- // we can do.
- tries++
- continue
- }
- return nil, &os.PathError{Op: "openat2", Path: fullPath, Err: err}
- }
- runtime.KeepAlive(dir)
- return os.NewFile(uintptr(fd), fullPath), nil
- }
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/README.md b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/README.md
deleted file mode 100644
index 5dcb6ae00..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-## gocompat ##
-
-This directory contains backports of stdlib functions from later Go versions so
-the filepath-securejoin can continue to be used by projects that are stuck with
-Go 1.18 support. Note that often filepath-securejoin is added in security
-patches for old releases, so avoiding the need to bump Go compiler requirements
-is a huge plus to downstreams.
-
-The source code is licensed under the same license as the Go stdlib. See the
-source files for the precise license information.
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/doc.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/doc.go
deleted file mode 100644
index 4b1803f58..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/doc.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-//go:build linux && go1.20
-
-// Copyright (C) 2025 SUSE LLC. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package gocompat includes compatibility shims (backported from future Go
-// stdlib versions) to permit filepath-securejoin to be used with older Go
-// versions (often filepath-securejoin is added in security patches for old
-// releases, so avoiding the need to bump Go compiler requirements is a huge
-// plus to downstreams).
-package gocompat
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_go120.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_go120.go
deleted file mode 100644
index 4a114bd3d..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_go120.go
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-//go:build linux && go1.20
-
-// Copyright (C) 2024 SUSE LLC. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gocompat
-
-import (
- "fmt"
-)
-
-// WrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
-// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
-// is only guaranteed to give you baseErr.
-func WrapBaseError(baseErr, extraErr error) error {
- return fmt.Errorf("%w: %w", extraErr, baseErr)
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_unsupported.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_unsupported.go
deleted file mode 100644
index 3061016a6..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_errors_unsupported.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-//go:build linux && !go1.20
-
-// Copyright (C) 2024 SUSE LLC. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gocompat
-
-import (
- "fmt"
-)
-
-type wrappedError struct {
- inner error
- isError error
-}
-
-func (err wrappedError) Is(target error) bool {
- return err.isError == target
-}
-
-func (err wrappedError) Unwrap() error {
- return err.inner
-}
-
-func (err wrappedError) Error() string {
- return fmt.Sprintf("%v: %v", err.isError, err.inner)
-}
-
-// WrapBaseError is a helper that is equivalent to fmt.Errorf("%w: %w"), except
-// that on pre-1.20 Go versions only errors.Is() works properly (errors.Unwrap)
-// is only guaranteed to give you baseErr.
-func WrapBaseError(baseErr, extraErr error) error {
- return wrappedError{
- inner: baseErr,
- isError: extraErr,
- }
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_go121.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_go121.go
deleted file mode 100644
index d4a938186..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_go121.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-//go:build linux && go1.21
-
-// Copyright (C) 2024-2025 SUSE LLC. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package gocompat
-
-import (
- "cmp"
- "slices"
- "sync"
-)
-
-// SlicesDeleteFunc is equivalent to Go 1.21's slices.DeleteFunc.
-func SlicesDeleteFunc[S ~[]E, E any](slice S, delFn func(E) bool) S {
- return slices.DeleteFunc(slice, delFn)
-}
-
-// SlicesContains is equivalent to Go 1.21's slices.Contains.
-func SlicesContains[S ~[]E, E comparable](slice S, val E) bool {
- return slices.Contains(slice, val)
-}
-
-// SlicesClone is equivalent to Go 1.21's slices.Clone.
-func SlicesClone[S ~[]E, E any](slice S) S {
- return slices.Clone(slice)
-}
-
-// SyncOnceValue is equivalent to Go 1.21's sync.OnceValue.
-func SyncOnceValue[T any](f func() T) func() T {
- return sync.OnceValue(f)
-}
-
-// SyncOnceValues is equivalent to Go 1.21's sync.OnceValues.
-func SyncOnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
- return sync.OnceValues(f)
-}
-
-// CmpOrdered is equivalent to Go 1.21's cmp.Ordered generic type definition.
-type CmpOrdered = cmp.Ordered
-
-// CmpCompare is equivalent to Go 1.21's cmp.Compare.
-func CmpCompare[T CmpOrdered](x, y T) int {
- return cmp.Compare(x, y)
-}
-
-// Max2 is equivalent to Go 1.21's max builtin (but only for two parameters).
-func Max2[T CmpOrdered](x, y T) T {
- return max(x, y)
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_unsupported.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_unsupported.go
deleted file mode 100644
index 0ea6218aa..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat/gocompat_generics_unsupported.go
+++ /dev/null
@@ -1,187 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-//go:build linux && !go1.21
-
-// Copyright (C) 2021, 2022 The Go Authors. All rights reserved.
-// Copyright (C) 2024-2025 SUSE LLC. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE.BSD file.
-
-package gocompat
-
-import (
- "sync"
-)
-
-// These are very minimal implementations of functions that appear in Go 1.21's
-// stdlib, included so that we can build on older Go versions. Most are
-// borrowed directly from the stdlib, and a few are modified to be "obviously
-// correct" without needing to copy too many other helpers.
-
-// clearSlice is equivalent to Go 1.21's builtin clear.
-// Copied from the Go 1.24 stdlib implementation.
-func clearSlice[S ~[]E, E any](slice S) {
- var zero E
- for i := range slice {
- slice[i] = zero
- }
-}
-
-// slicesIndexFunc is equivalent to Go 1.21's slices.IndexFunc.
-// Copied from the Go 1.24 stdlib implementation.
-func slicesIndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
- for i := range s {
- if f(s[i]) {
- return i
- }
- }
- return -1
-}
-
-// SlicesDeleteFunc is equivalent to Go 1.21's slices.DeleteFunc.
-// Copied from the Go 1.24 stdlib implementation.
-func SlicesDeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
- i := slicesIndexFunc(s, del)
- if i == -1 {
- return s
- }
- // Don't start copying elements until we find one to delete.
- for j := i + 1; j < len(s); j++ {
- if v := s[j]; !del(v) {
- s[i] = v
- i++
- }
- }
- clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC
- return s[:i]
-}
-
-// SlicesContains is equivalent to Go 1.21's slices.Contains.
-// Similar to the stdlib slices.Contains, except that we don't have
-// slices.Index so we need to use slices.IndexFunc for this non-Func helper.
-func SlicesContains[S ~[]E, E comparable](s S, v E) bool {
- return slicesIndexFunc(s, func(e E) bool { return e == v }) >= 0
-}
-
-// SlicesClone is equivalent to Go 1.21's slices.Clone.
-// Copied from the Go 1.24 stdlib implementation.
-func SlicesClone[S ~[]E, E any](s S) S {
- // Preserve nil in case it matters.
- if s == nil {
- return nil
- }
- return append(S([]E{}), s...)
-}
-
-// SyncOnceValue is equivalent to Go 1.21's sync.OnceValue.
-// Copied from the Go 1.25 stdlib implementation.
-func SyncOnceValue[T any](f func() T) func() T {
- // Use a struct so that there's a single heap allocation.
- d := struct {
- f func() T
- once sync.Once
- valid bool
- p any
- result T
- }{
- f: f,
- }
- return func() T {
- d.once.Do(func() {
- defer func() {
- d.f = nil
- d.p = recover()
- if !d.valid {
- panic(d.p)
- }
- }()
- d.result = d.f()
- d.valid = true
- })
- if !d.valid {
- panic(d.p)
- }
- return d.result
- }
-}
-
-// SyncOnceValues is equivalent to Go 1.21's sync.OnceValues.
-// Copied from the Go 1.25 stdlib implementation.
-func SyncOnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {
- // Use a struct so that there's a single heap allocation.
- d := struct {
- f func() (T1, T2)
- once sync.Once
- valid bool
- p any
- r1 T1
- r2 T2
- }{
- f: f,
- }
- return func() (T1, T2) {
- d.once.Do(func() {
- defer func() {
- d.f = nil
- d.p = recover()
- if !d.valid {
- panic(d.p)
- }
- }()
- d.r1, d.r2 = d.f()
- d.valid = true
- })
- if !d.valid {
- panic(d.p)
- }
- return d.r1, d.r2
- }
-}
-
-// CmpOrdered is equivalent to Go 1.21's cmp.Ordered generic type definition.
-// Copied from the Go 1.25 stdlib implementation.
-type CmpOrdered interface {
- ~int | ~int8 | ~int16 | ~int32 | ~int64 |
- ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
- ~float32 | ~float64 |
- ~string
-}
-
-// isNaN reports whether x is a NaN without requiring the math package.
-// This will always return false if T is not floating-point.
-// Copied from the Go 1.25 stdlib implementation.
-func isNaN[T CmpOrdered](x T) bool {
- return x != x
-}
-
-// CmpCompare is equivalent to Go 1.21's cmp.Compare.
-// Copied from the Go 1.25 stdlib implementation.
-func CmpCompare[T CmpOrdered](x, y T) int {
- xNaN := isNaN(x)
- yNaN := isNaN(y)
- if xNaN {
- if yNaN {
- return 0
- }
- return -1
- }
- if yNaN {
- return +1
- }
- if x < y {
- return -1
- }
- if x > y {
- return +1
- }
- return 0
-}
-
-// Max2 is equivalent to Go 1.21's max builtin for two parameters.
-func Max2[T CmpOrdered](x, y T) T {
- m := x
- if y > m {
- m = y
- }
- return m
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion/kernel_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion/kernel_linux.go
deleted file mode 100644
index cb6de4186..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion/kernel_linux.go
+++ /dev/null
@@ -1,123 +0,0 @@
-// SPDX-License-Identifier: BSD-3-Clause
-
-// Copyright (C) 2022 The Go Authors. All rights reserved.
-// Copyright (C) 2025 SUSE LLC. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE.BSD file.
-
-// The parsing logic is very loosely based on the Go stdlib's
-// src/internal/syscall/unix/kernel_version_linux.go but with an API that looks
-// a bit like runc's libcontainer/system/kernelversion.
-//
-// TODO(cyphar): This API has been copied around to a lot of different projects
-// (Docker, containerd, runc, and now filepath-securejoin) -- maybe we should
-// put it in a separate project?
-
-// Package kernelversion provides a simple mechanism for checking whether the
-// running kernel is at least as new as some baseline kernel version. This is
-// often useful when checking for features that would be too complicated to
-// test support for (or in cases where we know that some kernel features in
-// backport-heavy kernels are broken and need to be avoided).
-package kernelversion
-
-import (
- "bytes"
- "errors"
- "fmt"
- "strconv"
- "strings"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
-)
-
-// KernelVersion is a numeric representation of the key numerical elements of a
-// kernel version (for instance, "4.1.2-default-1" would be represented as
-// KernelVersion{4, 1, 2}).
-type KernelVersion []uint64
-
-func (kver KernelVersion) String() string {
- var str strings.Builder
- for idx, elem := range kver {
- if idx != 0 {
- _, _ = str.WriteRune('.')
- }
- _, _ = str.WriteString(strconv.FormatUint(elem, 10))
- }
- return str.String()
-}
-
-var errInvalidKernelVersion = errors.New("invalid kernel version")
-
-// parseKernelVersion parses a string and creates a KernelVersion based on it.
-func parseKernelVersion(kverStr string) (KernelVersion, error) {
- kver := make(KernelVersion, 1, 3)
- for idx, ch := range kverStr {
- if '0' <= ch && ch <= '9' {
- v := &kver[len(kver)-1]
- *v = (*v * 10) + uint64(ch-'0')
- } else {
- if idx == 0 || kverStr[idx-1] < '0' || '9' < kverStr[idx-1] {
- // "." must be preceded by a digit while in version section
- return nil, fmt.Errorf("%w %q: kernel version has dot(s) followed by non-digit in version section", errInvalidKernelVersion, kverStr)
- }
- if ch != '.' {
- break
- }
- kver = append(kver, 0)
- }
- }
- if len(kver) < 2 {
- return nil, fmt.Errorf("%w %q: kernel versions must contain at least two components", errInvalidKernelVersion, kverStr)
- }
- return kver, nil
-}
-
-// getKernelVersion gets the current kernel version.
-var getKernelVersion = gocompat.SyncOnceValues(func() (KernelVersion, error) {
- var uts unix.Utsname
- if err := unix.Uname(&uts); err != nil {
- return nil, err
- }
- // Remove the \x00 from the release.
- release := uts.Release[:]
- return parseKernelVersion(string(release[:bytes.IndexByte(release, 0)]))
-})
-
-// GreaterEqualThan returns true if the the host kernel version is greater than
-// or equal to the provided [KernelVersion]. When doing this comparison, any
-// non-numerical suffixes of the host kernel version are ignored.
-//
-// If the number of components provided is not equal to the number of numerical
-// components of the host kernel version, any missing components are treated as
-// 0. This means that GreaterEqualThan(KernelVersion{4}) will be treated the
-// same as GreaterEqualThan(KernelVersion{4, 0, 0, ..., 0, 0}), and that if the
-// host kernel version is "4" then GreaterEqualThan(KernelVersion{4, 1}) will
-// return false (because the host version will be treated as "4.0").
-func GreaterEqualThan(wantKver KernelVersion) (bool, error) {
- hostKver, err := getKernelVersion()
- if err != nil {
- return false, err
- }
-
- // Pad out the kernel version lengths to match one another.
- cmpLen := gocompat.Max2(len(hostKver), len(wantKver))
- hostKver = append(hostKver, make(KernelVersion, cmpLen-len(hostKver))...)
- wantKver = append(wantKver, make(KernelVersion, cmpLen-len(wantKver))...)
-
- for i := 0; i < cmpLen; i++ {
- switch gocompat.CmpCompare(hostKver[i], wantKver[i]) {
- case -1:
- // host < want
- return false, nil
- case +1:
- // host > want
- return true, nil
- case 0:
- continue
- }
- }
- // equal version values
- return true, nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/doc.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/doc.go
deleted file mode 100644
index 4635714f6..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/doc.go
+++ /dev/null
@@ -1,12 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package linux returns information about what features are supported on the
-// running kernel.
-package linux
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/mount_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/mount_linux.go
deleted file mode 100644
index b29905bff..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/mount_linux.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package linux
-
-import (
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/kernelversion"
-)
-
-// HasNewMountAPI returns whether the new fsopen(2) mount API is supported on
-// the running kernel.
-var HasNewMountAPI = gocompat.SyncOnceValue(func() bool {
- // All of the pieces of the new mount API we use (fsopen, fsconfig,
- // fsmount, open_tree) were added together in Linux 5.2[1,2], so we can
- // just check for one of the syscalls and the others should also be
- // available.
- //
- // Just try to use open_tree(2) to open a file without OPEN_TREE_CLONE.
- // This is equivalent to openat(2), but tells us if open_tree is
- // available (and thus all of the other basic new mount API syscalls).
- // open_tree(2) is most light-weight syscall to test here.
- //
- // [1]: merge commit 400913252d09
- // [2]:
- fd, err := unix.OpenTree(-int(unix.EBADF), "/", unix.OPEN_TREE_CLOEXEC)
- if err != nil {
- return false
- }
- _ = unix.Close(fd)
-
- // RHEL 8 has a backport of fsopen(2) that appears to have some very
- // difficult to debug performance pathology. As such, it seems prudent to
- // simply reject pre-5.2 kernels.
- isNotBackport, _ := kernelversion.GreaterEqualThan(kernelversion.KernelVersion{5, 2})
- return isNotBackport
-})
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/openat2_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/openat2_linux.go
deleted file mode 100644
index 399609dc3..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux/openat2_linux.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package linux
-
-import (
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
-)
-
-// HasOpenat2 returns whether openat2(2) is supported on the running kernel.
-var HasOpenat2 = gocompat.SyncOnceValue(func() bool {
- fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{
- Flags: unix.O_PATH | unix.O_CLOEXEC,
- Resolve: unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_IN_ROOT,
- })
- if err != nil {
- return false
- }
- _ = unix.Close(fd)
- return true
-})
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_linux.go
deleted file mode 100644
index 21e0a62e8..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_linux.go
+++ /dev/null
@@ -1,544 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package procfs provides a safe API for operating on /proc on Linux. Note
-// that this is the *internal* procfs API, mainy needed due to Go's
-// restrictions on cyclic dependencies and its incredibly minimal visibility
-// system without making a separate internal/ package.
-package procfs
-
-import (
- "errors"
- "fmt"
- "io"
- "os"
- "runtime"
- "strconv"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/assert"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
-)
-
-// The kernel guarantees that the root inode of a procfs mount has an
-// f_type of PROC_SUPER_MAGIC and st_ino of PROC_ROOT_INO.
-const (
- procSuperMagic = 0x9fa0 // PROC_SUPER_MAGIC
- procRootIno = 1 // PROC_ROOT_INO
-)
-
-// verifyProcHandle checks that the handle is from a procfs filesystem.
-// Contrast this to [verifyProcRoot], which also verifies that the handle is
-// the root of a procfs mount.
-func verifyProcHandle(procHandle fd.Fd) error {
- if statfs, err := fd.Fstatfs(procHandle); err != nil {
- return err
- } else if statfs.Type != procSuperMagic {
- return fmt.Errorf("%w: incorrect procfs root filesystem type 0x%x", errUnsafeProcfs, statfs.Type)
- }
- return nil
-}
-
-// verifyProcRoot verifies that the handle is the root of a procfs filesystem.
-// Contrast this to [verifyProcHandle], which only verifies if the handle is
-// some file on procfs (regardless of what file it is).
-func verifyProcRoot(procRoot fd.Fd) error {
- if err := verifyProcHandle(procRoot); err != nil {
- return err
- }
- if stat, err := fd.Fstat(procRoot); err != nil {
- return err
- } else if stat.Ino != procRootIno {
- return fmt.Errorf("%w: incorrect procfs root inode number %d", errUnsafeProcfs, stat.Ino)
- }
- return nil
-}
-
-type procfsFeatures struct {
- // hasSubsetPid was added in Linux 5.8, along with hidepid=ptraceable (and
- // string-based hidepid= values). Before this patchset, it was not really
- // safe to try to modify procfs superblock flags because the superblock was
- // shared -- so if this feature is not available, **you should not set any
- // superblock flags**.
- //
- // 6814ef2d992a ("proc: add option to mount only a pids subset")
- // fa10fed30f25 ("proc: allow to mount many instances of proc in one pid namespace")
- // 24a71ce5c47f ("proc: instantiate only pids that we can ptrace on 'hidepid=4' mount option")
- // 1c6c4d112e81 ("proc: use human-readable values for hidepid")
- // 9ff7258575d5 ("Merge branch 'proc-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace")
- hasSubsetPid bool
-}
-
-var getProcfsFeatures = gocompat.SyncOnceValue(func() procfsFeatures {
- if !linux.HasNewMountAPI() {
- return procfsFeatures{}
- }
- procfsCtx, err := fd.Fsopen("proc", unix.FSOPEN_CLOEXEC)
- if err != nil {
- return procfsFeatures{}
- }
- defer procfsCtx.Close() //nolint:errcheck // close failures aren't critical here
-
- return procfsFeatures{
- hasSubsetPid: unix.FsconfigSetString(int(procfsCtx.Fd()), "subset", "pid") == nil,
- }
-})
-
-func newPrivateProcMount(subset bool) (_ *Handle, Err error) {
- procfsCtx, err := fd.Fsopen("proc", unix.FSOPEN_CLOEXEC)
- if err != nil {
- return nil, err
- }
- defer procfsCtx.Close() //nolint:errcheck // close failures aren't critical here
-
- if subset && getProcfsFeatures().hasSubsetPid {
- // Try to configure hidepid=ptraceable,subset=pid if possible, but
- // ignore errors.
- _ = unix.FsconfigSetString(int(procfsCtx.Fd()), "hidepid", "ptraceable")
- _ = unix.FsconfigSetString(int(procfsCtx.Fd()), "subset", "pid")
- }
-
- // Get an actual handle.
- if err := unix.FsconfigCreate(int(procfsCtx.Fd())); err != nil {
- return nil, os.NewSyscallError("fsconfig create procfs", err)
- }
- // TODO: Output any information from the fscontext log to debug logs.
- procRoot, err := fd.Fsmount(procfsCtx, unix.FSMOUNT_CLOEXEC, unix.MS_NODEV|unix.MS_NOEXEC|unix.MS_NOSUID)
- if err != nil {
- return nil, err
- }
- defer func() {
- if Err != nil {
- _ = procRoot.Close()
- }
- }()
- return newHandle(procRoot)
-}
-
-func clonePrivateProcMount() (_ *Handle, Err error) {
- // Try to make a clone without using AT_RECURSIVE if we can. If this works,
- // we can be sure there are no over-mounts and so if the root is valid then
- // we're golden. Otherwise, we have to deal with over-mounts.
- procRoot, err := fd.OpenTree(nil, "/proc", unix.OPEN_TREE_CLONE)
- if err != nil || hookForcePrivateProcRootOpenTreeAtRecursive(procRoot) {
- procRoot, err = fd.OpenTree(nil, "/proc", unix.OPEN_TREE_CLONE|unix.AT_RECURSIVE)
- }
- if err != nil {
- return nil, fmt.Errorf("creating a detached procfs clone: %w", err)
- }
- defer func() {
- if Err != nil {
- _ = procRoot.Close()
- }
- }()
- return newHandle(procRoot)
-}
-
-func privateProcRoot(subset bool) (*Handle, error) {
- if !linux.HasNewMountAPI() || hookForceGetProcRootUnsafe() {
- return nil, fmt.Errorf("new mount api: %w", unix.ENOTSUP)
- }
- // Try to create a new procfs mount from scratch if we can. This ensures we
- // can get a procfs mount even if /proc is fake (for whatever reason).
- procRoot, err := newPrivateProcMount(subset)
- if err != nil || hookForcePrivateProcRootOpenTree(procRoot) {
- // Try to clone /proc then...
- procRoot, err = clonePrivateProcMount()
- }
- return procRoot, err
-}
-
-func unsafeHostProcRoot() (_ *Handle, Err error) {
- procRoot, err := os.OpenFile("/proc", unix.O_PATH|unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
- if err != nil {
- return nil, err
- }
- defer func() {
- if Err != nil {
- _ = procRoot.Close()
- }
- }()
- return newHandle(procRoot)
-}
-
-// Handle is a wrapper around an *os.File handle to "/proc", which can be used
-// to do further procfs-related operations in a safe way.
-type Handle struct {
- Inner fd.Fd
- // Does this handle have subset=pid set?
- isSubset bool
-}
-
-func newHandle(procRoot fd.Fd) (*Handle, error) {
- if err := verifyProcRoot(procRoot); err != nil {
- // This is only used in methods that
- _ = procRoot.Close()
- return nil, err
- }
- proc := &Handle{Inner: procRoot}
- // With subset=pid we can be sure that /proc/uptime will not exist.
- if err := fd.Faccessat(proc.Inner, "uptime", unix.F_OK, unix.AT_SYMLINK_NOFOLLOW); err != nil {
- proc.isSubset = errors.Is(err, os.ErrNotExist)
- }
- return proc, nil
-}
-
-// Close closes the underlying file for the Handle.
-func (proc *Handle) Close() error { return proc.Inner.Close() }
-
-var getCachedProcRoot = gocompat.SyncOnceValue(func() *Handle {
- procRoot, err := getProcRoot(true)
- if err != nil {
- return nil // just don't cache if we see an error
- }
- if !procRoot.isSubset {
- return nil // we only cache verified subset=pid handles
- }
-
- // Disarm (*Handle).Close() to stop someone from accidentally closing
- // the global handle.
- procRoot.Inner = fd.NopCloser(procRoot.Inner)
- return procRoot
-})
-
-// OpenProcRoot tries to open a "safer" handle to "/proc".
-func OpenProcRoot() (*Handle, error) {
- if proc := getCachedProcRoot(); proc != nil {
- return proc, nil
- }
- return getProcRoot(true)
-}
-
-// OpenUnsafeProcRoot opens a handle to "/proc" without any overmounts or
-// masked paths (but also without "subset=pid").
-func OpenUnsafeProcRoot() (*Handle, error) { return getProcRoot(false) }
-
-func getProcRoot(subset bool) (*Handle, error) {
- proc, err := privateProcRoot(subset)
- if err != nil {
- // Fall back to using a /proc handle if making a private mount failed.
- // If we have openat2, at least we can avoid some kinds of over-mount
- // attacks, but without openat2 there's not much we can do.
- proc, err = unsafeHostProcRoot()
- }
- return proc, err
-}
-
-var hasProcThreadSelf = gocompat.SyncOnceValue(func() bool {
- return unix.Access("/proc/thread-self/", unix.F_OK) == nil
-})
-
-var errUnsafeProcfs = errors.New("unsafe procfs detected")
-
-// lookup is a very minimal wrapper around [procfsLookupInRoot] which is
-// intended to be called from the external API.
-func (proc *Handle) lookup(subpath string) (*os.File, error) {
- handle, err := procfsLookupInRoot(proc.Inner, subpath)
- if err != nil {
- return nil, err
- }
- return handle, nil
-}
-
-// procfsBase is an enum indicating the prefix of a subpath in operations
-// involving [Handle]s.
-type procfsBase string
-
-const (
- // ProcRoot refers to the root of the procfs (i.e., "/proc/").
- ProcRoot procfsBase = "/proc"
- // ProcSelf refers to the current process' subdirectory (i.e.,
- // "/proc/self/").
- ProcSelf procfsBase = "/proc/self"
- // ProcThreadSelf refers to the current thread's subdirectory (i.e.,
- // "/proc/thread-self/"). In multi-threaded programs (i.e., all Go
- // programs) where one thread has a different CLONE_FS, it is possible for
- // "/proc/self" to point the wrong thread and so "/proc/thread-self" may be
- // necessary. Note that on pre-3.17 kernels, "/proc/thread-self" doesn't
- // exist and so a fallback will be used in that case.
- ProcThreadSelf procfsBase = "/proc/thread-self"
- // TODO: Switch to an interface setup so we can have a more type-safe
- // version of ProcPid and remove the need to worry about invalid string
- // values.
-)
-
-// prefix returns a prefix that can be used with the given [Handle].
-func (base procfsBase) prefix(proc *Handle) (string, error) {
- switch base {
- case ProcRoot:
- return ".", nil
- case ProcSelf:
- return "self", nil
- case ProcThreadSelf:
- threadSelf := "thread-self"
- if !hasProcThreadSelf() || hookForceProcSelfTask() {
- // Pre-3.17 kernels don't have /proc/thread-self, so do it
- // manually.
- threadSelf = "self/task/" + strconv.Itoa(unix.Gettid())
- if err := fd.Faccessat(proc.Inner, threadSelf, unix.F_OK, unix.AT_SYMLINK_NOFOLLOW); err != nil || hookForceProcSelf() {
- // In this case, we running in a pid namespace that doesn't
- // match the /proc mount we have. This can happen inside runc.
- //
- // Unfortunately, there is no nice way to get the correct TID
- // to use here because of the age of the kernel, so we have to
- // just use /proc/self and hope that it works.
- threadSelf = "self"
- }
- }
- return threadSelf, nil
- }
- return "", fmt.Errorf("invalid procfs base %q", base)
-}
-
-// ProcThreadSelfCloser is a callback that needs to be called when you are done
-// operating on an [os.File] fetched using [ProcThreadSelf].
-//
-// [os.File]: https://pkg.go.dev/os#File
-type ProcThreadSelfCloser func()
-
-// open is the core lookup operation for [Handle]. It returns a handle to
-// "/proc//". If the returned [ProcThreadSelfCloser] is non-nil,
-// you should call it after you are done interacting with the returned handle.
-//
-// In general you should use prefer to use the other helpers, as they remove
-// the need to interact with [procfsBase] and do not return a nil
-// [ProcThreadSelfCloser] for [procfsBase] values other than [ProcThreadSelf]
-// where it is necessary.
-func (proc *Handle) open(base procfsBase, subpath string) (_ *os.File, closer ProcThreadSelfCloser, Err error) {
- prefix, err := base.prefix(proc)
- if err != nil {
- return nil, nil, err
- }
- subpath = prefix + "/" + subpath
-
- switch base {
- case ProcRoot:
- file, err := proc.lookup(subpath)
- if errors.Is(err, os.ErrNotExist) {
- // The Handle handle in use might be a subset=pid one, which will
- // result in spurious errors. In this case, just open a temporary
- // unmasked procfs handle for this operation.
- proc, err2 := OpenUnsafeProcRoot() // !subset=pid
- if err2 != nil {
- return nil, nil, err
- }
- defer proc.Close() //nolint:errcheck // close failures aren't critical here
-
- file, err = proc.lookup(subpath)
- }
- return file, nil, err
-
- case ProcSelf:
- file, err := proc.lookup(subpath)
- return file, nil, err
-
- case ProcThreadSelf:
- // We need to lock our thread until the caller is done with the handle
- // because between getting the handle and using it we could get
- // interrupted by the Go runtime and hit the case where the underlying
- // thread is swapped out and the original thread is killed, resulting
- // in pull-your-hair-out-hard-to-debug issues in the caller.
- runtime.LockOSThread()
- defer func() {
- if Err != nil {
- runtime.UnlockOSThread()
- closer = nil
- }
- }()
-
- file, err := proc.lookup(subpath)
- return file, runtime.UnlockOSThread, err
- }
- // should never be reached
- return nil, nil, fmt.Errorf("[internal error] invalid procfs base %q", base)
-}
-
-// OpenThreadSelf returns a handle to "/proc/thread-self/" (or an
-// equivalent handle on older kernels where "/proc/thread-self" doesn't exist).
-// Once finished with the handle, you must call the returned closer function
-// (runtime.UnlockOSThread). You must not pass the returned *os.File to other
-// Go threads or use the handle after calling the closer.
-func (proc *Handle) OpenThreadSelf(subpath string) (_ *os.File, _ ProcThreadSelfCloser, Err error) {
- return proc.open(ProcThreadSelf, subpath)
-}
-
-// OpenSelf returns a handle to /proc/self/.
-func (proc *Handle) OpenSelf(subpath string) (*os.File, error) {
- file, closer, err := proc.open(ProcSelf, subpath)
- assert.Assert(closer == nil, "closer for ProcSelf must be nil")
- return file, err
-}
-
-// OpenRoot returns a handle to /proc/.
-func (proc *Handle) OpenRoot(subpath string) (*os.File, error) {
- file, closer, err := proc.open(ProcRoot, subpath)
- assert.Assert(closer == nil, "closer for ProcRoot must be nil")
- return file, err
-}
-
-// OpenPid returns a handle to /proc/$pid/ (pid can be a pid or tid).
-// This is mainly intended for usage when operating on other processes.
-func (proc *Handle) OpenPid(pid int, subpath string) (*os.File, error) {
- return proc.OpenRoot(strconv.Itoa(pid) + "/" + subpath)
-}
-
-// checkSubpathOvermount checks if the dirfd and path combination is on the
-// same mount as the given root.
-func checkSubpathOvermount(root, dir fd.Fd, path string) error {
- // Get the mntID of our procfs handle.
- expectedMountID, err := fd.GetMountID(root, "")
- if err != nil {
- return fmt.Errorf("get root mount id: %w", err)
- }
- // Get the mntID of the target magic-link.
- gotMountID, err := fd.GetMountID(dir, path)
- if err != nil {
- return fmt.Errorf("get subpath mount id: %w", err)
- }
- // As long as the directory mount is alive, even with wrapping mount IDs,
- // we would expect to see a different mount ID here. (Of course, if we're
- // using unsafeHostProcRoot() then an attaker could change this after we
- // did this check.)
- if expectedMountID != gotMountID {
- return fmt.Errorf("%w: subpath %s/%s has an overmount obscuring the real path (mount ids do not match %d != %d)",
- errUnsafeProcfs, dir.Name(), path, expectedMountID, gotMountID)
- }
- return nil
-}
-
-// Readlink performs a readlink operation on "/proc//" in a way
-// that should be free from race attacks. This is most commonly used to get the
-// real path of a file by looking at "/proc/self/fd/$n", with the same safety
-// protections as [Open] (as well as some additional checks against
-// overmounts).
-func (proc *Handle) Readlink(base procfsBase, subpath string) (string, error) {
- link, closer, err := proc.open(base, subpath)
- if closer != nil {
- defer closer()
- }
- if err != nil {
- return "", fmt.Errorf("get safe %s/%s handle: %w", base, subpath, err)
- }
- defer link.Close() //nolint:errcheck // close failures aren't critical here
-
- // Try to detect if there is a mount on top of the magic-link. This should
- // be safe in general (a mount on top of the path afterwards would not
- // affect the handle itself) and will definitely be safe if we are using
- // privateProcRoot() (at least since Linux 5.12[1], when anonymous mount
- // namespaces were completely isolated from external mounts including mount
- // propagation events).
- //
- // [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts
- // onto targets that reside on shared mounts").
- if err := checkSubpathOvermount(proc.Inner, link, ""); err != nil {
- return "", fmt.Errorf("check safety of %s/%s magiclink: %w", base, subpath, err)
- }
-
- // readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See Linux commit
- // 65cfc6722361 ("readlinkat(), fchownat() and fstatat() with empty
- // relative pathnames").
- return fd.Readlinkat(link, "")
-}
-
-// ProcSelfFdReadlink gets the real path of the given file by looking at
-// readlink(/proc/thread-self/fd/$n).
-//
-// This is just a wrapper around [Handle.Readlink].
-func ProcSelfFdReadlink(fd fd.Fd) (string, error) {
- procRoot, err := OpenProcRoot() // subset=pid
- if err != nil {
- return "", err
- }
- defer procRoot.Close() //nolint:errcheck // close failures aren't critical here
-
- fdPath := "fd/" + strconv.Itoa(int(fd.Fd()))
- return procRoot.Readlink(ProcThreadSelf, fdPath)
-}
-
-// CheckProcSelfFdPath returns whether the given file handle matches the
-// expected path. (This is inherently racy.)
-func CheckProcSelfFdPath(path string, file fd.Fd) error {
- if err := fd.IsDeadInode(file); err != nil {
- return err
- }
- actualPath, err := ProcSelfFdReadlink(file)
- if err != nil {
- return fmt.Errorf("get path of handle: %w", err)
- }
- if actualPath != path {
- return fmt.Errorf("%w: handle path %q doesn't match expected path %q", internal.ErrPossibleBreakout, actualPath, path)
- }
- return nil
-}
-
-// ReopenFd takes an existing file descriptor and "re-opens" it through
-// /proc/thread-self/fd/. This allows for O_PATH file descriptors to be
-// upgraded to regular file descriptors, as well as changing the open mode of a
-// regular file descriptor. Some filesystems have unique handling of open(2)
-// which make this incredibly useful (such as /dev/ptmx).
-func ReopenFd(handle fd.Fd, flags int) (*os.File, error) {
- procRoot, err := OpenProcRoot() // subset=pid
- if err != nil {
- return nil, err
- }
- defer procRoot.Close() //nolint:errcheck // close failures aren't critical here
-
- // We can't operate on /proc/thread-self/fd/$n directly when doing a
- // re-open, so we need to open /proc/thread-self/fd and then open a single
- // final component.
- procFdDir, closer, err := procRoot.OpenThreadSelf("fd/")
- if err != nil {
- return nil, fmt.Errorf("get safe /proc/thread-self/fd handle: %w", err)
- }
- defer procFdDir.Close() //nolint:errcheck // close failures aren't critical here
- defer closer()
-
- // Try to detect if there is a mount on top of the magic-link we are about
- // to open. If we are using unsafeHostProcRoot(), this could change after
- // we check it (and there's nothing we can do about that) but for
- // privateProcRoot() this should be guaranteed to be safe (at least since
- // Linux 5.12[1], when anonymous mount namespaces were completely isolated
- // from external mounts including mount propagation events).
- //
- // [1]: Linux commit ee2e3f50629f ("mount: fix mounting of detached mounts
- // onto targets that reside on shared mounts").
- fdStr := strconv.Itoa(int(handle.Fd()))
- if err := checkSubpathOvermount(procRoot.Inner, procFdDir, fdStr); err != nil {
- return nil, fmt.Errorf("check safety of /proc/thread-self/fd/%s magiclink: %w", fdStr, err)
- }
-
- flags |= unix.O_CLOEXEC
- // Rather than just wrapping fd.Openat, open-code it so we can copy
- // handle.Name().
- reopenFd, err := unix.Openat(int(procFdDir.Fd()), fdStr, flags, 0)
- if err != nil {
- return nil, fmt.Errorf("reopen fd %d: %w", handle.Fd(), err)
- }
- return os.NewFile(uintptr(reopenFd), handle.Name()), nil
-}
-
-// Test hooks used in the procfs tests to verify that the fallback logic works.
-// See testing_mocks_linux_test.go and procfs_linux_test.go for more details.
-var (
- hookForcePrivateProcRootOpenTree = hookDummyFile
- hookForcePrivateProcRootOpenTreeAtRecursive = hookDummyFile
- hookForceGetProcRootUnsafe = hookDummy
-
- hookForceProcSelfTask = hookDummy
- hookForceProcSelf = hookDummy
-)
-
-func hookDummy() bool { return false }
-func hookDummyFile(_ io.Closer) bool { return false }
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_lookup_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_lookup_linux.go
deleted file mode 100644
index 1ad1f18ee..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs/procfs_lookup_linux.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// This code is adapted to be a minimal version of the libpathrs proc resolver
-// .
-// As we only need O_PATH|O_NOFOLLOW support, this is not too much to port.
-
-package procfs
-
-import (
- "fmt"
- "os"
- "path"
- "path/filepath"
- "strings"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/internal/consts"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
-)
-
-// procfsLookupInRoot is a stripped down version of completeLookupInRoot,
-// entirely designed to support the very small set of features necessary to
-// make procfs handling work. Unlike completeLookupInRoot, we always have
-// O_PATH|O_NOFOLLOW behaviour for trailing symlinks.
-//
-// The main restrictions are:
-//
-// - ".." is not supported (as it requires either os.Root-style replays,
-// which is more bug-prone; or procfs verification, which is not possible
-// due to re-entrancy issues).
-// - Absolute symlinks for the same reason (and all absolute symlinks in
-// procfs are magic-links, which we want to skip anyway).
-// - If statx is supported (checkSymlinkOvermount), any mount-point crossings
-// (which is the main attack of concern against /proc).
-// - Partial lookups are not supported, so the symlink stack is not needed.
-// - Trailing slash special handling is not necessary in most cases (if we
-// operating on procfs, it's usually with programmer-controlled strings
-// that will then be re-opened), so we skip it since whatever re-opens it
-// can deal with it. It's a creature comfort anyway.
-//
-// If the system supports openat2(), this is implemented using equivalent flags
-// (RESOLVE_BENEATH | RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS).
-func procfsLookupInRoot(procRoot fd.Fd, unsafePath string) (Handle *os.File, _ error) {
- unsafePath = filepath.ToSlash(unsafePath) // noop
-
- // Make sure that an empty unsafe path still returns something sane, even
- // with openat2 (which doesn't have AT_EMPTY_PATH semantics yet).
- if unsafePath == "" {
- unsafePath = "."
- }
-
- // This is already checked by getProcRoot, but make sure here since the
- // core security of this lookup is based on this assumption.
- if err := verifyProcRoot(procRoot); err != nil {
- return nil, err
- }
-
- if linux.HasOpenat2() {
- // We prefer being able to use RESOLVE_NO_XDEV if we can, to be
- // absolutely sure we are operating on a clean /proc handle that
- // doesn't have any cheeky overmounts that could trick us (including
- // symlink mounts on top of /proc/thread-self). RESOLVE_BENEATH isn't
- // strictly needed, but just use it since we have it.
- //
- // NOTE: /proc/self is technically a magic-link (the contents of the
- // symlink are generated dynamically), but it doesn't use
- // nd_jump_link() so RESOLVE_NO_MAGICLINKS allows it.
- //
- // TODO: It would be nice to have RESOLVE_NO_DOTDOT, purely for
- // self-consistency with the backup O_PATH resolver.
- handle, err := fd.Openat2(procRoot, unsafePath, &unix.OpenHow{
- Flags: unix.O_PATH | unix.O_NOFOLLOW | unix.O_CLOEXEC,
- Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_MAGICLINKS,
- })
- if err != nil {
- // TODO: Once we bump the minimum Go version to 1.20, we can use
- // multiple %w verbs for this wrapping. For now we need to use a
- // compatibility shim for older Go versions.
- // err = fmt.Errorf("%w: %w", errUnsafeProcfs, err)
- return nil, gocompat.WrapBaseError(err, errUnsafeProcfs)
- }
- return handle, nil
- }
-
- // To mirror openat2(RESOLVE_BENEATH), we need to return an error if the
- // path is absolute.
- if path.IsAbs(unsafePath) {
- return nil, fmt.Errorf("%w: cannot resolve absolute paths in procfs resolver", internal.ErrPossibleBreakout)
- }
-
- currentDir, err := fd.Dup(procRoot)
- if err != nil {
- return nil, fmt.Errorf("clone root fd: %w", err)
- }
- defer func() {
- // If a handle is not returned, close the internal handle.
- if Handle == nil {
- _ = currentDir.Close()
- }
- }()
-
- var (
- linksWalked int
- currentPath string
- remainingPath = unsafePath
- )
- for remainingPath != "" {
- // Get the next path component.
- var part string
- if i := strings.IndexByte(remainingPath, '/'); i == -1 {
- part, remainingPath = remainingPath, ""
- } else {
- part, remainingPath = remainingPath[:i], remainingPath[i+1:]
- }
- if part == "" {
- // no-op component, but treat it the same as "."
- part = "."
- }
- if part == ".." {
- // not permitted
- return nil, fmt.Errorf("%w: cannot walk into '..' in procfs resolver", internal.ErrPossibleBreakout)
- }
-
- // Apply the component lexically to the path we are building.
- // currentPath does not contain any symlinks, and we are lexically
- // dealing with a single component, so it's okay to do a filepath.Clean
- // here. (Not to mention that ".." isn't allowed.)
- nextPath := path.Join("/", currentPath, part)
- // If we logically hit the root, just clone the root rather than
- // opening the part and doing all of the other checks.
- if nextPath == "/" {
- // Jump to root.
- rootClone, err := fd.Dup(procRoot)
- if err != nil {
- return nil, fmt.Errorf("clone root fd: %w", err)
- }
- _ = currentDir.Close()
- currentDir = rootClone
- currentPath = nextPath
- continue
- }
-
- // Try to open the next component.
- nextDir, err := fd.Openat(currentDir, part, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
- if err != nil {
- return nil, err
- }
-
- // Make sure we are still on procfs and haven't crossed mounts.
- if err := verifyProcHandle(nextDir); err != nil {
- _ = nextDir.Close()
- return nil, fmt.Errorf("check %q component is on procfs: %w", part, err)
- }
- if err := checkSubpathOvermount(procRoot, nextDir, ""); err != nil {
- _ = nextDir.Close()
- return nil, fmt.Errorf("check %q component is not overmounted: %w", part, err)
- }
-
- // We are emulating O_PATH|O_NOFOLLOW, so we only need to traverse into
- // trailing symlinks if we are not the final component. Otherwise we
- // can just return the currentDir.
- if remainingPath != "" {
- st, err := nextDir.Stat()
- if err != nil {
- _ = nextDir.Close()
- return nil, fmt.Errorf("stat component %q: %w", part, err)
- }
-
- if st.Mode()&os.ModeType == os.ModeSymlink {
- // readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See
- // Linux commit 65cfc6722361 ("readlinkat(), fchownat() and
- // fstatat() with empty relative pathnames").
- linkDest, err := fd.Readlinkat(nextDir, "")
- // We don't need the handle anymore.
- _ = nextDir.Close()
- if err != nil {
- return nil, err
- }
-
- linksWalked++
- if linksWalked > consts.MaxSymlinkLimit {
- return nil, &os.PathError{Op: "securejoin.procfsLookupInRoot", Path: "/proc/" + unsafePath, Err: unix.ELOOP}
- }
-
- // Update our logical remaining path.
- remainingPath = linkDest + "/" + remainingPath
- // Absolute symlinks are probably magiclinks, we reject them.
- if path.IsAbs(linkDest) {
- return nil, fmt.Errorf("%w: cannot jump to / in procfs resolver -- possible magiclink", internal.ErrPossibleBreakout)
- }
- continue
- }
- }
-
- // Walk into the next component.
- _ = currentDir.Close()
- currentDir = nextDir
- currentPath = nextPath
- }
-
- // One final sanity-check.
- if err := verifyProcHandle(currentDir); err != nil {
- return nil, fmt.Errorf("check final handle is on procfs: %w", err)
- }
- if err := checkSubpathOvermount(procRoot, currentDir, ""); err != nil {
- return nil, fmt.Errorf("check final handle is not overmounted: %w", err)
- }
- return currentDir, nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/lookup_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/lookup_linux.go
deleted file mode 100644
index f47504e66..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/lookup_linux.go
+++ /dev/null
@@ -1,399 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package pathrs
-
-import (
- "errors"
- "fmt"
- "os"
- "path"
- "path/filepath"
- "strings"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/internal/consts"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs"
-)
-
-type symlinkStackEntry struct {
- // (dir, remainingPath) is what we would've returned if the link didn't
- // exist. This matches what openat2(RESOLVE_IN_ROOT) would return in
- // this case.
- dir *os.File
- remainingPath string
- // linkUnwalked is the remaining path components from the original
- // Readlink which we have yet to walk. When this slice is empty, we
- // drop the link from the stack.
- linkUnwalked []string
-}
-
-func (se symlinkStackEntry) String() string {
- return fmt.Sprintf("<%s>/%s [->%s]", se.dir.Name(), se.remainingPath, strings.Join(se.linkUnwalked, "/"))
-}
-
-func (se symlinkStackEntry) Close() {
- _ = se.dir.Close()
-}
-
-type symlinkStack []*symlinkStackEntry
-
-func (s *symlinkStack) IsEmpty() bool {
- return s == nil || len(*s) == 0
-}
-
-func (s *symlinkStack) Close() {
- if s != nil {
- for _, link := range *s {
- link.Close()
- }
- // TODO: Switch to clear once we switch to Go 1.21.
- *s = nil
- }
-}
-
-var (
- errEmptyStack = errors.New("[internal] stack is empty")
- errBrokenSymlinkStack = errors.New("[internal error] broken symlink stack")
-)
-
-func (s *symlinkStack) popPart(part string) error {
- if s == nil || s.IsEmpty() {
- // If there is nothing in the symlink stack, then the part was from the
- // real path provided by the user, and this is a no-op.
- return errEmptyStack
- }
- if part == "." {
- // "." components are no-ops -- we drop them when doing SwapLink.
- return nil
- }
-
- tailEntry := (*s)[len(*s)-1]
-
- // Double-check that we are popping the component we expect.
- if len(tailEntry.linkUnwalked) == 0 {
- return fmt.Errorf("%w: trying to pop component %q of empty stack entry %s", errBrokenSymlinkStack, part, tailEntry)
- }
- headPart := tailEntry.linkUnwalked[0]
- if headPart != part {
- return fmt.Errorf("%w: trying to pop component %q but the last stack entry is %s (%q)", errBrokenSymlinkStack, part, tailEntry, headPart)
- }
-
- // Drop the component, but keep the entry around in case we are dealing
- // with a "tail-chained" symlink.
- tailEntry.linkUnwalked = tailEntry.linkUnwalked[1:]
- return nil
-}
-
-func (s *symlinkStack) PopPart(part string) error {
- if err := s.popPart(part); err != nil {
- if errors.Is(err, errEmptyStack) {
- // Skip empty stacks.
- err = nil
- }
- return err
- }
-
- // Clean up any of the trailing stack entries that are empty.
- for lastGood := len(*s) - 1; lastGood >= 0; lastGood-- {
- entry := (*s)[lastGood]
- if len(entry.linkUnwalked) > 0 {
- break
- }
- entry.Close()
- (*s) = (*s)[:lastGood]
- }
- return nil
-}
-
-func (s *symlinkStack) push(dir *os.File, remainingPath, linkTarget string) error {
- if s == nil {
- return nil
- }
- // Split the link target and clean up any "" parts.
- linkTargetParts := gocompat.SlicesDeleteFunc(
- strings.Split(linkTarget, "/"),
- func(part string) bool { return part == "" || part == "." })
-
- // Copy the directory so the caller doesn't close our copy.
- dirCopy, err := fd.Dup(dir)
- if err != nil {
- return err
- }
-
- // Add to the stack.
- *s = append(*s, &symlinkStackEntry{
- dir: dirCopy,
- remainingPath: remainingPath,
- linkUnwalked: linkTargetParts,
- })
- return nil
-}
-
-func (s *symlinkStack) SwapLink(linkPart string, dir *os.File, remainingPath, linkTarget string) error {
- // If we are currently inside a symlink resolution, remove the symlink
- // component from the last symlink entry, but don't remove the entry even
- // if it's empty. If we are a "tail-chained" symlink (a trailing symlink we
- // hit during a symlink resolution) we need to keep the old symlink until
- // we finish the resolution.
- if err := s.popPart(linkPart); err != nil {
- if !errors.Is(err, errEmptyStack) {
- return err
- }
- // Push the component regardless of whether the stack was empty.
- }
- return s.push(dir, remainingPath, linkTarget)
-}
-
-func (s *symlinkStack) PopTopSymlink() (*os.File, string, bool) {
- if s == nil || s.IsEmpty() {
- return nil, "", false
- }
- tailEntry := (*s)[0]
- *s = (*s)[1:]
- return tailEntry.dir, tailEntry.remainingPath, true
-}
-
-// partialLookupInRoot tries to lookup as much of the request path as possible
-// within the provided root (a-la RESOLVE_IN_ROOT) and opens the final existing
-// component of the requested path, returning a file handle to the final
-// existing component and a string containing the remaining path components.
-func partialLookupInRoot(root fd.Fd, unsafePath string) (*os.File, string, error) {
- return lookupInRoot(root, unsafePath, true)
-}
-
-func completeLookupInRoot(root fd.Fd, unsafePath string) (*os.File, error) {
- handle, remainingPath, err := lookupInRoot(root, unsafePath, false)
- if remainingPath != "" && err == nil {
- // should never happen
- err = fmt.Errorf("[bug] non-empty remaining path when doing a non-partial lookup: %q", remainingPath)
- }
- // lookupInRoot(partial=false) will always close the handle if an error is
- // returned, so no need to double-check here.
- return handle, err
-}
-
-func lookupInRoot(root fd.Fd, unsafePath string, partial bool) (Handle *os.File, _ string, _ error) {
- unsafePath = filepath.ToSlash(unsafePath) // noop
-
- // This is very similar to SecureJoin, except that we operate on the
- // components using file descriptors. We then return the last component we
- // managed open, along with the remaining path components not opened.
-
- // Try to use openat2 if possible.
- if linux.HasOpenat2() {
- return lookupOpenat2(root, unsafePath, partial)
- }
-
- // Get the "actual" root path from /proc/self/fd. This is necessary if the
- // root is some magic-link like /proc/$pid/root, in which case we want to
- // make sure when we do procfs.CheckProcSelfFdPath that we are using the
- // correct root path.
- logicalRootPath, err := procfs.ProcSelfFdReadlink(root)
- if err != nil {
- return nil, "", fmt.Errorf("get real root path: %w", err)
- }
-
- currentDir, err := fd.Dup(root)
- if err != nil {
- return nil, "", fmt.Errorf("clone root fd: %w", err)
- }
- defer func() {
- // If a handle is not returned, close the internal handle.
- if Handle == nil {
- _ = currentDir.Close()
- }
- }()
-
- // symlinkStack is used to emulate how openat2(RESOLVE_IN_ROOT) treats
- // dangling symlinks. If we hit a non-existent path while resolving a
- // symlink, we need to return the (dir, remainingPath) that we had when we
- // hit the symlink (treating the symlink as though it were a regular file).
- // The set of (dir, remainingPath) sets is stored within the symlinkStack
- // and we add and remove parts when we hit symlink and non-symlink
- // components respectively. We need a stack because of recursive symlinks
- // (symlinks that contain symlink components in their target).
- //
- // Note that the stack is ONLY used for book-keeping. All of the actual
- // path walking logic is still based on currentPath/remainingPath and
- // currentDir (as in SecureJoin).
- var symStack *symlinkStack
- if partial {
- symStack = new(symlinkStack)
- defer symStack.Close()
- }
-
- var (
- linksWalked int
- currentPath string
- remainingPath = unsafePath
- )
- for remainingPath != "" {
- // Save the current remaining path so if the part is not real we can
- // return the path including the component.
- oldRemainingPath := remainingPath
-
- // Get the next path component.
- var part string
- if i := strings.IndexByte(remainingPath, '/'); i == -1 {
- part, remainingPath = remainingPath, ""
- } else {
- part, remainingPath = remainingPath[:i], remainingPath[i+1:]
- }
- // If we hit an empty component, we need to treat it as though it is
- // "." so that trailing "/" and "//" components on a non-directory
- // correctly return the right error code.
- if part == "" {
- part = "."
- }
-
- // Apply the component lexically to the path we are building.
- // currentPath does not contain any symlinks, and we are lexically
- // dealing with a single component, so it's okay to do a filepath.Clean
- // here.
- nextPath := path.Join("/", currentPath, part)
- // If we logically hit the root, just clone the root rather than
- // opening the part and doing all of the other checks.
- if nextPath == "/" {
- if err := symStack.PopPart(part); err != nil {
- return nil, "", fmt.Errorf("walking into root with part %q failed: %w", part, err)
- }
- // Jump to root.
- rootClone, err := fd.Dup(root)
- if err != nil {
- return nil, "", fmt.Errorf("clone root fd: %w", err)
- }
- _ = currentDir.Close()
- currentDir = rootClone
- currentPath = nextPath
- continue
- }
-
- // Try to open the next component.
- nextDir, err := fd.Openat(currentDir, part, unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
- switch err {
- case nil:
- st, err := nextDir.Stat()
- if err != nil {
- _ = nextDir.Close()
- return nil, "", fmt.Errorf("stat component %q: %w", part, err)
- }
-
- switch st.Mode() & os.ModeType { //nolint:exhaustive // just a glorified if statement
- case os.ModeSymlink:
- // readlinkat implies AT_EMPTY_PATH since Linux 2.6.39. See
- // Linux commit 65cfc6722361 ("readlinkat(), fchownat() and
- // fstatat() with empty relative pathnames").
- linkDest, err := fd.Readlinkat(nextDir, "")
- // We don't need the handle anymore.
- _ = nextDir.Close()
- if err != nil {
- return nil, "", err
- }
-
- linksWalked++
- if linksWalked > consts.MaxSymlinkLimit {
- return nil, "", &os.PathError{Op: "securejoin.lookupInRoot", Path: logicalRootPath + "/" + unsafePath, Err: unix.ELOOP}
- }
-
- // Swap out the symlink's component for the link entry itself.
- if err := symStack.SwapLink(part, currentDir, oldRemainingPath, linkDest); err != nil {
- return nil, "", fmt.Errorf("walking into symlink %q failed: push symlink: %w", part, err)
- }
-
- // Update our logical remaining path.
- remainingPath = linkDest + "/" + remainingPath
- // Absolute symlinks reset any work we've already done.
- if path.IsAbs(linkDest) {
- // Jump to root.
- rootClone, err := fd.Dup(root)
- if err != nil {
- return nil, "", fmt.Errorf("clone root fd: %w", err)
- }
- _ = currentDir.Close()
- currentDir = rootClone
- currentPath = "/"
- }
-
- default:
- // If we are dealing with a directory, simply walk into it.
- _ = currentDir.Close()
- currentDir = nextDir
- currentPath = nextPath
-
- // The part was real, so drop it from the symlink stack.
- if err := symStack.PopPart(part); err != nil {
- return nil, "", fmt.Errorf("walking into directory %q failed: %w", part, err)
- }
-
- // If we are operating on a .., make sure we haven't escaped.
- // We only have to check for ".." here because walking down
- // into a regular component component cannot cause you to
- // escape. This mirrors the logic in RESOLVE_IN_ROOT, except we
- // have to check every ".." rather than only checking after a
- // rename or mount on the system.
- if part == ".." {
- // Make sure the root hasn't moved.
- if err := procfs.CheckProcSelfFdPath(logicalRootPath, root); err != nil {
- return nil, "", fmt.Errorf("root path moved during lookup: %w", err)
- }
- // Make sure the path is what we expect.
- fullPath := logicalRootPath + nextPath
- if err := procfs.CheckProcSelfFdPath(fullPath, currentDir); err != nil {
- return nil, "", fmt.Errorf("walking into %q had unexpected result: %w", part, err)
- }
- }
- }
-
- default:
- if !partial {
- return nil, "", err
- }
- // If there are any remaining components in the symlink stack, we
- // are still within a symlink resolution and thus we hit a dangling
- // symlink. So pretend that the first symlink in the stack we hit
- // was an ENOENT (to match openat2).
- if oldDir, remainingPath, ok := symStack.PopTopSymlink(); ok {
- _ = currentDir.Close()
- return oldDir, remainingPath, err
- }
- // We have hit a final component that doesn't exist, so we have our
- // partial open result. Note that we have to use the OLD remaining
- // path, since the lookup failed.
- return currentDir, oldRemainingPath, err
- }
- }
-
- // If the unsafePath had a trailing slash, we need to make sure we try to
- // do a relative "." open so that we will correctly return an error when
- // the final component is a non-directory (to match openat2). In the
- // context of openat2, a trailing slash and a trailing "/." are completely
- // equivalent.
- if strings.HasSuffix(unsafePath, "/") {
- nextDir, err := fd.Openat(currentDir, ".", unix.O_PATH|unix.O_NOFOLLOW|unix.O_CLOEXEC, 0)
- if err != nil {
- if !partial {
- _ = currentDir.Close()
- currentDir = nil
- }
- return currentDir, "", err
- }
- _ = currentDir.Close()
- currentDir = nextDir
- }
-
- // All of the components existed!
- return currentDir, "", nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/mkdir_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/mkdir_linux.go
deleted file mode 100644
index f3c62b0da..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/mkdir_linux.go
+++ /dev/null
@@ -1,246 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package pathrs
-
-import (
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "strings"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/gocompat"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/linux"
-)
-
-var errInvalidMode = errors.New("invalid permission mode")
-
-// modePermExt is like os.ModePerm except that it also includes the set[ug]id
-// and sticky bits.
-const modePermExt = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
-
-//nolint:cyclop // this function needs to handle a lot of cases
-func toUnixMode(mode os.FileMode) (uint32, error) {
- sysMode := uint32(mode.Perm())
- if mode&os.ModeSetuid != 0 {
- sysMode |= unix.S_ISUID
- }
- if mode&os.ModeSetgid != 0 {
- sysMode |= unix.S_ISGID
- }
- if mode&os.ModeSticky != 0 {
- sysMode |= unix.S_ISVTX
- }
- // We don't allow file type bits.
- if mode&os.ModeType != 0 {
- return 0, fmt.Errorf("%w %+.3o (%s): type bits not permitted", errInvalidMode, mode, mode)
- }
- // We don't allow other unknown modes.
- if mode&^modePermExt != 0 || sysMode&unix.S_IFMT != 0 {
- return 0, fmt.Errorf("%w %+.3o (%s): unknown mode bits", errInvalidMode, mode, mode)
- }
- return sysMode, nil
-}
-
-// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
-// in two respects:
-//
-// - The caller provides the root directory as an *[os.File] (preferably O_PATH)
-// handle. This means that the caller can be sure which root directory is
-// being used. Note that this can be emulated by using /proc/self/fd/... as
-// the root path with [os.MkdirAll].
-//
-// - Once all of the directories have been created, an *[os.File] O_PATH handle
-// to the directory at unsafePath is returned to the caller. This is done in
-// an effectively-race-free way (an attacker would only be able to swap the
-// final directory component), which is not possible to emulate with
-// [MkdirAll].
-//
-// In addition, the returned handle is obtained far more efficiently than doing
-// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after
-// doing [MkdirAll]. If you intend to open the directory after creating it, you
-// should use MkdirAllHandle.
-//
-// [SecureJoin]: https://pkg.go.dev/github.com/cyphar/filepath-securejoin#SecureJoin
-func MkdirAllHandle(root *os.File, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
- unixMode, err := toUnixMode(mode)
- if err != nil {
- return nil, err
- }
- // On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid
- // bits. We could also silently ignore them but since we have very few
- // users it seems more prudent to return an error so users notice that
- // these bits will not be set.
- if unixMode&^0o1777 != 0 {
- return nil, fmt.Errorf("%w for mkdir %+.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
- }
-
- // Try to open as much of the path as possible.
- currentDir, remainingPath, err := partialLookupInRoot(root, unsafePath)
- defer func() {
- if Err != nil {
- _ = currentDir.Close()
- }
- }()
- if err != nil && !errors.Is(err, unix.ENOENT) {
- return nil, fmt.Errorf("find existing subpath of %q: %w", unsafePath, err)
- }
-
- // If there is an attacker deleting directories as we walk into them,
- // detect this proactively. Note this is guaranteed to detect if the
- // attacker deleted any part of the tree up to currentDir.
- //
- // Once we walk into a dead directory, partialLookupInRoot would not be
- // able to walk further down the tree (directories must be empty before
- // they are deleted), and if the attacker has removed the entire tree we
- // can be sure that anything that was originally inside a dead directory
- // must also be deleted and thus is a dead directory in its own right.
- //
- // This is mostly a quality-of-life check, because mkdir will simply fail
- // later if the attacker deletes the tree after this check.
- if err := fd.IsDeadInode(currentDir); err != nil {
- return nil, fmt.Errorf("finding existing subpath of %q: %w", unsafePath, err)
- }
-
- // Re-open the path to match the O_DIRECTORY reopen loop later (so that we
- // always return a non-O_PATH handle). We also check that we actually got a
- // directory.
- if reopenDir, err := Reopen(currentDir, unix.O_DIRECTORY|unix.O_CLOEXEC); errors.Is(err, unix.ENOTDIR) {
- return nil, fmt.Errorf("cannot create subdirectories in %q: %w", currentDir.Name(), unix.ENOTDIR)
- } else if err != nil {
- return nil, fmt.Errorf("re-opening handle to %q: %w", currentDir.Name(), err)
- } else { //nolint:revive // indent-error-flow lint doesn't make sense here
- _ = currentDir.Close()
- currentDir = reopenDir
- }
-
- remainingParts := strings.Split(remainingPath, string(filepath.Separator))
- if gocompat.SlicesContains(remainingParts, "..") {
- // The path contained ".." components after the end of the "real"
- // components. We could try to safely resolve ".." here but that would
- // add a bunch of extra logic for something that it's not clear even
- // needs to be supported. So just return an error.
- //
- // If we do filepath.Clean(remainingPath) then we end up with the
- // problem that ".." can erase a trailing dangling symlink and produce
- // a path that doesn't quite match what the user asked for.
- return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath)
- }
-
- // Create the remaining components.
- for _, part := range remainingParts {
- switch part {
- case "", ".":
- // Skip over no-op paths.
- continue
- }
-
- // NOTE: mkdir(2) will not follow trailing symlinks, so we can safely
- // create the final component without worrying about symlink-exchange
- // attacks.
- //
- // If we get -EEXIST, it's possible that another program created the
- // directory at the same time as us. In that case, just continue on as
- // if we created it (if the created inode is not a directory, the
- // following open call will fail).
- if err := unix.Mkdirat(int(currentDir.Fd()), part, unixMode); err != nil && !errors.Is(err, unix.EEXIST) {
- err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
- // Make the error a bit nicer if the directory is dead.
- if deadErr := fd.IsDeadInode(currentDir); deadErr != nil {
- // TODO: Once we bump the minimum Go version to 1.20, we can use
- // multiple %w verbs for this wrapping. For now we need to use a
- // compatibility shim for older Go versions.
- // err = fmt.Errorf("%w (%w)", err, deadErr)
- err = gocompat.WrapBaseError(err, deadErr)
- }
- return nil, err
- }
-
- // Get a handle to the next component. O_DIRECTORY means we don't need
- // to use O_PATH.
- var nextDir *os.File
- if linux.HasOpenat2() {
- nextDir, err = openat2(currentDir, part, &unix.OpenHow{
- Flags: unix.O_NOFOLLOW | unix.O_DIRECTORY | unix.O_CLOEXEC,
- Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_SYMLINKS | unix.RESOLVE_NO_XDEV,
- })
- } else {
- nextDir, err = fd.Openat(currentDir, part, unix.O_NOFOLLOW|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
- }
- if err != nil {
- return nil, err
- }
- _ = currentDir.Close()
- currentDir = nextDir
-
- // It's possible that the directory we just opened was swapped by an
- // attacker. Unfortunately there isn't much we can do to protect
- // against this, and MkdirAll's behaviour is that we will reuse
- // existing directories anyway so the need to protect against this is
- // incredibly limited (and arguably doesn't even deserve mention here).
- //
- // Ideally we might want to check that the owner and mode match what we
- // would've created -- unfortunately, it is non-trivial to verify that
- // the owner and mode of the created directory match. While plain Unix
- // DAC rules seem simple enough to emulate, there are a bunch of other
- // factors that can change the mode or owner of created directories
- // (default POSIX ACLs, mount options like uid=1,gid=2,umask=0 on
- // filesystems like vfat, etc etc). We used to try to verify this but
- // it just lead to a series of spurious errors.
- //
- // We could also check that the directory is non-empty, but
- // unfortunately some pseduofilesystems (like cgroupfs) create
- // non-empty directories, which would result in different spurious
- // errors.
- }
- return currentDir, nil
-}
-
-// MkdirAll is a race-safe alternative to the [os.MkdirAll] function,
-// where the new directory is guaranteed to be within the root directory (if an
-// attacker can move directories from inside the root to outside the root, the
-// created directory tree might be outside of the root but the key constraint
-// is that at no point will we walk outside of the directory tree we are
-// creating).
-//
-// Effectively, MkdirAll(root, unsafePath, mode) is equivalent to
-//
-// path, _ := securejoin.SecureJoin(root, unsafePath)
-// err := os.MkdirAll(path, mode)
-//
-// But is much safer. The above implementation is unsafe because if an attacker
-// can modify the filesystem tree between [SecureJoin] and [os.MkdirAll], it is
-// possible for MkdirAll to resolve unsafe symlink components and create
-// directories outside of the root.
-//
-// If you plan to open the directory after you have created it or want to use
-// an open directory handle as the root, you should use [MkdirAllHandle] instead.
-// This function is a wrapper around [MkdirAllHandle].
-//
-// [SecureJoin]: https://pkg.go.dev/github.com/cyphar/filepath-securejoin#SecureJoin
-func MkdirAll(root, unsafePath string, mode os.FileMode) error {
- rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
- if err != nil {
- return err
- }
- defer rootDir.Close() //nolint:errcheck // close failures aren't critical here
-
- f, err := MkdirAllHandle(rootDir, unsafePath, mode)
- if err != nil {
- return err
- }
- _ = f.Close()
- return nil
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/open_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/open_linux.go
deleted file mode 100644
index 7492d8cfa..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/open_linux.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package pathrs
-
-import (
- "os"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs"
-)
-
-// OpenatInRoot is equivalent to [OpenInRoot], except that the root is provided
-// using an *[os.File] handle, to ensure that the correct root directory is used.
-func OpenatInRoot(root *os.File, unsafePath string) (*os.File, error) {
- handle, err := completeLookupInRoot(root, unsafePath)
- if err != nil {
- return nil, &os.PathError{Op: "securejoin.OpenInRoot", Path: unsafePath, Err: err}
- }
- return handle, nil
-}
-
-// OpenInRoot safely opens the provided unsafePath within the root.
-// Effectively, OpenInRoot(root, unsafePath) is equivalent to
-//
-// path, _ := securejoin.SecureJoin(root, unsafePath)
-// handle, err := os.OpenFile(path, unix.O_PATH|unix.O_CLOEXEC)
-//
-// But is much safer. The above implementation is unsafe because if an attacker
-// can modify the filesystem tree between [SecureJoin] and [os.OpenFile], it is
-// possible for the returned file to be outside of the root.
-//
-// Note that the returned handle is an O_PATH handle, meaning that only a very
-// limited set of operations will work on the handle. This is done to avoid
-// accidentally opening an untrusted file that could cause issues (such as a
-// disconnected TTY that could cause a DoS, or some other issue). In order to
-// use the returned handle, you can "upgrade" it to a proper handle using
-// [Reopen].
-//
-// [SecureJoin]: https://pkg.go.dev/github.com/cyphar/filepath-securejoin#SecureJoin
-func OpenInRoot(root, unsafePath string) (*os.File, error) {
- rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
- if err != nil {
- return nil, err
- }
- defer rootDir.Close() //nolint:errcheck // close failures aren't critical here
- return OpenatInRoot(rootDir, unsafePath)
-}
-
-// Reopen takes an *[os.File] handle and re-opens it through /proc/self/fd.
-// Reopen(file, flags) is effectively equivalent to
-//
-// fdPath := fmt.Sprintf("/proc/self/fd/%d", file.Fd())
-// os.OpenFile(fdPath, flags|unix.O_CLOEXEC)
-//
-// But with some extra hardenings to ensure that we are not tricked by a
-// maliciously-configured /proc mount. While this attack scenario is not
-// common, in container runtimes it is possible for higher-level runtimes to be
-// tricked into configuring an unsafe /proc that can be used to attack file
-// operations. See [CVE-2019-19921] for more details.
-//
-// [CVE-2019-19921]: https://github.com/advisories/GHSA-fh74-hm69-rqjw
-func Reopen(handle *os.File, flags int) (*os.File, error) {
- return procfs.ReopenFd(handle, flags)
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/openat2_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/openat2_linux.go
deleted file mode 100644
index 937bc435f..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/openat2_linux.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-package pathrs
-
-import (
- "errors"
- "fmt"
- "os"
- "path/filepath"
- "strings"
-
- "golang.org/x/sys/unix"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/fd"
- "github.com/cyphar/filepath-securejoin/pathrs-lite/procfs"
-)
-
-func openat2(dir fd.Fd, path string, how *unix.OpenHow) (*os.File, error) {
- file, err := fd.Openat2(dir, path, how)
- if err != nil {
- return nil, err
- }
- // If we are using RESOLVE_IN_ROOT, the name we generated may be wrong.
- if how.Resolve&unix.RESOLVE_IN_ROOT == unix.RESOLVE_IN_ROOT {
- if actualPath, err := procfs.ProcSelfFdReadlink(file); err == nil {
- // TODO: Ideally we would not need to dup the fd, but you cannot
- // easily just swap an *os.File with one from the same fd
- // (the GC will close the old one, and you cannot clear the
- // finaliser easily because it is associated with an internal
- // field of *os.File not *os.File itself).
- newFile, err := fd.DupWithName(file, actualPath)
- if err != nil {
- return nil, err
- }
- file = newFile
- }
- }
- return file, nil
-}
-
-func lookupOpenat2(root fd.Fd, unsafePath string, partial bool) (*os.File, string, error) {
- if !partial {
- file, err := openat2(root, unsafePath, &unix.OpenHow{
- Flags: unix.O_PATH | unix.O_CLOEXEC,
- Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
- })
- return file, "", err
- }
- return partialLookupOpenat2(root, unsafePath)
-}
-
-// partialLookupOpenat2 is an alternative implementation of
-// partialLookupInRoot, using openat2(RESOLVE_IN_ROOT) to more safely get a
-// handle to the deepest existing child of the requested path within the root.
-func partialLookupOpenat2(root fd.Fd, unsafePath string) (*os.File, string, error) {
- // TODO: Implement this as a git-bisect-like binary search.
-
- unsafePath = filepath.ToSlash(unsafePath) // noop
- endIdx := len(unsafePath)
- var lastError error
- for endIdx > 0 {
- subpath := unsafePath[:endIdx]
-
- handle, err := openat2(root, subpath, &unix.OpenHow{
- Flags: unix.O_PATH | unix.O_CLOEXEC,
- Resolve: unix.RESOLVE_IN_ROOT | unix.RESOLVE_NO_MAGICLINKS,
- })
- if err == nil {
- // Jump over the slash if we have a non-"" remainingPath.
- if endIdx < len(unsafePath) {
- endIdx++
- }
- // We found a subpath!
- return handle, unsafePath[endIdx:], lastError
- }
- if errors.Is(err, unix.ENOENT) || errors.Is(err, unix.ENOTDIR) {
- // That path doesn't exist, let's try the next directory up.
- endIdx = strings.LastIndexByte(subpath, '/')
- lastError = err
- continue
- }
- return nil, "", fmt.Errorf("open subpath: %w", err)
- }
- // If we couldn't open anything, the whole subpath is missing. Return a
- // copy of the root fd so that the caller doesn't close this one by
- // accident.
- rootClone, err := fd.Dup(root)
- if err != nil {
- return nil, "", err
- }
- return rootClone, unsafePath, lastError
-}
diff --git a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/procfs/procfs_linux.go b/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/procfs/procfs_linux.go
deleted file mode 100644
index ec187a414..000000000
--- a/kubelink/vendor/github.com/cyphar/filepath-securejoin/pathrs-lite/procfs/procfs_linux.go
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: MPL-2.0
-
-//go:build linux
-
-// Copyright (C) 2024-2025 Aleksa Sarai
-// Copyright (C) 2024-2025 SUSE LLC
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-// Package procfs provides a safe API for operating on /proc on Linux.
-package procfs
-
-import (
- "os"
-
- "github.com/cyphar/filepath-securejoin/pathrs-lite/internal/procfs"
-)
-
-// This package mostly just wraps internal/procfs APIs. This is necessary
-// because we are forced to export some things from internal/procfs in order to
-// avoid some dependency cycle issues, but we don't want users to see or use
-// them.
-
-// ProcThreadSelfCloser is a callback that needs to be called when you are done
-// operating on an [os.File] fetched using [Handle.OpenThreadSelf].
-//
-// [os.File]: https://pkg.go.dev/os#File
-type ProcThreadSelfCloser = procfs.ProcThreadSelfCloser
-
-// Handle is a wrapper around an *os.File handle to "/proc", which can be used
-// to do further procfs-related operations in a safe way.
-type Handle struct {
- inner *procfs.Handle
-}
-
-// Close close the resources associated with this [Handle]. Note that if this
-// [Handle] was created with [OpenProcRoot], on some kernels the underlying
-// procfs handle is cached and so this Close operation may be a no-op. However,
-// you should always call Close on [Handle]s once you are done with them.
-func (proc *Handle) Close() error { return proc.inner.Close() }
-
-// OpenProcRoot tries to open a "safer" handle to "/proc" (i.e., one with the
-// "subset=pid" mount option applied, available from Linux 5.8). Unless you
-// plan to do many [Handle.OpenRoot] operations, users should prefer to use
-// this over [OpenUnsafeProcRoot] which is far more dangerous to keep open.
-//
-// If a safe handle cannot be opened, OpenProcRoot will fall back to opening a
-// regular "/proc" handle.
-//
-// Note that using [Handle.OpenRoot] will still work with handles returned by
-// this function. If a subpath cannot be operated on with a safe "/proc"
-// handle, then [OpenUnsafeProcRoot] will be called internally and a temporary
-// unsafe handle will be used.
-func OpenProcRoot() (*Handle, error) {
- proc, err := procfs.OpenProcRoot()
- if err != nil {
- return nil, err
- }
- return &Handle{inner: proc}, nil
-}
-
-// OpenUnsafeProcRoot opens a handle to "/proc" without any overmounts or
-// masked paths. You must be extremely careful to make sure this handle is
-// never leaked to a container and that you program cannot be tricked into
-// writing to arbitrary paths within it.
-//
-// This is not necessary if you just wish to use [Handle.OpenRoot], as handles
-// returned by [OpenProcRoot] will fall back to using a *temporary* unsafe
-// handle in that case. You should only really use this if you need to do many
-// operations with [Handle.OpenRoot] and the performance overhead of making
-// many procfs handles is an issue. If you do use OpenUnsafeProcRoot, you
-// should make sure to close the handle as soon as possible to avoid
-// known-fd-number attacks.
-func OpenUnsafeProcRoot() (*Handle, error) {
- proc, err := procfs.OpenUnsafeProcRoot()
- if err != nil {
- return nil, err
- }
- return &Handle{inner: proc}, nil
-}
-
-// OpenThreadSelf returns a handle to "/proc/thread-self/" (or an
-// equivalent handle on older kernels where "/proc/thread-self" doesn't exist).
-// Once finished with the handle, you must call the returned closer function
-// ([runtime.UnlockOSThread]). You must not pass the returned *os.File to other
-// Go threads or use the handle after calling the closer.
-//
-// [runtime.UnlockOSThread]: https://pkg.go.dev/runtime#UnlockOSThread
-func (proc *Handle) OpenThreadSelf(subpath string) (*os.File, ProcThreadSelfCloser, error) {
- return proc.inner.OpenThreadSelf(subpath)
-}
-
-// OpenSelf returns a handle to /proc/self/.
-//
-// Note that in Go programs with non-homogenous threads, this may result in
-// spurious errors. If you are monkeying around with APIs that are
-// thread-specific, you probably want to use [Handle.OpenThreadSelf] instead
-// which will guarantee that the handle refers to the same thread as the caller
-// is executing on.
-func (proc *Handle) OpenSelf(subpath string) (*os.File, error) {
- return proc.inner.OpenSelf(subpath)
-}
-
-// OpenRoot returns a handle to /proc/.
-//
-// You should only use this when you need to operate on global procfs files
-// (such as sysctls in /proc/sys). Unlike [Handle.OpenThreadSelf],
-// [Handle.OpenSelf], and [Handle.OpenPid], the procfs handle used internally
-// for this operation will never use "subset=pid", which makes it a more juicy
-// target for [CVE-2024-21626]-style attacks (and doing something like opening
-// a directory with OpenRoot effectively leaks [OpenUnsafeProcRoot] as long as
-// the file descriptor is open).
-//
-// [CVE-2024-21626]: https://github.com/opencontainers/runc/security/advisories/GHSA-xr7r-f8xq-vfvv
-func (proc *Handle) OpenRoot(subpath string) (*os.File, error) {
- return proc.inner.OpenRoot(subpath)
-}
-
-// OpenPid returns a handle to /proc/$pid/ (pid can be a pid or tid).
-// This is mainly intended for usage when operating on other processes.
-//
-// You should not use this for the current thread, as special handling is
-// needed for /proc/thread-self (or /proc/self/task/) when dealing with
-// goroutine scheduling -- use [Handle.OpenThreadSelf] instead.
-//
-// To refer to the current thread-group, you should use prefer
-// [Handle.OpenSelf] to passing os.Getpid as the pid argument.
-func (proc *Handle) OpenPid(pid int, subpath string) (*os.File, error) {
- return proc.inner.OpenPid(pid, subpath)
-}
-
-// ProcSelfFdReadlink gets the real path of the given file by looking at
-// /proc/self/fd/ with [readlink]. It is effectively just shorthand for
-// something along the lines of:
-//
-// proc, err := procfs.OpenProcRoot()
-// if err != nil {
-// return err
-// }
-// link, err := proc.OpenThreadSelf(fmt.Sprintf("fd/%d", f.Fd()))
-// if err != nil {
-// return err
-// }
-// defer link.Close()
-// var buf [4096]byte
-// n, err := unix.Readlinkat(int(link.Fd()), "", buf[:])
-// if err != nil {
-// return err
-// }
-// pathname := buf[:n]
-//
-// [readlink]: https://pkg.go.dev/golang.org/x/sys/unix#Readlinkat
-func ProcSelfFdReadlink(f *os.File) (string, error) {
- return procfs.ProcSelfFdReadlink(f)
-}
diff --git a/kubelink/vendor/github.com/fxamacker/cbor/v2/README.md b/kubelink/vendor/github.com/fxamacker/cbor/v2/README.md
index af0a79507..d072b81c7 100644
--- a/kubelink/vendor/github.com/fxamacker/cbor/v2/README.md
+++ b/kubelink/vendor/github.com/fxamacker/cbor/v2/README.md
@@ -1,30 +1,31 @@
-# CBOR Codec in Go
-
-
+
CBOR Codec
[fxamacker/cbor](https://github.com/fxamacker/cbor) is a library for encoding and decoding [CBOR](https://www.rfc-editor.org/info/std94) and [CBOR Sequences](https://www.rfc-editor.org/rfc/rfc8742.html).
CBOR is a [trusted alternative](https://www.rfc-editor.org/rfc/rfc8949.html#name-comparison-of-other-binary-) to JSON, MessagePack, Protocol Buffers, etc. CBOR is an Internet Standard defined by [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94) and is designed to be relevant for decades.
-`fxamacker/cbor` is used in projects by Arm Ltd., Cisco, EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, Kubernetes, Let's Encrypt (ISRG), Linux Foundation, Microsoft, Mozilla, Oasis Protocol, Tailscale, Teleport, [etc](https://github.com/fxamacker/cbor#who-uses-fxamackercbor).
+`fxamacker/cbor` is used in projects by Arm Ltd., EdgeX Foundry, Flow Foundation, Fraunhofer‑AISEC, IBM, Kubernetes[*](https://github.com/search?q=org%3Akubernetes%20fxamacker%2Fcbor&type=code), Let's Encrypt, Linux Foundation, Microsoft, Oasis Protocol, Red Hat[*](https://github.com/search?q=org%3Aopenshift+fxamacker%2Fcbor&type=code), Tailscale[*](https://github.com/search?q=org%3Atailscale+fxamacker%2Fcbor&type=code), Veraison[*](https://github.com/search?q=org%3Averaison+fxamacker%2Fcbor&type=code), [etc](https://github.com/fxamacker/cbor#who-uses-fxamackercbor).
-See [Quick Start](#quick-start) and [Releases](https://github.com/fxamacker/cbor/releases/). 🆕 `UnmarshalFirst` and `DiagnoseFirst` can decode CBOR Sequences. `cbor.MarshalToBuffer()` and `UserBufferEncMode` accepts user-specified buffer.
+See [Quick Start](#quick-start) and [Releases](https://github.com/fxamacker/cbor/releases/). 🆕 `UnmarshalFirst` and `DiagnoseFirst` can decode CBOR Sequences. `MarshalToBuffer` and `UserBufferEncMode` accepts user-specified buffer.
## fxamacker/cbor
[](https://github.com/fxamacker/cbor/actions?query=workflow%3Aci)
-[](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A596%25%22)
+[](https://github.com/fxamacker/cbor/actions?query=workflow%3A%22cover+%E2%89%A597%25%22)
[](https://github.com/fxamacker/cbor/actions/workflows/codeql-analysis.yml)
[](#fuzzing-and-code-coverage)
[](https://goreportcard.com/report/github.com/fxamacker/cbor)
+[](https://github.com/fxamacker/cbor#fuzzing-and-code-coverage)
`fxamacker/cbor` is a CBOR codec in full conformance with [IETF STD 94 (RFC 8949)](https://www.rfc-editor.org/info/std94). It also supports CBOR Sequences ([RFC 8742](https://www.rfc-editor.org/rfc/rfc8742.html)) and Extended Diagnostic Notation ([Appendix G of RFC 8610](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G)).
Features include full support for CBOR tags, [Core Deterministic Encoding](https://www.rfc-editor.org/rfc/rfc8949.html#name-core-deterministic-encoding), duplicate map key detection, etc.
+API is mostly same as `encoding/json`, plus interfaces that simplify concurrency and CBOR options.
+
Design balances trade-offs between security, speed, concurrency, encoded data size, usability, etc.
-Highlights
+ 🔎 Highlights
__🚀 Speed__
@@ -38,7 +39,7 @@ Codec passed multiple confidential security assessments in 2022. No vulnerabili
__🗜️ Data Size__
-Struct tags (`toarray`, `keyasint`, `omitempty`) automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
+Struct tag options (`toarray`, `keyasint`, `omitempty`, `omitzero`) and field tag "-" automatically reduce size of encoded structs. Encoding optionally shrinks float64→32→16 when values fit.
__:jigsaw: Usability__
@@ -58,164 +59,205 @@ Features include CBOR [extension points](https://www.rfc-editor.org/rfc/rfc8949.
`fxamacker/cbor` has configurable limits, etc. that defend against malicious CBOR data.
-By contrast, `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security).
-
-Example decoding with encoding/gob 💥 fatal error (out of memory)
-
-```Go
-// Example of encoding/gob having "fatal error: runtime: out of memory"
-// while decoding 181 bytes.
-package main
-import (
- "bytes"
- "encoding/gob"
- "encoding/hex"
- "fmt"
-)
-
-// Example data is from https://github.com/golang/go/issues/24446
-// (shortened to 181 bytes).
-const data = "4dffb503010102303001ff30000109010130010800010130010800010130" +
- "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" +
- "860001013001ff860001013001ffb80000001eff850401010e3030303030" +
- "30303030303030303001ff3000010c0104000016ffb70201010830303030" +
- "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" +
- "303030303030303030303030303030303030303030303030303030303030" +
- "30"
-
-type X struct {
- J *X
- K map[string]int
-}
-
-func main() {
- raw, _ := hex.DecodeString(data)
- decoder := gob.NewDecoder(bytes.NewReader(raw))
-
- var x X
- decoder.Decode(&x) // fatal error: runtime: out of memory
- fmt.Println("Decoding finished.")
-}
-```
-
-
-
-
-
-`fxamacker/cbor` is fast at rejecting malformed CBOR data. E.g. attempts to
-decode 10 bytes of malicious CBOR data to `[]byte` (with default settings):
-
-| Codec | Speed (ns/op) | Memory | Allocs |
-| :---- | ------------: | -----: | -----: |
-| fxamacker/cbor 2.5.0 | 44 ± 5% | 32 B/op | 2 allocs/op |
-| ugorji/go 1.2.11 | 5353261 ± 4% | 67111321 B/op | 13 allocs/op |
-
-Benchmark details
-
-Latest comparison used:
-- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
-- go1.19.10, linux/amd64, i5-13600K (disabled all e-cores, DDR4 @2933)
-- go test -bench=. -benchmem -count=20
-
-#### Prior comparisons
-
-| Codec | Speed (ns/op) | Memory | Allocs |
-| :---- | ------------: | -----: | -----: |
-| fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
-| fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
-| ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
-| ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate |
-
-- Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
-- go1.19.6, linux/amd64, i5-13600K (DDR4)
-- go test -bench=. -benchmem -count=20
-
-
-
-
-
-### Smaller Encodings with Struct Tags
-
-Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs.
-
-Example encoding 3-level nested Go struct to 1 byte CBOR
-
-https://go.dev/play/p/YxwvfPdFQG2
-
-```Go
-// Example encoding nested struct (with omitempty tag)
-// - encoding/json: 18 byte JSON
-// - fxamacker/cbor: 1 byte CBOR
-package main
-
-import (
- "encoding/hex"
- "encoding/json"
- "fmt"
-
- "github.com/fxamacker/cbor/v2"
-)
-
-type GrandChild struct {
- Quux int `json:",omitempty"`
-}
-
-type Child struct {
- Baz int `json:",omitempty"`
- Qux GrandChild `json:",omitempty"`
-}
-
-type Parent struct {
- Foo Child `json:",omitempty"`
- Bar int `json:",omitempty"`
-}
-
-func cb() {
- results, _ := cbor.Marshal(Parent{})
- fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
-
- text, _ := cbor.Diagnose(results) // Diagnostic Notation
- fmt.Println("DN: " + text)
-}
-
-func js() {
- results, _ := json.Marshal(Parent{})
- fmt.Println("hex(JSON): " + hex.EncodeToString(results))
-
- text := string(results) // JSON
- fmt.Println("JSON: " + text)
-}
-
-func main() {
- cb()
- fmt.Println("-------------")
- js()
-}
-```
-
-Output (DN is Diagnostic Notation):
-```
-hex(CBOR): a0
-DN: {}
--------------
-hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
-JSON: {"Foo":{"Qux":{}}}
-```
-
-
-
-
-
-Example using different struct tags together:
+Notably, `fxamacker/cbor` is fast at rejecting malformed CBOR data.
+
+> [!NOTE]
+> Benchmarks rejecting 10 bytes of malicious CBOR data decoding to `[]byte`:
+>
+> | Codec | Speed (ns/op) | Memory | Allocs |
+> | :---- | ------------: | -----: | -----: |
+> | fxamacker/cbor 2.7.0 | 47 ± 7% | 32 B/op | 2 allocs/op |
+> | ugorji/go 1.2.12 | 5878187 ± 3% | 67111556 B/op | 13 allocs/op |
+>
+> Faster hardware (overclocked DDR4 or DDR5) can reduce speed difference.
+>
+> 🔎 Benchmark details
+>
+> Latest comparison for decoding CBOR data to Go `[]byte`:
+> - Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
+> - go1.22.7, linux/amd64, i5-13600K (DDR4-2933, disabled e-cores)
+> - go test -bench=. -benchmem -count=20
+>
+> #### Prior comparisons
+>
+> | Codec | Speed (ns/op) | Memory | Allocs |
+> | :---- | ------------: | -----: | -----: |
+> | fxamacker/cbor 2.5.0-beta2 | 44.33 ± 2% | 32 B/op | 2 allocs/op |
+> | fxamacker/cbor 0.1.0 - 2.4.0 | ~44.68 ± 6% | 32 B/op | 2 allocs/op |
+> | ugorji/go 1.2.10 | 5524792.50 ± 3% | 67110491 B/op | 12 allocs/op |
+> | ugorji/go 1.1.0 - 1.2.6 | 💥 runtime: | out of memory: | cannot allocate |
+>
+> - Input: `[]byte{0x9B, 0x00, 0x00, 0x42, 0xFA, 0x42, 0xFA, 0x42, 0xFA, 0x42}`
+> - go1.19.6, linux/amd64, i5-13600K (DDR4)
+> - go test -bench=. -benchmem -count=20
+>
+>
+
+In contrast, some codecs can crash or use excessive resources while decoding bad data.
+
+> [!WARNING]
+> Go's `encoding/gob` is [not designed to be hardened against adversarial inputs](https://pkg.go.dev/encoding/gob#hdr-Security).
+>
+> 🔎 gob fatal error (out of memory) 💥 decoding 181 bytes
+>
+> ```Go
+> // Example of encoding/gob having "fatal error: runtime: out of memory"
+> // while decoding 181 bytes (all Go versions as of Dec. 8, 2024).
+> package main
+> import (
+> "bytes"
+> "encoding/gob"
+> "encoding/hex"
+> "fmt"
+> )
+>
+> // Example data is from https://github.com/golang/go/issues/24446
+> // (shortened to 181 bytes).
+> const data = "4dffb503010102303001ff30000109010130010800010130010800010130" +
+> "01ffb80001014a01ffb60001014b01ff860001013001ff860001013001ff" +
+> "860001013001ff860001013001ffb80000001eff850401010e3030303030" +
+> "30303030303030303001ff3000010c0104000016ffb70201010830303030" +
+> "3030303001ff3000010c000030ffb6040405fcff00303030303030303030" +
+> "303030303030303030303030303030303030303030303030303030303030" +
+> "30"
+>
+> type X struct {
+> J *X
+> K map[string]int
+> }
+>
+> func main() {
+> raw, _ := hex.DecodeString(data)
+> decoder := gob.NewDecoder(bytes.NewReader(raw))
+>
+> var x X
+> decoder.Decode(&x) // fatal error: runtime: out of memory
+> fmt.Println("Decoding finished.")
+> }
+> ```
+>
+>
+>
+
+### Smaller Encodings with Struct Tag Options
+
+Struct tags automatically reduce encoded size of structs and improve speed.
+
+We can write less code by using struct tag options:
+- `toarray`: encode without field names (decode back to original struct)
+- `keyasint`: encode field names as integers (decode back to original struct)
+- `omitempty`: omit empty field when encoding
+- `omitzero`: omit zero-value field when encoding
+
+As a special case, struct field tag "-" omits the field.
+
+NOTE: When a struct uses `toarray`, the encoder will ignore `omitempty` and `omitzero` to prevent position of encoded array elements from changing. This allows decoder to match encoded elements to their Go struct field.

-API is mostly same as `encoding/json`, plus interfaces that simplify concurrency for CBOR options.
+> [!NOTE]
+> `fxamacker/cbor` can encode a 3-level nested Go struct to 1 byte!
+> - `encoding/json`: 18 bytes of JSON
+> - `fxamacker/cbor`: 1 byte of CBOR
+>
+> 🔎 Encoding 3-level nested Go struct with omitempty
+>
+> https://go.dev/play/p/YxwvfPdFQG2
+>
+> ```Go
+> // Example encoding nested struct (with omitempty tag)
+> // - encoding/json: 18 byte JSON
+> // - fxamacker/cbor: 1 byte CBOR
+>
+> package main
+>
+> import (
+> "encoding/hex"
+> "encoding/json"
+> "fmt"
+>
+> "github.com/fxamacker/cbor/v2"
+> )
+>
+> type GrandChild struct {
+> Quux int `json:",omitempty"`
+> }
+>
+> type Child struct {
+> Baz int `json:",omitempty"`
+> Qux GrandChild `json:",omitempty"`
+> }
+>
+> type Parent struct {
+> Foo Child `json:",omitempty"`
+> Bar int `json:",omitempty"`
+> }
+>
+> func cb() {
+> results, _ := cbor.Marshal(Parent{})
+> fmt.Println("hex(CBOR): " + hex.EncodeToString(results))
+>
+> text, _ := cbor.Diagnose(results) // Diagnostic Notation
+> fmt.Println("DN: " + text)
+> }
+>
+> func js() {
+> results, _ := json.Marshal(Parent{})
+> fmt.Println("hex(JSON): " + hex.EncodeToString(results))
+>
+> text := string(results) // JSON
+> fmt.Println("JSON: " + text)
+> }
+>
+> func main() {
+> cb()
+> fmt.Println("-------------")
+> js()
+> }
+> ```
+>
+> Output (DN is Diagnostic Notation):
+> ```
+> hex(CBOR): a0
+> DN: {}
+> -------------
+> hex(JSON): 7b22466f6f223a7b22517578223a7b7d7d7d
+> JSON: {"Foo":{"Qux":{}}}
+> ```
+>
+>
+
## Quick Start
__Install__: `go get github.com/fxamacker/cbor/v2` and `import "github.com/fxamacker/cbor/v2"`.
+> [!TIP]
+>
+> Tinygo users can try beta/experimental branch [feature/cbor-tinygo-beta](https://github.com/fxamacker/cbor/tree/feature/cbor-tinygo-beta).
+>
+> 🔎 More about tinygo feature branch
+>
+> ### Tinygo
+>
+> Branch [feature/cbor-tinygo-beta](https://github.com/fxamacker/cbor/tree/feature/cbor-tinygo-beta) is based on fxamacker/cbor v2.7.0 and it can be compiled using tinygo v0.33 (also compiles with golang/go).
+>
+> It passes unit tests (with both go1.22 and tinygo v0.33) and is considered beta/experimental for tinygo.
+>
+> :warning: The `feature/cbor-tinygo-beta` branch does not get fuzz tested yet.
+>
+> Changes in this feature branch only affect tinygo compiled software. Summary of changes:
+> - default `DecOptions.MaxNestedLevels` is reduced to 16 (was 32). User can specify higher limit but 24+ crashes tests when compiled with tinygo v0.33.
+> - disabled decoding CBOR tag data to Go interface because tinygo v0.33 is missing needed feature.
+> - encoding error message can be different when encoding function type.
+>
+> Related tinygo issues:
+> - https://github.com/tinygo-org/tinygo/issues/4277
+> - https://github.com/tinygo-org/tinygo/issues/4458
+>
+>
+
+
### Key Points
This library can encode and decode CBOR (RFC 8949) and CBOR Sequences (RFC 8742).
@@ -252,16 +294,17 @@ rest, err = cbor.UnmarshalFirst(b, &v) // decode []byte b to v
// DiagnoseFirst translates first CBOR data item to text and returns remaining bytes.
text, rest, err = cbor.DiagnoseFirst(b) // decode []byte b to Diagnostic Notation text
-// NOTE: Unmarshal returns ExtraneousDataError if there are remaining bytes,
-// but new funcs UnmarshalFirst and DiagnoseFirst do not.
+// NOTE: Unmarshal() returns ExtraneousDataError if there are remaining bytes, but
+// UnmarshalFirst() and DiagnoseFirst() allow trailing bytes.
```
-__IMPORTANT__: 👉 CBOR settings allow trade-offs between speed, security, encoding size, etc.
-
-- Different CBOR libraries may use different default settings.
-- CBOR-based formats or protocols usually require specific settings.
-
-For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
+> [!IMPORTANT]
+> CBOR settings allow trade-offs between speed, security, encoding size, etc.
+>
+> - Different CBOR libraries may use different default settings.
+> - CBOR-based formats or protocols usually require specific settings.
+>
+> For example, WebAuthn uses "CTAP2 Canonical CBOR" which is available as a preset.
### Presets
@@ -312,9 +355,63 @@ err = em.MarshalToBuffer(v, &buf) // encode v to provided buf
### Struct Tags
-Struct tags (`toarray`, `keyasint`, `omitempty`) reduce encoded size of structs.
+Struct tag options (`toarray`, `keyasint`, `omitempty`, `omitzero`) reduce encoded size of structs.
+
+As a special case, struct field tag "-" omits the field.
+
+ 🔎 Example encoding with struct field tag "-"
+
+https://go.dev/play/p/aWEIFxd7InX
+
+```Go
+// https://github.com/fxamacker/cbor/issues/652
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/fxamacker/cbor/v2"
+)
+
+// The `cbor:"-"` tag omits the Type field when encoding to CBOR.
+type Entity struct {
+ _ struct{} `cbor:",toarray"`
+ ID uint64 `json:"id"`
+ Type string `cbor:"-" json:"typeOf"`
+ Name string `json:"name"`
+}
+
+func main() {
+ entity := Entity{
+ ID: 1,
+ Type: "int64",
+ Name: "Identifier",
+ }
+
+ c, _ := cbor.Marshal(entity)
+ diag, _ := cbor.Diagnose(c)
+ fmt.Printf("CBOR in hex: %x\n", c)
+ fmt.Printf("CBOR in edn: %s\n", diag)
+
+ j, _ := json.Marshal(entity)
+ fmt.Printf("JSON: %s\n", string(j))
+
+ fmt.Printf("JSON encoding is %d bytes\n", len(j))
+ fmt.Printf("CBOR encoding is %d bytes\n", len(c))
+
+ // Output:
+ // CBOR in hex: 82016a4964656e746966696572
+ // CBOR in edn: [1, "Identifier"]
+ // JSON: {"id":1,"typeOf":"int64","name":"Identifier"}
+ // JSON encoding is 45 bytes
+ // CBOR encoding is 13 bytes
+}
+```
+
+
-Example encoding 3-level nested Go struct to 1 byte CBOR
+ 🔎 Example encoding 3-level nested Go struct to 1 byte CBOR
https://go.dev/play/p/YxwvfPdFQG2
@@ -382,13 +479,13 @@ JSON: {"Foo":{"Qux":{}}}
-Example using several struct tags
+ 🔎 Example using struct tag options

-Struct tags simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys.
+Struct tag options simplify use of CBOR-based protocols that require CBOR arrays or maps with integer keys.
### CBOR Tags
@@ -404,7 +501,7 @@ em, err := opts.EncModeWithSharedTags(ts) // mutable shared CBOR tags
`TagSet` and modes using it are safe for concurrent use. Equivalent API is available for `DecMode`.
-Example using TagSet and TagOptions
+ 🔎 Example using TagSet and TagOptions
```go
// Use signedCWT struct defined in "Decoding CWT" example.
@@ -430,16 +527,149 @@ if err := dm.Unmarshal(data, &v); err != nil {
em, _ := cbor.EncOptions{}.EncModeWithTags(tags)
// Marshal signedCWT with tag number.
-if data, err := cbor.Marshal(v); err != nil {
+if data, err := em.Marshal(v); err != nil {
return err
}
```
+👉 `fxamacker/cbor` allows user apps to use almost any current or future CBOR tag number by implementing `cbor.Marshaler` and `cbor.Unmarshaler` interfaces.
+
+Basically, `MarshalCBOR` and `UnmarshalCBOR` functions can be implemented by user apps and those functions will automatically be called by this CBOR codec's `Marshal`, `Unmarshal`, etc.
+
+The following [example](https://github.com/fxamacker/cbor/blob/master/example_embedded_json_tag_for_cbor_test.go) shows how to encode and decode a tagged CBOR data item with tag number 262. The tag content is a JSON object "embedded" as a CBOR byte string (major type 2).
+
+ 🔎 Example using Embedded JSON Tag for CBOR (tag 262)
+
+```go
+// https://github.com/fxamacker/cbor/issues/657
+
+package cbor_test
+
+// NOTE: RFC 8949 does not mention tag number 262. IANA assigned
+// CBOR tag number 262 as "Embedded JSON Object" specified by the
+// document Embedded JSON Tag for CBOR:
+//
+// "Tag 262 can be applied to a byte string (major type 2) to indicate
+// that the byte string is a JSON Object. The length of the byte string
+// indicates the content."
+//
+// For more info, see Embedded JSON Tag for CBOR at:
+// https://github.com/toravir/CBOR-Tag-Specs/blob/master/embeddedJSON.md
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+
+ "github.com/fxamacker/cbor/v2"
+)
+
+// cborTagNumForEmbeddedJSON is the CBOR tag number 262.
+const cborTagNumForEmbeddedJSON = 262
+
+// EmbeddedJSON represents a Go value to be encoded as a tagged CBOR data item
+// with tag number 262 and the tag content is a JSON object "embedded" as a
+// CBOR byte string (major type 2).
+type EmbeddedJSON struct {
+ any
+}
+
+func NewEmbeddedJSON(val any) EmbeddedJSON {
+ return EmbeddedJSON{val}
+}
+
+// MarshalCBOR encodes EmbeddedJSON to a tagged CBOR data item with the
+// tag number 262 and the tag content is a JSON object that is
+// "embedded" as a CBOR byte string.
+func (v EmbeddedJSON) MarshalCBOR() ([]byte, error) {
+ // Encode v to JSON object.
+ data, err := json.Marshal(v)
+ if err != nil {
+ return nil, err
+ }
+
+ // Create cbor.Tag representing a tagged CBOR data item.
+ tag := cbor.Tag{
+ Number: cborTagNumForEmbeddedJSON,
+ Content: data,
+ }
+
+ // Marshal to a tagged CBOR data item.
+ return cbor.Marshal(tag)
+}
+
+// UnmarshalCBOR decodes a tagged CBOR data item to EmbeddedJSON.
+// The byte slice provided to this function must contain a single
+// tagged CBOR data item with the tag number 262 and tag content
+// must be a JSON object "embedded" as a CBOR byte string.
+func (v *EmbeddedJSON) UnmarshalCBOR(b []byte) error {
+ // Unmarshal tagged CBOR data item.
+ var tag cbor.Tag
+ if err := cbor.Unmarshal(b, &tag); err != nil {
+ return err
+ }
+
+ // Check tag number.
+ if tag.Number != cborTagNumForEmbeddedJSON {
+ return fmt.Errorf("got tag number %d, expect tag number %d", tag.Number, cborTagNumForEmbeddedJSON)
+ }
+
+ // Check tag content.
+ jsonData, isByteString := tag.Content.([]byte)
+ if !isByteString {
+ return fmt.Errorf("got tag content type %T, expect tag content []byte", tag.Content)
+ }
+
+ // Unmarshal JSON object.
+ return json.Unmarshal(jsonData, v)
+}
+
+// MarshalJSON encodes EmbeddedJSON to a JSON object.
+func (v EmbeddedJSON) MarshalJSON() ([]byte, error) {
+ return json.Marshal(v.any)
+}
+
+// UnmarshalJSON decodes a JSON object.
+func (v *EmbeddedJSON) UnmarshalJSON(b []byte) error {
+ dec := json.NewDecoder(bytes.NewReader(b))
+ dec.UseNumber()
+ return dec.Decode(&v.any)
+}
+
+func Example_embeddedJSONTagForCBOR() {
+ value := NewEmbeddedJSON(map[string]any{
+ "name": "gopher",
+ "id": json.Number("42"),
+ })
+
+ data, err := cbor.Marshal(value)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("cbor: %x\n", data)
+
+ var v EmbeddedJSON
+ err = cbor.Unmarshal(data, &v)
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Printf("%+v\n", v.any)
+ for k, v := range v.any.(map[string]any) {
+ fmt.Printf(" %s: %v (%T)\n", k, v, v)
+ }
+}
+```
+
+
+
+
### Functions and Interfaces
-Functions and interfaces at a glance
+ 🔎 Functions and interfaces at a glance
Common functions with same API as `encoding/json`:
- `Marshal`, `Unmarshal`
@@ -453,7 +683,7 @@ because RFC 8949 treats CBOR data item with remaining bytes as malformed.
Other useful functions:
- `Diagnose`, `DiagnoseFirst` produce human-readable [Extended Diagnostic Notation](https://www.rfc-editor.org/rfc/rfc8610.html#appendix-G) from CBOR data.
- `UnmarshalFirst` decodes first CBOR data item and return any remaining bytes.
-- `Wellformed` returns true if the the CBOR data item is well-formed.
+- `Wellformed` returns true if the CBOR data item is well-formed.
Interfaces identical or comparable to Go `encoding` packages include:
`Marshaler`, `Unmarshaler`, `BinaryMarshaler`, and `BinaryUnmarshaler`.
@@ -472,15 +702,28 @@ Default limits may need to be increased for systems handling very large data (e.
## Status
-v2.7.0 (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality.
+[v2.9.0](https://github.com/fxamacker/cbor/releases/tag/v2.9.0) (Jul 13, 2025) improved interoperability/transcoding between CBOR & JSON, refactored tests, and improved docs.
+- Add opt-in support for `encoding.TextMarshaler` and `encoding.TextUnmarshaler` to encode and decode from CBOR text string.
+- Add opt-in support for `json.Marshaler` and `json.Unmarshaler` via user-provided transcoding function.
+- Update docs for TimeMode, Tag, RawTag, and add example for Embedded JSON Tag for CBOR.
+
+v2.9.0 passed fuzz tests and is production quality.
+
+The minimum version of Go required to build:
+- v2.8.0 and newer releases require go 1.20+.
+- v2.7.1 and older releases require go 1.17+.
For more details, see [release notes](https://github.com/fxamacker/cbor/releases).
-### Prior Release
+### Prior Releases
+
+[v2.8.0](https://github.com/fxamacker/cbor/releases/tag/v2.8.0) (March 30, 2025) is a small release primarily to add `omitzero` option to struct field tags and fix bugs. It passed fuzz tests (billions of executions) and is production quality.
+
+[v2.7.0](https://github.com/fxamacker/cbor/releases/tag/v2.7.0) (June 23, 2024) adds features and improvements that help large projects (e.g. Kubernetes) use CBOR as an alternative to JSON and Protocol Buffers. Other improvements include speedups, improved memory use, bug fixes, new serialization options, etc. It passed fuzz tests (5+ billion executions) and is production quality.
[v2.6.0](https://github.com/fxamacker/cbor/releases/tag/v2.6.0) (February 2024) adds important new features, optimizations, and bug fixes. It is especially useful to systems that need to convert data between CBOR and JSON. New options and optimizations improve handling of bignum, integers, maps, and strings.
-v2.5.0 was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023).
+[v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) was released on Sunday, August 13, 2023 with new features and important bug fixes. It is fuzz tested and production quality after extended beta [v2.5.0-beta](https://github.com/fxamacker/cbor/releases/tag/v2.5.0-beta) (Dec 2022) -> [v2.5.0](https://github.com/fxamacker/cbor/releases/tag/v2.5.0) (Aug 2023).
__IMPORTANT__: 👉 Before upgrading from v2.4 or older release, please read the notable changes highlighted in the release notes. v2.5.0 is a large release with bug fixes to error handling for extraneous data in `Unmarshal`, etc. that should be reviewed before upgrading.
@@ -489,7 +732,7 @@ See [v2.5.0 release notes](https://github.com/fxamacker/cbor/releases/tag/v2.5.0
See ["Version and API Changes"](https://github.com/fxamacker/cbor#versions-and-api-changes) section for more info about version numbering, etc.
"); err != nil {
- return err
- }
- return nil
- case DoctypeNode:
- if _, err := w.WriteString("')
- case RawNode:
- _, err := w.WriteString(n.Data)
- return err
- default:
- return errors.New("html: unknown node type")
- }
-
- // Render the opening tag.
- if err := w.WriteByte('<'); err != nil {
- return err
- }
- if _, err := w.WriteString(n.Data); err != nil {
- return err
- }
- for _, a := range n.Attr {
- if err := w.WriteByte(' '); err != nil {
- return err
- }
- if a.Namespace != "" {
- if _, err := w.WriteString(a.Namespace); err != nil {
- return err
- }
- if err := w.WriteByte(':'); err != nil {
- return err
- }
- }
- if _, err := w.WriteString(a.Key); err != nil {
- return err
- }
- if _, err := w.WriteString(`="`); err != nil {
- return err
- }
- if err := escape(w, a.Val); err != nil {
- return err
- }
- if err := w.WriteByte('"'); err != nil {
- return err
- }
- }
- if voidElements[n.Data] {
- if n.FirstChild != nil {
- return fmt.Errorf("html: void element <%s> has child nodes", n.Data)
- }
- _, err := w.WriteString("/>")
- return err
- }
- if err := w.WriteByte('>'); err != nil {
- return err
- }
-
- // Add initial newline where there is danger of a newline being ignored.
- if c := n.FirstChild; c != nil && c.Type == TextNode && strings.HasPrefix(c.Data, "\n") {
- switch n.Data {
- case "pre", "listing", "textarea":
- if err := w.WriteByte('\n'); err != nil {
- return err
- }
- }
- }
-
- // Render any child nodes
- if childTextNodesAreLiteral(n) {
- for c := n.FirstChild; c != nil; c = c.NextSibling {
- if c.Type == TextNode {
- if _, err := w.WriteString(c.Data); err != nil {
- return err
- }
- } else {
- if err := render1(w, c); err != nil {
- return err
- }
- }
- }
- if n.Data == "plaintext" {
- // Don't render anything else. must be the
- // last element in the file, with no closing tag.
- return plaintextAbort
- }
- } else {
- for c := n.FirstChild; c != nil; c = c.NextSibling {
- if err := render1(w, c); err != nil {
- return err
- }
- }
- }
-
- // Render the closing tag.
- if _, err := w.WriteString(""); err != nil {
- return err
- }
- if _, err := w.WriteString(n.Data); err != nil {
- return err
- }
- return w.WriteByte('>')
-}
-
-func childTextNodesAreLiteral(n *Node) bool {
- // Per WHATWG HTML 13.3, if the parent of the current node is a style,
- // script, xmp, iframe, noembed, noframes, or plaintext element, and the
- // current node is a text node, append the value of the node's data
- // literally. The specification is not explicit about it, but we only
- // enforce this if we are in the HTML namespace (i.e. when the namespace is
- // "").
- // NOTE: we also always include noscript elements, although the
- // specification states that they should only be rendered as such if
- // scripting is enabled for the node (which is not something we track).
- if n.Namespace != "" {
- return false
- }
-
- switch n.Data {
- case "iframe", "noembed", "noframes", "noscript", "plaintext", "script", "style", "xmp":
- // We need to check if n is a node that was fostered from a HTML namespace
- // into a non-HTML namespace (in which case, different rules apply to it).
- // We do this by walking up the tree until we find a node with a non-empty
- // namespace. If we find such a node, we also have to check if it's
- // an HTML integration point. If it isn't, then the node we're currently
- // looking at is foster-parented and we should return false.
- for p := n.Parent; p != nil; p = p.Parent {
- if p.Namespace != "" {
- if !htmlIntegrationPoint(p) {
- return false
- }
- break
- }
- }
-
- return true
- default:
- return false
- }
-}
-
-// writeDoctypeQuoted writes s to w surrounded by quotes. Normally it will use double
-// quotes, but if s contains a double quote, it will use single quotes.
-// If s contains any '>' characters, they are replaced with > in order
-// to prevent triggering an abrupt-doctype-system-identifier parse error.
-// It is used for writing the identifiers in a doctype declaration.
-// In valid HTML, they can't contain both types of quotes.
-func writeDoctypeQuoted(w writer, s string) error {
- var q byte = '"'
- if strings.Contains(s, `"`) {
- // parseDoctype will never produce a Node with both quote types, but a user
- // can construct their own Node that violates this assumption.
- if strings.Contains(s, `'`) {
- return errors.New("doctype contains both quote types, cannot be safely rendered")
- }
- q = '\''
- }
- if err := w.WriteByte(q); err != nil {
- return err
- }
- if _, err := w.WriteString(strings.ReplaceAll(s, ">", ">")); err != nil {
- return err
- }
- if err := w.WriteByte(q); err != nil {
- return err
- }
- return nil
-}
-
-// Section 12.1.2, "Elements", gives this list of void elements. Void elements
-// are those that can't have any contents.
-var voidElements = map[string]bool{
- "area": true,
- "base": true,
- "br": true,
- "col": true,
- "embed": true,
- "hr": true,
- "img": true,
- "input": true,
- "keygen": true, // "keygen" has been removed from the spec, but are kept here for backwards compatibility.
- "link": true,
- "meta": true,
- "param": true,
- "source": true,
- "track": true,
- "wbr": true,
-}
diff --git a/kubelink/vendor/golang.org/x/net/html/token.go b/kubelink/vendor/golang.org/x/net/html/token.go
deleted file mode 100644
index 058dfb216..000000000
--- a/kubelink/vendor/golang.org/x/net/html/token.go
+++ /dev/null
@@ -1,1291 +0,0 @@
-// Copyright 2010 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 html
-
-import (
- "bytes"
- "errors"
- "io"
- "strconv"
- "strings"
-
- "golang.org/x/net/html/atom"
-)
-
-// A TokenType is the type of a Token.
-type TokenType uint32
-
-const (
- // ErrorToken means that an error occurred during tokenization.
- ErrorToken TokenType = iota
- // TextToken means a text node.
- TextToken
- // A StartTagToken looks like .
- StartTagToken
- // An EndTagToken looks like .
- EndTagToken
- // A SelfClosingTagToken tag looks like .
- SelfClosingTagToken
- // A CommentToken looks like .
- CommentToken
- // A DoctypeToken looks like
- DoctypeToken
-)
-
-// ErrBufferExceeded means that the buffering limit was exceeded.
-var ErrBufferExceeded = errors.New("max buffer exceeded")
-
-// String returns a string representation of the TokenType.
-func (t TokenType) String() string {
- switch t {
- case ErrorToken:
- return "Error"
- case TextToken:
- return "Text"
- case StartTagToken:
- return "StartTag"
- case EndTagToken:
- return "EndTag"
- case SelfClosingTagToken:
- return "SelfClosingTag"
- case CommentToken:
- return "Comment"
- case DoctypeToken:
- return "Doctype"
- }
- return "Invalid(" + strconv.Itoa(int(t)) + ")"
-}
-
-// An Attribute is an attribute namespace-key-value triple. Namespace is
-// non-empty for foreign attributes like xlink, Key is alphabetic (and hence
-// does not contain escapable characters like '&', '<' or '>'), and Val is
-// unescaped (it looks like "a"
- case EndTagToken:
- return "" + t.tagString() + ">"
- case SelfClosingTagToken:
- return "<" + t.tagString() + "/>"
- case CommentToken:
- return ""
- case DoctypeToken:
- return ""
- }
- return "Invalid(" + strconv.Itoa(int(t.Type)) + ")"
-}
-
-// span is a range of bytes in a Tokenizer's buffer. The start is inclusive,
-// the end is exclusive.
-type span struct {
- start, end int
-}
-
-// A Tokenizer returns a stream of HTML Tokens.
-type Tokenizer struct {
- // r is the source of the HTML text.
- r io.Reader
- // tt is the TokenType of the current token.
- tt TokenType
- // err is the first error encountered during tokenization. It is possible
- // for tt != Error && err != nil to hold: this means that Next returned a
- // valid token but the subsequent Next call will return an error token.
- // For example, if the HTML text input was just "plain", then the first
- // Next call would set z.err to io.EOF but return a TextToken, and all
- // subsequent Next calls would return an ErrorToken.
- // err is never reset. Once it becomes non-nil, it stays non-nil.
- err error
- // readErr is the error returned by the io.Reader r. It is separate from
- // err because it is valid for an io.Reader to return (n int, err1 error)
- // such that n > 0 && err1 != nil, and callers should always process the
- // n > 0 bytes before considering the error err1.
- readErr error
- // buf[raw.start:raw.end] holds the raw bytes of the current token.
- // buf[raw.end:] is buffered input that will yield future tokens.
- raw span
- buf []byte
- // maxBuf limits the data buffered in buf. A value of 0 means unlimited.
- maxBuf int
- // buf[data.start:data.end] holds the raw bytes of the current token's data:
- // a text token's text, a tag token's tag name, etc.
- data span
- // pendingAttr is the attribute key and value currently being tokenized.
- // When complete, pendingAttr is pushed onto attr. nAttrReturned is
- // incremented on each call to TagAttr.
- pendingAttr [2]span
- attr [][2]span
- attrNames map[string]bool
- nAttrReturned int
- // rawTag is the "script" in "" that closes the next token. If
- // non-empty, the subsequent call to Next will return a raw or RCDATA text
- // token: one that treats "
" as text instead of an element.
- // rawTag's contents are lower-cased.
- rawTag string
- // textIsRaw is whether the current text token's data is not escaped.
- textIsRaw bool
- // convertNUL is whether NUL bytes in the current token's data should
- // be converted into \ufffd replacement characters.
- convertNUL bool
- // allowCDATA is whether CDATA sections are allowed in the current context.
- allowCDATA bool
-}
-
-// AllowCDATA sets whether or not the tokenizer recognizes as
-// the text "foo". The default value is false, which means to recognize it as
-// a bogus comment "" instead.
-//
-// Strictly speaking, an HTML5 compliant tokenizer should allow CDATA if and
-// only if tokenizing foreign content, such as MathML and SVG. However,
-// tracking foreign-contentness is difficult to do purely in the tokenizer,
-// as opposed to the parser, due to HTML integration points: an