Bump reva

This commit is contained in:
André Duffeck
2026-02-11 10:05:53 +01:00
committed by Ralf Haferkamp
parent 99aba9cfa1
commit ae3582afa3
36 changed files with 619 additions and 228 deletions
+5 -5
View File
@@ -65,7 +65,7 @@ require (
github.com/open-policy-agent/opa v1.12.3
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45
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
@@ -130,7 +130,7 @@ require (
github.com/Masterminds/semver/v3 v3.4.0 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
github.com/agnivade/levenshtein v1.2.1 // indirect
github.com/ajg/form v1.5.1 // indirect
@@ -181,7 +181,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/crewjam/httperr v0.2.0 // indirect
github.com/crewjam/saml v0.4.14 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
@@ -208,7 +208,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.6.2 // indirect
github.com/go-git/go-git/v5 v5.13.2 // indirect
github.com/go-git/go-git/v5 v5.16.5 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-kit/log v0.2.1 // indirect
@@ -353,7 +353,7 @@ require (
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
github.com/spacewander/go-suffix-tree v0.0.0-20191010040751-0865e368c784 // indirect
github.com/spf13/cast v1.10.0 // indirect
+12 -12
View File
@@ -91,8 +91,8 @@ github.com/Nerzal/gocloak/v13 v13.9.0 h1:YWsJsdM5b0yhM2Ba3MLydiOlujkBry4TtdzfIzS
github.com/Nerzal/gocloak/v13 v13.9.0/go.mod h1:YYuDcXZ7K2zKECyVP7pPqjKxx2AzYSpKDj8d6GuyM10=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -272,8 +272,8 @@ github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1n
github.com/cs3org/go-cs3apis v0.0.0-20250908152307-4ca807afe54e h1:fC/BWMVWNFlSbzvSp2xTaH0qpJiq7ScRrOsCzpgi1xI=
github.com/cs3org/go-cs3apis v0.0.0-20250908152307-4ca807afe54e/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -327,8 +327,8 @@ github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0o
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/egirna/icap v0.0.0-20181108071049-d5ee18bd70bc h1:6IxmRbXV8WXVkcYcTzkU219A3UZeNMX/e6X2sve1wXA=
github.com/egirna/icap v0.0.0-20181108071049-d5ee18bd70bc/go.mod h1:FdVN2WHg7zOHhJ7kZQdDorfFhIfqZaHttjAzDDvAXHE=
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/emvi/iso-639-1 v1.1.1 h1:7jrl1Sqw9ZYWmCOaH+cpQotLbGr/khwlLPXlBvE8WXU=
@@ -393,8 +393,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
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.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
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=
@@ -969,8 +969,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.20260204102724-10bcda1b3068 h1:i09YEVYbiUBMhxyak93REn/ZJOTRhAN4I3PXp2nCXgU=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8 h1:psi6BI08DZSxWRc+CxIU05kG1vQrpJ9Alv1U1d8HMLY=
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8/go.mod h1:EIhSI2Tv1FCdtvv5AO4ublX4yrM+1KZCcrkSImDVCDg=
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45 h1:MWLmUuf9LmEf4hD7RYFtm6n5DysPdAU+LhnAcJCG1kA=
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45/go.mod h1:IJPAAnJlnX7kkC1Kmg2Uq3we95rkU+IMq+3Jbntv2JU=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@@ -1142,8 +1142,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
+20
View File
@@ -6,6 +6,7 @@
package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors"
import (
"fmt"
"strconv"
)
@@ -178,3 +179,22 @@ type ErrMalformedMessage string
func (dke ErrMalformedMessage) Error() string {
return "openpgp: malformed message " + string(dke)
}
// ErrEncryptionKeySelection is returned if encryption key selection fails (v2 API).
type ErrEncryptionKeySelection struct {
PrimaryKeyId string
PrimaryKeyErr error
EncSelectionKeyId *string
EncSelectionErr error
}
func (eks ErrEncryptionKeySelection) Error() string {
prefix := fmt.Sprintf("openpgp: key selection for primary key %s:", eks.PrimaryKeyId)
if eks.PrimaryKeyErr != nil {
return fmt.Sprintf("%s invalid primary key: %s", prefix, eks.PrimaryKeyErr)
}
if eks.EncSelectionKeyId != nil {
return fmt.Sprintf("%s invalid encryption key %s: %s", prefix, *eks.EncSelectionKeyId, eks.EncSelectionErr)
}
return fmt.Sprintf("%s no encryption key: %s", prefix, eks.EncSelectionErr)
}
+12
View File
@@ -173,6 +173,11 @@ type Config struct {
// weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
// The default behavior, when the config or flag is nil, is to enable the feature.
NonDeterministicSignaturesViaNotation *bool
// InsecureAllowAllKeyFlagsWhenMissing determines how a key without valid key flags is handled.
// When set to true, a key without flags is treated as if all flags are enabled.
// This behavior is consistent with GPG.
InsecureAllowAllKeyFlagsWhenMissing bool
}
func (c *Config) Random() io.Reader {
@@ -403,6 +408,13 @@ func (c *Config) RandomizeSignaturesViaNotation() bool {
return *c.NonDeterministicSignaturesViaNotation
}
func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
if c == nil {
return false
}
return c.InsecureAllowAllKeyFlagsWhenMissing
}
// BoolPointer is a helper function to set a boolean pointer in the Config.
// e.g., config.CheckPacketSequence = BoolPointer(true)
func BoolPointer(value bool) *bool {
+6 -1
View File
@@ -1048,12 +1048,17 @@ func (pk *PublicKey) VerifyDirectKeySignature(sig *Signature) (err error) {
// KeyIdString returns the public key's fingerprint in capital hex
// (e.g. "6C7EE1B8621CC013").
func (pk *PublicKey) KeyIdString() string {
return fmt.Sprintf("%X", pk.Fingerprint[12:20])
return fmt.Sprintf("%016X", pk.KeyId)
}
// KeyIdShortString returns the short form of public key's fingerprint
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
// This function will return the full key id for v5 and v6 keys
// since the short key id is undefined for them.
func (pk *PublicKey) KeyIdShortString() string {
if pk.Version >= 5 {
return pk.KeyIdString()
}
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
}
+3 -1
View File
@@ -1288,7 +1288,9 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
if sig.IssuerKeyId != nil && sig.Version == 4 {
keyId := make([]byte, 8)
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId})
// Note: making this critical breaks RPM <=4.16.
// See: https://github.com/ProtonMail/go-crypto/issues/263
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId})
}
// Notation Data
for _, notation := range sig.Notations {
+48 -1
View File
@@ -6,6 +6,51 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased] ##
## [0.4.1] - 2025-01-28 ##
### Fixed ###
- The restrictions added for `root` paths passed to `SecureJoin` in 0.4.0 was
found to be too strict and caused some regressions when folks tried to
update, so this restriction has been relaxed to only return an error if the
path contains a `..` component. We still recommend users use `filepath.Clean`
(and even `filepath.EvalSymlinks`) on the `root` path they are using, but at
least you will no longer be punished for "trivial" unclean paths.
## [0.4.0] - 2025-01-13 ##
### Breaking ####
- `SecureJoin(VFS)` will now return an error if the provided `root` is not a
`filepath.Clean`'d path.
While it is ultimately the responsibility of the caller to ensure the root is
a safe path to use, passing a path like `/symlink/..` as a root would result
in the `SecureJoin`'d path being placed in `/` even though `/symlink/..`
might be a different directory, and so we should more strongly discourage
such usage.
All major users of `securejoin.SecureJoin` already ensure that the paths they
provide are safe (and this is ultimately a question of user error), but
removing this foot-gun is probably a good idea. Of course, this is
necessarily a breaking API change (though we expect no real users to be
affected by it).
Thanks to [Erik Sjölund](https://github.com/eriksjolund), who initially
reported this issue as a possible security issue.
- `MkdirAll` and `MkdirHandle` now take an `os.FileMode`-style mode argument
instead of a raw `unix.S_*`-style mode argument, which may cause compile-time
type errors depending on how you use `filepath-securejoin`. For most users,
there will be no change in behaviour aside from the type change (as the
bottom `0o777` bits are the same in both formats, and most users are probably
only using those bits).
However, if you were using `unix.S_ISVTX` to set the sticky bit with
`MkdirAll(Handle)` you will need to switch to `os.ModeSticky` otherwise you
will get a runtime error with this update. In addition, the error message you
will get from passing `unix.S_ISUID` and `unix.S_ISGID` will be different as
they are treated as invalid bits now (note that previously passing said bits
was also an error).
## [0.3.6] - 2024-12-17 ##
### Compatibility ###
@@ -193,7 +238,9 @@ This is our first release of `github.com/cyphar/filepath-securejoin`,
containing a full implementation with a coverage of 93.5% (the only missing
cases are the error cases, which are hard to mocktest at the moment).
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...HEAD
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...HEAD
[0.4.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.0...v0.4.1
[0.4.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...v0.4.0
[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6
[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5
[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
+1 -1
View File
@@ -1 +1 @@
0.3.6
0.4.1
+45 -4
View File
@@ -1,5 +1,5 @@
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
// Copyright (C) 2017-2025 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -24,6 +24,31 @@ func IsNotExist(err error) bool {
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)
}
// errUnsafeRoot is returned if the user provides SecureJoinVFS with a path
// that contains ".." components.
var errUnsafeRoot = errors.New("root path provided to SecureJoin contains '..' components")
// stripVolume just gets rid of the Windows volume included in a path. Based on
// some godbolt tests, the Go compiler is smart enough to make this a no-op on
// Linux.
func stripVolume(path string) string {
return path[len(filepath.VolumeName(path)):]
}
// hasDotDot checks if the path contains ".." components in a platform-agnostic
// way.
func hasDotDot(path string) bool {
// If we are on Windows, strip any volume letters. It turns out that
// C:..\foo may (or may not) be a valid pathname and we need to handle that
// leading "..".
path = stripVolume(path)
// Look for "/../" in the path, but we need to handle leading and trailing
// ".."s by adding separators. Doing this with filepath.Separator is ugly
// so just convert to Unix-style "/" first.
path = filepath.ToSlash(path)
return strings.Contains("/"+path+"/", "/../")
}
// SecureJoinVFS joins the two given path components (similar to [filepath.Join]) except
// that the returned path is guaranteed to be scoped inside the provided root
// path (when evaluated). Any symbolic links in the path are evaluated with the
@@ -46,7 +71,22 @@ func IsNotExist(err error) bool {
// provided via direct input or when evaluating symlinks. Therefore:
//
// "C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
//
// If the provided root is not [filepath.Clean] then an error will be returned,
// as such root paths are bordering on somewhat unsafe and using such paths is
// not best practice. We also strongly suggest that any root path is first
// fully resolved using [filepath.EvalSymlinks] or otherwise constructed to
// avoid containing symlink components. Of course, the root also *must not* be
// attacker-controlled.
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
// The root path must not contain ".." components, otherwise when we join
// the subpath we will end up with a weird path. We could work around this
// in other ways but users shouldn't be giving us non-lexical root paths in
// the first place.
if hasDotDot(root) {
return "", errUnsafeRoot
}
// Use the os.* VFS implementation if none was specified.
if vfs == nil {
vfs = osVFS{}
@@ -59,9 +99,10 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
linksWalked int
)
for remainingPath != "" {
if v := filepath.VolumeName(remainingPath); v != "" {
remainingPath = remainingPath[len(v):]
}
// On Windows, if we managed to end up at a path referencing a volume,
// drop the volume to make sure we don't end up with broken paths or
// escaping the root volume.
remainingPath = stripVolume(remainingPath)
// Get the next path component.
var part string
+35 -14
View File
@@ -21,6 +21,33 @@ var (
errPossibleAttack = errors.New("possible attack detected")
)
// modePermExt is like os.ModePerm except that it also includes the set[ug]id
// and sticky bits.
const modePermExt = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
//nolint:cyclop // this function needs to handle a lot of cases
func toUnixMode(mode os.FileMode) (uint32, error) {
sysMode := uint32(mode.Perm())
if mode&os.ModeSetuid != 0 {
sysMode |= unix.S_ISUID
}
if mode&os.ModeSetgid != 0 {
sysMode |= unix.S_ISGID
}
if mode&os.ModeSticky != 0 {
sysMode |= unix.S_ISVTX
}
// We don't allow file type bits.
if mode&os.ModeType != 0 {
return 0, fmt.Errorf("%w %+.3o (%s): type bits not permitted", errInvalidMode, mode, mode)
}
// We don't allow other unknown modes.
if mode&^modePermExt != 0 || sysMode&unix.S_IFMT != 0 {
return 0, fmt.Errorf("%w %+.3o (%s): unknown mode bits", errInvalidMode, mode, mode)
}
return sysMode, nil
}
// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
// in two respects:
//
@@ -39,17 +66,17 @@ var (
// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after
// doing [MkdirAll]. If you intend to open the directory after creating it, you
// should use MkdirAllHandle.
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err error) {
// Make sure there are no os.FileMode bits set.
if mode&^0o7777 != 0 {
return nil, fmt.Errorf("%w for mkdir 0o%.3o", errInvalidMode, mode)
func MkdirAllHandle(root *os.File, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
unixMode, err := toUnixMode(mode)
if err != nil {
return nil, err
}
// On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid
// bits. We could also silently ignore them but since we have very few
// users it seems more prudent to return an error so users notice that
// these bits will not be set.
if mode&^0o1777 != 0 {
return nil, fmt.Errorf("%w for mkdir 0o%.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
if unixMode&^0o1777 != 0 {
return nil, fmt.Errorf("%w for mkdir %+.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
}
// Try to open as much of the path as possible.
@@ -104,9 +131,6 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath)
}
// Make sure the mode doesn't have any type bits.
mode &^= unix.S_IFMT
// Create the remaining components.
for _, part := range remainingParts {
switch part {
@@ -123,7 +147,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
// directory at the same time as us. In that case, just continue on as
// if we created it (if the created inode is not a directory, the
// following open call will fail).
if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil && !errors.Is(err, unix.EEXIST) {
if err := unix.Mkdirat(int(currentDir.Fd()), part, unixMode); err != nil && !errors.Is(err, unix.EEXIST) {
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
// Make the error a bit nicer if the directory is dead.
if deadErr := isDeadInode(currentDir); deadErr != nil {
@@ -196,10 +220,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
// If you plan to open the directory after you have created it or want to use
// an open directory handle as the root, you should use [MkdirAllHandle] instead.
// This function is a wrapper around [MkdirAllHandle].
//
// NOTE: The mode argument must be set the unix mode bits (unix.S_I...), not
// the Go generic mode bits ([os.FileMode]...).
func MkdirAll(root, unsafePath string, mode int) error {
func MkdirAll(root, unsafePath string, mode os.FileMode) error {
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
if err != nil {
return err
+46 -10
View File
@@ -8,6 +8,7 @@ import (
"time"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
@@ -72,9 +73,16 @@ type CloneOptions struct {
// Tags describe how the tags will be fetched from the remote repository,
// by default is AllTags.
Tags TagMode
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// ProxyOptions provides info required for connecting to a proxy.
ProxyOptions transport.ProxyOptions
@@ -153,9 +161,16 @@ type PullOptions struct {
// Force allows the pull to update a local branch even when the remote
// branch does not descend from it.
Force bool
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// ProxyOptions provides info required for connecting to a proxy.
ProxyOptions transport.ProxyOptions
@@ -211,9 +226,16 @@ type FetchOptions struct {
// Force allows the fetch to update a local branch even when the remote
// branch does not descend from it.
Force bool
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// ProxyOptions provides info required for connecting to a proxy.
ProxyOptions transport.ProxyOptions
@@ -267,9 +289,16 @@ type PushOptions struct {
// Force allows the push to update a remote branch even when the local
// branch does not descend from it.
Force bool
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// RequireRemoteRefs only allows a remote ref to be updated if its current
// value is the one specified here.
@@ -693,9 +722,16 @@ func (o *CreateTagOptions) loadConfigTagger(r *Repository) error {
type ListOptions struct {
// Auth credentials, if required, to use with the remote repository.
Auth transport.AuthMethod
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
InsecureSkipTLS bool
// CABundle specify additional ca bundle with system cert pool
// ClientCert is the client certificate to use for mutual TLS authentication
// over the HTTPS protocol.
ClientCert []byte
// ClientKey is the client key to use for mutual TLS authentication over
// the HTTPS protocol.
ClientKey []byte
// CABundle specifies an additional CA bundle to use together with the
// system cert pool.
CABundle []byte
// PeelingOption defines how peeled objects are handled during a
// remote list.
+25 -6
View File
@@ -1,9 +1,11 @@
package idxfile
import (
"bufio"
"bytes"
"crypto"
"encoding/hex"
"errors"
"fmt"
"io"
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -25,12 +27,15 @@ const (
// Decoder reads and decodes idx files from an input stream.
type Decoder struct {
*bufio.Reader
io.Reader
h hash.Hash
}
// NewDecoder builds a new idx stream decoder, that reads from r.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{bufio.NewReader(r)}
h := hash.New(crypto.SHA1)
tr := io.TeeReader(r, h)
return &Decoder{tr, h}
}
// Decode reads from the stream and decode the content into the MemoryIndex struct.
@@ -45,7 +50,7 @@ func (d *Decoder) Decode(idx *MemoryIndex) error {
readObjectNames,
readCRC32,
readOffsets,
readChecksums,
readPackChecksum,
}
for _, f := range flow {
@@ -54,11 +59,21 @@ func (d *Decoder) Decode(idx *MemoryIndex) error {
}
}
actual := d.h.Sum(nil)
if err := readIdxChecksum(idx, d); err != nil {
return err
}
if !bytes.Equal(actual, idx.IdxChecksum[:]) {
return fmt.Errorf("%w: checksum mismatch: %q instead of %q",
ErrMalformedIdxFile, hex.EncodeToString(idx.IdxChecksum[:]), hex.EncodeToString(actual))
}
return nil
}
func validateHeader(r io.Reader) error {
var h = make([]byte, 4)
h := make([]byte, 4)
if _, err := io.ReadFull(r, h); err != nil {
return err
}
@@ -165,11 +180,15 @@ func readOffsets(idx *MemoryIndex, r io.Reader) error {
return nil
}
func readChecksums(idx *MemoryIndex, r io.Reader) error {
func readPackChecksum(idx *MemoryIndex, r io.Reader) error {
if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
return err
}
return nil
}
func readIdxChecksum(idx *MemoryIndex, r io.Reader) error {
if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil {
return err
}
+25 -23
View File
@@ -4,6 +4,7 @@ import (
"bytes"
"io"
"sort"
"sync"
encbin "encoding/binary"
@@ -57,8 +58,9 @@ type MemoryIndex struct {
PackfileChecksum [hash.Size]byte
IdxChecksum [hash.Size]byte
offsetHash map[int64]plumbing.Hash
offsetHashIsFull bool
offsetHash map[int64]plumbing.Hash
offsetBuildOnce sync.Once
mu sync.RWMutex
}
var _ Index = (*MemoryIndex)(nil)
@@ -126,13 +128,13 @@ func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) {
offset := idx.getOffset(k, i)
if !idx.offsetHashIsFull {
// Save the offset for reverse lookup
if idx.offsetHash == nil {
idx.offsetHash = make(map[int64]plumbing.Hash)
}
idx.offsetHash[int64(offset)] = h
// Save the offset for reverse lookup
idx.mu.Lock()
if idx.offsetHash == nil {
idx.offsetHash = make(map[int64]plumbing.Hash)
}
idx.offsetHash[int64(offset)] = h
idx.mu.Unlock()
return int64(offset), nil
}
@@ -173,20 +175,17 @@ func (idx *MemoryIndex) FindHash(o int64) (plumbing.Hash, error) {
var hash plumbing.Hash
var ok bool
if idx.offsetHash != nil {
if hash, ok = idx.offsetHash[o]; ok {
return hash, nil
}
var genErr error
idx.offsetBuildOnce.Do(func() {
genErr = idx.genOffsetHash()
})
if genErr != nil {
return plumbing.ZeroHash, genErr
}
// Lazily generate the reverse offset/hash map if required.
if !idx.offsetHashIsFull || idx.offsetHash == nil {
if err := idx.genOffsetHash(); err != nil {
return plumbing.ZeroHash, err
}
hash, ok = idx.offsetHash[o]
}
idx.mu.RLock()
hash, ok = idx.offsetHash[o]
idx.mu.RUnlock()
if !ok {
return plumbing.ZeroHash, plumbing.ErrObjectNotFound
@@ -202,8 +201,7 @@ func (idx *MemoryIndex) genOffsetHash() error {
return err
}
idx.offsetHash = make(map[int64]plumbing.Hash, count)
idx.offsetHashIsFull = true
offsetHash := make(map[int64]plumbing.Hash, count)
var hash plumbing.Hash
i := uint32(0)
@@ -212,11 +210,15 @@ func (idx *MemoryIndex) genOffsetHash() error {
for secondLevel := uint32(0); i < fanoutValue; i++ {
copy(hash[:], idx.Names[mappedFirstLevel][secondLevel*objectIDLength:])
offset := int64(idx.getOffset(mappedFirstLevel, int(secondLevel)))
idx.offsetHash[offset] = hash
offsetHash[offset] = hash
secondLevel++
}
}
idx.mu.Lock()
idx.offsetHash = offsetHash
idx.mu.Unlock()
return nil
}
+1 -1
View File
@@ -30,7 +30,7 @@ type Reader struct {
func NewReader(r io.Reader) (*Reader, error) {
zlib, err := sync.GetZlibReader(r)
if err != nil {
return nil, packfile.ErrZLib.AddDetails(err.Error())
return nil, packfile.ErrZLib.AddDetails("%s", err.Error())
}
return &Reader{
+16 -20
View File
@@ -47,7 +47,6 @@ type Parser struct {
oi []*objectInfo
oiByHash map[plumbing.Hash]*objectInfo
oiByOffset map[int64]*objectInfo
checksum plumbing.Hash
cache *cache.BufferLRU
// delta content by offset, only used if source is not seekable
@@ -133,28 +132,27 @@ func (p *Parser) onFooter(h plumbing.Hash) error {
// Parse start decoding phase of the packfile.
func (p *Parser) Parse() (plumbing.Hash, error) {
if err := p.init(); err != nil {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
if err := p.indexObjects(); err != nil {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
var err error
p.checksum, err = p.scanner.Checksum()
checksum, err := p.scanner.Checksum()
if err != nil && err != io.EOF {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
if err := p.resolveDeltas(); err != nil {
return plumbing.ZeroHash, err
return plumbing.ZeroHash, wrapEOF(err)
}
if err := p.onFooter(p.checksum); err != nil {
return plumbing.ZeroHash, err
if err := p.onFooter(checksum); err != nil {
return plumbing.ZeroHash, wrapEOF(err)
}
return p.checksum, nil
return checksum, nil
}
func (p *Parser) init() error {
@@ -218,7 +216,7 @@ func (p *Parser) indexObjects() error {
if !ok {
// can't find referenced object in this pack file
// this must be a "thin" pack.
parent = &objectInfo{ //Placeholder parent
parent = &objectInfo{ // Placeholder parent
SHA1: oh.Reference,
ExternalRef: true, // mark as an external reference that must be resolved
Type: plumbing.AnyObject,
@@ -531,6 +529,13 @@ func (p *Parser) readData(w io.Writer, o *objectInfo) error {
return nil
}
func wrapEOF(err error) error {
if err == io.ErrUnexpectedEOF || err == io.EOF {
return fmt.Errorf("%w: %w", ErrMalformedPackFile, err)
}
return err
}
// applyPatchBase applies the patch to target.
//
// Note that ota will be updated based on the description in resolveObject.
@@ -558,15 +563,6 @@ func applyPatchBase(ota *objectInfo, base io.ReaderAt, delta io.Reader, target i
return nil
}
func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) {
hasher := plumbing.NewHasher(t, int64(len(data)))
if _, err := hasher.Write(data); err != nil {
return plumbing.ZeroHash, err
}
return hasher.Sum(), nil
}
type objectInfo struct {
Offset int64
Length int64
+32 -13
View File
@@ -3,12 +3,15 @@ package packfile
import (
"bufio"
"bytes"
"crypto"
"errors"
"fmt"
"hash"
gohash "hash"
"hash/crc32"
"io"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/hash"
"github.com/go-git/go-git/v5/utils/binary"
"github.com/go-git/go-git/v5/utils/ioutil"
"github.com/go-git/go-git/v5/utils/sync"
@@ -24,6 +27,8 @@ var (
ErrUnsupportedVersion = NewError("unsupported packfile version")
// ErrSeekNotSupported returned if seek is not support
ErrSeekNotSupported = NewError("not seek support")
// ErrMalformedPackFile is returned by the parser when the pack file is corrupted.
ErrMalformedPackFile = errors.New("malformed PACK file")
)
// ObjectHeader contains the information related to the object, this information
@@ -37,8 +42,9 @@ type ObjectHeader struct {
}
type Scanner struct {
r *scannerReader
crc hash.Hash32
r *scannerReader
crc gohash.Hash32
packHasher hash.Hash
// pendingObject is used to detect if an object has been read, or still
// is waiting to be read
@@ -56,10 +62,12 @@ func NewScanner(r io.Reader) *Scanner {
_, ok := r.(io.ReadSeeker)
crc := crc32.NewIEEE()
hasher := hash.New(crypto.SHA1)
return &Scanner{
r: newScannerReader(r, crc),
r: newScannerReader(r, io.MultiWriter(crc, hasher)),
crc: crc,
IsSeekable: ok,
packHasher: hasher,
}
}
@@ -68,6 +76,7 @@ func (s *Scanner) Reset(r io.Reader) {
s.r.Reset(r)
s.crc.Reset()
s.packHasher.Reset()
s.IsSeekable = ok
s.pendingObject = nil
s.version = 0
@@ -114,7 +123,7 @@ func (s *Scanner) Header() (version, objects uint32, err error) {
// readSignature reads a returns the signature field in the packfile.
func (s *Scanner) readSignature() ([]byte, error) {
var sig = make([]byte, 4)
sig := make([]byte, 4)
if _, err := io.ReadFull(s.r, sig); err != nil {
return []byte{}, err
}
@@ -322,7 +331,6 @@ func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err erro
func (s *Scanner) ReadObject() (io.ReadCloser, error) {
s.pendingObject = nil
zr, err := sync.GetZlibReader(s.r)
if err != nil {
return nil, fmt.Errorf("zlib reset error: %s", err)
}
@@ -374,7 +382,18 @@ func (s *Scanner) Checksum() (plumbing.Hash, error) {
return plumbing.ZeroHash, err
}
return binary.ReadHash(s.r)
s.r.Flush()
actual := plumbing.Hash(s.packHasher.Sum(nil))
packChecksum, err := binary.ReadHash(s.r)
if err != nil {
return plumbing.ZeroHash, err
}
if actual != packChecksum {
return plumbing.ZeroHash, fmt.Errorf("%w: checksum mismatch: %q instead of %q", ErrMalformedPackFile, packChecksum, actual)
}
return packChecksum, nil
}
// Close reads the reader until io.EOF
@@ -401,17 +420,17 @@ func (s *Scanner) Flush() error {
// to the crc32 hash writer.
type scannerReader struct {
reader io.Reader
crc io.Writer
writer io.Writer
rbuf *bufio.Reader
wbuf *bufio.Writer
offset int64
}
func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
func newScannerReader(r io.Reader, w io.Writer) *scannerReader {
sr := &scannerReader{
rbuf: bufio.NewReader(nil),
wbuf: bufio.NewWriterSize(nil, 64),
crc: h,
rbuf: bufio.NewReader(nil),
wbuf: bufio.NewWriterSize(nil, 64),
writer: w,
}
sr.Reset(r)
@@ -421,7 +440,7 @@ func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
func (r *scannerReader) Reset(reader io.Reader) {
r.reader = reader
r.rbuf.Reset(r.reader)
r.wbuf.Reset(r.crc)
r.wbuf.Reset(r.writer)
r.offset = 0
if seeker, ok := r.reader.(io.ReadSeeker); ok {
+72
View File
@@ -62,10 +62,55 @@ type Commit struct {
ParentHashes []plumbing.Hash
// Encoding is the encoding of the commit.
Encoding MessageEncoding
// List of extra headers of the commit
ExtraHeaders []ExtraHeader
s storer.EncodedObjectStorer
}
// ExtraHeader holds any non-standard header
type ExtraHeader struct {
// Header name
Key string
// Value of the header
Value string
}
// Implement fmt.Formatter for ExtraHeader
func (h ExtraHeader) Format(f fmt.State, verb rune) {
switch verb {
case 'v':
fmt.Fprintf(f, "ExtraHeader{Key: %v, Value: %v}", h.Key, h.Value)
default:
fmt.Fprintf(f, "%s", h.Key)
if len(h.Value) > 0 {
fmt.Fprint(f, " ")
// Content may be spread on multiple lines, if so we need to
// prepend each of them with a space for "continuation".
value := strings.TrimSuffix(h.Value, "\n")
lines := strings.Split(value, "\n")
fmt.Fprint(f, strings.Join(lines, "\n "))
}
}
}
// Parse an extra header and indicate whether it may be continue on the next line
func parseExtraHeader(line []byte) (ExtraHeader, bool) {
split := bytes.SplitN(line, []byte{' '}, 2)
out := ExtraHeader {
Key: string(bytes.TrimRight(split[0], "\n")),
Value: "",
}
if len(split) == 2 {
out.Value += string(split[1])
return out, true
} else {
return out, false
}
}
// GetCommit gets a commit from an object storer and decodes it.
func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
o, err := s.EncodedObject(plumbing.CommitObject, h)
@@ -204,6 +249,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
var mergetag bool
var pgpsig bool
var msgbuf bytes.Buffer
var extraheader *ExtraHeader = nil
for {
line, err := r.ReadBytes('\n')
if err != nil && err != io.EOF {
@@ -230,7 +276,19 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
}
}
if extraheader != nil {
if len(line) > 0 && line[0] == ' ' {
extraheader.Value += string(line[1:])
continue
} else {
extraheader.Value = strings.TrimRight(extraheader.Value, "\n")
c.ExtraHeaders = append(c.ExtraHeaders, *extraheader)
extraheader = nil
}
}
if !message {
original_line := line
line = bytes.TrimSpace(line)
if len(line) == 0 {
message = true
@@ -261,6 +319,13 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
case headerpgp:
c.PGPSignature += string(data) + "\n"
pgpsig = true
default:
h, maybecontinued := parseExtraHeader(original_line)
if maybecontinued {
extraheader = &h
} else {
c.ExtraHeaders = append(c.ExtraHeaders, h)
}
}
} else {
msgbuf.Write(line)
@@ -341,6 +406,13 @@ func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) {
}
}
for _, header := range c.ExtraHeaders {
if _, err = fmt.Fprintf(w, "\n%s", header); err != nil {
return err
}
}
if c.PGPSignature != "" && includeSig {
if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil {
return err
@@ -262,9 +262,8 @@ func decodeShallow(p *advRefsDecoder) decoderStateFn {
p.line = bytes.TrimPrefix(p.line, shallow)
if len(p.line) != hashSize {
p.error(fmt.Sprintf(
"malformed shallow hash: wrong length, expected 40 bytes, read %d bytes",
len(p.line)))
p.error("malformed shallow hash: wrong length, expected 40 bytes, read %d bytes",
len(p.line))
return nil
}
@@ -62,7 +62,7 @@ func (req *ReferenceUpdateRequest) encodeCommands(e *pktline.Encoder,
}
for _, cmd := range cmds[1:] {
if err := e.Encodef(formatCommand(cmd)); err != nil {
if err := e.Encodef("%s", formatCommand(cmd)); err != nil {
return err
}
}
+10 -2
View File
@@ -113,9 +113,17 @@ type Endpoint struct {
Port int
// Path is the repository path.
Path string
// InsecureSkipTLS skips ssl verify if protocol is https
// InsecureSkipTLS skips SSL verification if Protocol is HTTPS.
InsecureSkipTLS bool
// CaBundle specify additional ca bundle with system cert pool
// ClientCert specifies an optional client certificate to use for mutual
// TLS authentication if Protocol is HTTPS.
ClientCert []byte
// ClientKey specifies an optional client key to use for mutual TLS
// authentication if Protocol is HTTPS.
ClientKey []byte
// CaBundle specifies an optional CA bundle to use for SSL verification
// if Protocol is HTTPS. The bundle is added in addition to the system
// CA bundle.
CaBundle []byte
// Proxy provides info required for connecting to a proxy.
Proxy ProxyOptions
+28 -4
View File
@@ -15,12 +15,13 @@ import (
"strings"
"sync"
"github.com/golang/groupcache/lru"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/utils/ioutil"
"github.com/golang/groupcache/lru"
)
// it requires a bytes.Buffer, because we need to know the length
@@ -185,6 +186,18 @@ func transportWithInsecureTLS(transport *http.Transport) {
transport.TLSClientConfig.InsecureSkipVerify = true
}
func transportWithClientCert(transport *http.Transport, cert, key []byte) error {
keyPair, err := tls.X509KeyPair(cert, key)
if err != nil {
return err
}
if transport.TLSClientConfig == nil {
transport.TLSClientConfig = &tls.Config{}
}
transport.TLSClientConfig.Certificates = []tls.Certificate{keyPair}
return nil
}
func transportWithCABundle(transport *http.Transport, caBundle []byte) error {
rootCAs, err := x509.SystemCertPool()
if err != nil {
@@ -206,6 +219,11 @@ func transportWithProxy(transport *http.Transport, proxyURL *url.URL) {
}
func configureTransport(transport *http.Transport, ep *transport.Endpoint) error {
if len(ep.ClientCert) > 0 && len(ep.ClientKey) > 0 {
if err := transportWithClientCert(transport, ep.ClientCert, ep.ClientKey); err != nil {
return err
}
}
if len(ep.CaBundle) > 0 {
if err := transportWithCABundle(transport, ep.CaBundle); err != nil {
return err
@@ -230,7 +248,7 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
// We need to configure the http transport if there are transport specific
// options present in the endpoint.
if len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
if len(ep.ClientKey) > 0 || len(ep.ClientCert) > 0 || len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
var transport *http.Transport
// if the client wasn't configured to have a cache for transports then just configure
// the transport and use it directly, otherwise try to use the cache.
@@ -242,9 +260,13 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
}
transport = tr.Clone()
configureTransport(transport, ep)
if err := configureTransport(transport, ep); err != nil {
return nil, err
}
} else {
transportOpts := transportOptions{
clientCert: string(ep.ClientCert),
clientKey: string(ep.ClientKey),
caBundle: string(ep.CaBundle),
insecureSkipTLS: ep.InsecureSkipTLS,
}
@@ -260,7 +282,9 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
if !found {
transport = c.client.Transport.(*http.Transport).Clone()
configureTransport(transport, ep)
if err := configureTransport(transport, ep); err != nil {
return nil, err
}
c.addTransport(transportOpts, transport)
}
}
+4 -2
View File
@@ -9,8 +9,10 @@ import (
type transportOptions struct {
insecureSkipTLS bool
// []byte is not comparable.
caBundle string
proxyURL url.URL
clientCert string
clientKey string
caBundle string
proxyURL url.URL
}
func (c *client) addTransport(opts transportOptions, transport *http.Transport) {
+58 -18
View File
@@ -54,7 +54,7 @@ func (a *KeyboardInteractive) String() string {
}
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{
a.Challenge,
@@ -78,7 +78,7 @@ func (a *Password) String() string {
}
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
})
@@ -101,7 +101,7 @@ func (a *PasswordCallback) String() string {
}
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
})
@@ -150,7 +150,7 @@ func (a *PublicKeys) String() string {
}
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
})
@@ -211,7 +211,7 @@ func (a *PublicKeysCallback) String() string {
}
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
return a.SetHostKeyCallback(&ssh.ClientConfig{
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
User: a.User,
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
})
@@ -230,11 +230,26 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
// ~/.ssh/known_hosts
// /etc/ssh/ssh_known_hosts
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
kh, err := newKnownHosts(files...)
return ssh.HostKeyCallback(kh), err
kh, err := NewKnownHostsDb(files...)
if err != nil {
return nil, err
}
return kh.HostKeyCallback(), nil
}
func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
// NewKnownHostsDb returns knownhosts.HostKeyDB based on a file based on a
// known_hosts file. http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
//
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
// environment variable, example:
//
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
//
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
//
// ~/.ssh/known_hosts
// /etc/ssh/ssh_known_hosts
func NewKnownHostsDb(files ...string) (*knownhosts.HostKeyDB, error) {
var err error
if len(files) == 0 {
@@ -247,7 +262,7 @@ func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
return nil, err
}
return knownhosts.New(files...)
return knownhosts.NewDB(files...)
}
func getDefaultKnownHostsFiles() ([]string, error) {
@@ -289,25 +304,50 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {
}
// HostKeyCallbackHelper is a helper that provides common functionality to
// configure HostKeyCallback into a ssh.ClientConfig.
// configure HostKeyCallback and HostKeyAlgorithms into a ssh.ClientConfig.
type HostKeyCallbackHelper struct {
// HostKeyCallback is the function type used for verifying server keys.
// If nil default callback will be create using NewKnownHostsCallback
// If nil, a default callback will be created using NewKnownHostsDb
// without argument.
HostKeyCallback ssh.HostKeyCallback
// HostKeyAlgorithms is a list of supported host key algorithms that will
// be used for host key verification.
HostKeyAlgorithms []string
// fallback allows for injecting the fallback call, which is called
// when a HostKeyCallback is not set.
fallback func(files ...string) (ssh.HostKeyCallback, error)
}
// SetHostKeyCallback sets the field HostKeyCallback in the given cfg. If
// HostKeyCallback is empty a default callback is created using
// NewKnownHostsCallback.
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
var err error
// SetHostKeyCallbackAndAlgorithms sets the field HostKeyCallback and HostKeyAlgorithms in the given cfg.
// If the host key callback or algorithms is empty it is left empty. It will be handled by the dial method,
// falling back to knownhosts.
func (m *HostKeyCallbackHelper) SetHostKeyCallbackAndAlgorithms(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
if cfg == nil {
cfg = &ssh.ClientConfig{}
}
if m.HostKeyCallback == nil {
if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil {
return cfg, err
if m.fallback == nil {
m.fallback = NewKnownHostsCallback
}
hkcb, err := m.fallback()
if err != nil {
return nil, fmt.Errorf("cannot create known hosts callback: %w", err)
}
cfg.HostKeyCallback = hkcb
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
return cfg, err
}
cfg.HostKeyCallback = m.HostKeyCallback
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
return cfg, nil
}
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
return m.SetHostKeyCallbackAndAlgorithms(cfg)
}
+8 -9
View File
@@ -11,7 +11,6 @@ import (
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
"github.com/skeema/knownhosts"
"github.com/kevinburke/ssh_config"
"golang.org/x/crypto/ssh"
@@ -127,17 +126,17 @@ func (c *command) connect() error {
}
hostWithPort := c.getHostWithPort()
if config.HostKeyCallback == nil {
kh, err := newKnownHosts()
db, err := NewKnownHostsDb()
if err != nil {
return err
}
config.HostKeyCallback = kh.HostKeyCallback()
config.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
} else if len(config.HostKeyAlgorithms) == 0 {
// Set the HostKeyAlgorithms based on HostKeyCallback.
// For background see https://github.com/go-git/go-git/issues/411 as well as
// https://github.com/golang/go/issues/29286 for root cause.
config.HostKeyAlgorithms = knownhosts.HostKeyAlgorithms(config.HostKeyCallback, hostWithPort)
config.HostKeyCallback = db.HostKeyCallback()
config.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort)
} else {
// If the user gave a custom HostKeyCallback, we do not try to detect host key algorithms
// based on knownhosts functionality, as the user may be requesting a FixedKey or using a
// different key approval strategy. In that case, the user is responsible for populating
// HostKeyAlgorithms appropriately
}
overrideConfig(c.config, config)
+11 -9
View File
@@ -114,7 +114,7 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
o.RemoteURL = r.c.URLs[len(r.c.URLs)-1]
}
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
if err != nil {
return err
}
@@ -416,7 +416,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
o.RemoteURL = r.c.URLs[0]
}
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
if err != nil {
return nil, err
}
@@ -532,8 +532,8 @@ func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) {
return false, nil
}
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
if err != nil {
return nil, err
}
@@ -541,8 +541,8 @@ func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool,
return c.NewUploadPackSession(ep, auth)
}
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
if err != nil {
return nil, err
}
@@ -550,13 +550,15 @@ func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, ca
return c.NewReceivePackSession(ep, auth)
}
func newClient(url string, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
func newClient(url string, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
ep, err := transport.NewEndpoint(url)
if err != nil {
return nil, nil, err
}
ep.InsecureSkipTLS = insecure
ep.CaBundle = cabundle
ep.ClientCert = clientCert
ep.ClientKey = clientKey
ep.CaBundle = caBundle
ep.Proxy = proxyOpts
c, err := client.NewClient(ep)
@@ -1356,7 +1358,7 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
return nil, ErrEmptyUrls
}
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
if err != nil {
return nil, err
}
+3
View File
@@ -19,6 +19,7 @@ import (
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-billy/v5/util"
"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/revision"
@@ -930,6 +931,8 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
Tags: o.Tags,
RemoteName: o.RemoteName,
InsecureSkipTLS: o.InsecureSkipTLS,
ClientCert: o.ClientCert,
ClientKey: o.ClientKey,
CABundle: o.CABundle,
ProxyOptions: o.ProxyOptions,
}, o.ReferenceName)
+18 -9
View File
@@ -2,6 +2,8 @@ package filesystem
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"os"
"sync"
@@ -87,6 +89,11 @@ func (s *ObjectStorage) loadIdxFile(h plumbing.Hash) (err error) {
return err
}
if !bytes.Equal(idxf.PackfileChecksum[:], h[:]) {
return fmt.Errorf("%w: packfile mismatch: target is %q not %q",
idxfile.ErrMalformedIdxFile, hex.EncodeToString(idxf.PackfileChecksum[:]), h.String())
}
s.index[h] = idxf
return err
}
@@ -186,7 +193,8 @@ func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
}
func (s *ObjectStorage) encodedObjectSizeFromUnpacked(h plumbing.Hash) (
size int64, err error) {
size int64, err error,
) {
f, err := s.dir.Object(h)
if err != nil {
if os.IsNotExist(err) {
@@ -274,7 +282,8 @@ func (s *ObjectStorage) storePackfileInCache(hash plumbing.Hash, p *packfile.Pac
}
func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
size int64, err error) {
size int64, err error,
) {
if err := s.requireIndex(); err != nil {
return 0, err
}
@@ -310,7 +319,8 @@ func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
// EncodedObjectSize returns the plaintext size of the given object,
// without actually reading the full object data from storage.
func (s *ObjectStorage) EncodedObjectSize(h plumbing.Hash) (
size int64, err error) {
size int64, err error,
) {
size, err = s.encodedObjectSizeFromUnpacked(h)
if err != nil && err != plumbing.ErrObjectNotFound {
return 0, err
@@ -371,7 +381,8 @@ func (s *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (p
// DeltaObject returns the object with the given hash, by searching for
// it in the packfile and the git object directories.
func (s *ObjectStorage) DeltaObject(t plumbing.ObjectType,
h plumbing.Hash) (plumbing.EncodedObject, error) {
h plumbing.Hash,
) (plumbing.EncodedObject, error) {
obj, err := s.getFromUnpacked(h)
if err == plumbing.ErrObjectNotFound {
obj, err = s.getFromPackfile(h, true)
@@ -451,8 +462,8 @@ var copyBufferPool = sync.Pool{
// Get returns the object with the given hash, by searching for it in
// the packfile.
func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
plumbing.EncodedObject, error) {
plumbing.EncodedObject, error,
) {
if err := s.requireIndex(); err != nil {
return nil, err
}
@@ -509,9 +520,7 @@ func (s *ObjectStorage) decodeDeltaObjectAt(
return nil, err
}
var (
base plumbing.Hash
)
var base plumbing.Hash
switch header.Type {
case plumbing.REFDeltaObject:
+4 -2
View File
@@ -131,7 +131,9 @@ func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
}
if !root.IsDir() {
l.Add(ctor(root))
if !root.Skip() {
l.Add(ctor(root))
}
return nil
}
@@ -148,7 +150,7 @@ func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
}
return err
}
if current.IsDir() {
if current.IsDir() || current.Skip() {
continue
}
l.Add(ctor(current))
+20 -23
View File
@@ -297,18 +297,16 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
case noMoreNoders:
return ret, nil
case onlyFromRemains:
if err = ret.AddRecursiveDelete(from); err != nil {
return nil, err
if !from.Skip() {
if err = ret.AddRecursiveDelete(from); err != nil {
return nil, err
}
}
if err = ii.nextFrom(); err != nil {
return nil, err
}
case onlyToRemains:
if to.Skip() {
if err = ret.AddRecursiveDelete(to); err != nil {
return nil, err
}
} else {
if !to.Skip() {
if err = ret.AddRecursiveInsert(to); err != nil {
return nil, err
}
@@ -317,26 +315,25 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
return nil, err
}
case bothHaveNodes:
if from.Skip() {
if err = ret.AddRecursiveDelete(from); err != nil {
return nil, err
var err error
switch {
case from.Skip():
if from.Name() == to.Name() {
err = ii.nextBoth()
} else {
err = ii.nextFrom()
}
if err := ii.nextBoth(); err != nil {
return nil, err
case to.Skip():
if from.Name() == to.Name() {
err = ii.nextBoth()
} else {
err = ii.nextTo()
}
break
}
if to.Skip() {
if err = ret.AddRecursiveDelete(to); err != nil {
return nil, err
}
if err := ii.nextBoth(); err != nil {
return nil, err
}
break
default:
err = diffNodes(&ret, ii)
}
if err = diffNodes(&ret, ii); err != nil {
if err != nil {
return nil, err
}
default:
+9 -1
View File
@@ -36,7 +36,15 @@ func NewRootNode(idx *index.Index) noder.Noder {
parent := fullpath
fullpath = path.Join(fullpath, part)
if _, ok := m[fullpath]; ok {
// It's possible that the first occurrence of subdirectory is skipped.
// The parent node can be created with SkipWorktree set to true, but
// if any future children do not skip their subtree, the entire lineage
// of the tree needs to have this value set to false so that subdirectories
// are not ignored.
if parentNode, ok := m[fullpath]; ok {
if e.SkipWorktree == false {
parentNode.skip = false
}
continue
}
+20 -8
View File
@@ -12,6 +12,7 @@ import (
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-billy/v5/util"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
@@ -79,6 +80,8 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
Progress: o.Progress,
Force: o.Force,
InsecureSkipTLS: o.InsecureSkipTLS,
ClientCert: o.ClientCert,
ClientKey: o.ClientKey,
CABundle: o.CABundle,
ProxyOptions: o.ProxyOptions,
})
@@ -307,13 +310,20 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error {
return err
}
var removedFiles []string
if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset {
if err := w.resetIndex(t, dirs, opts.Files); err != nil {
if removedFiles, err = w.resetIndex(t, dirs, opts.Files); err != nil {
return err
}
}
if opts.Mode == MergeReset || opts.Mode == HardReset {
if opts.Mode == MergeReset && len(removedFiles) > 0 {
if err := w.resetWorktree(t, removedFiles); err != nil {
return err
}
}
if opts.Mode == HardReset {
if err := w.resetWorktree(t, opts.Files); err != nil {
return err
}
@@ -362,23 +372,24 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
return w.ResetSparsely(opts, nil)
}
func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) error {
func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]string, error) {
idx, err := w.r.Storer.Index()
if err != nil {
return err
return nil, err
}
b := newIndexBuilder(idx)
changes, err := w.diffTreeWithStaging(t, true)
if err != nil {
return err
return nil, err
}
var removedFiles []string
for _, ch := range changes {
a, err := ch.Action()
if err != nil {
return err
return nil, err
}
var name string
@@ -389,7 +400,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
name = ch.To.String()
e, err = t.FindEntry(name)
if err != nil {
return err
return nil, err
}
case merkletrie.Delete:
name = ch.From.String()
@@ -403,6 +414,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
}
b.Remove(name)
removedFiles = append(removedFiles, name)
if e == nil {
continue
}
@@ -421,7 +433,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
idx.SkipUnless(dirs)
}
return w.r.Storer.SetIndex(idx)
return removedFiles, w.r.Storer.SetIndex(idx)
}
func inFiles(files []string, v string) bool {
@@ -84,7 +84,7 @@ type Lookup struct {
// New returns a new Lookup instance
func New(b metadata.Backend, um usermapper.Mapper, o *options.Options, tm node.TimeManager) *Lookup {
idHistoryConf := o.Options.IDCache
idHistoryConf.Database = o.Options.IDCache.Table + "_history"
idHistoryConf.Table = o.Options.IDCache.Table + "_history"
idHistoryConf.TTL = 1 * time.Minute
spaceRootCache, _ := lru.New[string, string](1000)
+9 -15
View File
@@ -381,6 +381,15 @@ func (t *Tree) Move(ctx context.Context, oldNode *node.Node, newNode *node.Node)
newNode.ID = oldNode.ID
}
// rename node
err = os.Rename(
filepath.Join(oldParent, oldNode.Name),
filepath.Join(newParent, newNode.Name),
)
if err != nil {
return errors.Wrap(err, "posixfs: could not move child")
}
// update the id cache
// invalidate old tree
err = t.lookup.IDCache.DeleteByPath(ctx, filepath.Join(oldNode.ParentPath(), oldNode.Name))
@@ -391,21 +400,6 @@ func (t *Tree) Move(ctx context.Context, oldNode *node.Node, newNode *node.Node)
t.log.Error().Err(err).Str("spaceID", newNode.SpaceID).Str("id", newNode.ID).Str("path", filepath.Join(newNode.ParentPath(), newNode.Name)).Msg("could not cache id")
}
// rename node
err = os.Rename(
filepath.Join(oldParent, oldNode.Name),
filepath.Join(newParent, newNode.Name),
)
if err != nil {
if err := t.lookup.CacheID(ctx, oldNode.SpaceID, oldNode.ID, filepath.Join(oldNode.ParentPath(), oldNode.Name)); err != nil {
t.log.Error().Err(err).Str("spaceID", oldNode.SpaceID).Str("id", oldNode.ID).Str("path", filepath.Join(oldNode.ParentPath(), oldNode.Name)).Msg("could not reset cached id after failed move")
}
if err := t.WarmupIDCache(filepath.Join(oldNode.ParentPath(), oldNode.Name), false, false); err != nil {
t.log.Error().Err(err).Str("spaceID", oldNode.SpaceID).Str("id", oldNode.ID).Str("path", filepath.Join(oldNode.ParentPath(), oldNode.Name)).Msg("could not warum cached after failed move")
}
return errors.Wrap(err, "posixfs: could not move child")
}
// update target parentid and name
attribs := node.Attributes{}
attribs.SetString(prefixes.ParentidAttr, newNode.ParentID)
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2024 Skeema LLC and the Skeema Knownhosts authors
Copyright 2025 Skeema LLC and the Skeema Knownhosts authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+1 -1
View File
@@ -116,7 +116,7 @@ config := &ssh.ClientConfig{
## License
**Source code copyright 2024 Skeema LLC and the Skeema Knownhosts authors**
**Source code copyright 2025 Skeema LLC and the Skeema Knownhosts authors**
```text
Licensed under the Apache License, Version 2.0 (the "License");
+7 -7
View File
@@ -51,7 +51,7 @@ github.com/Microsoft/go-winio/pkg/guid
## explicit; go 1.18
github.com/Nerzal/gocloak/v13
github.com/Nerzal/gocloak/v13/pkg/jwx
# github.com/ProtonMail/go-crypto v1.1.5
# github.com/ProtonMail/go-crypto v1.1.6
## explicit; go 1.17
github.com/ProtonMail/go-crypto/bitcurves
github.com/ProtonMail/go-crypto/brainpool
@@ -342,7 +342,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1
github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1
github.com/cs3org/go-cs3apis/cs3/tx/v1beta1
github.com/cs3org/go-cs3apis/cs3/types/v1beta1
# github.com/cyphar/filepath-securejoin v0.3.6
# github.com/cyphar/filepath-securejoin v0.4.1
## explicit; go 1.18
github.com/cyphar/filepath-securejoin
# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
@@ -496,8 +496,8 @@ 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.13.2
## explicit; go 1.21
# github.com/go-git/go-git/v5 v5.16.5
## explicit; go 1.24.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
@@ -1376,7 +1376,7 @@ github.com/opencloud-eu/icap-client
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068
## explicit; go 1.18
github.com/opencloud-eu/libre-graph-api-go
# github.com/opencloud-eu/reva/v2 v2.42.4-0.20260209135152-4433469d98e8
# github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45
## explicit; go 1.24.1
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
@@ -2041,8 +2041,8 @@ github.com/shurcooL/vfsgen
# github.com/sirupsen/logrus v1.9.4
## explicit; go 1.17
github.com/sirupsen/logrus
# github.com/skeema/knownhosts v1.3.0
## explicit; go 1.17
# github.com/skeema/knownhosts v1.3.1
## explicit; go 1.22
github.com/skeema/knownhosts
# github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8
## explicit; go 1.20