From c0418c23254f6d05c15eb2c528dfb728c93bf251 Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Thu, 21 May 2026 11:31:01 +0200 Subject: [PATCH] [full-ci] chore: bump reva to v2.46.0 (#2809) --- go.mod | 12 +- go.sum | 24 +- .../go-git/go-git/v5/config/config.go | 31 +- .../go-git/go-git/v5/config/modules.go | 52 ++++ .../go-git/go-git/v5/config/optbool.go | 82 ++++++ .../go-git/v5/internal/pathutil/dotgit.go | 21 ++ .../go-git/go-git/v5/internal/pathutil/hfs.go | 99 +++++++ .../go-git/v5/internal/pathutil/ntfs.go | 187 +++++++++++++ .../go-git/v5/internal/pathutil/tree.go | 66 +++++ .../go-git/go-git/v5/internal/url/url.go | 37 ++- .../v5/plumbing/format/idxfile/decoder.go | 164 ++++++++++- .../v5/plumbing/format/idxfile/idxfile.go | 29 +- .../v5/plumbing/format/objfile/reader.go | 18 +- .../v5/plumbing/format/packfile/diff_delta.go | 3 - .../v5/plumbing/format/packfile/fsobject.go | 8 +- .../v5/plumbing/format/packfile/packfile.go | 21 +- .../v5/plumbing/format/packfile/parser.go | 72 ++++- .../plumbing/format/packfile/patch_delta.go | 113 +++++--- .../v5/plumbing/format/packfile/scanner.go | 154 +++++++++- .../go-git/go-git/v5/plumbing/object/tree.go | 23 ++ .../v5/plumbing/transport/ssh/common.go | 34 ++- .../github.com/go-git/go-git/v5/repository.go | 13 +- .../v5/storage/filesystem/dotgit/dotgit.go | 19 +- .../github.com/go-git/go-git/v5/submodule.go | 82 +++++- .../go-git/go-git/v5/utils/binary/read.go | 15 + .../github.com/go-git/go-git/v5/worktree.go | 115 +------- .../go-git/go-git/v5/worktree_fs.go | 264 ++++++++++++++++++ .../go-git/go-git/v5/worktree_status.go | 9 + .../services/authprovider/authprovider.go | 8 +- .../grpc/services/gateway/storageprovider.go | 20 +- .../services/groupprovider/groupprovider.go | 8 +- .../services/userprovider/userprovider.go | 14 +- .../v2/pkg/auth/manager/appauth/appauth.go | 3 +- .../reva/v2/pkg/auth/manager/demo/demo.go | 3 +- .../auth/manager/impersonator/impersonator.go | 3 +- .../reva/v2/pkg/auth/manager/json/json.go | 3 +- .../reva/v2/pkg/auth/manager/ldap/ldap.go | 5 +- .../v2/pkg/auth/manager/machine/machine.go | 3 +- .../pkg/auth/manager/ocmshares/ocmshares.go | 3 +- .../reva/v2/pkg/auth/manager/oidc/oidc.go | 3 +- .../auth/manager/publicshares/publicshares.go | 3 +- .../v2/pkg/auth/manager/registry/registry.go | 3 +- .../serviceaccounts/serviceaccounts.go | 3 +- .../reva/v2/pkg/conversions/main.go | 22 -- .../reva/v2/pkg/group/manager/json/json.go | 3 +- .../reva/v2/pkg/group/manager/ldap/ldap.go | 5 +- .../reva/v2/pkg/group/manager/null/null.go | 3 +- .../v2/pkg/group/manager/registry/registry.go | 7 +- .../v2/pkg/share/manager/jsoncs3/jsoncs3.go | 43 +-- .../manager/jsoncs3/migrations/migration.go | 14 +- .../reva/v2/pkg/storage/cache/kv.go | 32 ++- .../pkg/storage/fs/posix/idcache/idcache.go | 88 +++++- .../v2/pkg/storage/fs/posix/lookup/lookup.go | 8 - .../reva/v2/pkg/storage/fs/posix/posix.go | 4 +- .../pkg/storage/fs/posix/tree/cache_test.py | 45 --- .../v2/pkg/storage/fs/posix/tree/fix_test.py | 10 - .../fs/posix/tree/generate_cache_test.py | 128 --------- .../v2/pkg/storage/fs/posix/tree/getxattr.py | 23 -- .../reva/v2/pkg/tenant/manager/ldap/ldap.go | 5 +- .../v2/pkg/tenant/manager/memory/memory.go | 3 +- .../reva/v2/pkg/tenant/manager/null/null.go | 3 +- .../pkg/tenant/manager/registry/registry.go | 3 +- .../reva/v2/pkg/user/manager/demo/demo.go | 3 +- .../reva/v2/pkg/user/manager/json/json.go | 8 +- .../reva/v2/pkg/user/manager/ldap/ldap.go | 9 +- .../reva/v2/pkg/user/manager/memory/memory.go | 3 +- .../v2/pkg/user/manager/registry/registry.go | 3 +- .../opencloud-eu/reva/v2/pkg/utils/ldap.go | 12 +- .../reva/v2/pkg/utils/ldap/reconnect.go | 1 + vendor/github.com/shamaton/msgpack/v2/.tagpr | 7 + .../github.com/shamaton/msgpack/v2/README.md | 41 +-- .../shamaton/msgpack/v2/ext/decode.go | 3 +- .../shamaton/msgpack/v2/ext/encode.go | 3 +- .../shamaton/msgpack/v2/ext/encode_stream.go | 36 ++- .../msgpack/v2/internal/common/common.go | 3 +- .../msgpack/v2/internal/decoding/decoding.go | 2 +- .../msgpack/v2/internal/decoding/ext.go | 60 +++- .../msgpack/v2/internal/decoding/int.go | 1 - .../msgpack/v2/internal/decoding/interface.go | 18 +- .../msgpack/v2/internal/decoding/string.go | 6 +- .../msgpack/v2/internal/decoding/struct.go | 64 ++--- .../msgpack/v2/internal/decoding/uint.go | 1 - .../msgpack/v2/internal/encoding/encoding.go | 1 - .../msgpack/v2/internal/encoding/ext.go | 6 +- .../msgpack/v2/internal/encoding/map.go | 1 - .../msgpack/v2/internal/encoding/slice.go | 1 - .../msgpack/v2/internal/encoding/struct.go | 12 +- .../v2/internal/stream/decoding/bin.go | 1 - .../v2/internal/stream/decoding/decoding.go | 3 +- .../v2/internal/stream/decoding/ext.go | 7 +- .../v2/internal/stream/decoding/string.go | 8 +- .../v2/internal/stream/decoding/struct.go | 6 +- .../v2/internal/stream/encoding/encoding.go | 1 - .../v2/internal/stream/encoding/ext.go | 6 +- .../v2/internal/stream/encoding/map.go | 1 - .../v2/internal/stream/encoding/set.go | 27 +- .../v2/internal/stream/encoding/slice.go | 1 - .../v2/internal/stream/encoding/struct.go | 2 - .../shamaton/msgpack/v2/time/decode.go | 116 ++++++-- .../shamaton/msgpack/v2/time/decode_stream.go | 9 + .../go.etcd.io/etcd/api/v3/version/version.go | 2 +- vendor/modules.txt | 13 +- 102 files changed, 2096 insertions(+), 713 deletions(-) create mode 100644 vendor/github.com/go-git/go-git/v5/config/optbool.go create mode 100644 vendor/github.com/go-git/go-git/v5/internal/pathutil/dotgit.go create mode 100644 vendor/github.com/go-git/go-git/v5/internal/pathutil/hfs.go create mode 100644 vendor/github.com/go-git/go-git/v5/internal/pathutil/ntfs.go create mode 100644 vendor/github.com/go-git/go-git/v5/internal/pathutil/tree.go create mode 100644 vendor/github.com/go-git/go-git/v5/worktree_fs.go delete mode 100644 vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/cache_test.py delete mode 100644 vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/fix_test.py delete mode 100644 vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/generate_cache_test.py delete mode 100644 vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/getxattr.py create mode 100644 vendor/github.com/shamaton/msgpack/v2/.tagpr diff --git a/go.mod b/go.mod index ed7afa2628..47ff25f542 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/open-policy-agent/opa v1.15.2 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d - github.com/opencloud-eu/reva/v2 v2.45.0 + github.com/opencloud-eu/reva/v2 v2.46.0 github.com/opensearch-project/opensearch-go/v4 v4.6.0 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 @@ -205,7 +205,7 @@ require ( github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.9.0 // indirect - github.com/go-git/go-git/v5 v5.19.0 // indirect + github.com/go-git/go-git/v5 v5.19.1 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-kit/log v0.2.1 // indirect @@ -346,7 +346,7 @@ require ( github.com/sergi/go-diff v1.4.0 // indirect github.com/sethvargo/go-diceware v0.5.0 // indirect github.com/sethvargo/go-password v0.3.1 // indirect - github.com/shamaton/msgpack/v2 v2.4.0 // indirect + github.com/shamaton/msgpack/v2 v2.4.1 // indirect github.com/shirou/gopsutil/v4 v4.26.3 // indirect github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect @@ -377,9 +377,9 @@ require ( github.com/yashtewari/glob-intersection v0.2.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect - go.etcd.io/etcd/api/v3 v3.6.10 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.10 // indirect - go.etcd.io/etcd/client/v3 v3.6.10 // indirect + go.etcd.io/etcd/api/v3 v3.6.11 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.11 // indirect + go.etcd.io/etcd/client/v3 v3.6.11 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect diff --git a/go.sum b/go.sum index 36b54c58e6..337f189dc0 100644 --- a/go.sum +++ b/go.sum @@ -386,8 +386,8 @@ github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmm github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.19.0 h1:+WkVUQZSy/F1Gb13udrMKjIM2PrzsNfDKFSfo5tkMtc= -github.com/go-git/go-git/v5 v5.19.0/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= +github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00= +github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -952,8 +952,8 @@ github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIft github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= -github.com/opencloud-eu/reva/v2 v2.45.0 h1:62XctwzrGKjmWng9QI+tACEDQb6R9fG7oHlMPfQkjNs= -github.com/opencloud-eu/reva/v2 v2.45.0/go.mod h1:tUL2X47YxLHrnBDArHrIP73UJliMI0PaY/3tPs31dTM= +github.com/opencloud-eu/reva/v2 v2.46.0 h1:wYUHIHOsLP9vad/K19/52RRfoooeHUGY4BSeowNrbpY= +github.com/opencloud-eu/reva/v2 v2.46.0/go.mod h1:fWAzVpZlJQEY/qeIrd9d3U+LpqY9JGsjJ2dc0a1jCEs= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -1110,8 +1110,8 @@ github.com/sethvargo/go-diceware v0.5.0 h1:exrQ7GpaBo00GqRVM1N8ChXSsi3oS7tjQiIeh github.com/sethvargo/go-diceware v0.5.0/go.mod h1:Lg1SyPS7yQO6BBgTN5r4f2MUDkqGfLWsOjHPY0kA8iw= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= -github.com/shamaton/msgpack/v2 v2.4.0 h1:O5Z08MRmbo0lA9o2xnQ4TXx6teJbPqEurqcCOQ8Oi/4= -github.com/shamaton/msgpack/v2 v2.4.0/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= +github.com/shamaton/msgpack/v2 v2.4.1 h1:JtJ141QoQ3NqgPDsjq2v9VXlaON8SiQOwEaoNLEK/MQ= +github.com/shamaton/msgpack/v2 v2.4.1/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc= @@ -1282,12 +1282,12 @@ github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= -go.etcd.io/etcd/api/v3 v3.6.10 h1:jlwjtELjA8yi2VWpOFH+0w0lGr3K6mVDyn0RDB9aaAY= -go.etcd.io/etcd/api/v3 v3.6.10/go.mod h1:pdV4VeFmvhdNjB4LWRkC8ReLyRBAxUOze3GarMhE2sk= -go.etcd.io/etcd/client/pkg/v3 v3.6.10 h1:tBT7podcPhuVbCVkAEzx8bC5I+aqxfLwBN8/As1arrA= -go.etcd.io/etcd/client/pkg/v3 v3.6.10/go.mod h1:WEy3PpwbbEBVRdh1NVJYsuUe/8eyI21PNJRazeD8z/Y= -go.etcd.io/etcd/client/v3 v3.6.10 h1:J598zJ+C/ZPvImypmq5waj84+bovePrlZERHklf34y0= -go.etcd.io/etcd/client/v3 v3.6.10/go.mod h1:iHhUDUcEwaKs1YFq3MgmI9U4zhTVasp/vgdVbFf1RS8= +go.etcd.io/etcd/api/v3 v3.6.11 h1:XFGTgrJ8nak3kB4NgMG8t7NT+lEeuuvKQAqUHKVgkWQ= +go.etcd.io/etcd/api/v3 v3.6.11/go.mod h1:HYfTh0jyh+uFgp6gMbxJteIDYY97yMuYz85Rnw6Gy9o= +go.etcd.io/etcd/client/pkg/v3 v3.6.11 h1:e41mp315Yn3QMGPmEzCyLsMINgJXTY/dX8kM++1csxU= +go.etcd.io/etcd/client/pkg/v3 v3.6.11/go.mod h1:DysuMe/inqRyC/1tjRR6hReH/VV9Lufs27YKSKBWWJg= +go.etcd.io/etcd/client/v3 v3.6.11 h1:LAByD96VmmeuairkvdAcE0RZnrmGz/q3ceeWePo9bwc= +go.etcd.io/etcd/client/v3 v3.6.11/go.mod h1:vOTDMCo+fGPEClJqcFEFSqZ+8e7WKV7AyqJjX//HR2w= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/vendor/github.com/go-git/go-git/v5/config/config.go b/vendor/github.com/go-git/go-git/v5/config/config.go index 33f6e37d26..3ae6a571e4 100644 --- a/vendor/github.com/go-git/go-git/v5/config/config.go +++ b/vendor/github.com/go-git/go-git/v5/config/config.go @@ -61,6 +61,16 @@ type Config struct { CommentChar string // RepositoryFormatVersion identifies the repository format and layout version. RepositoryFormatVersion format.RepositoryFormatVersion + // ProtectNTFS controls whether NTFS-specific path protections are + // applied (e.g. rejecting .git trailing spaces/periods, alternate + // data streams, 8.3 short names). When unset, defaults to true on + // Windows. + ProtectNTFS OptBool + // ProtectHFS controls whether HFS+-specific path protections are + // applied (e.g. rejecting .git with Unicode zero-width or + // directional characters that HFS+ would normalize away). + // When unset, defaults to true on macOS. + ProtectHFS OptBool } User struct { @@ -266,6 +276,8 @@ const ( repositoryFormatVersionKey = "repositoryformatversion" objectFormat = "objectformat" mirrorKey = "mirror" + protectNTFSKey = "protectNTFS" + protectHFSKey = "protectHFS" // DefaultPackWindow holds the number of previous objects used to // generate deltas. The value 10 is the same used by git command. @@ -309,6 +321,14 @@ func (c *Config) unmarshalCore() { c.Core.Worktree = s.Options.Get(worktreeKey) c.Core.CommentChar = s.Options.Get(commentCharKey) + + if parsed := parseConfigBool(s.Options.Get(protectNTFSKey)); parsed.IsSet() { + c.Core.ProtectNTFS = parsed + } + + if parsed := parseConfigBool(s.Options.Get(protectHFSKey)); parsed.IsSet() { + c.Core.ProtectHFS = parsed + } } func (c *Config) unmarshalUser() { @@ -379,7 +399,8 @@ func unmarshalSubmodules(fc *format.Config, submodules map[string]*Submodule) { m := &Submodule{} m.unmarshal(sub) - if m.Validate() == ErrModuleBadPath { + if err := m.Validate(); errors.Is(err, ErrModuleBadPath) || + errors.Is(err, ErrModuleBadName) { continue } @@ -436,6 +457,14 @@ func (c *Config) marshalCore() { if c.Core.Worktree != "" { s.SetOption(worktreeKey, c.Core.Worktree) } + + if c.Core.ProtectNTFS.IsSet() { + s.SetOption(protectNTFSKey, c.Core.ProtectNTFS.FormatBool()) + } + + if c.Core.ProtectHFS.IsSet() { + s.SetOption(protectHFSKey, c.Core.ProtectHFS.FormatBool()) + } } func (c *Config) marshalExtensions() { diff --git a/vendor/github.com/go-git/go-git/v5/config/modules.go b/vendor/github.com/go-git/go-git/v5/config/modules.go index 1c10aa354e..5fdd838645 100644 --- a/vendor/github.com/go-git/go-git/v5/config/modules.go +++ b/vendor/github.com/go-git/go-git/v5/config/modules.go @@ -3,8 +3,11 @@ package config import ( "bytes" "errors" + "fmt" "regexp" + "strings" + "github.com/go-git/go-git/v5/internal/pathutil" format "github.com/go-git/go-git/v5/plumbing/format/config" ) @@ -12,6 +15,7 @@ var ( ErrModuleEmptyURL = errors.New("module config: empty URL") ErrModuleEmptyPath = errors.New("module config: empty path") ErrModuleBadPath = errors.New("submodule has an invalid path") + ErrModuleBadName = errors.New("ignoring suspicious submodule name") ) var ( @@ -94,6 +98,10 @@ type Submodule struct { // Validate validates the fields and sets the default values. func (m *Submodule) Validate() error { + if err := validSubmoduleName(m.Name); err != nil { + return fmt.Errorf("%w: %q", ErrModuleBadName, m.Name) + } + if m.Path == "" { return ErrModuleEmptyPath } @@ -109,6 +117,50 @@ func (m *Submodule) Validate() error { return nil } +// validSubmoduleName mirrors canonical Git's check_submodule_name in +// submodule-config.c [1]: reject empty names and any name with a ".." +// path component, using both '/' and '\\' as separators so the rule +// is consistent across platforms. The component check is delegated to +// `pathutil.IsHFSDot` and `pathutil.IsNTFSDot` with `.` as the needle, +// which both cover the bare ".." case and reject components that +// resolve to ".." after HFS+ Unicode normalisation (ignored code +// points, e.g. `..`) or NTFS trailing-space/dot/ADS +// canonicalisation (e.g. `.. `, `..::$INDEX_ALLOCATION`). +// `.gitmodules` is attacker-controlled by definition, so both checks +// run unconditionally regardless of host OS. +// +// The additional checks (bare ".", NUL byte, leading or trailing +// separator, drive-letter prefix) close go-git-specific edge cases +// the canonical loop does not exercise: canonical Git treats names +// as opaque C strings, while Go strings carry NULs through and the +// billy filesystem layer is path-aware in ways Git's working storage +// is not. +// +// [1]: https://github.com/git/git/blob/v2.54.0/submodule-config.c#L214-L237 +func validSubmoduleName(name string) error { + if name == "" || name == "." { + return ErrModuleBadName + } + for _, seg := range strings.FieldsFunc(name, isPathSep) { + if pathutil.IsHFSDot(seg, ".") || pathutil.IsNTFSDot(seg, ".", "") { + return ErrModuleBadName + } + } + // go-git-specific defensive checks beyond canonical Git. + if strings.ContainsRune(name, 0) { + return ErrModuleBadName + } + if isPathSep(rune(name[0])) || isPathSep(rune(name[len(name)-1])) { + return ErrModuleBadName + } + if len(name) >= 2 && name[1] == ':' { + return ErrModuleBadName + } + return nil +} + +func isPathSep(r rune) bool { return r == '/' || r == '\\' } + func (m *Submodule) unmarshal(s *format.Subsection) { m.raw = s diff --git a/vendor/github.com/go-git/go-git/v5/config/optbool.go b/vendor/github.com/go-git/go-git/v5/config/optbool.go new file mode 100644 index 0000000000..cb89fbf42b --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/config/optbool.go @@ -0,0 +1,82 @@ +package config + +import ( + "strconv" + "strings" +) + +// OptBool is a tri-state boolean: unset, explicitly false, or explicitly true. +// Its zero value (OptBoolUnset) means the setting was not specified, which +// allows merge logic based on reflect.Value.IsZero to skip unset fields while +// still letting an explicit "false" override a previously set "true". +type OptBool byte + +const ( + // OptBoolUnset indicates the setting was not specified. + OptBoolUnset OptBool = iota + // OptBoolFalse indicates the setting was explicitly set to false. + OptBoolFalse + // OptBoolTrue indicates the setting was explicitly set to true. + OptBoolTrue +) + +// NewOptBool converts a plain bool into an OptBool. +func NewOptBool(v bool) OptBool { + if v { + return OptBoolTrue + } + return OptBoolFalse +} + +// IsTrue returns whether the value is explicitly true. +func (o OptBool) IsTrue() bool { return o == OptBoolTrue } + +// IsSet returns whether the value was explicitly specified (true or false). +func (o OptBool) IsSet() bool { return o != OptBoolUnset } + +func (o OptBool) String() string { + switch o { + case OptBoolTrue: + return "true" + case OptBoolFalse: + return "false" + default: + return "unset" + } +} + +// FormatBool returns the strconv-formatted value. Only meaningful when IsSet. +func (o OptBool) FormatBool() string { + return strconv.FormatBool(o.IsTrue()) +} + +// parseConfigBool mirrors upstream Git's git_parse_maybe_bool: it +// accepts true/yes/on (→ OptBoolTrue) and false/no/off (→ +// OptBoolFalse) case-insensitively, plus any decimal integer (zero +// → OptBoolFalse, non-zero → OptBoolTrue). Empty or otherwise +// unrecognised values return OptBoolUnset, leaving the caller's +// platform default in place. The empty-string handling is the only +// intentional divergence from upstream, which returns false for +// empty: in our unmarshalCore caller, an empty value means the key +// is unset and the platform default should apply. +// +// Reference: upstream Git git_parse_maybe_bool_text at parse.c +// L157-L173 and git_parse_maybe_bool at parse.c L174-L182 in tag +// v2.54.0[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/parse.c#L157-L182 +func parseConfigBool(v string) OptBool { + switch strings.ToLower(v) { + case "true", "yes", "on": + return OptBoolTrue + case "false", "no", "off": + return OptBoolFalse + } + if i, err := strconv.Atoi(v); err == nil { + if i != 0 { + return OptBoolTrue + } + return OptBoolFalse + } + return OptBoolUnset +} diff --git a/vendor/github.com/go-git/go-git/v5/internal/pathutil/dotgit.go b/vendor/github.com/go-git/go-git/v5/internal/pathutil/dotgit.go new file mode 100644 index 0000000000..e50ee9ce5d --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/internal/pathutil/dotgit.go @@ -0,0 +1,21 @@ +package pathutil + +import "strings" + +// IsDotGitName reports whether name is `.git` or its 8.3 NTFS short +// alias `git~1`, case-insensitively. Both are forbidden as path +// components (and as submodule names) because they refer to the +// repository's own metadata directory. +// +// File names that do not conform to the 8.3 format (up to eight +// characters for the basename, three for the file extension) are +// associated with a so-called "short name" on NTFS — at least on +// the `C:` drive by default — which means that `git~1/` is a valid +// way to refer to `.git/`. +func IsDotGitName(name string) bool { + switch strings.ToLower(name) { + case ".git", "git~1": + return true + } + return false +} diff --git a/vendor/github.com/go-git/go-git/v5/internal/pathutil/hfs.go b/vendor/github.com/go-git/go-git/v5/internal/pathutil/hfs.go new file mode 100644 index 0000000000..66fc12f891 --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/internal/pathutil/hfs.go @@ -0,0 +1,99 @@ +package pathutil + +import "unicode" + +// hfsIgnoredCodepoints contains Unicode code points that HFS+ ignores +// during path normalization. A path component containing these +// characters between the bytes of ".git" (or ".gitmodules", etc.) +// will be treated as that name by HFS+, so they have to be filtered +// out before comparison. +// +// See upstream Git utf8.c next_hfs_char in tag v2.54.0[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/utf8.c#L703-L740 +var hfsIgnoredCodepoints = map[rune]struct{}{ + 0x200c: {}, // ZERO WIDTH NON-JOINER + 0x200d: {}, // ZERO WIDTH JOINER + 0x200e: {}, // LEFT-TO-RIGHT MARK + 0x200f: {}, // RIGHT-TO-LEFT MARK + 0x202a: {}, // LEFT-TO-RIGHT EMBEDDING + 0x202b: {}, // RIGHT-TO-LEFT EMBEDDING + 0x202c: {}, // POP DIRECTIONAL FORMATTING + 0x202d: {}, // LEFT-TO-RIGHT OVERRIDE + 0x202e: {}, // RIGHT-TO-LEFT OVERRIDE + 0x206a: {}, // INHIBIT SYMMETRIC SWAPPING + 0x206b: {}, // ACTIVATE SYMMETRIC SWAPPING + 0x206c: {}, // INHIBIT ARABIC FORM SHAPING + 0x206d: {}, // ACTIVATE ARABIC FORM SHAPING + 0x206e: {}, // NATIONAL DIGIT SHAPES + 0x206f: {}, // NOMINAL DIGIT SHAPES + 0xfeff: {}, // ZERO WIDTH NO-BREAK SPACE +} + +// IsHFSDot reports whether part would be treated as "." on an +// HFS+ filesystem after stripping ignored Unicode code points and +// folding ASCII to lower case. The needle is the lowercase ASCII +// suffix without the leading dot (e.g. "git", "gitmodules"). It +// mirrors upstream Git's is_hfs_dot_generic and is the building +// block of IsHFSDotGit / IsHFSDotGitmodules. +// +// Reference: upstream Git utf8.c is_hfs_dot_generic at L741-L774 and +// the dotgit family at L784-L809 in tag v2.54.0[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/utf8.c#L741-L809 +func IsHFSDot(part, needle string) bool { + runes := []rune(part) + i := 0 + + // skip ignored code points, then expect '.' + for i < len(runes) { + if _, ok := hfsIgnoredCodepoints[runes[i]]; !ok { + break + } + i++ + } + if i >= len(runes) || runes[i] != '.' { + return false + } + i++ + + // match needle case-insensitively, skipping ignored code points + for _, expected := range needle { + for i < len(runes) { + if _, ok := hfsIgnoredCodepoints[runes[i]]; !ok { + break + } + i++ + } + if i >= len(runes) { + return false + } + r := runes[i] + if r > 127 { + return false + } + if unicode.ToLower(r) != expected { + return false + } + i++ + } + + // skip trailing ignored code points + for i < len(runes) { + if _, ok := hfsIgnoredCodepoints[runes[i]]; !ok { + break + } + i++ + } + + // must be at end of component + return i == len(runes) +} + +// IsHFSDotGit reports whether part is an HFS+ equivalent of ".git". +func IsHFSDotGit(part string) bool { return IsHFSDot(part, "git") } + +// IsHFSDotGitmodules reports whether part is an HFS+ equivalent of +// ".gitmodules", catching attempts to plant the file via Unicode +// code points that HFS+ would strip during normalisation. +func IsHFSDotGitmodules(part string) bool { return IsHFSDot(part, "gitmodules") } diff --git a/vendor/github.com/go-git/go-git/v5/internal/pathutil/ntfs.go b/vendor/github.com/go-git/go-git/v5/internal/pathutil/ntfs.go new file mode 100644 index 0000000000..2ca6c28348 --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/internal/pathutil/ntfs.go @@ -0,0 +1,187 @@ +package pathutil + +import "strings" + +// IsNTFSDotGit ports upstream Git's is_ntfs_dotgit. It detects path +// components that NTFS would resolve to ".git": the canonical name +// itself and its 8.3 short-name alias "git~1", each followed by any +// number of trailing spaces or periods (which NTFS silently trims) +// and an optional Alternate Data Stream suffix (":"). The +// bare strings ".git" and "git~1" also match, mirroring upstream. +// +// Reference: upstream Git path.c is_ntfs_dotgit at L1415-L1449 +// in tag v2.54.0[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/path.c#L1415-L1449 +func IsNTFSDotGit(part string) bool { + var i int + switch { + case len(part) >= 4 && part[0] == '.' && + asciiToLower(part[1]) == 'g' && + asciiToLower(part[2]) == 'i' && + asciiToLower(part[3]) == 't': + i = 4 + case len(part) >= 5 && + asciiToLower(part[0]) == 'g' && + asciiToLower(part[1]) == 'i' && + asciiToLower(part[2]) == 't' && + part[3] == '~' && part[4] == '1': + i = 5 + default: + return false + } + + for ; i < len(part); i++ { + c := part[i] + if c == ':' { + return true + } + if c != '.' && c != ' ' { + return false + } + } + return true +} + +// WindowsValidPath reports whether part is a valid Windows / NTFS +// path component for the worktree filesystem abstraction. It rejects +// NTFS-disguised variants of `.git` and `git~1` (trailing spaces, +// periods, Alternate Data Streams) and Windows reserved device +// names. Bare `.git` and `git~1` are allowed at this layer; the +// caller decides whether they are permissible at the current path +// position. +func WindowsValidPath(part string) bool { + if IsNTFSDotGit(part) && !IsDotGitName(part) { + return false + } + return !isWindowsReservedName(part) +} + +// windowsReservedNames lists the Windows reserved device names. +// A path component is reserved if its base name (ignoring trailing +// spaces, extensions, and NTFS Alternate Data Streams) matches one of +// these case-insensitively. +// +// See upstream Git compat/mingw.c is_valid_win32_path(). +var windowsReservedNames = []string{ + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", + "CONIN$", "CONOUT$", +} + +func isWindowsReservedName(part string) bool { + for _, name := range windowsReservedNames { + if len(part) < len(name) { + continue + } + if !strings.EqualFold(part[:len(name)], name) { + continue + } + // Exact match or followed by space, dot, colon (ADS), or separator. + if len(part) == len(name) { + return true + } + switch part[len(name)] { + case ' ', '.', ':': + return true + } + } + return false +} + +// IsNTFSDot ports upstream Git's is_ntfs_dot_generic. It detects NTFS +// path-component variants of a dotfile name that attackers can use to +// bypass case-insensitive comparisons against the canonical name on +// Windows. The dotgit parameter is the lowercase name without the +// leading dot (e.g. "gitmodules"); shortnamePrefix is the canonical +// 6-character NTFS short-name prefix used as a fall-back match +// (e.g. "gi7eba" for ".gitmodules"). +// +// Reference: upstream Git path.c is_ntfs_dot_generic at L1451-L1507 +// in tag v2.54.0[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/path.c#L1451-L1507 +func IsNTFSDot(name, dotgit, shortnamePrefix string) bool { + // onlySpacesAndPeriods returns true when the suffix from start + // onwards consists only of trailing spaces and periods, possibly + // terminated by a NTFS Alternate Data Stream colon. Mirrors the + // only_spaces_and_periods label in upstream's is_ntfs_dot_generic. + onlySpacesAndPeriods := func(start int) bool { + for i := start; i < len(name); i++ { + c := name[i] + if c == ':' { + return true + } + if c != ' ' && c != '.' { + return false + } + } + return true + } + + // Pattern 1: "." prefix + trailing spaces / periods / ADS. + if len(name) >= len(dotgit)+1 && name[0] == '.' && + strings.EqualFold(name[1:1+len(dotgit)], dotgit) { + if onlySpacesAndPeriods(len(dotgit) + 1) { + return true + } + } + + // Pattern 2: standard NTFS short name ~[1-4]. + if len(dotgit) >= 6 && len(name) >= 8 && + strings.EqualFold(name[:6], dotgit[:6]) && + name[6] == '~' && name[7] >= '1' && name[7] <= '4' { + if onlySpacesAndPeriods(8) { + return true + } + } + + // Pattern 3: fall-back NTFS short name keyed by shortnamePrefix. + if len(shortnamePrefix) < 6 || len(name) < 8 { + return false + } + sawTilde := false + i := 0 + for i < 8 { + c := name[i] + switch { + case sawTilde: + if c < '0' || c > '9' { + return false + } + case c == '~': + i++ + if i >= len(name) || name[i] < '1' || name[i] > '9' { + return false + } + sawTilde = true + case i >= 6: + return false + case c&0x80 != 0: + return false + default: + if asciiToLower(c) != shortnamePrefix[i] { + return false + } + } + i++ + } + return onlySpacesAndPeriods(8) +} + +// IsNTFSDotGitmodules reports whether part is an NTFS-equivalent of +// ".gitmodules" — the file name (or any of its variants that NTFS +// would resolve to it) that attackers can use to plant submodule +// configuration disguised as a symlink. The 6-character canonical +// short-name prefix "gi7eba" mirrors upstream Git's is_ntfs_dotgitmodules. +func IsNTFSDotGitmodules(part string) bool { + return IsNTFSDot(part, "gitmodules", "gi7eba") +} + +func asciiToLower(c byte) byte { + if c >= 'A' && c <= 'Z' { + return c + ('a' - 'A') + } + return c +} diff --git a/vendor/github.com/go-git/go-git/v5/internal/pathutil/tree.go b/vendor/github.com/go-git/go-git/v5/internal/pathutil/tree.go new file mode 100644 index 0000000000..e610cd4a8b --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/internal/pathutil/tree.go @@ -0,0 +1,66 @@ +package pathutil + +import ( + "fmt" + "path/filepath" + "strings" +) + +// ErrInvalidPath is returned by ValidTreePath when its argument is +// not a safe path to materialise into the worktree. +var ErrInvalidPath = fmt.Errorf("invalid path") + +// ValidTreePath rejects path strings that, if materialised into a +// worktree, would let an attacker-controlled tree entry escape the +// worktree or rewrite repository metadata. It rejects: +// +// - control characters (< 0x20, 0x7f); +// - empty paths and "." / ".." components; +// - Windows volume name prefixes (e.g. C:); +// - .git, its 8.3 NTFS short-name git~1, plus their HFS+ and NTFS +// variants — at every position, not just the root. +// +// HFS+/NTFS variants of `.git` are always rejected at this layer +// regardless of runtime config: tree paths are canonical UTF-8 with +// no zero-width characters or NTFS short-name forms, so an entry +// that looks like a disguised `.git` is suspicious anywhere. Windows +// reserved device names (CON, NUL, etc.) are not policed here — they +// are legitimate filenames on non-Windows filesystems and upstream +// Git accepts them. The wrapper layer (validPath in package git) +// rejects them at materialisation time when core.protectNTFS is on. +// +// Mirrors upstream Git's verify_path_internal at read-cache.c#L987 +// in tag v2.54.0[1] with protect_hfs / protect_ntfs treated as +// always-on for `.git`-disguise detection (tree paths are not +// application-supplied) and is_valid_win32_path left to the wrapper. +// +// [1]: https://github.com/git/git/blob/v2.54.0/read-cache.c#L987 +func ValidTreePath(p string) error { + for i := 0; i < len(p); i++ { + if p[i] < 0x20 || p[i] == 0x7f { + return fmt.Errorf("%w %q: contains control character", ErrInvalidPath, p) + } + } + + parts := strings.FieldsFunc(p, func(r rune) bool { return r == '\\' || r == '/' }) + if len(parts) == 0 { + return fmt.Errorf("%w: %q", ErrInvalidPath, p) + } + + // Volume names are not supported, in both formats: \\ and :. + if vol := filepath.VolumeName(p); vol != "" { + return fmt.Errorf("%w: %q", ErrInvalidPath, p) + } + + for _, part := range parts { + if part == "." || part == ".." { + return fmt.Errorf("%w %q: cannot use %q", ErrInvalidPath, p, part) + } + + if IsDotGitName(part) || IsHFSDotGit(part) || IsNTFSDotGit(part) { + return fmt.Errorf("%w component: %q", ErrInvalidPath, p) + } + } + + return nil +} diff --git a/vendor/github.com/go-git/go-git/v5/internal/url/url.go b/vendor/github.com/go-git/go-git/v5/internal/url/url.go index 2662448693..e40947c90c 100644 --- a/vendor/github.com/go-git/go-git/v5/internal/url/url.go +++ b/vendor/github.com/go-git/go-git/v5/internal/url/url.go @@ -2,12 +2,14 @@ package url import ( "regexp" + "runtime" + "strings" ) var ( isSchemeRegExp = regexp.MustCompile(`^[^:]+://`) - // Ref: https://github.com/git/git/blob/master/Documentation/urls.txt#L37 + // Ref: https://github.com/git/git/blob/v2.54.0/Documentation/urls.adoc#L41-L48 scpLikeUrlRegExp = regexp.MustCompile(`^(?:(?P[^@]+)@)?(?P[^:\s]+):(?:(?P[0-9]{1,5}):)?(?P[^\\].*)$`) ) @@ -20,7 +22,38 @@ func MatchesScheme(url string) bool { // MatchesScpLike returns true if the given string matches an SCP-like // format scheme. func MatchesScpLike(url string) bool { - return scpLikeUrlRegExp.MatchString(url) + if !scpLikeUrlRegExp.MatchString(url) { + return false + } + // Mirror canonical Git's url_is_local_not_ssh in connect.c[1] for + // the cases the regex above cannot disambiguate by itself: a URL + // is treated as a local path (not SCP-style SSH) when a `/` + // precedes the first `:` (e.g. `./relative:path`, + // `/abs/with:colon/file`), or — on Windows only — when it has a + // DOS drive prefix like `C:foo` where the host is a single + // ASCII letter. + // + // [1]: https://github.com/git/git/blob/v2.54.0/connect.c#L710-L716 + if before, _, _ := strings.Cut(url, ":"); strings.Contains(before, "/") { + return false + } + if runtime.GOOS == "windows" && hasDosDrivePrefix(url) { + return false + } + return true +} + +// hasDosDrivePrefix reports whether s begins with `:` (a +// Windows drive prefix such as `C:` or `c:`). Mirrors canonical Git's +// win32_has_dos_drive_prefix[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/compat/win32/path-utils.c#L20-L29 +func hasDosDrivePrefix(s string) bool { + if len(s) < 2 || s[1] != ':' { + return false + } + c := s[0] + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') } // FindScpLikeComponents returns the user, host, port and path of the diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go index 9e006a7262..825fad9a58 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "io" + "io/fs" "github.com/go-git/go-git/v5/plumbing/hash" "github.com/go-git/go-git/v5/utils/binary" @@ -25,35 +26,88 @@ const ( objectIDLength = hash.Size ) +// Byte sizes of the idx v2 layout elements, used by the size formula +// in [validateIdxV2Size]. See [gitformat-pack] for the canonical +// layout. +// +// [gitformat-pack]: https://git-scm.com/docs/gitformat-pack +const ( + headerLen = 8 // magic + version + fanoutLen = fanout * 4 // uint32 per bucket + crc32Len = 4 // CRC32 per object + offset32Len = 4 // 32-bit offset per object + offset64Len = 8 // 64-bit overflow offset + trailerHashes = 2 // pack checksum + idx checksum, each hashsz +) + +// statInput is the optional shape the [Decoder] probes for at the +// start of [Decoder.Decode] to learn the on-disk length of the idx +// blob, which it uses to validate the canonical-Git size formula +// before any allocations driven by the fanout table. Callers that +// pass an [*os.File] or a `billy.File` backed by an `*os.File` +// (the production call sites in `storage/filesystem`) satisfy it +// directly; arbitrary [io.Reader]s do not, and decode for them +// retains the pre-existing behaviour of erroring out at the +// truncated-payload boundary instead. +// +// The interface is intentionally unexported so the public +// [NewDecoder] signature stays compatible with v5. +type statInput interface { + Stat() (fs.FileInfo, error) +} + // Decoder reads and decodes idx files from an input stream. type Decoder struct { io.Reader - h hash.Hash + src io.Reader + h hash.Hash } // NewDecoder builds a new idx stream decoder, that reads from r. func NewDecoder(r io.Reader) *Decoder { h := hash.New(crypto.SHA1) tr := io.TeeReader(r, h) - return &Decoder{tr, h} + return &Decoder{tr, r, h} } // Decode reads from the stream and decode the content into the MemoryIndex struct. func (d *Decoder) Decode(idx *MemoryIndex) error { + idxSize := int64(-1) + if in, ok := d.src.(statInput); ok { + fi, err := in.Stat() + if err != nil { + return fmt.Errorf("%w: stat input: %w", ErrMalformedIdxFile, err) + } + idxSize = fi.Size() + } + if err := validateHeader(d); err != nil { return err } - flow := []func(*MemoryIndex, io.Reader) error{ + headerFlow := []func(*MemoryIndex, io.Reader) error{ readVersion, readFanout, + } + for _, f := range headerFlow { + if err := f(idx, d); err != nil { + return err + } + } + + if idxSize >= 0 { + if err := validateIdxV2Size(idx, idxSize); err != nil { + return err + } + } + + bodyFlow := []func(*MemoryIndex, io.Reader) error{ readObjectNames, readCRC32, readOffsets, readPackChecksum, } - - for _, f := range flow { + for _, f := range bodyFlow { if err := f(idx, d); err != nil { return err } @@ -199,3 +253,103 @@ func readIdxChecksum(idx *MemoryIndex, r io.Reader) error { return nil } + +// validateIdxV2Size enforces the size formula used by canonical Git +// load_idx for idx v2 files: the on-disk length must lie within +// [minSize, maxSize] where +// +// perObject = hashsz + crc32Len + offset32Len +// minSize = headerLen + fanoutLen + trailerHashes*hashsz + nr*perObject +// maxSize = minSize + (nr-1)*offset64Len when nr > 0 +// +// with nr taken from the last fanout entry and hashsz fixed at +// [objectIDLength] (SHA-1 in v5). Multiplications use a self-checking +// overflow guard so inputs whose claimed object count overflows the +// formula are rejected rather than wrapping into a smaller value. +func validateIdxV2Size(idx *MemoryIndex, idxSize int64) error { + nr := int64(idx.Fanout[fanout-1]) + hashsz := int64(objectIDLength) + + minSize := minIdxV2Size(nr, hashsz) + maxSize := maxIdxV2Size(nr, hashsz) + if minSize < 0 || maxSize < 0 { + return fmt.Errorf("%w: object count %d is inconsistent with file size", ErrMalformedIdxFile, nr) + } + + if idxSize < minSize || idxSize > maxSize { + return fmt.Errorf("%w: file size %d is inconsistent with object count %d", ErrMalformedIdxFile, idxSize, nr) + } + return nil +} + +// minIdxV2Size returns the minimum on-disk size of an idx v2 file +// holding nr objects with the given hash size, mirroring the +// computation in canonical Git load_idx. Returns -1 when any +// intermediate multiplication or addition would overflow int64. +func minIdxV2Size(nr, hashsz int64) int64 { + perObject := hashsz + crc32Len + offset32Len + fixed := int64(headerLen+fanoutLen) + trailerHashes*hashsz + + objects, ok := mulInt64(nr, perObject) + if !ok { + return -1 + } + sum, ok := addInt64(fixed, objects) + if !ok { + return -1 + } + return sum +} + +// maxIdxV2Size returns the maximum on-disk size of an idx v2 file +// holding nr objects with the given hash size, mirroring the +// computation in canonical Git load_idx. Returns -1 on overflow. +func maxIdxV2Size(nr, hashsz int64) int64 { + minSize := minIdxV2Size(nr, hashsz) + if minSize < 0 { + return -1 + } + if nr == 0 { + return minSize + } + overflow, ok := mulInt64(nr-1, offset64Len) + if !ok { + return -1 + } + sum, ok := addInt64(minSize, overflow) + if !ok { + return -1 + } + return sum +} + +// mulInt64 returns a*b and whether the result fits in an int64 without +// overflow. Negative operands or overflow yield ok=false. The overflow +// check uses the standard self-inverse identity: a*b/b == a only when +// the multiplication did not wrap. +func mulInt64(a, b int64) (int64, bool) { + if a < 0 || b < 0 { + return 0, false + } + if a == 0 || b == 0 { + return 0, true + } + c := a * b + if c/b != a { + return 0, false + } + return c, true +} + +// addInt64 returns a+b and whether the result fits in an int64 without +// overflow. Negative operands or overflow yield ok=false. +func addInt64(a, b int64) (int64, bool) { + if a < 0 || b < 0 { + return 0, false + } + c := a + b + if c < a { + return 0, false + } + return c, true +} diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go index 136c3e2aca..f068c25e57 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go @@ -2,6 +2,7 @@ package idxfile import ( "bytes" + "fmt" "io" "sort" "sync" @@ -126,7 +127,10 @@ func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) { return 0, plumbing.ErrObjectNotFound } - offset := idx.getOffset(k, i) + offset, err := idx.getOffset(k, i) + if err != nil { + return 0, err + } // Save the offset for reverse lookup idx.mu.Lock() @@ -141,17 +145,19 @@ func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) { const isO64Mask = uint64(1) << 31 -func (idx *MemoryIndex) getOffset(firstLevel, secondLevel int) uint64 { +func (idx *MemoryIndex) getOffset(firstLevel, secondLevel int) (uint64, error) { offset := secondLevel << 2 ofs := encbin.BigEndian.Uint32(idx.Offset32[firstLevel][offset : offset+4]) if (uint64(ofs) & isO64Mask) != 0 { offset := 8 * (uint64(ofs) & ^isO64Mask) - n := encbin.BigEndian.Uint64(idx.Offset64[offset : offset+8]) - return n + if l := uint64(len(idx.Offset64)); l < 8 || offset > l-8 { + return 0, fmt.Errorf("%w: offset64 index out of range", ErrMalformedIdxFile) + } + return encbin.BigEndian.Uint64(idx.Offset64[offset : offset+8]), nil } - return uint64(ofs) + return uint64(ofs), nil } // FindCRC32 implements the Index interface. @@ -209,8 +215,11 @@ func (idx *MemoryIndex) genOffsetHash() error { mappedFirstLevel := idx.FanoutMapping[firstLevel] for secondLevel := uint32(0); i < fanoutValue; i++ { copy(hash[:], idx.Names[mappedFirstLevel][secondLevel*objectIDLength:]) - offset := int64(idx.getOffset(mappedFirstLevel, int(secondLevel))) - offsetHash[offset] = hash + off, err := idx.getOffset(mappedFirstLevel, int(secondLevel)) + if err != nil { + return err + } + offsetHash[int64(off)] = hash secondLevel++ } } @@ -291,7 +300,11 @@ func (i *idxfileEntryIter) Next() (*Entry, error) { mappedFirstLevel := i.idx.FanoutMapping[i.firstLevel] entry := new(Entry) copy(entry.Hash[:], i.idx.Names[mappedFirstLevel][i.secondLevel*objectIDLength:]) - entry.Offset = i.idx.getOffset(mappedFirstLevel, i.secondLevel) + var err error + entry.Offset, err = i.idx.getOffset(mappedFirstLevel, i.secondLevel) + if err != nil { + return nil, err + } entry.CRC32 = i.idx.getCRC32(mappedFirstLevel, i.secondLevel) i.secondLevel++ diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/objfile/reader.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/objfile/reader.go index 621883a67d..f9842ed9af 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/objfile/reader.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/objfile/reader.go @@ -11,9 +11,10 @@ import ( ) var ( - ErrClosed = errors.New("objfile: already closed") - ErrHeader = errors.New("objfile: invalid header") - ErrNegativeSize = errors.New("objfile: negative object size") + ErrClosed = errors.New("objfile: already closed") + ErrHeader = errors.New("objfile: invalid header") + ErrHeaderNotRead = errors.New("objfile: Header must be called before Read") + ErrNegativeSize = errors.New("objfile: negative object size") ) // Reader reads and decodes compressed objfile data from a provided io.Reader. @@ -100,12 +101,23 @@ func (r *Reader) prepareForRead(t plumbing.ObjectType, size int64) { // // If Read encounters the end of the data stream it will return err == io.EOF, // either in the current call if n > 0 or in a subsequent call. +// +// Read returns ErrHeaderNotRead if Header has not been called successfully. func (r *Reader) Read(p []byte) (n int, err error) { + if r.multi == nil { + return 0, ErrHeaderNotRead + } return r.multi.Read(p) } // Hash returns the hash of the object data stream that has been read so far. +// It returns the zero plumbing.Hash if Header has not been called +// successfully — guarding against the nil hasher that prepareForRead has +// not yet allocated. func (r *Reader) Hash() plumbing.Hash { + if r.multi == nil { + return plumbing.ZeroHash + } return r.hasher.Sum() } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go index 8898e5830e..a24b63b416 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/diff_delta.go @@ -19,9 +19,6 @@ const ( // https://github.com/git/git/blob/f7466e94375b3be27f229c78873f0acf8301c0a5/diff-delta.c#L428 // Max size of a copy operation (64KB). maxCopySize = 64 * 1024 - - // Min size of a copy operation. - minCopySize = 4 ) // GetDelta returns an EncodedObject of type OFSDeltaObject. Base and Target object, diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/fsobject.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/fsobject.go index 238339daf8..93a6fafca6 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/fsobject.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/fsobject.go @@ -78,7 +78,13 @@ func (o *FSObject) Reader() (io.ReadCloser, error) { _ = f.Close() return nil, err } - return ioutil.NewReadCloserWithCloser(r, f.Close), nil + // Cap the lazy stream at the resolved object size: well-formed + // content reaches EOF inside the bound, an inflated stream that + // runs past surfaces ErrInflatedSizeMismatch on the byte just + // past the limit. For delta-resolved objects o.size is the + // expanded size, which is what the caller is reading here. + bounded := newBoundedReadCloser(r, o.size) + return ioutil.NewReadCloserWithCloser(bounded, f.Close), nil } r, err := p.getObjectContent(o.offset) if err != nil { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go index 6852702257..f7fb958f9f 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/packfile.go @@ -126,11 +126,17 @@ func (p *Packfile) nextObjectHeader() (*ObjectHeader, error) { return h, err } -func (p *Packfile) getDeltaObjectSize(buf *bytes.Buffer) int64 { +func (p *Packfile) getDeltaObjectSize(buf *bytes.Buffer) (int64, error) { delta := buf.Bytes() - _, delta = decodeLEB128(delta) // skip src size - sz, _ := decodeLEB128(delta) - return int64(sz) + _, delta, err := decodeLEB128(delta) // skip src size + if err != nil { + return 0, err + } + sz, _, err := decodeLEB128(delta) + if err != nil { + return 0, err + } + return int64(sz), nil } func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) { @@ -145,7 +151,7 @@ func (p *Packfile) getObjectSize(h *ObjectHeader) (int64, error) { return 0, err } - return p.getDeltaObjectSize(buf), nil + return p.getDeltaObjectSize(buf) default: return 0, ErrInvalidObject.AddDetails("type %q", h.Type) } @@ -233,7 +239,10 @@ func (p *Packfile) getNextObject(h *ObjectHeader, hash plumbing.Hash) (plumbing. return nil, err } - size = p.getDeltaObjectSize(buf) + size, err = p.getDeltaObjectSize(buf) + if err != nil { + return nil, err + } if size <= smallObjectThreshold { var obj = new(plumbing.MemoryObject) obj.SetSize(size) diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go index 2659c27e5f..7774d2dc1b 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go @@ -26,6 +26,45 @@ var ( ErrDeltaNotCached = errors.New("delta could not be found in cache") ) +// maxObjectPreallocBytes caps the up-front size hint passed to +// bytes.Buffer.Grow when staging an object's contents, so a malformed length +// cannot trigger a huge or out-of-range allocation. The buffer still grows +// dynamically as data is written; this is purely a hint cap. +const maxObjectPreallocBytes = 1 << 30 // 1 GiB + +// maxObjectsPrealloc caps the up-front capacity reserved from the pack's +// declared object count, so a header advertising an absurd quantity cannot +// trigger a multi-gigabyte allocation. The slice and maps still grow +// organically beyond this hint. +const maxObjectsPrealloc = 1 << 16 // 64 Ki entries + +// Match upstream Git's pack depth ceiling: pack-objects.h OE_DEPTH_BITS, +// enforced in builtin/pack-objects.c as (1 << OE_DEPTH_BITS) - 1. +const maxDeltaChainDepth = 4095 + +// growHint returns a non-negative int64 size, clamped to a sane upper bound, +// suitable for passing to bytes.Buffer.Grow. +func growHint(n int64) int { + switch { + case n <= 0: + return 0 + case n > maxObjectPreallocBytes: + return maxObjectPreallocBytes + default: + return int(n) + } +} + +// objectsHint returns a non-negative count, clamped to maxObjectsPrealloc, +// suitable for passing to make() as the capacity hint for slices or maps +// sized from a pack's declared object count. +func objectsHint(n uint32) int { + if n > maxObjectsPrealloc { + return maxObjectsPrealloc + } + return int(n) +} + // Observer interface is implemented by index encoders. type Observer interface { // OnHeader is called when a new packfile is opened. @@ -166,9 +205,10 @@ func (p *Parser) init() error { } p.count = c - p.oiByHash = make(map[plumbing.Hash]*objectInfo, p.count) - p.oiByOffset = make(map[int64]*objectInfo, p.count) - p.oi = make([]*objectInfo, p.count) + hint := objectsHint(p.count) + p.oiByHash = make(map[plumbing.Hash]*objectInfo, hint) + p.oiByOffset = make(map[int64]*objectInfo, hint) + p.oi = make([]*objectInfo, 0, hint) return nil } @@ -261,7 +301,7 @@ func (p *Parser) indexObjects() error { } if delta && !p.scanner.IsSeekable { buf.Reset() - buf.Grow(int(oh.Length)) + buf.Grow(growHint(oh.Length)) writers = append(writers, buf) } @@ -306,7 +346,7 @@ func (p *Parser) indexObjects() error { } p.oiByOffset[oh.Offset] = ota - p.oi[i] = ota + p.oi = append(p.oi, ota) } return nil @@ -317,8 +357,12 @@ func (p *Parser) resolveDeltas() error { defer sync.PutBytesBuffer(buf) for _, obj := range p.oi { + if err := checkDeltaChainDepth(obj); err != nil { + return err + } + buf.Reset() - buf.Grow(int(obj.Length)) + buf.Grow(growHint(obj.Length)) err := p.get(obj, buf) if err != nil { return err @@ -337,6 +381,9 @@ func (p *Parser) resolveDeltas() error { // create it once and reuse across all children. r := bytes.NewReader(buf.Bytes()) for _, child := range obj.Children { + if err := checkDeltaChainDepth(child); err != nil { + return err + } // Even though we are discarding the output, we still need to read it to // so that the scanner can advance to the next object, and the SHA1 can be // calculated. @@ -356,6 +403,17 @@ func (p *Parser) resolveDeltas() error { return nil } +func checkDeltaChainDepth(o *objectInfo) error { + var depth int + for current := o; current != nil && current.DiskType.IsDelta(); current = current.Parent { + depth++ + if depth > maxDeltaChainDepth { + return fmt.Errorf("%w: delta chain depth exceeds %d", ErrMalformedPackFile, maxDeltaChainDepth) + } + } + return nil +} + func (p *Parser) resolveExternalRef(o *objectInfo) { if ref, ok := p.oiByHash[o.SHA1]; ok && ref.ExternalRef { p.oiByHash[o.SHA1] = o @@ -405,7 +463,7 @@ func (p *Parser) get(o *objectInfo, buf *bytes.Buffer) (err error) { if o.DiskType.IsDelta() { b := sync.GetBytesBuffer() defer sync.PutBytesBuffer(b) - buf.Grow(int(o.Length)) + buf.Grow(growHint(o.Length)) err := p.get(o.Parent, b) if err != nil { return err diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go index a9c6b9b56f..4bcb491141 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/patch_delta.go @@ -31,10 +31,15 @@ const ( // premptively made available for a patch operation. maxPatchPreemptionSize uint = 65536 - // minDeltaSize defines the smallest size for a delta. - minDeltaSize = 4 + // minDeltaSize is the smallest valid delta: a 1-byte srcSz LEB128 + // header followed by a 1-byte targetSz LEB128 header (the + // shortest case being targetSz=0 with no operations). + minDeltaSize = 2 ) +// uintBits is the bit width of uint on the current platform (32 or 64). +const uintBits = 32 << (^uint(0) >> 63) + type offset struct { mask byte shift uint @@ -142,7 +147,7 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo baseBuf := bufio.NewReader(baseRd) basePos := uint(0) - for { + for remainingTargetSz > 0 { cmd, err := deltaBuf.ReadByte() if err == io.EOF { _ = dstWr.CloseWithError(ErrInvalidDelta) @@ -166,9 +171,9 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo return } - if invalidSize(sz, targetSz) || + if invalidSize(sz, remainingTargetSz) || invalidOffsetSize(offset, sz, srcSz) { - _ = dstWr.Close() + _ = dstWr.CloseWithError(ErrInvalidDelta) return } @@ -210,7 +215,7 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo case isCopyFromDelta(cmd): sz := uint(cmd) // cmd is the size itself - if invalidSize(sz, targetSz) { + if invalidSize(sz, remainingTargetSz) { _ = dstWr.CloseWithError(ErrInvalidDelta) return } @@ -225,40 +230,48 @@ func ReaderFromDelta(base plumbing.EncodedObject, deltaRC io.Reader) (io.ReadClo _ = dstWr.CloseWithError(ErrDeltaCmd) return } - - if remainingTargetSz <= 0 { - _ = dstWr.Close() - return - } } + + // Mirror upstream's `data != top` post-loop check: every byte + // of the delta payload must be consumed. + if _, err := deltaBuf.ReadByte(); err == nil { + _ = dstWr.CloseWithError(ErrInvalidDelta) + return + } else if err != io.EOF { + _ = dstWr.CloseWithError(err) + return + } + + _ = dstWr.Close() }() return dstRd, nil } func patchDelta(dst *bytes.Buffer, src, delta []byte) error { - if len(delta) < minCopySize { - return ErrInvalidDelta + srcSz, delta, err := decodeLEB128(delta) + if err != nil { + return fmt.Errorf("%w: %w", ErrInvalidDelta, err) } - - srcSz, delta := decodeLEB128(delta) if srcSz != uint(len(src)) { return ErrInvalidDelta } - targetSz, delta := decodeLEB128(delta) + targetSz, delta, err := decodeLEB128(delta) + if err != nil { + return fmt.Errorf("%w: %w", ErrInvalidDelta, err) + } remainingTargetSz := targetSz - var cmd byte - growSz := min(targetSz, maxPatchPreemptionSize) dst.Grow(int(growSz)) - for { + + for remainingTargetSz > 0 { if len(delta) == 0 { return ErrInvalidDelta } - cmd = delta[0] + cmd := delta[0] delta = delta[1:] switch { @@ -275,16 +288,16 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { return err } - if invalidSize(sz, targetSz) || + if invalidSize(sz, remainingTargetSz) || invalidOffsetSize(offset, sz, srcSz) { - break + return ErrInvalidDelta } dst.Write(src[offset : offset+sz]) remainingTargetSz -= sz case isCopyFromDelta(cmd): sz := uint(cmd) // cmd is the size itself - if invalidSize(sz, targetSz) { + if invalidSize(sz, remainingTargetSz) { return ErrInvalidDelta } @@ -299,10 +312,12 @@ func patchDelta(dst *bytes.Buffer, src, delta []byte) error { default: return ErrDeltaCmd } + } - if remainingTargetSz <= 0 { - break - } + // Mirror upstream's `data != top` post-loop check: every byte of + // the delta payload must be consumed. + if len(delta) != 0 { + return ErrInvalidDelta } return nil @@ -354,7 +369,7 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader, baselr := io.LimitReader(sr, 0).(*io.LimitedReader) deltalr := io.LimitReader(deltaBuf, 0).(*io.LimitedReader) - for { + for remainingTargetSz > 0 { buf := *bufp cmd, err := deltaBuf.ReadByte() if err == io.EOF { @@ -374,9 +389,9 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader, return 0, plumbing.ZeroHash, err } - if invalidSize(sz, targetSz) || + if invalidSize(sz, remainingTargetSz) || invalidOffsetSize(offset, sz, srcSz) { - return 0, plumbing.ZeroHash, err + return 0, plumbing.ZeroHash, ErrInvalidDelta } if _, err := sr.Seek(int64(offset), io.SeekStart); err != nil { @@ -389,7 +404,7 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader, remainingTargetSz -= sz } else if isCopyFromDelta(cmd) { sz := uint(cmd) // cmd is the size itself - if invalidSize(sz, targetSz) { + if invalidSize(sz, remainingTargetSz) { return 0, plumbing.ZeroHash, ErrInvalidDelta } deltalr.N = int64(sz) @@ -399,30 +414,41 @@ func patchDeltaWriter(dst io.Writer, base io.ReaderAt, delta io.Reader, remainingTargetSz -= sz } else { - return 0, plumbing.ZeroHash, err - } - if remainingTargetSz <= 0 { - break + return 0, plumbing.ZeroHash, ErrDeltaCmd } } + // Mirror upstream's `data != top` post-loop check: every byte of + // the delta payload must be consumed. + if _, err := deltaBuf.ReadByte(); err == nil { + return 0, plumbing.ZeroHash, ErrInvalidDelta + } else if err != io.EOF { + return 0, plumbing.ZeroHash, err + } + return targetSz, hasher.Sum(), nil } // Decodes a number encoded as an unsigned LEB128 at the start of some -// binary data and returns the decoded number and the rest of the -// stream. +// binary data and returns the decoded number, the rest of the stream, +// and an error if the encoded value does not fit in a uint. // // This must be called twice on the delta data buffer, first to get the // expected source buffer size, and again to get the target buffer size. -func decodeLEB128(input []byte) (uint, []byte) { +func decodeLEB128(input []byte) (uint, []byte, error) { if len(input) == 0 { - return 0, input + return 0, input, nil } var num, sz uint var b byte for { + // A continuation byte at shift > uintBits-7 cannot contribute + // without overflowing the accumulator. + if sz*7 > uintBits-7 { + return 0, input, ErrLengthOverflow + } + b = input[sz] num |= (uint(b) & payload) << (sz * 7) // concats 7 bits chunks sz++ @@ -432,12 +458,16 @@ func decodeLEB128(input []byte) (uint, []byte) { } } - return num, input[sz:] + return num, input[sz:], nil } func decodeLEB128ByteReader(input io.ByteReader) (uint, error) { var num, sz uint for { + if sz*7 > uintBits-7 { + return 0, ErrLengthOverflow + } + b, err := input.ReadByte() if err != nil { return 0, err @@ -529,8 +559,9 @@ func decodeSize(cmd byte, delta []byte) (uint, []byte, error) { return sz, delta, nil } -func invalidSize(sz, targetSz uint) bool { - return sz > targetSz +// invalidSize reports whether sz exceeds the remaining target size. +func invalidSize(sz, remaining uint) bool { + return sz > remaining } func invalidOffsetSize(offset, sz, srcSz uint) bool { diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/scanner.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/scanner.go index 8318aae40d..6d2907ecd3 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/scanner.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/scanner.go @@ -29,8 +29,100 @@ var ( ErrSeekNotSupported = NewError("not seek support") // ErrMalformedPackFile is returned by the parser when the pack file is corrupted. ErrMalformedPackFile = errors.New("malformed PACK file") + // ErrLengthOverflow is returned when a variable-length integer would not + // fit into its accumulator because the input declares more continuation + // bytes than the type can hold. + ErrLengthOverflow = errors.New("variable-length integer overflow") + // ErrInflatedSizeMismatch is returned when a packfile object inflates to + // more bytes than the size declared in its object header. A well-formed + // packfile never produces more data than the declared size; exceeding it + // indicates a structurally invalid entry. + ErrInflatedSizeMismatch = errors.New("packfile: inflated object exceeds declared size") ) +// boundedWriter passes writes through to w up to limit bytes total, then +// returns ErrInflatedSizeMismatch. It is used to enforce that a packfile +// object's inflated length does not exceed the size declared in its header. +type boundedWriter struct { + w io.Writer + limit int64 + n int64 +} + +// Write forwards p to the underlying writer while keeping the running total +// at or below limit. On overrun it forwards the legal prefix and reports +// the number of bytes actually consumed alongside ErrInflatedSizeMismatch, +// matching the contract in io.Writer. A write error from the underlying +// writer during overrun-handling is joined with ErrInflatedSizeMismatch so +// it is not silently dropped. +func (b *boundedWriter) Write(p []byte) (int, error) { + if b.n+int64(len(p)) > b.limit { + remain := int(b.limit - b.n) + err := error(ErrInflatedSizeMismatch) + if remain > 0 { + n, werr := b.w.Write(p[:remain]) + b.n += int64(n) + if werr != nil { + err = errors.Join(ErrInflatedSizeMismatch, werr) + } + return n, err + } + return 0, err + } + n, err := b.w.Write(p) + b.n += int64(n) + return n, err +} + +// boundedReadCloser wraps a ReadCloser and reports ErrInflatedSizeMismatch +// once more than limit bytes have been read. It is used by the on-demand +// object reader returned from FSObject.Reader so that a lazy Read of a +// packfile object cannot stream past its declared inflated size. +// +// The implementation builds on io.LimitedReader with the standard +// overrun-detection trick: request limit+1 bytes from the underlying so +// that the moment the sentinel byte materializes (LimitedReader.N drops +// to zero) we know the source produced more than limit bytes. +type boundedReadCloser struct { + lr io.LimitedReader + closer io.Closer + overrun bool +} + +// newBoundedReadCloser wraps rc so that the cumulative bytes returned from +// Read never exceed limit. The first call that would have returned a byte +// past limit instead returns ErrInflatedSizeMismatch; subsequent calls +// keep returning the same error. A negative limit is treated as zero, so +// the first byte produced by rc surfaces ErrInflatedSizeMismatch. +func newBoundedReadCloser(rc io.ReadCloser, limit int64) *boundedReadCloser { + if limit < 0 { + limit = 0 + } + return &boundedReadCloser{ + lr: io.LimitedReader{R: rc, N: limit + 1}, + closer: rc, + } +} + +// Read forwards Read up to the configured byte limit. When the underlying +// stream produces the limit+1 sentinel byte, the legal prefix is returned +// alongside ErrInflatedSizeMismatch; on subsequent calls only the error +// is returned. +func (b *boundedReadCloser) Read(p []byte) (int, error) { + if b.overrun { + return 0, ErrInflatedSizeMismatch + } + n, err := b.lr.Read(p) + if b.lr.N == 0 { + b.overrun = true + return n - 1, ErrInflatedSizeMismatch + } + return n, err +} + +// Close closes the underlying ReadCloser. +func (b *boundedReadCloser) Close() error { return b.closer.Close() } + // ObjectHeader contains the information related to the object, this information // is collected from the previous bytes to the content of the object. type ObjectHeader struct { @@ -220,6 +312,13 @@ func (s *Scanner) nextObjectHeader() (*ObjectHeader, error) { return nil, err } + // An OFS-delta references a base object that appears earlier + // in the pack; the negative offset must be strictly positive + // and not larger than the current object's offset. + if no <= 0 || no > h.Offset { + return nil, fmt.Errorf("%w: invalid OFS delta offset", ErrMalformedPackFile) + } + h.OffsetReference = h.Offset - no case plumbing.REFDeltaObject: var err error @@ -303,6 +402,13 @@ func (s *Scanner) readLength(first byte) (int64, error) { shift := firstLengthBits var err error for c&maskContinue > 0 { + // Mirrors unpack_object_header_buffer in canonical Git's + // packfile.c: a continuation byte at shift > 64-7 cannot + // contribute without overflowing an int64. + if shift > 64-lengthBits { + return 0, fmt.Errorf("%w: %w", ErrMalformedPackFile, ErrLengthOverflow) + } + if c, err = s.r.ReadByte(); err != nil { return 0, err } @@ -315,10 +421,18 @@ func (s *Scanner) readLength(first byte) (int64, error) { } // NextObject writes the content of the next object into the reader, returns -// the number of bytes written, the CRC32 of the content and an error, if any +// the number of bytes written, the CRC32 of the content and an error, if any. +// +// When a prior NextObjectHeader has stashed the object header in +// pendingObject, the inflated stream is bounded by the header's declared +// length and surfaces ErrInflatedSizeMismatch on overrun. func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err error) { + declaredSize := int64(-1) + if s.pendingObject != nil { + declaredSize = s.pendingObject.Length + } s.pendingObject = nil - written, err = s.copyObject(w) + written, err = s.copyObject(w, declaredSize) s.r.Flush() crc32 = s.crc.Sum32() @@ -327,23 +441,39 @@ func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err erro return } -// ReadObject returns a reader for the object content and an error +// ReadObject returns a reader for the object content and an error. +// +// When a prior NextObjectHeader has stashed the object header in +// pendingObject, the returned reader is bounded by the header's declared +// length so callers cannot stream past the declared inflated size; an +// overrun surfaces ErrInflatedSizeMismatch on the byte just past the +// limit. func (s *Scanner) ReadObject() (io.ReadCloser, error) { + declaredSize := int64(-1) + if s.pendingObject != nil { + declaredSize = s.pendingObject.Length + } s.pendingObject = nil zr, err := sync.GetZlibReader(s.r) if err != nil { return nil, fmt.Errorf("zlib reset error: %s", err) } - return ioutil.NewReadCloserWithCloser(zr.Reader, func() error { + rc := ioutil.NewReadCloserWithCloser(zr.Reader, func() error { sync.PutZlibReader(zr) return nil - }), nil + }) + if declaredSize >= 0 { + return newBoundedReadCloser(rc, declaredSize), nil + } + return rc, nil } -// ReadRegularObject reads and write a non-deltified object -// from it zlib stream in an object entry in the packfile. -func (s *Scanner) copyObject(w io.Writer) (n int64, err error) { +// copyObject inflates a non-deltified object's zlib stream into w. When +// declaredSize is non-negative, the write sink is wrapped in a +// boundedWriter so an overrun surfaces ErrInflatedSizeMismatch instead +// of being silently appended. +func (s *Scanner) copyObject(w io.Writer, declaredSize int64) (n int64, err error) { zr, err := sync.GetZlibReader(s.r) defer sync.PutZlibReader(zr) @@ -352,8 +482,14 @@ func (s *Scanner) copyObject(w io.Writer) (n int64, err error) { } defer ioutil.CheckClose(zr.Reader, &err) + + sink := w + if declaredSize >= 0 { + sink = &boundedWriter{w: w, limit: declaredSize} + } + buf := sync.GetByteSlice() - n, err = io.CopyBuffer(w, zr.Reader, *buf) + n, err = io.CopyBuffer(sink, zr.Reader, *buf) sync.PutByteSlice(buf) return } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go index d0d0036de6..3c004f5fdb 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/object/tree.go @@ -10,6 +10,7 @@ import ( "sort" "strings" + "github.com/go-git/go-git/v5/internal/pathutil" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/storer" @@ -118,7 +119,16 @@ func (t *Tree) Tree(path string) (*Tree, error) { } // TreeEntryFile returns the *File for a given *TreeEntry. +// +// The entry's name is validated against pathutil.ValidTreePath for +// the same reason FindEntry validates: TreeEntryFile is a boundary +// where attacker-controlled tree data leaves the trusted store as a +// *File whose Name a caller can hand to filesystem ops. func (t *Tree) TreeEntryFile(e *TreeEntry) (*File, error) { + if err := pathutil.ValidTreePath(e.Name); err != nil { + return nil, err + } + blob, err := GetBlob(t.s, e.Hash) if err != nil { return nil, err @@ -128,7 +138,16 @@ func (t *Tree) TreeEntryFile(e *TreeEntry) (*File, error) { } // FindEntry search a TreeEntry in this tree or any subtree. +// +// The lookup path is validated against pathutil.ValidTreePath to +// prevent attacker-controlled tree contents from leaking past this +// boundary as `.git`-shaped or path-traversal-shaped names. Callers +// that legitimately need to look up unsafe paths should walk the +// tree manually. func (t *Tree) FindEntry(path string) (*TreeEntry, error) { + if err := pathutil.ValidTreePath(path); err != nil { + return nil, err + } if t.t == nil { t.t = make(map[string]*Tree) } @@ -517,6 +536,10 @@ func (w *TreeWalker) Next() (name string, entry TreeEntry, err error) { continue } + if err := pathutil.ValidTreePath(entry.Name); err != nil { + return name, entry, err + } + if entry.Mode == filemode.Dir { obj, err = GetTree(w.s, entry.Hash) } diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go b/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go index ae6f2174a0..647955b2d3 100644 --- a/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go +++ b/vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go @@ -252,7 +252,39 @@ func (c *command) setAuthFromEndpoint() error { } func endpointToCommand(cmd string, ep *transport.Endpoint) string { - return fmt.Sprintf("%s '%s'", cmd, ep.Path) + var b strings.Builder + b.WriteString(cmd) + b.WriteByte(' ') + writeShellQuote(&b, ep.Path) + return b.String() +} + +// writeShellQuote writes s to b, wrapped in single quotes with +// embedded single quotes and exclamation marks escaped using the +// POSIX close-escape-reopen idiom: +// +// ' becomes '\'' +// ! becomes '\!' +// +// It is a direct port of canonical Git's sq_quote_buf (quote.c). +// The bang escape keeps the result safe when re-evaluated under +// csh-derived shells that perform history expansion. The output is +// safe to pass as a single argument through any POSIX shell and +// round-trips through git-shell's sq_dequote_to_argv. +func writeShellQuote(b *strings.Builder, s string) { + b.Grow(len(s) + 2) + b.WriteByte('\'') + for i := 0; i < len(s); i++ { + c := s[i] + if c == '\'' || c == '!' { + b.WriteString(`'\`) + b.WriteByte(c) + b.WriteByte('\'') + continue + } + b.WriteByte(c) + } + b.WriteByte('\'') } func overrideConfig(overrides *ssh.ClientConfig, c *ssh.ClientConfig) { diff --git a/vendor/github.com/go-git/go-git/v5/repository.go b/vendor/github.com/go-git/go-git/v5/repository.go index e0cefc4912..12af162390 100644 --- a/vendor/github.com/go-git/go-git/v5/repository.go +++ b/vendor/github.com/go-git/go-git/v5/repository.go @@ -1530,7 +1530,18 @@ func (r *Repository) Worktree() (*Worktree, error) { return nil, ErrIsBareRepository } - return &Worktree{r: r, Filesystem: r.wt}, nil + protectNTFS := defaultProtectNTFS() + protectHFS := defaultProtectHFS() + if cfg, err := r.Config(); err == nil { + if cfg.Core.ProtectNTFS.IsSet() { + protectNTFS = cfg.Core.ProtectNTFS.IsTrue() + } + if cfg.Core.ProtectHFS.IsSet() { + protectHFS = cfg.Core.ProtectHFS.IsTrue() + } + } + + return &Worktree{r: r, Filesystem: newWorktreeFilesystem(r.wt, protectNTFS, protectHFS)}, nil } func expand_ref(s storer.ReferenceStorer, ref plumbing.ReferenceName) (*plumbing.Reference, error) { diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go index 72c9ccfc14..eb85a11454 100644 --- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go +++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/dotgit.go @@ -75,6 +75,10 @@ var ( // ErrEmptyRefFile is returned when a reference file is attempted to be read, // but the file is empty ErrEmptyRefFile = errors.New("ref file is empty") + // ErrModuleNameEscape is returned when a submodule name would + // resolve outside the modules/ subtree, mirroring canonical Git's + // "ignoring suspicious submodule name" defence. + ErrModuleNameEscape = errors.New("submodule name escapes modules/ directory") ) // Options holds configuration for the storage. @@ -1127,9 +1131,20 @@ func (d *DotGit) PackRefs() (err error) { return nil } -// Module return a billy.Filesystem pointing to the module folder +// Module returns a billy.Filesystem pointing to the module folder. +// +// As a defence in depth against submodule name path traversal, +// refuse names whose joined path leaves the modules/ subtree once +// cleaned. The config-layer parser also validates submodule names, +// but Module may be reached from any caller that constructs a +// Submodule struct programmatically and so bypasses the parser. func (d *DotGit) Module(name string) (billy.Filesystem, error) { - return d.fs.Chroot(d.fs.Join(modulePath, name)) + p := d.fs.Join(modulePath, name) + cleaned := path.Clean(filepath.ToSlash(p)) + if cleaned != modulePath && !strings.HasPrefix(cleaned, modulePath+"/") { + return nil, ErrModuleNameEscape + } + return d.fs.Chroot(p) } func (d *DotGit) AddAlternate(remote string) error { diff --git a/vendor/github.com/go-git/go-git/v5/submodule.go b/vendor/github.com/go-git/go-git/v5/submodule.go index afabb6acad..2fe4ca2d27 100644 --- a/vendor/github.com/go-git/go-git/v5/submodule.go +++ b/vendor/github.com/go-git/go-git/v5/submodule.go @@ -6,9 +6,12 @@ import ( "errors" "fmt" "path" + "path/filepath" "github.com/go-git/go-billy/v5" "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/internal/pathutil" + giturl "github.com/go-git/go-git/v5/internal/url" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/format/index" "github.com/go-git/go-git/v5/plumbing/transport" @@ -119,6 +122,16 @@ func (s *Submodule) Repository() (*Repository, error) { exists = true } + // s.c.Path is sourced from the worktree's .gitmodules and is + // therefore tree-controlled. Apply the strict tree-path validator + // before chroot — the wrapper's tolerant validPath would let a + // final-position .git component through (e.g. "submodule/.git"), + // which a malicious .gitmodules could use to chroot the submodule + // worktree into the repository's actual .git directory. + if err := pathutil.ValidTreePath(s.c.Path); err != nil { + return nil, err + } + var worktree billy.Filesystem if worktree, err = s.w.Filesystem.Chroot(s.c.Path); err != nil { return nil, err @@ -138,18 +151,25 @@ func (s *Submodule) Repository() (*Repository, error) { return nil, err } - if !path.IsAbs(moduleEndpoint.Path) && moduleEndpoint.Protocol == "file" { - remotes, err := s.w.r.Remotes() + // A relative submodule URL such as "../X.git" must resolve against + // the parent repository's remote URL, not against the process CWD. + // Detect relativity from the raw configured URL because + // transport.NewEndpoint normalizes local paths to absolute form via + // filepath.Abs, which would otherwise mask the relative form here. + if giturl.IsLocalEndpoint(s.c.URL) && + !path.IsAbs(s.c.URL) && !filepath.IsAbs(s.c.URL) { + + base, err := defaultRemote(s.w.r) + if err != nil { + return nil, fmt.Errorf("resolving relative submodule URL: %w", err) + } + + rootEndpoint, err := transport.NewEndpoint(base.URLs[0]) if err != nil { return nil, err } - rootEndpoint, err := transport.NewEndpoint(remotes[0].c.URLs[0]) - if err != nil { - return nil, err - } - - rootEndpoint.Path = path.Join(rootEndpoint.Path, moduleEndpoint.Path) + rootEndpoint.Path = path.Join(rootEndpoint.Path, s.c.URL) *moduleEndpoint = *rootEndpoint } @@ -161,6 +181,52 @@ func (s *Submodule) Repository() (*Repository, error) { return r, err } +// defaultRemote returns the remote that relative submodule URLs are +// resolved against, mirroring canonical Git's repo_default_remote +// (remote.c) and resolve_relative_url (builtin/submodule--helper.c): +// +// 1. if HEAD is on a branch with branch..remote configured, +// use that remote; +// 2. else if exactly one remote is configured, use it; +// 3. otherwise fall back to DefaultRemoteName ("origin"). +// +// Each rule falls through unconditionally: a branch lookup that +// finds the branch but with an empty Remote does not short-circuit +// rule (2). Returns an error when the chosen remote is not configured. +func defaultRemote(r *Repository) (*config.RemoteConfig, error) { + cfg, err := r.Config() + if err != nil { + return nil, err + } + + if ref, err := r.Reference(plumbing.HEAD, false); err == nil && + ref.Type() == plumbing.SymbolicReference && + ref.Target().IsBranch() { + if b, ok := cfg.Branches[ref.Target().Short()]; ok && b.Remote != "" { + return lookupRemote(cfg, b.Remote) + } + } + + if len(cfg.Remotes) == 1 { + for name := range cfg.Remotes { + return lookupRemote(cfg, name) + } + } + + return lookupRemote(cfg, DefaultRemoteName) +} + +func lookupRemote(cfg *config.Config, name string) (*config.RemoteConfig, error) { + rc, ok := cfg.Remotes[name] + if !ok { + return nil, fmt.Errorf("remote %q not found", name) + } + if len(rc.URLs) == 0 { + return nil, fmt.Errorf("remote %q has no configured URL", name) + } + return rc, nil +} + // Update the registered submodule to match what the superproject expects, the // submodule should be initialized first calling the Init method or setting in // the options SubmoduleUpdateOptions.Init equals true diff --git a/vendor/github.com/go-git/go-git/v5/utils/binary/read.go b/vendor/github.com/go-git/go-git/v5/utils/binary/read.go index b8f9df1a24..71d9ad607b 100644 --- a/vendor/github.com/go-git/go-git/v5/utils/binary/read.go +++ b/vendor/github.com/go-git/go-git/v5/utils/binary/read.go @@ -5,11 +5,18 @@ package binary import ( "bufio" "encoding/binary" + "errors" "io" + "math" "github.com/go-git/go-git/v5/plumbing" ) +// ErrIntegerOverflow is returned when a Git-format variable-width integer +// would not fit into an int64 because the input declares more continuation +// bytes than the type can hold. +var ErrIntegerOverflow = errors.New("variable-width integer overflow") + // Read reads structured binary data from r into data. Bytes are read and // decoded in BigEndian order // https://golang.org/pkg/encoding/binary/#Read @@ -92,6 +99,14 @@ func ReadVariableWidthInt(r io.Reader) (int64, error) { var v = int64(c & maskLength) for c&maskContinue > 0 { + // Reject input that, after the v++ and shift below, would + // not fit in an int64. With v < (MaxInt64-127)>>7, the + // post-increment v is at most (MaxInt64-127)>>7 and the + // final (v << 7) + (c & 0x7F) stays within int64. + if v >= (math.MaxInt64-int64(maskLength))>>lengthBits { + return 0, ErrIntegerOverflow + } + v++ if err := Read(r, &c); err != nil { return 0, err diff --git a/vendor/github.com/go-git/go-git/v5/worktree.go b/vendor/github.com/go-git/go-git/v5/worktree.go index 55d7ebb1b6..d8ee9fdd13 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree.go +++ b/vendor/github.com/go-git/go-git/v5/worktree.go @@ -7,7 +7,6 @@ import ( "io" "os" "path/filepath" - "runtime" "strings" "github.com/go-git/go-billy/v5" @@ -458,10 +457,6 @@ func (w *Worktree) resetWorktree(t *object.Tree, files []string) error { filesMap := buildFilePathMap(files) for _, ch := range changes { - if err := w.validChange(ch); err != nil { - return err - } - if len(files) > 0 { file := "" if ch.From != nil { @@ -489,108 +484,6 @@ func (w *Worktree) resetWorktree(t *object.Tree, files []string) error { return w.r.Storer.SetIndex(idx) } -// worktreeDeny is a list of paths that are not allowed -// to be used when resetting the worktree. -var worktreeDeny = map[string]struct{}{ - // .git - GitDirName: {}, - - // For other historical reasons, file names that do not conform to the 8.3 - // format (up to eight characters for the basename, three for the file - // extension, certain characters not allowed such as `+`, etc) are associated - // with a so-called "short name", at least on the `C:` drive by default. - // Which means that `git~1/` is a valid way to refer to `.git/`. - "git~1": {}, -} - -// validPath checks whether paths are valid. -// The rules around invalid paths could differ from upstream based on how -// filesystems are managed within go-git, but they are largely the same. -// -// For upstream rules: -// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/read-cache.c#L946 -// https://github.com/git/git/blob/564d0252ca632e0264ed670534a51d18a689ef5d/path.c#L1383 -func validPath(paths ...string) error { - for _, p := range paths { - parts := strings.FieldsFunc(p, func(r rune) bool { return (r == '\\' || r == '/') }) - if len(parts) == 0 { - return fmt.Errorf("invalid path: %q", p) - } - - if _, denied := worktreeDeny[strings.ToLower(parts[0])]; denied { - return fmt.Errorf("invalid path prefix: %q", p) - } - - if runtime.GOOS == "windows" { - // Volume names are not supported, in both formats: \\ and :. - if vol := filepath.VolumeName(p); vol != "" { - return fmt.Errorf("invalid path: %q", p) - } - - if !windowsValidPath(parts[0]) { - return fmt.Errorf("invalid path: %q", p) - } - } - - for _, part := range parts { - if part == ".." { - return fmt.Errorf("invalid path %q: cannot use '..'", p) - } - } - } - return nil -} - -// windowsPathReplacer defines the chars that need to be replaced -// as part of windowsValidPath. -var windowsPathReplacer *strings.Replacer - -func init() { - windowsPathReplacer = strings.NewReplacer(" ", "", ".", "") -} - -func windowsValidPath(part string) bool { - if len(part) > 3 && strings.EqualFold(part[:4], GitDirName) { - // For historical reasons, file names that end in spaces or periods are - // automatically trimmed. Therefore, `.git . . ./` is a valid way to refer - // to `.git/`. - if windowsPathReplacer.Replace(part[4:]) == "" { - return false - } - - // For yet other historical reasons, NTFS supports so-called "Alternate Data - // Streams", i.e. metadata associated with a given file, referred to via - // `::`. There exists a default stream - // type for directories, allowing `.git/` to be accessed via - // `.git::$INDEX_ALLOCATION/`. - // - // For performance reasons, _all_ Alternate Data Streams of `.git/` are - // forbidden, not just `::$INDEX_ALLOCATION`. - if len(part) > 4 && part[4:5] == ":" { - return false - } - } - return true -} - -func (w *Worktree) validChange(ch merkletrie.Change) error { - action, err := ch.Action() - if err != nil { - return nil - } - - switch action { - case merkletrie.Delete: - return validPath(ch.From.String()) - case merkletrie.Insert: - return validPath(ch.To.String()) - case merkletrie.Modify: - return validPath(ch.From.String(), ch.To.String()) - } - - return nil -} - func (w *Worktree) checkoutChange(ch merkletrie.Change, t *object.Tree, idx *indexBuilder) error { a, err := ch.Action() if err != nil { @@ -763,10 +656,10 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) { } func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { - // https://github.com/git/git/commit/10ecfa76491e4923988337b2e2243b05376b40de - if strings.EqualFold(f.Name, gitmodulesFile) { - return ErrGitModulesSymlink - } + // .gitmodules symlink rejection (and its NTFS / HFS variants) is + // enforced by the worktreeFilesystem wrapper's Symlink method via + // validSymlinkName. See https://github.com/git/git/commit/10ecfa7 + // for the upstream rationale. from, err := f.Reader() if err != nil { diff --git a/vendor/github.com/go-git/go-git/v5/worktree_fs.go b/vendor/github.com/go-git/go-git/v5/worktree_fs.go new file mode 100644 index 0000000000..9bc2fd97dc --- /dev/null +++ b/vendor/github.com/go-git/go-git/v5/worktree_fs.go @@ -0,0 +1,264 @@ +package git + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + + "github.com/go-git/go-billy/v5" + + "github.com/go-git/go-git/v5/internal/pathutil" +) + +// defaultProtectHFS returns the default value for core.protectHFS +// when not explicitly configured. Matches upstream Git's +// PROTECT_HFS_DEFAULT[1], which the Makefile sets to 1 on Darwin +// and leaves at 0 on every other platform. +// +// [1]: https://github.com/git/git/blob/v2.54.0/config.mak.uname#L146 +func defaultProtectHFS() bool { + return runtime.GOOS == "darwin" +} + +// defaultProtectNTFS returns the default value for core.protectNTFS +// when not explicitly configured. Matches upstream Git's +// PROTECT_NTFS_DEFAULT, which has been 1 on every platform since +// 9102f958ee5 (CVE-2019-1353)[1]: WSL allows Linux processes to +// reach NTFS-mounted worktrees on Windows hosts, so the +// is_ntfs_dotgit guard cannot safely be gated on the runtime OS. +// +// [1]: https://github.com/git/git/commit/9102f958ee5 +func defaultProtectNTFS() bool { + return true +} + +// worktreeFilesystem wraps a billy.Filesystem and validates every path passed +// to a mutating operation. This prevents writing to, or deleting from, +// dangerous locations (e.g. .git/*, ../) regardless of which worktree +// code path triggers the operation. +type worktreeFilesystem struct { + billy.Filesystem + protectNTFS bool + protectHFS bool +} + +func newWorktreeFilesystem(fs billy.Filesystem, protectNTFS, protectHFS bool) *worktreeFilesystem { + return &worktreeFilesystem{Filesystem: fs, protectNTFS: protectNTFS, protectHFS: protectHFS} +} + +func (sfs *worktreeFilesystem) Create(filename string) (billy.File, error) { + if err := sfs.validPath(filename); err != nil { + return nil, fmt.Errorf("create: %w", err) + } + return sfs.Filesystem.Create(filename) +} + +func (sfs *worktreeFilesystem) Open(filename string) (billy.File, error) { + if err := sfs.validReadPath(filename); err != nil { + return nil, fmt.Errorf("open: %w", err) + } + return sfs.Filesystem.Open(filename) +} + +func (sfs *worktreeFilesystem) OpenFile(filename string, flag int, perm os.FileMode) (billy.File, error) { + if err := sfs.validPath(filename); err != nil { + return nil, fmt.Errorf("openfile: %w", err) + } + return sfs.Filesystem.OpenFile(filename, flag, perm) +} + +func (sfs *worktreeFilesystem) Stat(filename string) (os.FileInfo, error) { + if err := sfs.validReadPath(filename); err != nil { + return nil, fmt.Errorf("stat: %w", err) + } + return sfs.Filesystem.Stat(filename) +} + +func (sfs *worktreeFilesystem) Remove(filename string) error { + if err := sfs.validPath(filename); err != nil { + return fmt.Errorf("remove: %w", err) + } + return sfs.Filesystem.Remove(filename) +} + +func (sfs *worktreeFilesystem) Rename(from, to string) error { + if err := sfs.validPath(from, to); err != nil { + return fmt.Errorf("rename: %w", err) + } + return sfs.Filesystem.Rename(from, to) +} + +func (sfs *worktreeFilesystem) ReadDir(path string) ([]os.FileInfo, error) { + if err := sfs.validReadPath(path); err != nil { + return nil, fmt.Errorf("readdir: %w", err) + } + return sfs.Filesystem.ReadDir(path) +} + +func (sfs *worktreeFilesystem) Lstat(filename string) (os.FileInfo, error) { + if err := sfs.validReadPath(filename); err != nil { + return nil, fmt.Errorf("lstat: %w", err) + } + return sfs.Filesystem.Lstat(filename) +} + +func (sfs *worktreeFilesystem) Symlink(target, link string) error { + if err := sfs.validPath(link); err != nil { + return fmt.Errorf("symlink: %w", err) + } + if err := sfs.validSymlinkName(link); err != nil { + return fmt.Errorf("symlink: %w", err) + } + return sfs.Filesystem.Symlink(target, link) +} + +func (sfs *worktreeFilesystem) Readlink(link string) (string, error) { + if err := sfs.validReadPath(link); err != nil { + return "", fmt.Errorf("readlink: %w", err) + } + return sfs.Filesystem.Readlink(link) +} + +func (sfs *worktreeFilesystem) MkdirAll(path string, perm os.FileMode) error { + // MkdirAll on the worktree root is a no-op: the root always exists, + // so there is nothing to materialise. Mirroring the tolerance that + // validReadPath gives to read-side operations avoids breaking callers + // that walk a directory tree and pass the relative-to-root prefix + // ("") through to the worktree FS. + if path == "" || path == "." || path == "/" { + return nil + } + if err := sfs.validPath(path); err != nil { + return fmt.Errorf("mkdirall: %w", err) + } + return sfs.Filesystem.MkdirAll(path, perm) +} + +func (sfs *worktreeFilesystem) TempFile(_, _ string) (billy.File, error) { + return nil, fmt.Errorf("tempfile: %w", errUnsupportedOperation) +} + +func (sfs *worktreeFilesystem) Chroot(path string) (billy.Filesystem, error) { + if err := sfs.validReadPath(path); err != nil { + return nil, fmt.Errorf("chroot: %w", err) + } + return sfs.Filesystem.Chroot(path) +} + +// validReadPath is like validPath but treats the empty string and "." as +// valid references to the worktree root. Read-side operations on the root +// (e.g. ReadDir(""), Lstat(".")) are legitimate; mutating the root itself +// is not, so write-side operations continue to use validPath directly. +func (sfs *worktreeFilesystem) validReadPath(p string) error { + if p == "" || p == "." || p == "/" { + return nil + } + return sfs.validPath(p) +} + +var errUnsupportedOperation = errors.New("unsupported operation") + +// isDotGitVariant reports whether part is .git, git~1, or an HFS+ +// equivalent of .git (when protectHFS is true). NTFS variants of .git +// (e.g. ".git " with trailing space, ".git::$INDEX_ALLOCATION") are +// detected separately by pathutil.WindowsValidPath, which applies +// regardless of position in the path. Both validators reuse this +// helper. +func isDotGitVariant(part string, protectHFS bool) bool { + if pathutil.IsDotGitName(part) { + return true + } + if protectHFS && pathutil.IsHFSDotGit(part) { + return true + } + return false +} + +// validPath checks whether paths are valid for the worktree +// filesystem abstraction. It is intentionally tolerant of .git as +// the final path component of a multi-component path +// (e.g. "submodule/.git"), so that legitimate gitlink pointer files +// can still be Stat'd, Read, and Removed via the wrapper during +// submodule cleanup. Attacker-controlled tree-entry paths are +// validated separately by pathutil.ValidTreePath at the boundaries +// where data leaves the trusted store (Tree.FindEntry, the explicit +// callers in CherryPick and Submodule.Repository). +// +// For upstream rules: +// https://github.com/git/git/blob/v2.54.0/read-cache.c#L987 +// https://github.com/git/git/blob/v2.54.0/path.c#L1419 +func (sfs *worktreeFilesystem) validPath(paths ...string) error { + for _, p := range paths { + for i := 0; i < len(p); i++ { + if p[i] < 0x20 || p[i] == 0x7f { + return fmt.Errorf("invalid path %q: contains control character", p) + } + } + + parts := strings.FieldsFunc(p, func(r rune) bool { return (r == '\\' || r == '/') }) + if len(parts) == 0 { + return fmt.Errorf("invalid path: %q", p) + } + + if sfs.protectNTFS { + // Volume names are not supported, in both formats: \\ and :. + if vol := filepath.VolumeName(p); vol != "" { + return fmt.Errorf("invalid path: %q", p) + } + } + + for i, part := range parts { + if part == "." || part == ".." { + return fmt.Errorf("invalid path %q: cannot use %q", p, part) + } + + // Reject .git (and equivalents) as a path component when it is + // either the first component (root-level .git) or a non-final + // component (traversal into a .git directory, e.g. "a/.git/config"). + // A final non-first .git component (e.g. "submodule/.git") is + // allowed because submodule worktrees contain a .git pointer file. + if isDotGitVariant(part, sfs.protectHFS) && (i == 0 || i < len(parts)-1) { + return fmt.Errorf("invalid path component: %q", p) + } + + if sfs.protectNTFS && !pathutil.WindowsValidPath(part) { + return fmt.Errorf("invalid path: %q", p) + } + } + } + return nil +} + +// validSymlinkName checks the per-component name of a symlink for +// dotfile names that attackers can use to trick a checkout into +// writing a dangerous symlink. Each path component is compared +// against .gitmodules case-insensitively, against its NTFS variants +// (e.g. ".gitmodules .", ".gitmodules::$INDEX_ALLOCATION", or 8.3 +// short-name forms) when protectNTFS is on, and against its HFS+ +// variants (Unicode ignored code points folded into ".gitmodules") +// when protectHFS is on. +// +// Reference: upstream Git verify_path_internal at read-cache.c#L1004-L1024 +// in tag v2.54.0[1]. +// +// [1]: https://github.com/git/git/blob/v2.54.0/read-cache.c#L1004-L1024 +func (sfs *worktreeFilesystem) validSymlinkName(name string) error { + parts := strings.FieldsFunc(name, func(r rune) bool { + return r == '/' || r == '\\' + }) + for _, part := range parts { + if strings.EqualFold(part, gitmodulesFile) { + return ErrGitModulesSymlink + } + if sfs.protectNTFS && pathutil.IsNTFSDotGitmodules(part) { + return ErrGitModulesSymlink + } + if sfs.protectHFS && pathutil.IsHFSDotGitmodules(part) { + return ErrGitModulesSymlink + } + } + return nil +} diff --git a/vendor/github.com/go-git/go-git/v5/worktree_status.go b/vendor/github.com/go-git/go-git/v5/worktree_status.go index e7a60747b7..ecc3d7ab89 100644 --- a/vendor/github.com/go-git/go-git/v5/worktree_status.go +++ b/vendor/github.com/go-git/go-git/v5/worktree_status.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/go-git/go-billy/v5/util" + "github.com/go-git/go-git/v5/internal/pathutil" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/filemode" "github.com/go-git/go-git/v5/plumbing/format/gitignore" @@ -545,6 +546,14 @@ func (w *Worktree) addOrUpdateFileToIndex(idx *index.Index, filename string, h p } func (w *Worktree) doAddFileToIndex(idx *index.Index, filename string, h plumbing.Hash) error { + // Mirror upstream's Index.Add gate at the v5 caller boundary: the + // index feeds future trees, so a name that the tree-side + // pathutil.ValidTreePath gate would reject must not enter the + // index in the first place. v5 keeps Index.Add's existing signature + // for API compatibility, so the validation happens here. + if err := pathutil.ValidTreePath(filename); err != nil { + return err + } return w.doUpdateFileToIndex(idx.Add(filename), filename, h) } diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/authprovider/authprovider.go b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/authprovider/authprovider.go index acf553ae6d..667b3d2f67 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/authprovider/authprovider.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/authprovider/authprovider.go @@ -68,7 +68,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { return c, nil } -func getAuthManager(manager string, m map[string]map[string]interface{}) (auth.Manager, *plugin.RevaPlugin, error) { +func getAuthManager(manager string, m map[string]map[string]any, logger *zerolog.Logger) (auth.Manager, *plugin.RevaPlugin, error) { if manager == "" { return nil, nil, errtypes.InternalError("authsvc: driver not configured for auth manager") } @@ -86,7 +86,7 @@ func getAuthManager(manager string, m map[string]map[string]interface{}) (auth.M return authManager, p, nil } else if _, ok := err.(errtypes.NotFound); ok { if f, ok := registry.NewFuncs[manager]; ok { - authmgr, err := f(m[manager]) + authmgr, err := f(m[manager], logger) return authmgr, nil, err } } else { @@ -96,13 +96,13 @@ func getAuthManager(manager string, m map[string]map[string]interface{}) (auth.M } // New returns a new AuthProviderServiceServer. -func New(m map[string]interface{}, ss *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) { +func New(m map[string]interface{}, ss *grpc.Server, logger *zerolog.Logger) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err } - authManager, plug, err := getAuthManager(c.AuthManager, c.AuthManagers) + authManager, plug, err := getAuthManager(c.AuthManager, c.AuthManagers, logger) if err != nil { return nil, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/gateway/storageprovider.go b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/gateway/storageprovider.go index 158808ce0a..69bed97c6b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/gateway/storageprovider.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/gateway/storageprovider.go @@ -234,11 +234,25 @@ func (s *svc) CreateStorageSpace(ctx context.Context, req *provider.CreateStorag // here is happeing so that that grant is also visible when listing permission (shares) on the spaceroot. if req.GetType() != "personal" && createRes.GetStatus().GetCode() == rpc.Code_CODE_OK { rollbackFn := func() { - dsRes, dsErr := s.DeleteStorageSpace(ctx, &provider.DeleteStorageSpaceRequest{ + // Deleting a space needs to DeleteRequests, one to disable the Space ... + dr := &provider.DeleteStorageSpaceRequest{ Id: createRes.GetStorageSpace().GetId(), - }) + } + dsRes, dsErr := s.DeleteStorageSpace(ctx, dr) if dsErr != nil || dsRes.GetStatus().GetCode() != rpc.Code_CODE_OK { - log.Error().Err(dsErr).Interface("status", dsRes.GetStatus()).Interface("space_id", createRes.GetStorageSpace().GetId()).Msg("failed to delete space during rollback") + log.Error().Err(dsErr).Interface("status", dsRes.GetStatus()).Interface("space_id", createRes.GetStorageSpace().GetId()).Msg("failed to disable space during rollback") + // if disabling fails we won't be able to purge it either so give up here + return + } + // ... and other one to finally purge it + dr.Opaque = &typesv1beta1.Opaque{ + Map: map[string]*typesv1beta1.OpaqueEntry{ + "purge": {}, + }, + } + dsRes, dsErr = s.DeleteStorageSpace(ctx, dr) + if dsErr != nil || dsRes.GetStatus().GetCode() != rpc.Code_CODE_OK { + log.Error().Err(dsErr).Interface("status", dsRes.GetStatus()).Interface("space_id", createRes.GetStorageSpace().GetId()).Msg("failed to purge space during rollback") } } diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/groupprovider/groupprovider.go b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/groupprovider/groupprovider.go index 484d3c0f97..9e27f824be 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/groupprovider/groupprovider.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/groupprovider/groupprovider.go @@ -60,22 +60,22 @@ func parseConfig(m map[string]interface{}) (*config, error) { return c, nil } -func getDriver(c *config) (group.Manager, error) { +func getDriver(c *config, logger *zerolog.Logger) (group.Manager, error) { if f, ok := registry.NewFuncs[c.Driver]; ok { - return f(c.Drivers[c.Driver]) + return f(c.Drivers[c.Driver], logger) } return nil, errtypes.NotFound(fmt.Sprintf("driver %s not found for group manager", c.Driver)) } // New returns a new GroupProviderServiceServer. -func New(m map[string]interface{}, ss *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) { +func New(m map[string]any, ss *grpc.Server, logger *zerolog.Logger) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err } - groupManager, err := getDriver(c) + groupManager, err := getDriver(c, logger) if err != nil { return nil, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go index 85952e94f8..190395b07f 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/grpc/services/userprovider/userprovider.go @@ -84,7 +84,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { return c, nil } -func getDriver(c *config) (user.Manager, *plugin.RevaPlugin, error) { +func getDriver(c *config, logger *zerolog.Logger) (user.Manager, *plugin.RevaPlugin, error) { p, err := plugin.Load("userprovider", c.Driver) if err == nil { manager, ok := p.Plugin.(user.Manager) @@ -100,7 +100,7 @@ func getDriver(c *config) (user.Manager, *plugin.RevaPlugin, error) { } else if _, ok := err.(errtypes.NotFound); ok { // plugin not found, fetch the driver from the in-memory registry if f, ok := userRegistry.NewFuncs[c.Driver]; ok { - mgr, err := f(c.Drivers[c.Driver]) + mgr, err := f(c.Drivers[c.Driver], logger) return mgr, nil, err } } else { @@ -109,25 +109,25 @@ func getDriver(c *config) (user.Manager, *plugin.RevaPlugin, error) { return nil, nil, errtypes.NotFound(fmt.Sprintf("driver %s not found for user manager", c.Driver)) } -func getTenantManager(c *config) (tenant.Manager, error) { +func getTenantManager(c *config, logger *zerolog.Logger) (tenant.Manager, error) { if f, ok := tenantRegistry.NewFuncs[c.TenantDriver]; ok { - mgr, err := f(c.TenantDrivers[c.TenantDriver]) + mgr, err := f(c.TenantDrivers[c.TenantDriver], logger) return mgr, err } return nil, errtypes.NotFound(fmt.Sprintf("driver %s not found for tenant manager", c.TenantDriver)) } // New returns a new UserProviderServiceServer. -func New(m map[string]interface{}, ss *grpc.Server, _ *zerolog.Logger) (rgrpc.Service, error) { +func New(m map[string]interface{}, ss *grpc.Server, logger *zerolog.Logger) (rgrpc.Service, error) { c, err := parseConfig(m) if err != nil { return nil, err } - userManager, plug, err := getDriver(c) + userManager, plug, err := getDriver(c, logger) if err != nil { return nil, err } - tenantManager, err := getTenantManager(c) + tenantManager, err := getTenantManager(c, logger) if err != nil { return nil, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/appauth/appauth.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/appauth/appauth.go index b66569f902..c99083e772 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/appauth/appauth.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/appauth/appauth.go @@ -31,6 +31,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -42,7 +43,7 @@ type manager struct { } // New returns a new auth Manager. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, logger *zerolog.Logger) (auth.Manager, error) { mgr := &manager{} err := mgr.Configure(m) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/demo/demo.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/demo/demo.go index 4ed8d4efd9..7926e95f16 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/demo/demo.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/demo/demo.go @@ -27,6 +27,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry" "github.com/opencloud-eu/reva/v2/pkg/auth/scope" "github.com/opencloud-eu/reva/v2/pkg/errtypes" + "github.com/rs/zerolog" ) func init() { @@ -44,7 +45,7 @@ type Credentials struct { } // New returns a new auth Manager. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (auth.Manager, error) { // m not used mgr := &manager{} err := mgr.Configure(m) diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/impersonator/impersonator.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/impersonator/impersonator.go index a04f2873de..f59bdfbc36 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/impersonator/impersonator.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/impersonator/impersonator.go @@ -27,6 +27,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/auth" "github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry" "github.com/opencloud-eu/reva/v2/pkg/auth/scope" + "github.com/rs/zerolog" ) func init() { @@ -36,7 +37,7 @@ func init() { type mgr struct{} // New returns an auth manager implementation that allows to authenticate with any credentials. -func New(c map[string]interface{}) (auth.Manager, error) { +func New(c map[string]any, _ *zerolog.Logger) (auth.Manager, error) { return &mgr{}, nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/json/json.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/json/json.go index ccf15059be..89687790de 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/json/json.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/json/json.go @@ -32,6 +32,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/auth/scope" "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -78,7 +79,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a new auth Manager. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (auth.Manager, error) { mgr := &manager{} err := mgr.Configure(m) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ldap/ldap.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ldap/ldap.go index eaa559f278..5aac137d22 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ldap/ldap.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ldap/ldap.go @@ -39,6 +39,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/utils" ldapIdentity "github.com/opencloud-eu/reva/v2/pkg/utils/ldap" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -72,13 +73,13 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns an auth manager implementation that connects to a LDAP server to validate the user. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, logger *zerolog.Logger) (auth.Manager, error) { manager := &mgr{} err := manager.Configure(m) if err != nil { return nil, err } - manager.ldapClient, err = utils.GetLDAPClientWithReconnect(&manager.c.LDAPConn) + manager.ldapClient, err = utils.GetLDAPClientWithReconnect(&manager.c.LDAPConn, logger) if err != nil { return nil, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/machine/machine.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/machine/machine.go index 3dfc623ce0..5e566d450a 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/machine/machine.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/machine/machine.go @@ -32,6 +32,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/pkg/errors" + "github.com/rs/zerolog" ) // 'machine' is an authentication method used to impersonate users. @@ -60,7 +61,7 @@ func (m *manager) Configure(conf map[string]interface{}) error { } // New creates a new manager for the 'machine' authentication -func New(conf map[string]interface{}) (auth.Manager, error) { +func New(conf map[string]any, _ *zerolog.Logger) (auth.Manager, error) { m := &manager{} err := m.Configure(conf) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ocmshares/ocmshares.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ocmshares/ocmshares.go index 7ffc6fcec2..446a8ae56a 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ocmshares/ocmshares.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/ocmshares/ocmshares.go @@ -39,6 +39,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/utils" "github.com/opencloud-eu/reva/v2/pkg/utils/cfg" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -59,7 +60,7 @@ func (c *config) ApplyDefaults() { } // New creates a new ocmshares authentication manager. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (auth.Manager, error) { var mgr manager if err := mgr.Configure(m); err != nil { return nil, err diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/oidc/oidc.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/oidc/oidc.go index 209fdc1e9b..5762d2f5c4 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/oidc/oidc.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/oidc/oidc.go @@ -44,6 +44,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/rhttp" "github.com/opencloud-eu/reva/v2/pkg/sharedconf" "github.com/pkg/errors" + "github.com/rs/zerolog" "golang.org/x/oauth2" ) @@ -102,7 +103,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns an auth manager implementation that verifies the oidc token and obtains the user claims. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (auth.Manager, error) { manager := &mgr{} err := manager.Configure(m) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/publicshares/publicshares.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/publicshares/publicshares.go index a0288ded57..637fe284f7 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/publicshares/publicshares.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/publicshares/publicshares.go @@ -36,6 +36,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/opencloud-eu/reva/v2/pkg/utils" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -60,7 +61,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a new auth Manager. -func New(m map[string]interface{}) (auth.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (auth.Manager, error) { mgr := &manager{} err := mgr.Configure(m) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry/registry.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry/registry.go index 418e854e78..6d117cb06e 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry/registry.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry/registry.go @@ -20,11 +20,12 @@ package registry import ( "github.com/opencloud-eu/reva/v2/pkg/auth" + "github.com/rs/zerolog" ) // NewFunc is the function that auth implementations // should register to at init time. -type NewFunc func(map[string]interface{}) (auth.Manager, error) +type NewFunc func(map[string]any, *zerolog.Logger) (auth.Manager, error) // NewFuncs is a map containing all the registered auth managers. var NewFuncs = map[string]NewFunc{} diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go index cd1656b496..fe9eca1442 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go @@ -5,6 +5,7 @@ import ( authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + "github.com/rs/zerolog" "github.com/mitchellh/mapstructure" "github.com/opencloud-eu/reva/v2/pkg/auth" @@ -46,7 +47,7 @@ func (m *manager) Configure(config map[string]interface{}) error { } // New creates a new manager for the 'service' authentication -func New(conf map[string]interface{}) (auth.Manager, error) { +func New(conf map[string]any, _ *zerolog.Logger) (auth.Manager, error) { m := &manager{} err := m.Configure(conf) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/conversions/main.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/conversions/main.go index f110a64861..699dd89961 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/conversions/main.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/conversions/main.go @@ -30,9 +30,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/mime" - "github.com/opencloud-eu/reva/v2/pkg/publicshare" "github.com/opencloud-eu/reva/v2/pkg/storagespace" - "github.com/opencloud-eu/reva/v2/pkg/user" "github.com/opencloud-eu/reva/v2/pkg/utils" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" @@ -42,8 +40,6 @@ import ( ocm "github.com/cs3org/go-cs3apis/cs3/sharing/ocm/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" - publicsharemgr "github.com/opencloud-eu/reva/v2/pkg/publicshare/manager/registry" - usermgr "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" ) const ( @@ -413,24 +409,6 @@ func LocalGroupIDToString(groupID *grouppb.GroupId) string { return groupID.OpaqueId } -// GetUserManager returns a connection to a user share manager -func GetUserManager(manager string, m map[string]map[string]interface{}) (user.Manager, error) { - if f, ok := usermgr.NewFuncs[manager]; ok { - return f(m[manager]) - } - - return nil, fmt.Errorf("driver %s not found for user manager", manager) -} - -// GetPublicShareManager returns a connection to a public share manager -func GetPublicShareManager(manager string, m map[string]map[string]interface{}) (publicshare.Manager, error) { - if f, ok := publicsharemgr.NewFuncs[manager]; ok { - return f(m[manager]) - } - - return nil, fmt.Errorf("driver %s not found for public shares manager", manager) -} - // timestamp is assumed to be UTC ... just human readable ... // FIXME and ambiguous / error prone because there is no time zone ... func timestampToExpiration(t *types.Timestamp) string { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/json/json.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/json/json.go index 47e54edc66..06264fcbeb 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/json/json.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/json/json.go @@ -32,6 +32,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/group" "github.com/opencloud-eu/reva/v2/pkg/group/manager/registry" "github.com/pkg/errors" + "github.com/rs/zerolog" "google.golang.org/protobuf/proto" ) @@ -65,7 +66,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a group manager implementation that reads a json file to provide group metadata. -func New(m map[string]interface{}) (group.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (group.Manager, error) { c, err := parseConfig(m) if err != nil { return nil, err diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/ldap/ldap.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/ldap/ldap.go index 33e01f3a36..ed3f50cb9b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/ldap/ldap.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/ldap/ldap.go @@ -36,6 +36,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/sharedconf" "github.com/opencloud-eu/reva/v2/pkg/utils" ldapIdentity "github.com/opencloud-eu/reva/v2/pkg/utils/ldap" + "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" ) @@ -70,7 +71,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a group manager implementation that connects to a LDAP server to provide group metadata. -func New(m map[string]interface{}) (group.Manager, error) { +func New(m map[string]any, logger *zerolog.Logger) (group.Manager, error) { if sharedconf.MultiTenantEnabled() { return nil, errtypes.NotSupported("ldap group manager does not support multi-tenancy") } @@ -81,7 +82,7 @@ func New(m map[string]interface{}) (group.Manager, error) { return nil, err } - mgr.ldapClient, err = utils.GetLDAPClientWithReconnect(&mgr.c.LDAPConn) + mgr.ldapClient, err = utils.GetLDAPClientWithReconnect(&mgr.c.LDAPConn, logger) if err != nil { return nil, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/null/null.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/null/null.go index e6d913395a..d286183b5a 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/null/null.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/null/null.go @@ -27,6 +27,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/group" "github.com/opencloud-eu/reva/v2/pkg/group/manager/registry" + "github.com/rs/zerolog" ) func init() { @@ -37,7 +38,7 @@ type manager struct { } // New returns a group manager implementation that return NOT FOUND or empty result set for every call -func New(m map[string]interface{}) (group.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (group.Manager, error) { return &manager{}, nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/registry/registry.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/registry/registry.go index 4bc7701829..8d09364b75 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/registry/registry.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/group/manager/registry/registry.go @@ -18,11 +18,14 @@ package registry -import "github.com/opencloud-eu/reva/v2/pkg/group" +import ( + "github.com/opencloud-eu/reva/v2/pkg/group" + "github.com/rs/zerolog" +) // NewFunc is the function that group managers // should register at init time. -type NewFunc func(map[string]interface{}) (group.Manager, error) +type NewFunc func(map[string]any, *zerolog.Logger) (group.Manager, error) // NewFuncs is a map containing all the registered group managers. var NewFuncs = map[string]NewFunc{} diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go index cf336ecb02..6ab56d4bed 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/jsoncs3.go @@ -328,21 +328,13 @@ func (m *Manager) waitForInit(ctx context.Context) error { } } -// waitForMigrations blocks until both storage initialization and all data -// migrations have completed on this instance, or until ctx is cancelled. -// It is a strict superset of waitForInit and should be used by write operations -// to ensure no writes race with an in-progress migration. -func (m *Manager) waitForMigrations(ctx context.Context) error { - select { - case <-m.ready: - case <-ctx.Done(): - return errors.Wrap(ctx.Err(), "share manager not yet initialized") - } +// migrationsRunning returns true if migrations have not yet completed on this instance. +func (m *Manager) migrationsRunning() bool { select { case <-m.migrationsDone: - return nil - case <-ctx.Done(): - return errors.Wrap(ctx.Err(), "share manager migrations not yet complete") + return false + default: + return true } } @@ -396,11 +388,14 @@ func (m *Manager) ProcessEvents(ch <-chan events.Event) { func (m *Manager) Share(ctx context.Context, md *provider.ResourceInfo, g *collaboration.ShareGrant) (*collaboration.Share, error) { ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Share") defer span.End() - if err := m.waitForMigrations(ctx); err != nil { + if err := m.waitForInit(ctx); err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) return nil, err } + if m.migrationsRunning() { + return nil, errtypes.Aborted("share manager is currently migrating, please retry") + } user := ctxpkg.ContextMustGetUser(ctx) ts := utils.TSNow() @@ -603,9 +598,12 @@ func (m *Manager) Unshare(ctx context.Context, ref *collaboration.ShareReference ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "Unshare") defer span.End() - if err := m.waitForMigrations(ctx); err != nil { + if err := m.waitForInit(ctx); err != nil { return err } + if m.migrationsRunning() { + return errtypes.Aborted("share manager is currently migrating, please retry") + } s, err := m.get(ctx, ref) if err != nil { @@ -620,9 +618,12 @@ func (m *Manager) UpdateShare(ctx context.Context, ref *collaboration.ShareRefer ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "UpdateShare") defer span.End() - if err := m.waitForMigrations(ctx); err != nil { + if err := m.waitForInit(ctx); err != nil { return nil, err } + if m.migrationsRunning() { + return nil, errtypes.Aborted("share manager is currently migrating, please retry") + } var toUpdate *collaboration.Share @@ -1165,9 +1166,12 @@ func (m *Manager) UpdateReceivedShare(ctx context.Context, receivedShare *collab ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "UpdateReceivedShare") defer span.End() - if err := m.waitForMigrations(ctx); err != nil { + if err := m.waitForInit(ctx); err != nil { return nil, err } + if m.migrationsRunning() { + return nil, errtypes.Aborted("share manager is currently migrating, please retry") + } rs, err := m.getReceived(ctx, &collaboration.ShareReference{Spec: &collaboration.ShareReference_Id{Id: receivedShare.Share.Id}}) if err != nil { @@ -1330,7 +1334,10 @@ func (m *Manager) removeShare(ctx context.Context, s *collaboration.Share, skipS func (m *Manager) CleanupStaleShares(ctx context.Context) { log := appctx.GetLogger(ctx) - if err := m.waitForMigrations(ctx); err != nil { + if err := m.waitForInit(ctx); err != nil { + return + } + if m.migrationsRunning() { return } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/migrations/migration.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/migrations/migration.go index 94fd7af49b..4139d2fcd7 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/migrations/migration.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/share/manager/jsoncs3/migrations/migration.go @@ -144,7 +144,7 @@ func New(logger zerolog.Logger, // indefinitely until ctx is cancelled. A lock whose timestamp is older than // lockTTL is considered stale and will be taken over. func (m *Migrations) acquireLock(ctx context.Context) (string, error) { - m.logger.Debug().Str("instance", m.instanceID).Msg("acquiring migration lock") + m.logger.Info().Str("instance", m.instanceID).Msg("acquiring migration lock") for { // Fast path: create the lock file only if it does not exist yet. data, err := json.Marshal(lockData{Timestamp: time.Now(), InstanceID: m.instanceID}) @@ -157,7 +157,7 @@ func (m *Migrations) acquireLock(ctx context.Context) (string, error) { IfNoneMatch: []string{"*"}, }) if err == nil { - m.logger.Debug().Str("instance", m.instanceID).Msg("migration lock acquired") + m.logger.Info().Str("instance", m.instanceID).Msg("migration lock acquired") return res.Etag, nil } @@ -179,7 +179,7 @@ func (m *Migrations) acquireLock(ctx context.Context) (string, error) { if _, ok := err.(errtypes.IsNotFound); ok { // Lock was released between our upload attempt and the download; // retry acquiring it immediately. - m.logger.Debug().Str("instance", m.instanceID).Msg("migration lock vanished during read; retrying") + m.logger.Info().Str("instance", m.instanceID).Msg("migration lock vanished during read; retrying") continue } return "", err @@ -192,7 +192,7 @@ func (m *Migrations) acquireLock(ctx context.Context) (string, error) { } if stale { - m.logger.Debug(). + m.logger.Info(). Str("instance", m.instanceID). Str("held_by", existing.InstanceID). Time("lock_timestamp", existing.Timestamp). @@ -209,15 +209,15 @@ func (m *Migrations) acquireLock(ctx context.Context) (string, error) { IfMatchEtag: dl.Etag, }) if err == nil { - m.logger.Debug().Str("instance", m.instanceID).Msg("migration lock acquired via stale takeover") + m.logger.Info().Str("instance", m.instanceID).Msg("migration lock acquired via stale takeover") return res.Etag, nil } // Another instance took the stale lock before us; loop and retry. - m.logger.Debug().Str("instance", m.instanceID).Err(err).Msg("stale lock takeover lost race; retrying") + m.logger.Info().Str("instance", m.instanceID).Err(err).Msg("stale lock takeover lost race; retrying") continue } - m.logger.Debug(). + m.logger.Info(). Str("instance", m.instanceID). Str("held_by", existing.InstanceID). Time("lock_timestamp", existing.Timestamp). diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go index 94c134d3a6..b061992063 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/cache/kv.go @@ -8,21 +8,49 @@ import ( "crypto/tls" "crypto/x509" "errors" + "fmt" "os" "strings" + "syscall" "time" "github.com/cenkalti/backoff" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" + "github.com/rs/zerolog" ) -func NewNatsKeyValue(c Config) (jetstream.KeyValue, error) { +func NewNatsKeyValue(c Config, log *zerolog.Logger) (jetstream.KeyValue, error) { nodes := strings.Join(c.Nodes, ",") if nodes == "" { return nil, errors.New("at least one node is required") } - opts := []nats.Option{} + opts := []nats.Option{ + nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { + log.Error().Err(err).Msg("Disconnected from NATS. Trying to reconnect...") + }), + nats.ReconnectHandler(func(nc *nats.Conn) { + log.Error().Msgf("Successfully reconnected to NATS at: %s", nc.ConnectedUrl()) + }), + nats.ClosedHandler(func(nc *nats.Conn) { + // Alright, it's time to give up. Send ourselves a SIGTERM to trigger a graceful + // shutdown of the service. + log.Error().Msg("NATS connection closed permanently. Shutting down...") + + pid := os.Getpid() + process, err := os.FindProcess(pid) + if err != nil { + fmt.Printf("Error finding process: %v\n", err) + return + } + + err = process.Signal(syscall.SIGTERM) + if err != nil { + fmt.Printf("Error sending signal: %v\n", err) + return + } + }), + } if c.AuthUsername != "" || c.AuthPassword != "" { opts = append(opts, nats.UserInfo(c.AuthUsername, c.AuthPassword)) } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/idcache/idcache.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/idcache/idcache.go index 8c3a415461..91967f76d2 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/idcache/idcache.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/idcache/idcache.go @@ -21,9 +21,12 @@ package idcache import ( "context" "encoding/base32" + "errors" "path/filepath" "strings" + "time" + "github.com/cenkalti/backoff" "github.com/nats-io/nats.go/jetstream" "github.com/opencloud-eu/reva/v2/pkg/appctx" "github.com/opencloud-eu/reva/v2/pkg/errtypes" @@ -43,12 +46,16 @@ func NewStoreIDCache(kv jetstream.KeyValue) (*IDCache, error) { // Delete removes an entry from the cache func (c *IDCache) Delete(ctx context.Context, spaceID, nodeID string) error { var rerr error - v, err := c.kv.Get(ctx, cacheKey(spaceID, nodeID)) + v, err := retry(ctx, func() (jetstream.KeyValueEntry, error) { + return c.kv.Get(ctx, cacheKey(spaceID, nodeID)) + }) if err == nil { - rerr = c.kv.Purge(ctx, reverseCacheKey(string(v.Value()))) + rerr = retryErr(ctx, func() error { + return c.kv.Purge(ctx, reverseCacheKey(string(v.Value()))) + }) } - err = c.kv.Purge(ctx, cacheKey(spaceID, nodeID)) + err = retryErr(ctx, func() error { return c.kv.Purge(ctx, cacheKey(spaceID, nodeID)) }) if err != nil { return err } @@ -66,18 +73,22 @@ func (c *IDCache) DeleteByPath(ctx context.Context, path string) error { } appctx.GetLogger(ctx).Error().Err(err).Str("record", path).Msg("could not get spaceID and nodeID from cache") } else { - err := c.kv.Purge(ctx, baseKey) + err := retryErr(ctx, func() error { + return c.kv.Purge(ctx, baseKey) + }) if err != nil && err != jetstream.ErrKeyNotFound { appctx.GetLogger(ctx).Error().Err(err).Str("record", baseKey).Str("spaceID", spaceID).Str("nodeID", nodeID).Msg("could not purge from cache") } - err = c.kv.Purge(ctx, cacheKey(spaceID, nodeID)) + err = retryErr(ctx, func() error { + return c.kv.Purge(ctx, cacheKey(spaceID, nodeID)) + }) if err != nil && err != jetstream.ErrKeyNotFound { appctx.GetLogger(ctx).Error().Err(err).Str("record", cacheKey(spaceID, nodeID)).Str("spaceID", spaceID).Str("nodeID", nodeID).Msg("could not purge from cache") } } - watcher, err := c.kv.Watch(ctx, baseKey+".>") + watcher, err := retry(ctx, func() (jetstream.KeyWatcher, error) { return c.kv.Watch(ctx, baseKey+".>") }) if err != nil { return err } @@ -94,12 +105,16 @@ func (c *IDCache) DeleteByPath(ctx context.Context, path string) error { continue } - err = c.kv.Purge(ctx, key) + err = retryErr(ctx, func() error { + return c.kv.Purge(ctx, key) + }) if err != nil && err != jetstream.ErrKeyNotFound { appctx.GetLogger(ctx).Error().Err(err).Str("record", key).Str("spaceID", spaceID).Str("nodeID", nodeID).Msg("could not purge from cache") } - err = c.kv.Purge(ctx, cacheKey(spaceID, nodeID)) + err = retryErr(ctx, func() error { + return c.kv.Purge(ctx, cacheKey(spaceID, nodeID)) + }) if err != nil && err != jetstream.ErrKeyNotFound { appctx.GetLogger(ctx).Error().Err(err).Str("record", cacheKey(spaceID, nodeID)).Str("spaceID", spaceID).Str("nodeID", nodeID).Msg("could not purge from cache") } @@ -109,23 +124,31 @@ func (c *IDCache) DeleteByPath(ctx context.Context, path string) error { // DeletePath removes only the path entry from the cache func (c *IDCache) DeletePath(ctx context.Context, path string) error { - return c.kv.Purge(ctx, reverseCacheKey(path)) + return retryErr(ctx, func() error { + return c.kv.Purge(ctx, reverseCacheKey(path)) + }) } // Set adds a new entry to the cache func (c *IDCache) Set(ctx context.Context, spaceID, nodeID, val string) error { - _, err := c.kv.Put(ctx, cacheKey(spaceID, nodeID), []byte(val)) + _, err := retry(ctx, func() (uint64, error) { + return c.kv.Put(ctx, cacheKey(spaceID, nodeID), []byte(val)) + }) if err != nil { return err } - _, err = c.kv.Put(ctx, reverseCacheKey(val), []byte(cacheKey(spaceID, nodeID))) + _, err = retry(ctx, func() (uint64, error) { + return c.kv.Put(ctx, reverseCacheKey(val), []byte(cacheKey(spaceID, nodeID))) + }) return err } // Get returns the value for a given key func (c *IDCache) Get(ctx context.Context, spaceID, nodeID string) (string, error) { - record, err := c.kv.Get(ctx, cacheKey(spaceID, nodeID)) + record, err := retry(ctx, func() (jetstream.KeyValueEntry, error) { + return c.kv.Get(ctx, cacheKey(spaceID, nodeID)) + }) if err != nil { if err == jetstream.ErrKeyNotFound { return "", errtypes.NotFound("record not found in cache") @@ -136,7 +159,9 @@ func (c *IDCache) Get(ctx context.Context, spaceID, nodeID string) (string, erro } func (c *IDCache) getByReverseCacheKey(ctx context.Context, reverseKey string) (string, string, error) { - record, err := c.kv.Get(ctx, reverseKey) + record, err := retry(ctx, func() (jetstream.KeyValueEntry, error) { + return c.kv.Get(ctx, reverseKey) + }) if err != nil { if err == jetstream.ErrKeyNotFound { return "", "", errtypes.NotFound("record not found in cache") @@ -172,3 +197,40 @@ func reverseCacheKey(path string) string { return strings.Join(encoded, ".") } + +func retry[T any](ctx context.Context, f func() (T, error)) (T, error) { + var v T + var err error + b := backoff.NewExponentialBackOff() + for range 5 { + v, err = f() + if err == nil || err == jetstream.ErrKeyNotFound { + return v, err + } + + if !errors.Is(err, context.DeadlineExceeded) { + time.Sleep(b.NextBackOff()) + } + + appctx.GetLogger(ctx).Error().Err(err).Msg("error in jetstream kv operation, retrying") + } + return v, err +} + +func retryErr(ctx context.Context, f func() error) error { + var err error + b := backoff.NewExponentialBackOff() + for range 5 { + err = f() + if err == nil || err == jetstream.ErrKeyNotFound { + return err + } + + if !errors.Is(err, context.DeadlineExceeded) { + time.Sleep(b.NextBackOff()) + } + + appctx.GetLogger(ctx).Error().Err(err).Msg("error in jetstream kv operation, retrying") + } + return err +} diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup/lookup.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup/lookup.go index 3409749597..247735dd5f 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup/lookup.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup/lookup.go @@ -122,14 +122,6 @@ func (lu *Lookup) IDsForPath(ctx context.Context, path string) (string, string, // IDsForPath returns the space and opaque id for the given path spaceID, nodeID, err := lu.IDCache.GetByPath(ctx, path) if err != nil { - if _, ok := err.(errtypes.NotFound); !ok { - lu.log.Error().Err(err).Str("path", path).Msg("error looking up path in cache") - } - // fallback to disk - sID, nID, _, _, mErr := lu.metadataBackend.IdentifyPath(ctx, path) - if mErr == nil && nID != "" { - return sID, nID, nil - } return "", "", err } return spaceID, nodeID, nil diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/posix.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/posix.go index 0b2f616377..d09cfdbe5c 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/posix.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/posix.go @@ -73,7 +73,7 @@ func NewDefault(m map[string]interface{}, stream events.Stream, log *zerolog.Log o.IDCache.Database += "_v2" // Use a versioned bucket name to avoid conflicts with previous implementations o.IDCache.TTL = 0 // Disable TTL for the ID cache, as the posix driver relies on it for caching file IDs and we don't want them to expire - kv, err := cache.NewNatsKeyValue(o.IDCache) + kv, err := cache.NewNatsKeyValue(o.IDCache, log) if err != nil { return nil, errors.Wrap(err, "could not create nats key value store") } @@ -84,7 +84,7 @@ func NewDefault(m map[string]interface{}, stream events.Stream, log *zerolog.Log o.IDCache.Database += "_history" // Use a versioned bucket name to avoid conflicts with previous implementations o.IDCache.TTL = 24 * 60 * time.Minute - historyKv, err := cache.NewNatsKeyValue(o.IDCache) + historyKv, err := cache.NewNatsKeyValue(o.IDCache, log) if err != nil { return nil, errors.Wrap(err, "could not create nats key value store") } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/cache_test.py b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/cache_test.py deleted file mode 100644 index d354bdac91..0000000000 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/cache_test.py +++ /dev/null @@ -1,45 +0,0 @@ -import re -with open("/home/andre/src/opencloud/reva/pkg/storage/fs/posix/tree/assimilation_test.go", "r") as f: - text = f.read() - -new_content = """ -Describe("WarmupIDCache", func() { -var ( -tree *Tree -tmpDir string -logger zerolog.Logger -) - -BeforeEach(func() { -var err error -tmpDir, err = os.MkdirTemp("", "warmupidcache-*") -Expect(err).ToNot(HaveOccurred()) - -logger = zerolog.Nop() - -tree = &Tree{ -log: &logger, -options: &options.Options{ -Options: decomposedoptions.Options{ -Root: tmpDir, -}, -}, -} -}) - -AfterEach(func() { -os.RemoveAll(tmpDir) -}) - -It("returns nil for an empty directory", func() { -err := tree.WarmupIDCache(tmpDir, false, false) -Expect(err).ToNot(HaveOccurred()) -}) -}) -""" - -text = re.sub(r'Describe\("WarmupIDCache", func\(\).*$', new_content, text, flags=re.DOTALL) -text += "\n})\n" -with open("/home/andre/src/opencloud/reva/pkg/storage/fs/posix/tree/assimilation_test.go", "w") as f: - f.write(text) - diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/fix_test.py b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/fix_test.py deleted file mode 100644 index 8afcb79dc1..0000000000 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/fix_test.py +++ /dev/null @@ -1,10 +0,0 @@ -import re - -with open("assimilation_test.go", "r") as f: - text = f.read() - -# remove CreateTestStorageSpace which throws error -text = text.replace('_, err = env.CreateTestStorageSpace("personal", nil)\n\t\tExpect(err).ToNot(HaveOccurred())', '') - -with open("assimilation_test.go", "w") as f: - f.write(text) diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/generate_cache_test.py b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/generate_cache_test.py deleted file mode 100644 index d720250e2e..0000000000 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/generate_cache_test.py +++ /dev/null @@ -1,128 +0,0 @@ -import sys - -content = """ -Describe("WarmupIDCache", func() { -var ( -tree *Tree -tmpDir string -logger zerolog.Logger -ctx context.Context -) - -BeforeEach(func() { -ctx = context.Background() -var err error -tmpDir, err = os.MkdirTemp("", "warmupidcache-*") -Expect(err).ToNot(HaveOccurred()) - -logger = zerolog.Nop() - -o := &options.Options{ -Options: decomposedoptions.Options{ -Root: tmpDir, -}, -} - -// We need a backend and caches -c, _ := idcache.NewMemoryIDCache() -historyCache, _ := idcache.NewMemoryIDCache() -um := &usermapper.NullMapper{} - -backend := metadata.NewMessagePackBackend(o.FileMetadataCache) -lu, err := lookup.New(backend, um, o, &timemanager.Manager{}, c, historyCache) -Expect(err).ToNot(HaveOccurred()) - -tree = &Tree{ -log: &logger, -options: o, -lookup: lu, -} -}) - -AfterEach(func() { -os.RemoveAll(tmpDir) -}) - -It("returns nil for an empty directory", func() { -err := tree.WarmupIDCache(tmpDir, false, false) -Expect(err).ToNot(HaveOccurred()) -}) - - It("picks up new files and directories", func() { - subDir := filepath.Join(tmpDir, "sub") - err := os.Mkdir(subDir, 0755) - Expect(err).ToNot(HaveOccurred()) - - filePath := filepath.Join(subDir, "test.txt") - err = os.WriteFile(filePath, []byte("hello world"), 0644) - Expect(err).ToNot(HaveOccurred()) - - // Should not crash, tests basic traverse - err = tree.WarmupIDCache(tmpDir, false, false) - Expect(err).ToNot(HaveOccurred()) - }) - - It("verifies tree sizes and recursion", func() { - // Setup a small directory structure - subDir := filepath.Join(tmpDir, "sub2") - err := os.Mkdir(subDir, 0755) - Expect(err).ToNot(HaveOccurred()) - - nestedDir := filepath.Join(subDir, "nested") - err = os.Mkdir(nestedDir, 0755) - Expect(err).ToNot(HaveOccurred()) - - filePath := filepath.Join(nestedDir, "test.txt") - err = os.WriteFile(filePath, []byte("hello world"), 0644) // 11 bytes - Expect(err).ToNot(HaveOccurred()) - - // Run assimilation - err = tree.WarmupIDCache(tmpDir, true, false) - Expect(err).ToNot(HaveOccurred()) - - // If assimilation runs, the files will have xattrs and tree sizes evaluated - // wait, but without mocked idResolver for the Tree, it might fail? Let's check! - }) -}) -""" - -with open("assimilation_test.go", "r") as f: - text = f.read() - -import re - -# Add imports -imports = """ -import ( -"context" -"os" -"path/filepath" - -"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup" -"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata" -"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/idcache" -"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/usermapper" -"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/timemanager" - -. "github.com/onsi/ginkgo/v2" -. "github.com/onsi/gomega" -"github.com/rs/zerolog" - -"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options" -decomposedoptions "github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/options" -) -""" - -# Replace imports -start_import = text.find('import (') -end_import = text.find(')', start_import) + 1 -text = text[:start_import] + imports.strip() + text[end_import:] - -start_idx = text.find('Describe("WarmupIDCache", func() {') -end_idx = text.find('})\n\n})', start_idx) + 2 - -if start_idx != -1 and end_idx != -1: - new_text = text[:start_idx] + content.strip() + text[end_idx:] - with open("assimilation_test.go", "w") as f: - f.write(new_text) - diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/getxattr.py b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/getxattr.py deleted file mode 100644 index 612cfa79f3..0000000000 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/getxattr.py +++ /dev/null @@ -1,23 +0,0 @@ -with open("assimilation_test.go", "r") as f: - text = f.read() - -import re - -# We will inject the xattr check. -insert = """// verify that tree sizes are updated -// Since we used assimilate=true, the treesize xattr on sub2 and nested should be 11. - -b, err := env.Lookup.MetadataBackend().Get(env.Ctx, subDir, "user.oc.treesize") -Expect(err).ToNot(HaveOccurred()) -Expect(string(b)).To(Equal("11")) - -b, err = env.Lookup.MetadataBackend().Get(env.Ctx, nestedDir, "user.oc.treesize") -Expect(err).ToNot(HaveOccurred()) -Expect(string(b)).To(Equal("11"))""" - -# inject right after env.Tree.WarmupIDCache call -text = text.replace("err = env.Tree.WarmupIDCache(tmpDir+\"/users/admin\", true, false)\n\t\tExpect(err).ToNot(HaveOccurred())", "err = env.Tree.WarmupIDCache(tmpDir+\"/users/admin\", true, false)\n\t\tExpect(err).ToNot(HaveOccurred())\n\n" + insert) - -with open("assimilation_test.go", "w") as f: - f.write(text) - diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/ldap/ldap.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/ldap/ldap.go index 0a8ee54599..53ff58371b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/ldap/ldap.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/ldap/ldap.go @@ -32,6 +32,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/utils" ldapIdentity "github.com/opencloud-eu/reva/v2/pkg/utils/ldap" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -60,14 +61,14 @@ type manager struct { } // New returns a new user manager. -func New(m map[string]interface{}) (tenant.Manager, error) { +func New(m map[string]any, logger *zerolog.Logger) (tenant.Manager, error) { mgr := &manager{} err := mgr.Configure(m) if err != nil { return nil, err } - mgr.ldap, err = utils.GetLDAPClientWithReconnect(&mgr.conf.LDAPConn) + mgr.ldap, err = utils.GetLDAPClientWithReconnect(&mgr.conf.LDAPConn, logger) return mgr, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/memory/memory.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/memory/memory.go index e1e5ef4791..a9b4acef6b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/memory/memory.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/memory/memory.go @@ -27,6 +27,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/tenant" "github.com/opencloud-eu/reva/v2/pkg/tenant/manager/registry" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -57,7 +58,7 @@ type manager struct { } // New returns a new tenant manager. -func New(m map[string]interface{}) (tenant.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (tenant.Manager, error) { mgr := &manager{} err := mgr.Configure(m) return mgr, err diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/null/null.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/null/null.go index 6da25d813a..b438bf34f8 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/null/null.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/null/null.go @@ -26,6 +26,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/tenant" "github.com/opencloud-eu/reva/v2/pkg/tenant/manager/registry" + "github.com/rs/zerolog" ) func init() { @@ -36,7 +37,7 @@ type manager struct { } // New returns a tenant manager implementation that return NOT FOUND or empty result set for every call -func New(m map[string]interface{}) (tenant.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (tenant.Manager, error) { return &manager{}, nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/registry/registry.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/registry/registry.go index 0892ba15b0..d520958d7e 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/registry/registry.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/tenant/manager/registry/registry.go @@ -20,11 +20,12 @@ package registry import ( "github.com/opencloud-eu/reva/v2/pkg/tenant" + "github.com/rs/zerolog" ) // NewFunc is the function that tenant managers // should register at init time. -type NewFunc func(map[string]interface{}) (tenant.Manager, error) +type NewFunc func(map[string]any, *zerolog.Logger) (tenant.Manager, error) // NewFuncs is a map containing all the registered user managers. var NewFuncs = map[string]NewFunc{} diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go index 3333ae5421..e024c72d0c 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/demo/demo.go @@ -28,6 +28,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/user" "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" + "github.com/rs/zerolog" "google.golang.org/protobuf/proto" ) @@ -40,7 +41,7 @@ type manager struct { } // New returns a new user manager. -func New(m map[string]interface{}) (user.Manager, error) { +func New(m map[string]interface{}, _ *zerolog.Logger) (user.Manager, error) { mgr := &manager{} err := mgr.Configure(m) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go index 389acffef4..6ff5c4020a 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/json/json.go @@ -25,14 +25,14 @@ import ( "strconv" "strings" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/mitchellh/mapstructure" + "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/user" "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" "github.com/pkg/errors" + "github.com/rs/zerolog" "google.golang.org/protobuf/proto" - - userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "github.com/opencloud-eu/reva/v2/pkg/errtypes" ) func init() { @@ -65,7 +65,7 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a user manager implementation that reads a json file to provide user metadata. -func New(m map[string]interface{}) (user.Manager, error) { +func New(m map[string]interface{}, _ *zerolog.Logger) (user.Manager, error) { mgr := &manager{} err := mgr.Configure(m) if err != nil { diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go index 0156287293..b936aea0a4 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap/ldap.go @@ -34,6 +34,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" "github.com/opencloud-eu/reva/v2/pkg/utils" ldapIdentity "github.com/opencloud-eu/reva/v2/pkg/utils/ldap" + "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" ) @@ -68,14 +69,18 @@ func parseConfig(m map[string]interface{}) (*config, error) { } // New returns a user manager implementation that connects to a LDAP server to provide user metadata. -func New(m map[string]interface{}) (user.Manager, error) { +func New(m map[string]any, logger *zerolog.Logger) (user.Manager, error) { + if logger == nil { + nop := zerolog.Nop() + logger = &nop + } mgr := &manager{} err := mgr.Configure(m) if err != nil { return nil, err } - mgr.ldapClient, err = utils.GetLDAPClientWithReconnect(&mgr.c.LDAPConn) + mgr.ldapClient, err = utils.GetLDAPClientWithReconnect(&mgr.c.LDAPConn, logger) return mgr, err } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go index 518b80d6b1..637641c91b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/memory/memory.go @@ -30,6 +30,7 @@ import ( "github.com/opencloud-eu/reva/v2/pkg/user" "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry" "github.com/pkg/errors" + "github.com/rs/zerolog" ) func init() { @@ -68,7 +69,7 @@ type manager struct { } // New returns a new user manager. -func New(m map[string]interface{}) (user.Manager, error) { +func New(m map[string]any, _ *zerolog.Logger) (user.Manager, error) { mgr := &manager{} err := mgr.Configure(m) return mgr, err diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/registry/registry.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/registry/registry.go index c47658bdf0..442789a269 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/registry/registry.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/registry/registry.go @@ -20,11 +20,12 @@ package registry import ( "github.com/opencloud-eu/reva/v2/pkg/user" + "github.com/rs/zerolog" ) // NewFunc is the function that user managers // should register at init time. -type NewFunc func(map[string]interface{}) (user.Manager, error) +type NewFunc func(map[string]any, *zerolog.Logger) (user.Manager, error) // NewFuncs is a map containing all the registered user managers. var NewFuncs = map[string]NewFunc{} diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap.go index cd6459b12c..4f628e965b 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap.go @@ -26,9 +26,9 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/opencloud-eu/reva/v2/pkg/appctx" - "github.com/opencloud-eu/reva/v2/pkg/logger" ldapReconnect "github.com/opencloud-eu/reva/v2/pkg/utils/ldap" "github.com/pkg/errors" + "github.com/rs/zerolog" ) // LDAPConn holds the basic parameter for setting up an @@ -44,10 +44,14 @@ type LDAPConn struct { // GetLDAPClientWithReconnect initializes a long-lived LDAP connection that // automatically reconnects on connection errors. It allows to set TLS options // e.g. to add trusted Certificates or disable Certificate verification -func GetLDAPClientWithReconnect(c *LDAPConn) (ldap.Client, error) { +func GetLDAPClientWithReconnect(c *LDAPConn, logger *zerolog.Logger) (ldap.Client, error) { var tlsConf *tls.Config + if logger == nil { + nop := zerolog.Nop() + logger = &nop + } if c.Insecure { - logger.New().Warn().Msg("SSL Certificate verification is disabled. This is strongly discouraged for production environments.") + logger.Warn().Msg("SSL Certificate verification is disabled. This is strongly discouraged for production environments.") tlsConf = &tls.Config{ //nolint:gosec // We need the ability to run with "insecure" (dev/testing) InsecureSkipVerify: true, @@ -73,6 +77,8 @@ func GetLDAPClientWithReconnect(c *LDAPConn) (ldap.Client, error) { TLSConfig: tlsConf, }, ) + + conn.SetLogger(logger) return conn, nil } diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/reconnect.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/reconnect.go index b13c3051f3..d233af6b3d 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/reconnect.go +++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/utils/ldap/reconnect.go @@ -221,6 +221,7 @@ func (c *ConnWithReconnect) ldapConnect(config Config) (*ldap.Conn, error) { return nil, err } c.logger.Debug().Msg("LDAP Connected") + l.SetTimeout(30 * time.Second) if config.BindDN != "" { c.logger.Debug().Msgf("Binding as %s", config.BindDN) err = l.Bind(config.BindDN, config.BindPassword) diff --git a/vendor/github.com/shamaton/msgpack/v2/.tagpr b/vendor/github.com/shamaton/msgpack/v2/.tagpr new file mode 100644 index 0000000000..b8cc60c298 --- /dev/null +++ b/vendor/github.com/shamaton/msgpack/v2/.tagpr @@ -0,0 +1,7 @@ +[tagpr] + releaseBranch = v2 + versionFile = - + vPrefix = true + changelog = true + release = true + fixedMajorVersion = 2 diff --git a/vendor/github.com/shamaton/msgpack/v2/README.md b/vendor/github.com/shamaton/msgpack/v2/README.md index 93e3a5c85e..2f9ce16ffa 100644 --- a/vendor/github.com/shamaton/msgpack/v2/README.md +++ b/vendor/github.com/shamaton/msgpack/v2/README.md @@ -7,11 +7,13 @@ [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fshamaton%2Fmsgpack.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fshamaton%2Fmsgpack?ref=badge_shield) ## 📣 Announcement: `time.Time` decoding defaults to **UTC** in v3 + Starting with **v3.0.0**, when decoding MessagePack **Timestamp** into Go’s `time.Time`, the default `Location` will be **UTC** (previously `Local`). The instant is unchanged. To keep the old behavior, use `SetDecodedTimeAsLocal()`. ## Features + * Supported types : primitive / array / slice / struct / map / interface{} and time.Time * Renaming fields via `msgpack:"field_name"` * Omitting fields via `msgpack:"-"` @@ -22,11 +24,13 @@ To keep the old behavior, use `SetDecodedTimeAsLocal()`. ## Installation Current version is **msgpack/v2**. + ```sh go get -u github.com/shamaton/msgpack/v2 ``` ## Quick Start + ```go package main @@ -36,36 +40,36 @@ import ( ) type Struct struct { - String string + String string } // simple func main() { - v := Struct{String: "msgpack"} + v := Struct{String: "msgpack"} - d, err := msgpack.Marshal(v) - if err != nil { - panic(err) - } - r := Struct{} - if err = msgpack.Unmarshal(d, &r); err != nil { - panic(err) - } + d, err := msgpack.Marshal(v) + if err != nil { + panic(err) + } + r := Struct{} + if err = msgpack.Unmarshal(d, &r); err != nil { + panic(err) + } } // streaming func handle(w http.ResponseWriter, r *http.Request) { - var body Struct - if err := msgpack.UnmarshalRead(r, &body); err != nil { - panic(err) - } - if err := msgpack.MarshalWrite(w, body); err != nil { - panic(err) - } + var body Struct + if err := msgpack.UnmarshalRead(r, &body); err != nil { + panic(err) + } + if err := msgpack.MarshalWrite(w, body); err != nil { + panic(err) + } } ``` -## 📣 Announcement: `time.Time` decoding defaults to **UTC** in v3 +## v3: `time.Time` decoding defaults to **UTC** **TL;DR:** Starting with **v3.0.0**, when decoding MessagePack **Timestamp** into Go’s `time.Time`, the default `Location` will be **UTC** (previously `Local`). The **instant** is unchanged—only the display/location changes. This avoids host-dependent differences and aligns with common distributed systems practice. @@ -111,6 +115,7 @@ msgpack.SetDecodedTimeAsUTC() ``` ## Benchmark + This result made from [shamaton/msgpack_bench](https://github.com/shamaton/msgpack_bench) ![msgpack_bench](https://github.com/user-attachments/assets/ed5bc4c5-a149-4083-98b8-ee6820c00eae) diff --git a/vendor/github.com/shamaton/msgpack/v2/ext/decode.go b/vendor/github.com/shamaton/msgpack/v2/ext/decode.go index 6cae0eb11d..b3f672f5e7 100644 --- a/vendor/github.com/shamaton/msgpack/v2/ext/decode.go +++ b/vendor/github.com/shamaton/msgpack/v2/ext/decode.go @@ -23,8 +23,7 @@ type Decoder interface { } // DecoderCommon provides common utility methods for decoding data from bytes. -type DecoderCommon struct { -} +type DecoderCommon struct{} // ReadSize1 reads a single byte from the given index in the byte slice. // Returns the byte and the new index after reading. diff --git a/vendor/github.com/shamaton/msgpack/v2/ext/encode.go b/vendor/github.com/shamaton/msgpack/v2/ext/encode.go index 38afeefb6d..7fc22c8345 100644 --- a/vendor/github.com/shamaton/msgpack/v2/ext/encode.go +++ b/vendor/github.com/shamaton/msgpack/v2/ext/encode.go @@ -26,8 +26,7 @@ type Encoder interface { // EncoderCommon provides utility methods for encoding various types of values into bytes. // It includes methods to encode integers and unsigned integers of different sizes, // as well as methods to write raw byte slices into a target byte slice. -type EncoderCommon struct { -} +type EncoderCommon struct{} // SetByte1Int64 encodes a single byte from the given int64 value into the byte slice at the specified offset. // Returns the new offset after writing the byte. diff --git a/vendor/github.com/shamaton/msgpack/v2/ext/encode_stream.go b/vendor/github.com/shamaton/msgpack/v2/ext/encode_stream.go index 0172792bfd..be0cfcb638 100644 --- a/vendor/github.com/shamaton/msgpack/v2/ext/encode_stream.go +++ b/vendor/github.com/shamaton/msgpack/v2/ext/encode_stream.go @@ -32,14 +32,16 @@ func CreateStreamWriter(w io.Writer, buf *common.Buffer) StreamWriter { // WriteByte1Int64 writes a single byte representation of an int64 value. func (w *StreamWriter) WriteByte1Int64(value int64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value), ) } // WriteByte2Int64 writes a two-byte representation of an int64 value. func (w *StreamWriter) WriteByte2Int64(value int64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>8), byte(value), ) @@ -47,7 +49,8 @@ func (w *StreamWriter) WriteByte2Int64(value int64) error { // WriteByte4Int64 writes a four-byte representation of an int64 value. func (w *StreamWriter) WriteByte4Int64(value int64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>24), byte(value>>16), byte(value>>8), @@ -57,7 +60,8 @@ func (w *StreamWriter) WriteByte4Int64(value int64) error { // WriteByte8Int64 writes an eight-byte representation of an int64 value. func (w *StreamWriter) WriteByte8Int64(value int64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>56), byte(value>>48), byte(value>>40), @@ -71,14 +75,16 @@ func (w *StreamWriter) WriteByte8Int64(value int64) error { // WriteByte1Uint64 writes a single byte representation of a uint64 value. func (w *StreamWriter) WriteByte1Uint64(value uint64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value), ) } // WriteByte2Uint64 writes a two-byte representation of a uint64 value. func (w *StreamWriter) WriteByte2Uint64(value uint64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>8), byte(value), ) @@ -86,7 +92,8 @@ func (w *StreamWriter) WriteByte2Uint64(value uint64) error { // WriteByte4Uint64 writes a four-byte representation of a uint64 value. func (w *StreamWriter) WriteByte4Uint64(value uint64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>24), byte(value>>16), byte(value>>8), @@ -96,7 +103,8 @@ func (w *StreamWriter) WriteByte4Uint64(value uint64) error { // WriteByte8Uint64 writes an eight-byte representation of a uint64 value. func (w *StreamWriter) WriteByte8Uint64(value uint64) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>56), byte(value>>48), byte(value>>40), @@ -110,14 +118,16 @@ func (w *StreamWriter) WriteByte8Uint64(value uint64) error { // WriteByte1Int writes a single byte representation of an int value. func (w *StreamWriter) WriteByte1Int(value int) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value), ) } // WriteByte2Int writes a two-byte representation of an int value. func (w *StreamWriter) WriteByte2Int(value int) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>8), byte(value), ) @@ -125,7 +135,8 @@ func (w *StreamWriter) WriteByte2Int(value int) error { // WriteByte4Int writes a four-byte representation of an int value. func (w *StreamWriter) WriteByte4Int(value int) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>24), byte(value>>16), byte(value>>8), @@ -135,7 +146,8 @@ func (w *StreamWriter) WriteByte4Int(value int) error { // WriteByte4Uint32 writes a four-byte representation of a uint32 value. func (w *StreamWriter) WriteByte4Uint32(value uint32) error { - return w.buf.Write(w.w, + return w.buf.Write( + w.w, byte(value>>24), byte(value>>16), byte(value>>8), diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/common/common.go b/vendor/github.com/shamaton/msgpack/v2/internal/common/common.go index ffd37673bc..0c7f837253 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/common/common.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/common/common.go @@ -6,8 +6,7 @@ import ( ) // Common is used encoding/decoding -type Common struct { -} +type Common struct{} // CheckField returns flag whether should encode/decode or not and field name func (c *Common) CheckField(field reflect.StructField) (public, omit bool, name string) { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/decoding.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/decoding.go index e81c174edc..5e77636658 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/decoding.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/decoding.go @@ -19,7 +19,7 @@ type decoder struct { func Decode(data []byte, v interface{}, asArray bool) error { d := decoder{data: data, asArray: asArray} - if d.data == nil || len(d.data) < 1 { + if len(d.data) < 1 { return def.ErrNoData } rv := reflect.ValueOf(v) diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/ext.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/ext.go index a8567a8acd..b3a4ee9afc 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/ext.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/ext.go @@ -1,12 +1,17 @@ package decoding import ( + "encoding/binary" + + "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/ext" "github.com/shamaton/msgpack/v2/time" ) -var extCoderMap = map[int8]ext.Decoder{time.Decoder.Code(): time.Decoder} -var extCoders = []ext.Decoder{time.Decoder} +var ( + extCoderMap = map[int8]ext.Decoder{time.Decoder.Code(): time.Decoder} + extCoders = []ext.Decoder{time.Decoder} +) // AddExtDecoder adds decoders for extension types. func AddExtDecoder(f ext.Decoder) { @@ -45,6 +50,57 @@ func updateExtCoders() { } } +func (d *decoder) extEndOffset(offset int) (bool, int, error) { + code, offset, err := d.readSize1(offset) + if err != nil { + return false, 0, err + } + return d.extEndOffsetWithCode(code, offset) +} + +func (d *decoder) extEndOffsetWithCode(code byte, offset int) (bool, int, error) { + switch code { + case def.Fixext1: + _, offset, err := d.readSizeN(offset, def.Byte1+def.Byte1) + return true, offset, err + case def.Fixext2: + _, offset, err := d.readSizeN(offset, def.Byte1+def.Byte2) + return true, offset, err + case def.Fixext4: + _, offset, err := d.readSizeN(offset, def.Byte1+def.Byte4) + return true, offset, err + case def.Fixext8: + _, offset, err := d.readSizeN(offset, def.Byte1+def.Byte8) + return true, offset, err + case def.Fixext16: + _, offset, err := d.readSizeN(offset, def.Byte1+def.Byte16) + return true, offset, err + case def.Ext8: + size, offset, err := d.readSize1(offset) + if err != nil { + return true, 0, err + } + _, offset, err = d.readSizeN(offset, def.Byte1+int(size)) + return true, offset, err + case def.Ext16: + sizeBytes, offset, err := d.readSize2(offset) + if err != nil { + return true, 0, err + } + _, offset, err = d.readSizeN(offset, def.Byte1+int(binary.BigEndian.Uint16(sizeBytes))) + return true, offset, err + case def.Ext32: + sizeBytes, offset, err := d.readSize4(offset) + if err != nil { + return true, 0, err + } + _, offset, err = d.readSizeN(offset, def.Byte1+int(binary.BigEndian.Uint32(sizeBytes))) + return true, offset, err + default: + return false, 0, nil + } +} + /* var zero = time.Unix(0,0) diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/int.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/int.go index 92967ebbb2..3acf83aeaa 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/int.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/int.go @@ -16,7 +16,6 @@ func (d *decoder) isNegativeFixNum(v byte) bool { } func (d *decoder) asInt(offset int, k reflect.Kind) (int64, int, error) { - code, _, err := d.readSize1(offset) if err != nil { return 0, 0, err diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/interface.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/interface.go index c47a58164e..b429bddb99 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/interface.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/interface.go @@ -163,13 +163,19 @@ func (d *decoder) asInterface(offset int, k reflect.Kind) (interface{}, int, err */ // ext - for i := range extCoders { - if extCoders[i].IsType(offset, &d.data) { - v, offset, err := extCoders[i].AsValue(offset, k, &d.data) - if err != nil { - return nil, 0, err + isExt, _, err := d.extEndOffset(offset) + if err != nil { + return nil, 0, err + } + if isExt { + for i := range extCoders { + if extCoders[i].IsType(offset, &d.data) { + v, offset, err := extCoders[i].AsValue(offset, k, &d.data) + if err != nil { + return nil, 0, err + } + return v, offset, nil } - return v, offset, nil } } return nil, 0, d.errorTemplate(code, k) diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/string.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/string.go index 9aaf74dcaa..864156f4b5 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/string.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/string.go @@ -7,8 +7,10 @@ import ( "github.com/shamaton/msgpack/v2/def" ) -var emptyString = "" -var emptyBytes = []byte{} +var ( + emptyString = "" + emptyBytes = []byte{} +) func (d *decoder) isCodeString(code byte) bool { return d.isFixString(code) || code == def.Str8 || code == def.Str16 || code == def.Str32 diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/struct.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/struct.go index 5af60b106a..787d075a97 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/struct.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/struct.go @@ -18,8 +18,10 @@ type structCacheTypeArray struct { } // struct cache map -var mapSCTM = sync.Map{} -var mapSCTA = sync.Map{} +var ( + mapSCTM = sync.Map{} + mapSCTA = sync.Map{} +) func (d *decoder) setStruct(rv reflect.Value, offset int, k reflect.Kind) (int, error) { /* @@ -33,17 +35,23 @@ func (d *decoder) setStruct(rv reflect.Value, offset int, k reflect.Kind) (int, } */ - for i := range extCoders { - if extCoders[i].IsType(offset, &d.data) { - v, offset, err := extCoders[i].AsValue(offset, k, &d.data) - if err != nil { - return 0, err - } + isExt, _, err := d.extEndOffset(offset) + if err != nil { + return 0, err + } + if isExt { + for i := range extCoders { + if extCoders[i].IsType(offset, &d.data) { + v, offset, err := extCoders[i].AsValue(offset, k, &d.data) + if err != nil { + return 0, err + } - // Validate that the receptacle is of the right value type. - if rv.Type() == reflect.TypeOf(v) { - rv.Set(reflect.ValueOf(v)) - return offset, nil + // Validate that the receptacle is of the right value type. + if rv.Type() == reflect.TypeOf(v) { + rv.Set(reflect.ValueOf(v)) + return offset, nil + } } } } @@ -277,38 +285,14 @@ func (d *decoder) jumpOffset(offset int) (int, error) { } offset = o - case code == def.Fixext1: - offset += def.Byte1 + def.Byte1 - case code == def.Fixext2: - offset += def.Byte1 + def.Byte2 - case code == def.Fixext4: - offset += def.Byte1 + def.Byte4 - case code == def.Fixext8: - offset += def.Byte1 + def.Byte8 - case code == def.Fixext16: - offset += def.Byte1 + def.Byte16 - - case code == def.Ext8: - b, o, err := d.readSize1(offset) + default: + isExt, o, err := d.extEndOffsetWithCode(code, offset) if err != nil { return 0, err } - o += def.Byte1 + int(b) - offset = o - case code == def.Ext16: - bs, o, err := d.readSize2(offset) - if err != nil { - return 0, err + if isExt { + offset = o } - o += def.Byte1 + int(binary.BigEndian.Uint16(bs)) - offset = o - case code == def.Ext32: - bs, o, err := d.readSize4(offset) - if err != nil { - return 0, err - } - o += def.Byte1 + int(binary.BigEndian.Uint32(bs)) - offset = o } return offset, nil diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/uint.go b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/uint.go index cd739fc9a1..d59658da1e 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/decoding/uint.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/decoding/uint.go @@ -8,7 +8,6 @@ import ( ) func (d *decoder) asUint(offset int, k reflect.Kind) (uint64, int, error) { - code, _, err := d.readSize1(offset) if err != nil { return 0, 0, err diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/encoding.go b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/encoding.go index 62081b5d84..0e2b16ae4d 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/encoding.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/encoding.go @@ -253,7 +253,6 @@ func (e *encoder) calcLength(l int) (int, error) { } func (e *encoder) create(rv reflect.Value, offset int) int { - switch rv.Kind() { case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: v := rv.Uint() diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/ext.go b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/ext.go index c90f7fda8e..9f4024d98c 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/ext.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/ext.go @@ -7,8 +7,10 @@ import ( "github.com/shamaton/msgpack/v2/time" ) -var extCoderMap = map[reflect.Type]ext.Encoder{time.Encoder.Type(): time.Encoder} -var extCoders = []ext.Encoder{time.Encoder} +var ( + extCoderMap = map[reflect.Type]ext.Encoder{time.Encoder.Type(): time.Encoder} + extCoders = []ext.Encoder{time.Encoder} +) // AddExtEncoder adds encoders for extension types. func AddExtEncoder(f ext.Encoder) { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/map.go b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/map.go index 78ac9776c5..e457601cad 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/map.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/map.go @@ -296,7 +296,6 @@ func (e *encoder) calcFixedMap(rv reflect.Value) (int, bool) { } func (e *encoder) writeMapLength(l int, offset int) int { - // format if l <= 0x0f { offset = e.setByte1Int(def.FixMap+l, offset) diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/slice.go b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/slice.go index bae88c47ce..a4a983c717 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/slice.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/slice.go @@ -127,7 +127,6 @@ func (e *encoder) writeSliceLength(l int, offset int) int { } func (e *encoder) writeFixedSlice(rv reflect.Value, offset int) (int, bool) { - switch sli := rv.Interface().(type) { case []int: offset = e.writeSliceLength(len(sli), offset) diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/struct.go b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/struct.go index 3b6ed30735..1c2515e219 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/encoding/struct.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/encoding/struct.go @@ -19,11 +19,12 @@ type structCache struct { var cachemap = sync.Map{} -type structCalcFunc func(rv reflect.Value) (int, error) -type structWriteFunc func(rv reflect.Value, offset int) int +type ( + structCalcFunc func(rv reflect.Value) (int, error) + structWriteFunc func(rv reflect.Value, offset int) int +) func (e *encoder) getStructCalc(typ reflect.Type) structCalcFunc { - for j := range extCoders { if extCoders[j].Type() == typ { return extCoders[j].CalcByteSize @@ -33,11 +34,9 @@ func (e *encoder) getStructCalc(typ reflect.Type) structCalcFunc { return e.calcStructArray } return e.calcStructMap - } func (e *encoder) calcStruct(rv reflect.Value) (int, error) { - //if isTime, tm := e.isDateTime(rv); isTime { // size := e.calcTime(tm) // return size, nil @@ -178,7 +177,6 @@ func (e *encoder) calcSizeWithOmitEmpty(rv reflect.Value, name string, omit bool } func (e *encoder) getStructWriter(typ reflect.Type) structWriteFunc { - for i := range extCoders { if extCoders[i].Type() == typ { return func(rv reflect.Value, offset int) int { @@ -213,7 +211,6 @@ func (e *encoder) writeStruct(rv reflect.Value, offset int) int { } func (e *encoder) writeStructArray(rv reflect.Value, offset int) int { - cache, _ := cachemap.Load(rv.Type()) c := cache.(*structCache) @@ -236,7 +233,6 @@ func (e *encoder) writeStructArray(rv reflect.Value, offset int) int { } func (e *encoder) writeStructMap(rv reflect.Value, offset int) int { - cache, _ := cachemap.Load(rv.Type()) c := cache.(*structCache) diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/bin.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/bin.go index ed3fce8750..48a6f3b33d 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/bin.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/bin.go @@ -17,7 +17,6 @@ func (d *decoder) isCodeBin(v byte) bool { } func (d *decoder) asBinWithCode(code byte, k reflect.Kind) ([]byte, error) { - switch code { case def.Bin8: l, err := d.readSize1() diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/decoding.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/decoding.go index d2e452cb1e..1afbe525af 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/decoding.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/decoding.go @@ -29,7 +29,8 @@ func Decode(r io.Reader, v interface{}, asArray bool) error { rv = rv.Elem() - d := decoder{r: r, + d := decoder{ + r: r, buf: common.GetBuffer(), asArray: asArray, } diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/ext.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/ext.go index 4324705bf6..4e1885bcad 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/ext.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/ext.go @@ -2,13 +2,16 @@ package decoding import ( "encoding/binary" + "github.com/shamaton/msgpack/v2/def" "github.com/shamaton/msgpack/v2/ext" "github.com/shamaton/msgpack/v2/time" ) -var extCoderMap = map[int8]ext.StreamDecoder{time.StreamDecoder.Code(): time.StreamDecoder} -var extCoders = []ext.StreamDecoder{time.StreamDecoder} +var ( + extCoderMap = map[int8]ext.StreamDecoder{time.StreamDecoder.Code(): time.StreamDecoder} + extCoders = []ext.StreamDecoder{time.StreamDecoder} +) // AddExtDecoder adds decoders for extension types. func AddExtDecoder(f ext.StreamDecoder) { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/string.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/string.go index c756465056..d50d9f8309 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/string.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/string.go @@ -7,8 +7,10 @@ import ( "github.com/shamaton/msgpack/v2/def" ) -var emptyString = "" -var emptyBytes = []byte{} +var ( + emptyString = "" + emptyBytes = []byte{} +) func (d *decoder) isCodeString(code byte) bool { return d.isFixString(code) || code == def.Str8 || code == def.Str16 || code == def.Str32 @@ -83,7 +85,7 @@ func (d *decoder) asStringByteByLength(l int, _ reflect.Kind) ([]byte, error) { if l < 1 { return emptyBytes, nil } - + // avoid common buffer reference return d.copySizeN(l) } diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/struct.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/struct.go index d0cbda0cbd..1d60ed83df 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/struct.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/decoding/struct.go @@ -18,8 +18,10 @@ type structCacheTypeArray struct { } // struct cache map -var mapSCTM = sync.Map{} -var mapSCTA = sync.Map{} +var ( + mapSCTM = sync.Map{} + mapSCTA = sync.Map{} +) func (d *decoder) setStruct(code byte, rv reflect.Value, k reflect.Kind) error { if len(extCoders) > 0 { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/encoding.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/encoding.go index d53d9f4ee5..57d79455a7 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/encoding.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/encoding.go @@ -41,7 +41,6 @@ func Encode(w io.Writer, v any, asArray bool) error { } func (e *encoder) create(rv reflect.Value) error { - switch rv.Kind() { case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: v := rv.Uint() diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/ext.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/ext.go index 940feb0308..a6147135ef 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/ext.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/ext.go @@ -7,8 +7,10 @@ import ( "github.com/shamaton/msgpack/v2/time" ) -var extCoderMap = map[reflect.Type]ext.StreamEncoder{time.StreamEncoder.Type(): time.StreamEncoder} -var extCoders = []ext.StreamEncoder{time.StreamEncoder} +var ( + extCoderMap = map[reflect.Type]ext.StreamEncoder{time.StreamEncoder.Type(): time.StreamEncoder} + extCoders = []ext.StreamEncoder{time.StreamEncoder} +) // AddExtEncoder adds encoders for extension types. func AddExtEncoder(f ext.StreamEncoder) { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/map.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/map.go index 2f97ef4794..7fbaacd294 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/map.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/map.go @@ -8,7 +8,6 @@ import ( ) func (e *encoder) writeMapLength(l int) error { - // format if l <= 0x0f { if err := e.setByte1Int(def.FixMap + l); err != nil { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/set.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/set.go index 787acec865..409c9a5fc6 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/set.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/set.go @@ -5,14 +5,16 @@ func (e *encoder) setByte1Int64(value int64) error { } func (e *encoder) setByte2Int64(value int64) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>8), byte(value), ) } func (e *encoder) setByte4Int64(value int64) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>24), byte(value>>16), byte(value>>8), @@ -21,7 +23,8 @@ func (e *encoder) setByte4Int64(value int64) error { } func (e *encoder) setByte8Int64(value int64) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>56), byte(value>>48), byte(value>>40), @@ -38,14 +41,16 @@ func (e *encoder) setByte1Uint64(value uint64) error { } func (e *encoder) setByte2Uint64(value uint64) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>8), byte(value), ) } func (e *encoder) setByte4Uint64(value uint64) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>24), byte(value>>16), byte(value>>8), @@ -54,7 +59,8 @@ func (e *encoder) setByte4Uint64(value uint64) error { } func (e *encoder) setByte8Uint64(value uint64) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>56), byte(value>>48), byte(value>>40), @@ -67,20 +73,23 @@ func (e *encoder) setByte8Uint64(value uint64) error { } func (e *encoder) setByte1Int(value int) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value), ) } func (e *encoder) setByte2Int(value int) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>8), byte(value), ) } func (e *encoder) setByte4Int(value int) error { - return e.buf.Write(e.w, + return e.buf.Write( + e.w, byte(value>>24), byte(value>>16), byte(value>>8), diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/slice.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/slice.go index 6b7649a4b9..9d7cd6dd43 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/slice.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/slice.go @@ -32,7 +32,6 @@ func (e *encoder) writeSliceLength(l int) error { } func (e *encoder) writeFixedSlice(rv reflect.Value) (bool, error) { - switch sli := rv.Interface().(type) { case []int: for _, v := range sli { diff --git a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/struct.go b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/struct.go index 06eeac5854..8a51403188 100644 --- a/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/struct.go +++ b/vendor/github.com/shamaton/msgpack/v2/internal/stream/encoding/struct.go @@ -23,7 +23,6 @@ var cachemap = sync.Map{} type structWriteFunc func(rv reflect.Value) error func (e *encoder) getStructWriter(typ reflect.Type) structWriteFunc { - for i := range extCoders { if extCoders[i].Type() == typ { return func(rv reflect.Value) error { @@ -40,7 +39,6 @@ func (e *encoder) getStructWriter(typ reflect.Type) structWriteFunc { } func (e *encoder) writeStruct(rv reflect.Value) error { - for i := range extCoders { if extCoders[i].Type() == rv.Type() { w := ext.CreateStreamWriter(e.w, e.buf) diff --git a/vendor/github.com/shamaton/msgpack/v2/time/decode.go b/vendor/github.com/shamaton/msgpack/v2/time/decode.go index e5962d2bd6..947e741c05 100644 --- a/vendor/github.com/shamaton/msgpack/v2/time/decode.go +++ b/vendor/github.com/shamaton/msgpack/v2/time/decode.go @@ -24,30 +24,86 @@ func (td *timeDecoder) Code() int8 { return def.TimeStamp } -func (td *timeDecoder) IsType(offset int, d *[]byte) bool { - code, offset := td.ReadSize1(offset, d) +func (td *timeDecoder) readSize1Safe(index int, d *[]byte) (byte, int, bool) { + if len(*d) < index+def.Byte1 { + return 0, 0, false + } + v, next := td.ReadSize1(index, d) + return v, next, true +} - if code == def.Fixext4 { - t, _ := td.ReadSize1(offset, d) - return int8(t) == td.Code() - } else if code == def.Fixext8 { - t, _ := td.ReadSize1(offset, d) - return int8(t) == td.Code() - } else if code == def.Ext8 { - l, offset := td.ReadSize1(offset, d) - t, _ := td.ReadSize1(offset, d) - return l == 12 && int8(t) == td.Code() +func (td *timeDecoder) readSize4Safe(index int, d *[]byte) ([]byte, int, bool) { + if len(*d) < index+def.Byte4 { + return nil, 0, false + } + v, next := td.ReadSize4(index, d) + return v, next, true +} + +func (td *timeDecoder) readSize8Safe(index int, d *[]byte) ([]byte, int, bool) { + if len(*d) < index+def.Byte8 { + return nil, 0, false + } + v, next := td.ReadSize8(index, d) + return v, next, true +} + +func (td *timeDecoder) IsType(offset int, d *[]byte) bool { + code, offset, ok := td.readSize1Safe(offset, d) + if !ok { + return false + } + + switch code { + case def.Fixext4: + t, _, ok := td.readSize1Safe(offset, d) + if !ok || int8(t) != td.Code() { + return false + } + _, _, ok = td.readSize4Safe(offset+def.Byte1, d) + return ok + case def.Fixext8: + t, _, ok := td.readSize1Safe(offset, d) + if !ok || int8(t) != td.Code() { + return false + } + _, _, ok = td.readSize8Safe(offset+def.Byte1, d) + return ok + case def.Ext8: + l, offset, ok := td.readSize1Safe(offset, d) + if !ok { + return false + } + t, _, ok := td.readSize1Safe(offset, d) + if !ok || l != 12 || int8(t) != td.Code() { + return false + } + _, _, ok = td.readSize4Safe(offset+def.Byte1, d) + if !ok { + return false + } + _, _, ok = td.readSize8Safe(offset+def.Byte1+def.Byte4, d) + return ok } return false } func (td *timeDecoder) AsValue(offset int, k reflect.Kind, d *[]byte) (interface{}, int, error) { - code, offset := td.ReadSize1(offset, d) + code, offset, ok := td.readSize1Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } switch code { case def.Fixext4: - _, offset = td.ReadSize1(offset, d) - bs, offset := td.ReadSize4(offset, d) + _, offset, ok = td.readSize1Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } + bs, offset, ok := td.readSize4Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } v := time.Unix(int64(binary.BigEndian.Uint32(bs)), 0) if decodeAsLocal { return v, offset, nil @@ -55,8 +111,14 @@ func (td *timeDecoder) AsValue(offset int, k reflect.Kind, d *[]byte) (interface return v.UTC(), offset, nil case def.Fixext8: - _, offset = td.ReadSize1(offset, d) - bs, offset := td.ReadSize8(offset, d) + _, offset, ok = td.readSize1Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } + bs, offset, ok := td.readSize8Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } data64 := binary.BigEndian.Uint64(bs) nano := int64(data64 >> 34) if nano > 999999999 { @@ -69,10 +131,22 @@ func (td *timeDecoder) AsValue(offset int, k reflect.Kind, d *[]byte) (interface return v.UTC(), offset, nil case def.Ext8: - _, offset = td.ReadSize1(offset, d) - _, offset = td.ReadSize1(offset, d) - nanobs, offset := td.ReadSize4(offset, d) - secbs, offset := td.ReadSize8(offset, d) + _, offset, ok = td.readSize1Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } + _, offset, ok = td.readSize1Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } + nanobs, offset, ok := td.readSize4Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } + secbs, offset, ok := td.readSize8Safe(offset, d) + if !ok { + return zero, 0, def.ErrTooShortBytes + } nano := binary.BigEndian.Uint32(nanobs) if nano > 999999999 { return zero, 0, fmt.Errorf("in timestamp 96 formats, nanoseconds must not be larger than 999999999 : %d", nano) diff --git a/vendor/github.com/shamaton/msgpack/v2/time/decode_stream.go b/vendor/github.com/shamaton/msgpack/v2/time/decode_stream.go index 0de21c72b7..e570e3df48 100644 --- a/vendor/github.com/shamaton/msgpack/v2/time/decode_stream.go +++ b/vendor/github.com/shamaton/msgpack/v2/time/decode_stream.go @@ -33,6 +33,9 @@ func (td *timeStreamDecoder) IsType(code byte, innerType int8, dataLength int) b func (td *timeStreamDecoder) ToValue(code byte, data []byte, k reflect.Kind) (interface{}, error) { switch code { case def.Fixext4: + if len(data) < def.Byte4 { + return zero, def.ErrTooShortBytes + } v := time.Unix(int64(binary.BigEndian.Uint32(data)), 0) if decodeAsLocal { return v, nil @@ -40,6 +43,9 @@ func (td *timeStreamDecoder) ToValue(code byte, data []byte, k reflect.Kind) (in return v.UTC(), nil case def.Fixext8: + if len(data) < def.Byte8 { + return zero, def.ErrTooShortBytes + } data64 := binary.BigEndian.Uint64(data) nano := int64(data64 >> 34) if nano > 999999999 { @@ -52,6 +58,9 @@ func (td *timeStreamDecoder) ToValue(code byte, data []byte, k reflect.Kind) (in return v.UTC(), nil case def.Ext8: + if len(data) < def.Byte4+def.Byte8 { + return zero, def.ErrTooShortBytes + } nano := binary.BigEndian.Uint32(data[:4]) if nano > 999999999 { return zero, fmt.Errorf("in timestamp 96 formats, nanoseconds must not be larger than 999999999 : %d", nano) diff --git a/vendor/go.etcd.io/etcd/api/v3/version/version.go b/vendor/go.etcd.io/etcd/api/v3/version/version.go index 618f1f1b20..5d748b563c 100644 --- a/vendor/go.etcd.io/etcd/api/v3/version/version.go +++ b/vendor/go.etcd.io/etcd/api/v3/version/version.go @@ -26,7 +26,7 @@ import ( var ( // MinClusterVersion is the min cluster version this etcd binary is compatible with. MinClusterVersion = "3.0.0" - Version = "3.6.10" + Version = "3.6.11" APIVersion = "unknown" // Git SHA Value will be set during build diff --git a/vendor/modules.txt b/vendor/modules.txt index 28b03d0293..5513c96374 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -470,11 +470,12 @@ github.com/go-git/go-billy/v5/helper/polyfill github.com/go-git/go-billy/v5/memfs github.com/go-git/go-billy/v5/osfs github.com/go-git/go-billy/v5/util -# github.com/go-git/go-git/v5 v5.19.0 +# github.com/go-git/go-git/v5 v5.19.1 ## explicit; go 1.25.0 github.com/go-git/go-git/v5 github.com/go-git/go-git/v5/config github.com/go-git/go-git/v5/internal/path_util +github.com/go-git/go-git/v5/internal/pathutil github.com/go-git/go-git/v5/internal/revision github.com/go-git/go-git/v5/internal/url github.com/go-git/go-git/v5/plumbing @@ -1362,7 +1363,7 @@ github.com/opencloud-eu/icap-client # github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go -# github.com/opencloud-eu/reva/v2 v2.45.0 +# github.com/opencloud-eu/reva/v2 v2.46.0 ## explicit; go 1.25.0 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime @@ -1980,7 +1981,7 @@ github.com/sethvargo/go-diceware/diceware # github.com/sethvargo/go-password v0.3.1 ## explicit; go 1.21 github.com/sethvargo/go-password/password -# github.com/shamaton/msgpack/v2 v2.4.0 +# github.com/shamaton/msgpack/v2 v2.4.1 ## explicit; go 1.20 github.com/shamaton/msgpack/v2 github.com/shamaton/msgpack/v2/def @@ -2257,7 +2258,7 @@ go.etcd.io/bbolt go.etcd.io/bbolt/errors go.etcd.io/bbolt/internal/common go.etcd.io/bbolt/internal/freelist -# go.etcd.io/etcd/api/v3 v3.6.10 +# go.etcd.io/etcd/api/v3 v3.6.11 ## explicit; go 1.25.0 go.etcd.io/etcd/api/v3/authpb go.etcd.io/etcd/api/v3/etcdserverpb @@ -2266,7 +2267,7 @@ go.etcd.io/etcd/api/v3/mvccpb go.etcd.io/etcd/api/v3/v3rpc/rpctypes go.etcd.io/etcd/api/v3/version go.etcd.io/etcd/api/v3/versionpb -# go.etcd.io/etcd/client/pkg/v3 v3.6.10 +# go.etcd.io/etcd/client/pkg/v3 v3.6.11 ## explicit; go 1.25.0 go.etcd.io/etcd/client/pkg/v3/fileutil go.etcd.io/etcd/client/pkg/v3/logutil @@ -2275,7 +2276,7 @@ go.etcd.io/etcd/client/pkg/v3/tlsutil go.etcd.io/etcd/client/pkg/v3/transport go.etcd.io/etcd/client/pkg/v3/types go.etcd.io/etcd/client/pkg/v3/verify -# go.etcd.io/etcd/client/v3 v3.6.10 +# go.etcd.io/etcd/client/v3 v3.6.11 ## explicit; go 1.25.0 go.etcd.io/etcd/client/v3 go.etcd.io/etcd/client/v3/credentials