Bump reva
This commit is contained in:
committed by
Ralf Haferkamp
parent
99aba9cfa1
commit
ae3582afa3
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1 +1 @@
|
||||
0.3.6
|
||||
0.4.1
|
||||
|
||||
+45
-4
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
+2
-3
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
Vendored
+7
-7
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user