Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package sshamble for openSUSE:Factory checked in at 2026-07-31 15:30:30 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sshamble (Old) and /work/SRC/openSUSE:Factory/.sshamble.new.2004 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sshamble" Fri Jul 31 15:30:30 2026 rev:4 rq:1368482 version:0.3.10 Changes: -------- --- /work/SRC/openSUSE:Factory/sshamble/sshamble.changes 2025-09-22 16:40:17.010933473 +0200 +++ /work/SRC/openSUSE:Factory/.sshamble.new.2004/sshamble.changes 2026-07-31 16:10:31.291137783 +0200 @@ -1,0 +2,8 @@ +Wed Jul 29 07:38:59 UTC 2026 - Martin Hauke <[email protected]> + +- Update to version 0.3.10 + * fix: detect RSA public keys against modern OpenSSH (issue #48). + * bump dependencies. + * refactor code. + +------------------------------------------------------------------- Old: ---- sshamble-0.3.3.tar.gz New: ---- sshamble-0.3.10.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sshamble.spec ++++++ --- /var/tmp/diff_new_pack.LJNm2R/_old 2026-07-31 16:10:31.827156429 +0200 +++ /var/tmp/diff_new_pack.LJNm2R/_new 2026-07-31 16:10:31.831156569 +0200 @@ -1,8 +1,8 @@ # # spec file for package sshamble # -# Copyright (c) 2025 SUSE LLC and contributors -# Copyright (c) 2024-2025, Martin Hauke <[email protected]> +# Copyright (c) 2026 SUSE LLC and contributors +# Copyright (c) 2024-2026, Martin Hauke <[email protected]> # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,7 +18,7 @@ Name: sshamble -Version: 0.3.3 +Version: 0.3.10 Release: 0 Summary: Security testing toolset for SSH License: BSD-2-Clause ++++++ sshamble-0.3.3.tar.gz -> sshamble-0.3.10.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/README.md new/sshamble-0.3.10/README.md --- old/sshamble-0.3.3/README.md 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/README.md 2026-07-28 04:41:47.000000000 +0200 @@ -19,6 +19,7 @@ Binaries are available from the [releases page](https://github.com/runZeroInc/sshamble/releases). +*(Note: on MacOS, you will likely have to disable Gatekeeper for this binary, with `sudo xattr -rd com.apple.quarantine ./sshamble`. This is not an invitation to violate your asset owner's security policy.)* To build SSHamble from source, ensure that you have a recent version of Go (1.24+) installed. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/auth/config.go new/sshamble-0.3.10/auth/config.go --- old/sshamble-0.3.3/auth/config.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/auth/config.go 2026-07-28 04:41:47.000000000 +0200 @@ -69,18 +69,18 @@ } KeyExchanges = []string{ - "curve25519-sha256", + ssh.KeyExchangeMLKEM768X25519, + ssh.KeyExchangeCurve25519, "[email protected]", - "diffie-hellman-group-exchange-sha256", - "ecdh-sha2-nistp256", - "ecdh-sha2-nistp384", - "ecdh-sha2-nistp521", - "diffie-hellman-group14-sha256", - "diffie-hellman-group16-sha512", - "diffie-hellman-group14-sha1", - "diffie-hellman-group1-sha1", - "diffie-hellman-group-exchange-sha256", - "diffie-hellman-group-exchange-sha1", + ssh.KeyExchangeDHGEXSHA256, + ssh.KeyExchangeECDHP256, + ssh.KeyExchangeECDHP384, + ssh.KeyExchangeECDHP521, + ssh.KeyExchangeDH14SHA256, + ssh.KeyExchangeDH16SHA512, + ssh.InsecureKeyExchangeDH14SHA1, + ssh.InsecureKeyExchangeDH1SHA1, + ssh.InsecureKeyExchangeDHGEXSHA1, } KexMACs = []string{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/auth/config_test.go new/sshamble-0.3.10/auth/config_test.go --- old/sshamble-0.3.3/auth/config_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/auth/config_test.go 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,38 @@ +package auth + +import ( + "slices" + "testing" + + "github.com/runZeroInc/excrypto/x/crypto/ssh" +) + +func TestKeyExchangesIncludeMLKEM(t *testing.T) { + if len(KeyExchanges) == 0 { + t.Fatal("KeyExchanges must not be empty") + } + if got := KeyExchanges[0]; got != ssh.KeyExchangeMLKEM768X25519 { + t.Fatalf("first key exchange = %q, want %q", got, ssh.KeyExchangeMLKEM768X25519) + } + if !slices.Contains(KeyExchanges, ssh.KeyExchangeMLKEM768X25519) { + t.Fatalf("KeyExchanges does not include %q", ssh.KeyExchangeMLKEM768X25519) + } +} + +func TestKeyExchangesAreSupported(t *testing.T) { + supported := map[string]struct{}{ + "[email protected]": {}, + } + for _, kex := range ssh.SupportedAlgorithms().KeyExchanges { + supported[kex] = struct{}{} + } + for _, kex := range ssh.InsecureAlgorithms().KeyExchanges { + supported[kex] = struct{}{} + } + + for _, kex := range KeyExchanges { + if _, ok := supported[kex]; !ok { + t.Fatalf("unsupported configured key exchange: %q", kex) + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/auth/halfauth_research.go new/sshamble-0.3.10/auth/halfauth_research.go --- old/sshamble-0.3.3/auth/halfauth_research.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/auth/halfauth_research.go 2026-07-28 04:41:47.000000000 +0200 @@ -29,3 +29,8 @@ copy(s.SignData, data) return nil, ErrHalfAuth } + +// SignWithAlgorithm makes HalfSigner an ssh.AlgorithmSigner so RSA keys are probed with rsa-sha2-* instead of SHA-1 ssh-rsa. +func (s *HalfSigner) SignWithAlgorithm(rand io.Reader, data []byte, algorithm string) (*ssh.Signature, error) { + return s.Sign(rand, data) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/auth/options.go new/sshamble-0.3.10/auth/options.go --- old/sshamble-0.3.3/auth/options.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/auth/options.go 2026-07-28 04:41:47.000000000 +0200 @@ -2,6 +2,7 @@ import ( "net" + "slices" "time" "unicode/utf8" @@ -115,12 +116,7 @@ } func (o *Options) SkipStage(stage string) bool { - for _, skipStage := range o.skipStages { - if stage == skipStage { - return true - } - } - return false + return slices.Contains(o.skipStages, stage) } func ReverseString(s string) string { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/auth/results.go new/sshamble-0.3.10/auth/results.go --- old/sshamble-0.3.3/auth/results.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/auth/results.go 2026-07-28 04:41:47.000000000 +0200 @@ -1,6 +1,7 @@ package auth import ( + "slices" "strings" "time" @@ -58,43 +59,42 @@ } func (r *AuthResult) SupportsAuth(t string) bool { - for _, v := range r.Methods { - if v == t { - return true - } - } - return false + return slices.Contains(r.Methods, t) } func (r *AuthResult) SupportsHostKey(t string) bool { if r.KexInit == nil { return false } - for _, v := range r.KexInit.ServerHostKeyAlgos { - if v == t { - return true - } - } - return false + return slices.Contains(r.KexInit.ServerHostKeyAlgos, t) } func (r *AuthResult) SupportsPubKeyType(t string) bool { - // Example: ssh-ed25519,[email protected],[email protected],ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-256,rsa-sha2-512,ssh-rsa,ssh-dss okTypes, ok := r.Extensions["server-sig-algs"] - if !ok { - // Assume all types are supported unless the server - // has told us otherwise via the extension. + if !ok || strings.TrimSpace(okTypes) == "" { return true } - for _, kt := range strings.Split(okTypes, ",") { + // ssh-rsa keys are usable when the server advertises any RSA signature algo; + // modern OpenSSH (>= 8.8) only offers rsa-sha2-256/rsa-sha2-512, not ssh-rsa. + wanted := sigAlgosForKeyType(t) + for kt := range strings.SplitSeq(okTypes, ",") { kt = strings.TrimSpace(kt) - if strings.EqualFold(kt, t) { - return true + for _, w := range wanted { + if strings.EqualFold(kt, w) { + return true + } } } return false } +func sigAlgosForKeyType(t string) []string { + if t == "ssh-rsa" { + return []string{"ssh-rsa", "rsa-sha2-256", "rsa-sha2-512"} + } + return []string{t} +} + func (r *AuthResult) AddVuln(v VulnResult) { r.Vulns = append(r.Vulns, v) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/auth/results_test.go new/sshamble-0.3.10/auth/results_test.go --- old/sshamble-0.3.3/auth/results_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/auth/results_test.go 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,27 @@ +package auth + +import "testing" + +func TestSupportsPubKeyType(t *testing.T) { + const modern = "ssh-ed25519,ecdsa-sha2-nistp256,rsa-sha2-512,rsa-sha2-256" + const legacy = "ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa" + + cases := []struct { + sigAlgs string + keyType string + want bool + }{ + {modern, "ssh-rsa", true}, + {modern, "ssh-ed25519", true}, + {legacy, "ssh-rsa", true}, + {"ssh-ed25519", "ssh-rsa", false}, + {modern, "ecdsa-sha2-nistp521", false}, + } + + for _, tc := range cases { + r := &AuthResult{Extensions: map[string]string{"server-sig-algs": tc.sigAlgs}} + if got := r.SupportsPubKeyType(tc.keyType); got != tc.want { + t.Errorf("SupportsPubKeyType(%q) with %q = %v, want %v", tc.keyType, tc.sigAlgs, got, tc.want) + } + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/badkeys.go new/sshamble-0.3.10/badkeys/badkeys.go --- old/sshamble-0.3.3/badkeys/badkeys.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/badkeys/badkeys.go 2026-07-28 04:41:47.000000000 +0200 @@ -6,86 +6,10 @@ "io" "os" "path/filepath" - - "github.com/runZeroInc/excrypto/crypto/dsa" - "github.com/runZeroInc/excrypto/crypto/ecdh" - "github.com/runZeroInc/excrypto/crypto/ecdsa" - "github.com/runZeroInc/excrypto/crypto/ed25519" - - "github.com/runZeroInc/excrypto/crypto/rsa" - "github.com/runZeroInc/excrypto/crypto/sha256" - "github.com/runZeroInc/excrypto/crypto/x509" - "github.com/runZeroInc/excrypto/x/crypto/ssh" - - stddsa "crypto/dsa" - stdecdh "crypto/ecdh" - stdecdsa "crypto/ecdsa" - stded25519 "crypto/ed25519" - stdrsa "crypto/rsa" - - stdssh "golang.org/x/crypto/ssh" ) const BadKeysMetaURL = "https://update.badkeys.info/v0/badkeysdata.json" -// PrefixFromPublicKey implements the badkeys `blocklistmaker` hashing method -func PrefixFromPublicKey(pub any) ([]byte, error) { - var rawb []byte - switch pub := pub.(type) { - - case ssh.PublicKey: - if cpk, ok := pub.(ssh.CryptoPublicKey); ok { - return PrefixFromPublicKey(cpk.CryptoPublicKey()) - } - return nil, fmt.Errorf("unsupported excrypto ssh public key: %v", pub.Type()) - case stdssh.PublicKey: - if cpk, ok := pub.(stdssh.CryptoPublicKey); ok { - return PrefixFromPublicKey(cpk.CryptoPublicKey()) - } - return nil, fmt.Errorf("unsupported stdlib ssh public key: %v", pub.Type()) - - case *rsa.PublicKey: - rawb = pub.N.Bytes() - case *stdrsa.PublicKey: - rawb = pub.N.Bytes() - - case *ecdsa.PublicKey: - rawb = pub.X.Bytes() - case *stdecdsa.PublicKey: - rawb = pub.X.Bytes() - - case ed25519.PublicKey: - rawb = pub - case stded25519.PublicKey: - rawb = pub - - case x509.X25519PublicKey: - rawb = pub - /* - // Not defined by stdlib - case stdx509.X25519PublicKey: - rawb = pub - */ - - case *ecdh.PublicKey: - rawb = pub.Bytes() // Verify - case *stdecdh.PublicKey: - rawb = pub.Bytes() // Verify - - case *dsa.PublicKey: - rawb = pub.Y.Bytes() - case *stddsa.PublicKey: - rawb = pub.Y.Bytes() - - case nil: - return nil, fmt.Errorf("unsupported nil key") - default: - return nil, fmt.Errorf("unsupported key: %T", pub) - } - sha256sum := sha256.Sum256(rawb) - return sha256sum[0:15], nil -} - // GetExecutablePath returns the full path to the running binary func GetExecutablePath() string { filename, _ := os.Executable() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/large_e_test.go new/sshamble-0.3.10/badkeys/large_e_test.go --- old/sshamble-0.3.3/badkeys/large_e_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/large_e_test.go 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,89 @@ +package badkeys + +import ( + "math/big" + "testing" + + "github.com/runZeroInc/excrypto/crypto/rsa" + "github.com/runZeroInc/excrypto/crypto/x509" + "github.com/runZeroInc/excrypto/x/crypto/ssh" +) + +// TestLargeRSAExponentChain verifies that an RSA public key with a very large +// public exponent (> 2^31, here ~1397 bits) can round-trip through every layer +// sshamble relies on: x509 PKIX marshal/parse, SSH wire-format marshal/parse, +// and the badkeys fingerprinting helper. +// +// Companion fixture: badkeys/tests/rsa_2048_large_e.yml. +// Upstream: https://github.com/runZeroInc/excrypto/pull/78, +// https://github.com/zmap/zgrab2/pull/714. +func TestLargeRSAExponentChain(t *testing.T) { + // Real 2048-bit modulus paired with a deliberately oversized public exponent + // (matches the fixture in badkeys/tests/rsa_2048_large_e.yml). + const nDec = "18546553133979686295706754998899930900284384519884436022491634865630824361571989091727223217760704713562088617769642972778706810069040146840815408068924221304482467411754327717732219383528142044820647352015027973435620587096945826941424583339838200914868370389431967066469883816302332032037291718905474557496674201134355854318405776150935618480911797504314370087472798726795714549286504512398139371824841363689318200316196228724354062568227493421963401085883965211168501435120756253842277566370639741145538747372187952581380944184780911614372327720305137590972683248723463443042150650954101508292155326597597151852727" + const eDec = "180551674620205768325404188012693994494974589455798193523634712608811452174151879339798865932167024867098385482983633145471353137270073888488487188773259587111887661299046218807614426891874011840094262739842776840031222130852779463045751206192002039235899666579777076498654001351987196402428277588714906438364535810862902900704632084301624950750661275401550586885487965175176824611882045496694270842154131510904790726769" + + n, ok := new(big.Int).SetString(nDec, 10) + if !ok { + t.Fatalf("invalid N literal") + } + e, ok := new(big.Int).SetString(eDec, 10) + if !ok { + t.Fatalf("invalid E literal") + } + if e.BitLen() <= 32 { + t.Fatalf("test E is not large enough: bitlen=%d", e.BitLen()) + } + + pub := &rsa.PublicKey{N: n, E: e} + + // 1. x509 PKIX round-trip. + der, err := x509.MarshalPKIXPublicKey(pub) + if err != nil { + t.Fatalf("MarshalPKIXPublicKey: %v", err) + } + parsed, err := x509.ParsePKIXPublicKey(der) + if err != nil { + t.Fatalf("ParsePKIXPublicKey: %v", err) + } + rpub, ok := parsed.(*rsa.PublicKey) + if !ok { + t.Fatalf("ParsePKIXPublicKey returned %T, want *rsa.PublicKey", parsed) + } + if rpub.E.Cmp(e) != 0 || rpub.N.Cmp(n) != 0 { + t.Fatalf("x509 round-trip mismatch: got N.bits=%d E.bits=%d, want N.bits=%d E.bits=%d", + rpub.N.BitLen(), rpub.E.BitLen(), n.BitLen(), e.BitLen()) + } + + // 2. SSH wire-format round-trip (parseRSA used to reject E.BitLen() > 24). + sshPub, err := ssh.NewPublicKey(pub) + if err != nil { + t.Fatalf("ssh.NewPublicKey: %v", err) + } + sshWire := sshPub.Marshal() + reparsed, err := ssh.ParsePublicKey(sshWire) + if err != nil { + t.Fatalf("ssh.ParsePublicKey: %v", err) + } + cpk, ok := reparsed.(ssh.CryptoPublicKey) + if !ok { + t.Fatalf("ssh.ParsePublicKey result is not CryptoPublicKey: %T", reparsed) + } + rsshPub, ok := cpk.CryptoPublicKey().(*rsa.PublicKey) + if !ok { + t.Fatalf("ssh re-parse returned %T, want *rsa.PublicKey", cpk.CryptoPublicKey()) + } + if rsshPub.E.Cmp(e) != 0 || rsshPub.N.Cmp(n) != 0 { + t.Fatalf("ssh wire round-trip mismatch for large E: got E.bits=%d, want %d", rsshPub.E.BitLen(), e.BitLen()) + } + + // 3. badkeys fingerprint helper consumes the result. + prefix, err := PrefixFromPublicKey(reparsed) + if err != nil { + t.Fatalf("PrefixFromPublicKey: %v", err) + } + if len(prefix) != 15 { + t.Fatalf("unexpected prefix length: %d", len(prefix)) + } + t.Logf("large-E badkeys prefix: %s (E bitlen=%d)", PrefixToString(prefix), e.BitLen()) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/parser.go new/sshamble-0.3.10/badkeys/parser.go --- old/sshamble-0.3.3/badkeys/parser.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/parser.go 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,83 @@ +package badkeys + +import ( + stddsa "crypto/dsa" + stdecdh "crypto/ecdh" + stdecdsa "crypto/ecdsa" + stded25519 "crypto/ed25519" + stdrsa "crypto/rsa" + "encoding/base64" + "fmt" + + "github.com/runZeroInc/excrypto/crypto/dsa" + "github.com/runZeroInc/excrypto/crypto/ecdh" + "github.com/runZeroInc/excrypto/crypto/ecdsa" + "github.com/runZeroInc/excrypto/crypto/ed25519" + "github.com/runZeroInc/excrypto/crypto/rsa" + "github.com/runZeroInc/excrypto/crypto/sha256" + "github.com/runZeroInc/excrypto/crypto/x509" + "github.com/runZeroInc/excrypto/x/crypto/ssh" + stdssh "golang.org/x/crypto/ssh" +) + +// PrefixFromPublicKey implements the badkeys `blocklistmaker` hashing method +func PrefixFromPublicKey(pub any) ([]byte, error) { + var rawb []byte + switch pub := pub.(type) { + + case ssh.PublicKey: + if cpk, ok := pub.(ssh.CryptoPublicKey); ok { + return PrefixFromPublicKey(cpk.CryptoPublicKey()) + } + return nil, fmt.Errorf("unsupported excrypto ssh public key: %v", pub.Type()) + case stdssh.PublicKey: + if cpk, ok := pub.(stdssh.CryptoPublicKey); ok { + return PrefixFromPublicKey(cpk.CryptoPublicKey()) + } + return nil, fmt.Errorf("unsupported stdlib ssh public key: %v", pub.Type()) + + case *rsa.PublicKey: + rawb = pub.N.Bytes() + case *stdrsa.PublicKey: + rawb = pub.N.Bytes() + + case *ecdsa.PublicKey: + rawb = pub.X.Bytes() + case *stdecdsa.PublicKey: + rawb = pub.X.Bytes() + + case ed25519.PublicKey: + rawb = pub + case stded25519.PublicKey: + rawb = pub + + case x509.X25519PublicKey: + rawb = pub + /* + // Not defined by stdlib + case stdx509.X25519PublicKey: + rawb = pub + */ + + case *ecdh.PublicKey: + rawb = pub.Bytes() // Verify + case *stdecdh.PublicKey: + rawb = pub.Bytes() // Verify + + case *dsa.PublicKey: + rawb = pub.Y.Bytes() + case *stddsa.PublicKey: + rawb = pub.Y.Bytes() + + case nil: + return nil, fmt.Errorf("unsupported nil key") + default: + return nil, fmt.Errorf("unsupported key: %T", pub) + } + sha256sum := sha256.Sum256(rawb) + return sha256sum[0:15], nil +} + +func PrefixToString(prefix []byte) string { + return base64.RawStdEncoding.EncodeToString(prefix) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/parser_test.go new/sshamble-0.3.10/badkeys/parser_test.go --- old/sshamble-0.3.3/badkeys/parser_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/parser_test.go 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,207 @@ +package badkeys + +import ( + stddsa "crypto/dsa" + stdecdh "crypto/ecdh" + "crypto/ecdsa" + "crypto/ed25519" + stdrsa "crypto/rsa" + "encoding/pem" + "testing" + + exdsa "github.com/runZeroInc/excrypto/crypto/dsa" + execdh "github.com/runZeroInc/excrypto/crypto/ecdh" + execdsa "github.com/runZeroInc/excrypto/crypto/ecdsa" + exed25519 "github.com/runZeroInc/excrypto/crypto/ed25519" + "github.com/runZeroInc/excrypto/crypto/rsa" + "github.com/runZeroInc/excrypto/crypto/x509" + cases "github.com/runZeroInc/sshamble/badkeys/tests" + "go.yaml.in/yaml/v3" +) + +func getTests() []testCase { + files, err := cases.TestData.ReadDir(".") + if err != nil { + panic("readdir: " + err.Error()) + } + var testCases []testCase + for _, f := range files { + if f.IsDir() { + continue + } + data, err := cases.TestData.ReadFile(f.Name()) + if err != nil { + panic(err) + } + tc, err := parseTestCase(f.Name(), data) + if err != nil { + panic(err) + } + testCases = append(testCases, tc) + } + return testCases +} + +func parseTestCase(name string, data []byte) (testCase, error) { + tc := testCase{} + err := yaml.Unmarshal(data, &tc) + tc.File = name + return tc, err +} + +type testCase struct { + File string `yaml:"-"` + Name string `yaml:"name"` + KeyType string `yaml:"keyType"` + Comment string `yaml:"comment"` + Key string `yaml:"key"` +} + +func TestSuite(t *testing.T) { + testCases := getTests() + for _, tc := range testCases { + t.Run(tc.File, func(t *testing.T) { + runTestCase(t, tc) + }) + } +} + +func runTestCase(t *testing.T, tc testCase) { + t.Logf("running test case: %s (%s)", tc.Name, tc.File) + b, _ := pem.Decode([]byte(tc.Key)) + if b == nil { + t.Fatalf("failed to decode pem: no block in %s", tc.Key) + } + + var ktype string + decodeErrs := []string{} + var privateKey, publicKey any + var priOK, pubOK bool + + privateKey, err := x509.ParsePKCS1PrivateKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "priv-pkcs1: "+err.Error()) + privateKey, err = x509.ParsePKCS8PrivateKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "priv-pkcs8: "+err.Error()) + } else { + priOK = true + } + } else { + priOK = true + } + switch pk := privateKey.(type) { + case *rsa.PrivateKey: + ktype = "rsa" + publicKey = &pk.PublicKey + privateKey = pk + pubOK = true + case *stdrsa.PrivateKey: + ktype = "rsa" + publicKey = &pk.PublicKey + privateKey = pk + pubOK = true + case *execdsa.PrivateKey: + ktype = "ecdsa" + publicKey = &pk.PublicKey + privateKey = pk + pubOK = true + case *ecdsa.PrivateKey: + ktype = "ecdsa" + publicKey = &pk.PublicKey + privateKey = pk + pubOK = true + case exed25519.PrivateKey: + ktype = "ed25519" + publicKey = pk.Public() + privateKey = pk + pubOK = true + case ed25519.PrivateKey: + ktype = "ed25519" + publicKey = pk.Public() + privateKey = pk + pubOK = true + } + + if !pubOK { + publicKey, err = x509.ParsePKIXPublicKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "pub-pkix: "+err.Error()) + publicKey, err = x509.ParsePKCS1PublicKey(b.Bytes) + if err != nil { + decodeErrs = append(decodeErrs, "pub-pkcs1: "+err.Error()) + } else { + pubOK = true + } + } else { + pubOK = true + } + } else { + pubOK = true + } + + switch pk := publicKey.(type) { + case *rsa.PublicKey: + ktype = "rsa" + publicKey = pk + case *stdrsa.PublicKey: + ktype = "rsa" + publicKey = pk + case *execdsa.PublicKey: + ktype = "ecdsa" + publicKey = pk + case *ecdsa.PublicKey: + ktype = "ecdsa" + publicKey = pk + case exed25519.PublicKey: + ktype = "ed25519" + publicKey = pk + case ed25519.PublicKey: + ktype = "ed25519" + publicKey = pk + case *exdsa.PublicKey: + ktype = "dsa" + publicKey = pk + case *stddsa.PublicKey: + ktype = "dsa" + publicKey = pk + case *execdh.PublicKey: + if pk.Curve() == execdh.X25519() { + ktype = "x25519" + } else { + ktype = "ecdh" + } + publicKey = pk + case *stdecdh.PublicKey: + if pk.Curve() == stdecdh.X25519() { + ktype = "x25519" + } else { + ktype = "ecdh" + } + publicKey = pk + case x509.X25519PublicKey: + ktype = "x25519" + publicKey = pk + } + + t.Logf("pub:%v, priv:%v, type:%v, priv:%v, pub:%v", pubOK, priOK, ktype, privateKey, publicKey) + + if !pubOK { + for _, e := range decodeErrs { + t.Logf("decode: %s", e) + } + t.Fatalf("failed to parse public key, errors: %+v", decodeErrs) + } + + if ktype != tc.KeyType { + t.Fatalf("key type mismatch: got %v, want %v", ktype, tc.KeyType) + } + + praw, err := PrefixFromPublicKey(publicKey) + if err != nil { + t.Fatalf("failed to get raw prefix: %v", err) + } + + penc := PrefixToString(praw) + t.Logf("badkeys.hash: %s", penc) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/dsa_1024_legacy.yml new/sshamble-0.3.10/badkeys/tests/dsa_1024_legacy.yml --- old/sshamble-0.3.3/badkeys/tests/dsa_1024_legacy.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/dsa_1024_legacy.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,18 @@ +name: DSA 1024 Legacy Public Key +comment: Ancient and weak SSH DSS style key material, kept as a public key + fixture because DSA is legacy and typically only needed for parsing and + fingerprint coverage. +keyType: dsa +key: |- + -----BEGIN PUBLIC KEY----- + MIIBwDCCATQGByqGSM44BAEwggEnAoGBAO8S9U4Zn2HGWdjNNrfM/1k7vURd4G/4 + 00yVQX9GVwnGPfaOx+EGaajfsJEuiiKBzXeJjLZgN+5vk75CnxRYZRQexoCwssmP + hj4EOXPP8mIf/yUgSndk0kAuPqEuyZ5qE3tlhC7lJq1I5LbfeNq5sy2RxTJ/yGEq + jdXcWBD46ck1Ah0Amk6M09DvHPDIS0YFPhakHo0I/RpH4SFdtOSQcwKBgQCiuzWe + AaYPmQgRL1GbWIgWHh3cw0zYiltjOeLcQkhzBF2Wg/pMBlv6D+um2QGcIWdtOhT/ + 6EfGHfaY5riR/Q2/NXEPzHG816s7UPoFFfrW5ibg7/EMqeAzZRM3dFKIql0+o0SC + eopnYWHzNFu0xncOF2O9bI+JrrhYxdC68IEReQOBhQACgYEA0srq4q1Q5/rMfyIW + LvV2JS87xnKwraf6vnApqYCca7+hf11LJgq7PLbw/keFQ+edagrhRLjhdX0PZQ05 + Uv9gOqqxkNU6FMYlvL5ZxIQSsN2rityVgOsa4e7adNoEb1NzwFNAmf3xnT+hCTVW + rqfviwWsHrXEOMQSkeNKSUEdmeo= + -----END PUBLIC KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ecdsa_p256.yml new/sshamble-0.3.10/badkeys/tests/ecdsa_p256.yml --- old/sshamble-0.3.3/badkeys/tests/ecdsa_p256.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/ecdsa_p256.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,9 @@ +name: ECDSA P-256 Private Key +comment: Common modern elliptic-curve SSH key on the NIST P-256 curve. +keyType: ecdsa +key: |- + -----BEGIN PRIVATE KEY----- + MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgk1NiW92Y1XvDLIe9 + otm1R4epNmZwa0SoBULaZTpb67ShRANCAATZO3Lt3C4nlqacTFlTmMSLulETmclw + e4gHoNVOw+wV7zNhzjGCP09QuwPXdg6HlXtEqvVB3D7f+t91lDIB3sjm + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ecdsa_p384.yml new/sshamble-0.3.10/badkeys/tests/ecdsa_p384.yml --- old/sshamble-0.3.3/badkeys/tests/ecdsa_p384.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/ecdsa_p384.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,10 @@ +name: ECDSA P-384 Private Key +comment: Higher-strength ECDSA variant for coverage across supported NIST curves. +keyType: ecdsa +key: |- + -----BEGIN PRIVATE KEY----- + MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDunAsSiHOrREJrVrlE + zuVsuODLajuPAymv5Q0mTA8pIhSDQXIsa6hfDoe8K+ibAQShZANiAAQVlf80Fdxp + 0UhCHdY8Tx25Tqz/RVAalIJH2llPczoIEM/n6sk5gNR2FxfBLBuIndBeUQv0XBxI + yWxPcTUSOINxjcxKwsHaJtvosgZ4H2rfr3GLMMZzcm/pLw9tAAcvN4o= + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ecdsa_p521.yml new/sshamble-0.3.10/badkeys/tests/ecdsa_p521.yml --- old/sshamble-0.3.3/badkeys/tests/ecdsa_p521.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/ecdsa_p521.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,12 @@ +name: ECDSA P-521 Private Key +comment: Largest NIST ECDSA curve supported by typical SSH implementations. +keyType: ecdsa +key: |- + -----BEGIN PRIVATE KEY----- + MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBOHNQ7XYIdx1dtztT + 7hrnSH+Ava9MDje28iAoE0v20jqvJMqfSpzQtsI6bHDc2AINYuLHYgijIru9+9pt + d4OP10+hgYkDgYYABADUkYJ5XV0HTDXTYXeuQu7+fBf3lWokJ7DugVcorTCieI8d + Vvu02ez74j1sd8IlQ5NY/uJW4bUqT5rg/XLeYVz23ACsHKqGVmvs+Ql6b/XanjUM + 4LkVNuNIJ+pWdoeV/ipCphvEg3XnBNXGoMHGtRqBLpnbLbhzz416J3zff2vN84ZI + WA== + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/ed25519_modern.yml new/sshamble-0.3.10/badkeys/tests/ed25519_modern.yml --- old/sshamble-0.3.3/badkeys/tests/ed25519_modern.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/ed25519_modern.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,7 @@ +name: Ed25519 Modern Private Key +comment: Contemporary SSH key type with compact modern private key encoding. +keyType: ed25519 +key: |- + -----BEGIN PRIVATE KEY----- + MC4CAQAwBQYDK2VwBCIEICAy5fQCuNqe5FoE6+mLvyD4Gip4hoebhK0oOTY3F+Pl + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/embed.go new/sshamble-0.3.10/badkeys/tests/embed.go --- old/sshamble-0.3.3/badkeys/tests/embed.go 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/embed.go 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,9 @@ +package tables + +import ( + "embed" +) + +//go:embed all:*.yml + +var TestData embed.FS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_1024_legacy.yml new/sshamble-0.3.10/badkeys/tests/rsa_1024_legacy.yml --- old/sshamble-0.3.3/badkeys/tests/rsa_1024_legacy.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/rsa_1024_legacy.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,21 @@ +name: RSA 1024 Legacy Private Key +comment: Weak old RSA key size to cover insecure legacy PEM private key parsing + and hashing. +keyType: rsa +key: |- + -----BEGIN PRIVATE KEY----- + MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALM31Su5InGugVJw + 03P86pRf1QqGJmMVQqvDPA6nLdOSyjl8IJG9qjxFB68XMbqf6OOfZsHlx1CtmDF3 + xQA9YH9ZWX/mobnCDEFmheM8T2kPQe2tBdFCjccrF3W2gYA9tFUYZs+Ilvba27H6 + 2/ediivy0/ojB8jmx2KrWWYb2oWJAgMBAAECgYBmddktsh57F+VfmJmLimEiitdl + uORhaVpAkNHZHsMYXqe2zbxhFiopYt3NwFjx9VJUMGGzIQcenOCSB4Nda7mqn51K + 4eypvQt1ryPt30BKBUPLdCFg8OJYVQXzJZ0b4qYOmt1cTem/JUuevilFmGRUr65A + gDNa+qYEXC3nt3QKAQJBAOSr3BOV3gq4xMx/mgMJBPNb2CEtjRMbdLuPxHSzHo6k + 3YCHejMsbp9r4hp11DsFHXkzrOcptaz4oEMhamNguLkCQQDIovbUt3AwMs8J2QxI + kKNQCC/klF8QBZ9RuYqIj9AgP3o17AUnBexZNSJfpbWdwgy51OIjYXu41KRiPsoG + gStRAkAus+8BzQUJeY0wGwuQfSIWtfZhaFU8xsnDmOxNzVt7VK6sln7dvsBlLPqD + 3vbWiwfm/ifdGoaxaiIJM+TvoQx5AkBOOPMA5e/Qas67aSrX08RIPJjnhk01wutl + gnAXY/Jh0D3GbhloKHAIK6YWIlYXhoNNpEjyQFxrKERapV5AgPWxAkEAnliacbWt + aGbZS5tRI70DSrcH+u8XNkabzduTXRp84zJ6rlNGgHs6gBpA/b7V/FmnajL3X+14 + jog/d6f5pBwc7w== + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_2048_large_e.yml new/sshamble-0.3.10/badkeys/tests/rsa_2048_large_e.yml --- old/sshamble-0.3.3/badkeys/tests/rsa_2048_large_e.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/rsa_2048_large_e.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,21 @@ +name: RSA 2048 Public Key With Large Public Exponent +comment: |- + Real-world pathological RSA public key whose public exponent E exceeds the + historical 32-bit cap (here ~1397 bits, comparable to the keys observed in + the wild by zmap/zgrab2 and runZero). Verifies sshamble can parse, fingerprint, + and round-trip RSA keys with arbitrarily large E through excrypto. + See: https://github.com/runZeroInc/excrypto/pull/78 and https://github.com/zmap/zgrab2/pull/714 +keyType: rsa +key: |- + -----BEGIN PUBLIC KEY----- + MIIBzzANBgkqhkiG9w0BAQEFAAOCAbwAMIIBtwKCAQEAkurAjiPidJjnDVC/agAy + rMKpmW635I/jT5Jg9snANq6Awt1mXoHd3gUjq32EHwr7Tn+jIqtg8PSvShd6iq3o + StSVCyYFjP2kibX+PsZiv3WlJWiXOPjHWnd2fN77VRpZkh0mLxS1HV6ptIgDmTgv + RmQbfX8ZtcCbXORqHVy+ZYIm67wc0K142bco5mD0EzYz8Ldh3W1pEnz9VEKFImAR + m76pI4JQMuk+lK0oH0wyLe06maGqoVBKblmugo+lgghRBBg5KCfpu+OH+I/AIAtD + 6ACejQcuTcbJTbWcxzK74v1H8DqstPhLUU6cgwD/65p/Iw2j23iC3/A0oXWKjsd4 + twKBrxC0fZgQ7eYj8ptZqxZBkTN2C5UqUsu0Z1Xe4ACTg6K/QHFOWab/4DQnD5nh + ZnTEchDQbZriXOJg7YkdAVK+RNqJVpq0DeaBPPzN1+ylAH6QcRle+cPjglIZQaD5 + T795U+jbMcNWuVA4bf0WcXWjsgXkPdoU9V4mJBAo+6HeSz5IVc44a9Aa5czvpgqW + 7FOZQot0cEQFuHXPSKapd/6go3PmPflp53RqNsurSsF6NGs= + -----END PUBLIC KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_3072_modern.yml new/sshamble-0.3.10/badkeys/tests/rsa_3072_modern.yml --- old/sshamble-0.3.3/badkeys/tests/rsa_3072_modern.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/rsa_3072_modern.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,44 @@ +name: RSA 3072 Modern Private Key +comment: Modern RSA key size to complement the legacy RSA fixture. +keyType: rsa +key: |- + -----BEGIN PRIVATE KEY----- + MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQCyEplVLhDlygts + xcVGMHhU1JRZy0vX+uyPWluThZJ0+lP+wyoFhGeMPoAOPiDqN81Cqt8iAPu/hwNX + eaSxmnZG1M6YYYIFuVXy3lixz7XG7cGwJSzaEXv6Znm8dLaVhL22B2lBukHUzdNU + S6sSVp4MRIlFFzQFh4QDGGmFktArpwnbdNxH1exxZ5tcCvN3SVRNfH8hrOnBqmos + Hzx+NlsIetdJcAAZkGBuVRHnAaqBm5B8sK9xHhiYYoaTpLhE1QJv0v53HJvtD1TM + AyNSmtWgKD2YYyPg7qONJFl8xJ6LKqug8irCPL8uSOWyWCB7mPVnVJn+Dsp/A847 + J+rCw7bLuYYjRyo5c1fqN9iTnc9FTopAfLQP+TliHG3l7m8KvUWFoFCuMVSGYdii + 1VmYUsSbI6X4ReR84iyJSDRWHJfxIxrGmy/iZcQ53gx0h7Yn18eyjaurALDiTEGr + PQkYFzTfNzzqUA29pvnuSwb8OCMNRkZ6RSHOxXPpfmYR+nLKLcsCAwEAAQKCAYA0 + euPCpVVoi4+ramvV4EIOaiogfpgnGnXuV6hmhTahEAeCLR1sy9VCkUbnK6tCq9hr + /fTNzjFjhFRZSundR9L01BHg+de6fSEV9y53lOoTKw3qrkIWMJHzvZDCGDmZPio1 + A+Ei9FQjddeC39wWhv2ESwyTlp+Hw6UFzoOG0LpWKx/uE/Y5m5PX5jt0uGuBa9ST + E45aK94ma+VnhHstDTLuAFoDdCUrY4/v5FVm2ehY0zKVdPL3qPGBs4PK7oA6FPZp + hYRXaGV6fTIuSZADNcaG2DW/o2HT4bQnvgtn/U7s5D1BbH32uZDkbmHGhvaL3Uu3 + mXnT8EV8V5Qktgt1umhovq8yzrMXid8fX64X7EPSDrZZKSmFIlOONL/PM84gAWYo + NDy/uuZVS59Y6FjJwDcUwKaj82ng2HIHsJEanftv4X1FiLf3U2NLU/OGG/XKpVhz + MWO0NohN3katwWIRj9P10HVyYmhAdg/BcOC4UF0utItCFFrRq85Ks/YGy0bJ+LEC + gcEA2Q+A6b5rk9Lqm99OR7Oi4dI9g4irc/E94VhciZ4a3e1aDdC20IMFSOzMg8cT + GXudHJCHz3iiTr8U/grR/6pbk/zKj2Nqz02cN79W/ir8HxnO+PZkF8tqkwBNOoch + 3Fk/uyFwcp7GthVvFroPQZJcrGafpxpUlra/7v3REwyjNa/Mj30nG4swbtYqcTH1 + nqGztsFTcIrnihvud/uXodL977eSMagl47oROvPoCOZBdQnexlswXqsW8UG+QVZZ + A6Q7AoHBANIEk9O5pR0gHwsGVK82dl2ET0BYerp+bw5LHwvH3CcwqKqgk4hwjDOp + l+1g/A/rlKCBXqkODLevyry3g3m0hcFeWYQUIJDAQZa4Bx6Q6KgcYMfhCs+ut3D3 + nKGFW54eEHdJXMHX1VMZWtkO9hfi7dPtsiNBr49nGnNoZ6IpJSHQdGTbpnFOUZ5B + Kcp0Uw3bvf7xDcOvbd8OhqPDjLap7zHe4drH7s+/pIigaIrZMb/DjY4vGvhKBq1w + iwRE4yvTsQKBwHlSsBR2SbzzXAzJsQBkyLr9/0IA5wxEH/lPoVGOXKgE3ZvxN+2t + /gs3yEwzvlo0/KkVo8N0QwxOw5YHSD9l4hACcNnakL3UPGfwe9hPAGMF0ujXFzUx + jp0hwwjetS9rdzLKSwbV9m2Z52DFmHdnLoNyTPRixDsFZJxZpD3Ih4UfgPAG3RAJ + KYYaENj6OrZy3Xdd8ZxrF2XbnNvVdXiO7T7K1V0KBjuWKcFQyTYINTgMz0cBezS+ + 3nD09Tmh4NknNwKBwQCz4P/MZEErhQuRocZ+m6MdzekjL6VOfmQYVfctW+0QE/ki + y44RWnR6+1MJGfxKlngiUSwGl+YEniiDIpsVbqzxRiJdoe+Si1iiAtT2NR9QfZ3X + FMTxxK+RrkMVhOdHsnBf+xHbZUVte1X4e3wEUXVqAziEQgpgROq+1jmNBrirEaPf + jtIsUexOI3nr0V4hjIhbB5PGe0cbuZFQSnkHKt2YVVIU1RGSFo6SBvIDmqull5Ry + p53t81lsy+wFqAr6ysECgcEAhO7WzGyqyLAwIMnxKxOZOLJ0AltdSIVNnCp7zHUz + PpC0EVO4+mH0KWV0PYoxW+dtTYkjNLs9h3mljufjNsOAfKBFYcCk8BkMXOt5btGa + iCr+pxy6uYQHwki0mn6HZC82bHMKCqAj8R/8Wt8xandO983G7/vip3w+1fWZZtmX + 7lfBsPCMgxx5p5qQS2PNdEsTy3Hl+XvoBEt4Q07KU5HbaP8LH/04y4GVLlNn7A/n + n5X0SuT/56Ld5P3LKj6ZW1N3 + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/rsa_512_very_weak.yml new/sshamble-0.3.10/badkeys/tests/rsa_512_very_weak.yml --- old/sshamble-0.3.3/badkeys/tests/rsa_512_very_weak.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/rsa_512_very_weak.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,14 @@ +name: RSA 512 Very Weak Private Key +comment: Deliberately tiny RSA key to cover very old and insecure key material. +keyType: rsa +key: |- + -----BEGIN PRIVATE KEY----- + MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAqrkftwcmmAWKXpZl + eTSMVv52hhO2+EyyOcvfmA2PA2B+jsXqRLSmipuegR2QpjkywPikdex6qaT50MhV + jwSgOwIDAQABAkEAl5OTyAIuTu0kZEStxYZVT+vPuc0JN72Iw+3yEGOBZWG0qskm + fib4AYeKgD8nQlHzgJ/Ef49gXiWMxPHP0ICRIQIhANhCGlfzHaPc3cVRHYRjZLPT + t3GyVtW9xJ2jzVHNqILxAiEAyhjRNX+0XSwJ6t/2yxVLDqaQoxfmMUwhoy3GOG4x + PesCIFYoT9KGW2KzVe+M15Os/avfR5CWmp3hUvnzDpPuuZohAiBHhTrcnQIeiwxD + c9kqC7IRUD8ViMqtHY0162gf5ldE2wIhALuqGP6PJGfb6NINJ3gafNA46FGYIWUK + A+qF2TnHaPvm + -----END PRIVATE KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/badkeys/tests/x25519_modern.yml new/sshamble-0.3.10/badkeys/tests/x25519_modern.yml --- old/sshamble-0.3.3/badkeys/tests/x25519_modern.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/sshamble-0.3.10/badkeys/tests/x25519_modern.yml 2026-07-28 04:41:47.000000000 +0200 @@ -0,0 +1,9 @@ +name: X25519 Modern Public Key +comment: Modern Curve25519 public key coverage for the supported X25519 hashing + path. This is not post-quantum, but it is the closest modern key family + implemented beyond the classic SSH key types. +keyType: x25519 +key: |- + -----BEGIN PUBLIC KEY----- + MCowBQYDK2VuAyEAFyMUk2Xo7bihdKZr2+odfIPvvaJEj+jjEhC3xQc8tVk= + -----END PUBLIC KEY----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/check_pubkey.go new/sshamble-0.3.10/cmd/check_pubkey.go --- old/sshamble-0.3.3/cmd/check_pubkey.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/check_pubkey.go 2026-07-28 04:41:47.000000000 +0200 @@ -45,7 +45,6 @@ cnt := 0 for _, tk := range testHalfKeys { - tk := tk kt := tk.PublicKey().Type() cnt++ res := auth.SSHAuth(addr, options.WithStopStage("auth"), auth.SSHAuthHandlerSingle(ssh.AuthMethod( @@ -81,7 +80,6 @@ // Test to see if any full key is accepted cnt = 0 for _, tk := range testKeys { - tk := tk if _, ok := pubAccepted[base64.StdEncoding.EncodeToString(tk.PublicKey().Marshal())]; !ok { // Skip full key tests where the half key was not accepted continue diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/cmd_scan.go new/sshamble-0.3.10/cmd/cmd_scan.go --- old/sshamble-0.3.3/cmd/cmd_scan.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/cmd_scan.go 2026-07-28 04:41:47.000000000 +0200 @@ -3,6 +3,7 @@ import ( "bufio" "io" + "maps" "net" "os" "regexp" @@ -219,12 +220,12 @@ // Parse the check lists and categories enabledChecks := make(map[string]struct{}) - for _, v := range strings.Split(gEnabledChecks, ",") { + for v := range strings.SplitSeq(gEnabledChecks, ",") { v = strings.ToLower(strings.TrimSpace(v)) enabledChecks[v] = struct{}{} } enabledCategories := make(map[string]struct{}) - for _, v := range strings.Split(gEnabledCategories, ",") { + for v := range strings.SplitSeq(gEnabledCategories, ",") { v = strings.ToLower(strings.TrimSpace(v)) enabledCategories[v] = struct{}{} } @@ -455,7 +456,7 @@ func uniqueUsers(ustr string) []string { uniq := make(map[string]struct{}, 0) - for _, user := range strings.Split(ustr, ",") { + for user := range strings.SplitSeq(ustr, ",") { uniq[user] = struct{}{} } res := []string{} @@ -744,9 +745,7 @@ func (conf *ScanConfig) GetAllHostKeys(addr string, options *auth.Options, root *auth.AuthResult, cached *auth.AuthResult) { if cached != nil && len(cached.HostKeys) > 0 { // Use cached hostkeys if available - for k, v := range cached.HostKeys { - root.HostKeys[k] = v - } + maps.Copy(root.HostKeys, cached.HostKeys) return } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/interact.go new/sshamble-0.3.10/cmd/interact.go --- old/sshamble-0.3.3/cmd/interact.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/interact.go 2026-07-28 04:41:47.000000000 +0200 @@ -326,7 +326,7 @@ // Start piping our raw-mode stdin manager go conf.InteractRelay(addr, quit, sesInput, sclient, ses, state) - for _, cmd := range strings.Split(gInteractAuto, ",") { + for cmd := range strings.SplitSeq(gInteractAuto, ",") { cmd = strings.TrimSpace(cmd) if cmd == "" { continue @@ -582,7 +582,7 @@ case "sendb": data := processEscapedByteString(strings.Join(args[1:], " ")) - for i := 0; i < len(data); i++ { + for i := range data { _, err := shell.Write(data[i : i+1]) if err != nil { return false, err diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/os_freebsd.go new/sshamble-0.3.10/cmd/os_freebsd.go --- old/sshamble-0.3.3/cmd/os_freebsd.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/os_freebsd.go 2026-07-28 04:41:47.000000000 +0200 @@ -1,5 +1,4 @@ //go:build freebsd -// +build freebsd package cmd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/os_other.go new/sshamble-0.3.10/cmd/os_other.go --- old/sshamble-0.3.3/cmd/os_other.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/os_other.go 2026-07-28 04:41:47.000000000 +0200 @@ -1,5 +1,4 @@ //go:build !windows && !freebsd -// +build !windows,!freebsd package cmd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/os_windows.go new/sshamble-0.3.10/cmd/os_windows.go --- old/sshamble-0.3.3/cmd/os_windows.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/os_windows.go 2026-07-28 04:41:47.000000000 +0200 @@ -1,5 +1,4 @@ //go:build windows -// +build windows package cmd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/cmd/targets.go new/sshamble-0.3.10/cmd/targets.go --- old/sshamble-0.3.3/cmd/targets.go 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/cmd/targets.go 2026-07-28 04:41:47.000000000 +0200 @@ -11,7 +11,7 @@ func parseTargets(input string) ([]string, error) { res := []string{} - for _, target := range strings.Fields(strings.ReplaceAll(input, ",", " ")) { + for target := range strings.FieldsSeq(strings.ReplaceAll(input, ",", " ")) { target = strings.ToLower(strings.TrimSpace(target)) if target == "" { continue @@ -69,8 +69,8 @@ // Use a map to dedup and shuffle ports ports := make(map[int]bool) - bits := strings.Split(pspec, ",") - for _, bit := range bits { + bits := strings.SplitSeq(pspec, ",") + for bit := range bits { bit = strings.TrimSpace(bit) if bit == "" { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/go.mod new/sshamble-0.3.10/go.mod --- old/sshamble-0.3.3/go.mod 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/go.mod 2026-07-28 04:41:47.000000000 +0200 @@ -1,35 +1,38 @@ module github.com/runZeroInc/sshamble -go 1.24 +go 1.26.0 + +// NOTE: Uncomment to test with a development version of excrypto +// replace github.com/runZeroInc/excrypto => ../excrypto require ( github.com/google/go-cmp v0.7.0 github.com/logrusorgru/aurora/v3 v3.0.0 github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 - github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3 - github.com/sirupsen/logrus v1.9.3 - github.com/spf13/cobra v1.9.1 - github.com/spf13/viper v1.20.1 - github.com/ulikunitz/xz v0.5.12 - golang.org/x/crypto v0.41.0 - golang.org/x/term v0.34.0 - gonum.org/v1/gonum v0.16.0 + github.com/runZeroInc/excrypto v0.42.2 + github.com/sirupsen/logrus v1.9.4 + github.com/spf13/cobra v1.10.2 + github.com/spf13/viper v1.21.0 + github.com/ulikunitz/xz v0.5.16 + go.yaml.in/yaml/v3 v3.0.5 + golang.org/x/crypto v0.54.0 + golang.org/x/term v0.45.0 + gonum.org/v1/gonum v0.17.0 ) require ( - github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/fsnotify/fsnotify v1.10.1 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/sagikazarmark/locafero v0.10.0 // indirect - github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect - github.com/spf13/afero v1.14.0 // indirect - github.com/spf13/cast v1.9.2 // indirect - github.com/spf13/pflag v1.0.7 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/weppos/publicsuffix-go v0.40.2 // indirect - golang.org/x/net v0.43.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect + github.com/weppos/publicsuffix-go v0.50.3 // indirect + golang.org/x/net v0.57.0 // indirect + golang.org/x/sys v0.47.0 // indirect + golang.org/x/text v0.40.0 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/sshamble-0.3.3/go.sum new/sshamble-0.3.10/go.sum --- old/sshamble-0.3.3/go.sum 2025-08-19 06:31:06.000000000 +0200 +++ new/sshamble-0.3.10/go.sum 2026-07-28 04:41:47.000000000 +0200 @@ -1,29 +1,16 @@ -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/fsnotify/fsnotify v1.10.0 h1:Xx/5Ydg9CeBDX/wi4VJqStNtohYjitZhhlHt4h3St1M= +github.com/fsnotify/fsnotify v1.10.0/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= +github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= +github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github/v50 v50.2.0/go.mod h1:VBY8FB6yPIjrtKhozXv4FQupxKLS6H4m6xFZlT43q8Q= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -34,138 +21,74 @@ github.com/logrusorgru/aurora/v3 v3.0.0/go.mod h1:vsR12bk5grlLvLXAYrBsb5Oc/N+LxAlxggSjiwMnCUc= github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3 h1:2YMbJ6WbdQI9K73chxh9OWMDsZ2PNjAIRGTonp3T0l0= github.com/mmcloughlin/professor v0.0.0-20170922221822-6b97112ab8b3/go.mod h1:LQkXsHRSPIEklPCq8OMQAzYNS2NGtYStdNE/ej1oJU8= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3 h1:nHWx/OmoqfYMkTKY383N//ll3a36JjoszUqurhucPW4= -github.com/runZeroInc/excrypto v0.0.0-20250629231451-7e62a70a4cc3/go.mod h1:Kk/CHQ2eYnHRYmR1eT1mA6L8LbNXA10fg8m8CqF6s2E= +github.com/runZeroInc/excrypto v0.39.0 h1:QBUmJoVBdzfUTxLUN7wlro7d1ko4KBnwfPpVPDtDXSA= +github.com/runZeroInc/excrypto v0.39.0/go.mod h1:rj/ZnkrH3P9juT/ROY80Zsrmkv7ZFZwlXLU6cf7ZuGo= +github.com/runZeroInc/excrypto v0.42.1 h1:n9eADIuoxyL3/dlTP9dyIK0EE8um/6tWJJAea+x7aq4= +github.com/runZeroInc/excrypto v0.42.1/go.mod h1:2+D8ndV+Ux7bkqknBACE8nbu1f2cb4G+B6OdcKbBL5c= +github.com/runZeroInc/excrypto v0.42.2 h1:VJgxfApAsXaYUXgNGDyH+FM0BkbjqtDodXud+MalBSo= +github.com/runZeroInc/excrypto v0.42.2/go.mod h1:2+D8ndV+Ux7bkqknBACE8nbu1f2cb4G+B6OdcKbBL5c= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.10.0 h1:FM8Cv6j2KqIhM2ZK7HZjm4mpj9NBktLgowT1aN9q5Cc= -github.com/sagikazarmark/locafero v0.10.0/go.mod h1:Ieo3EUsjifvQu4NZwV5sPd4dwvu0OCgEQV7vjc9yDjw= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= -github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= -github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= -github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= -github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= -github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= -github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= -github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/weppos/publicsuffix-go v0.40.2 h1:LlnoSH0Eqbsi3ReXZWBKCK5lHyzf3sc1JEHH1cnlfho= -github.com/weppos/publicsuffix-go v0.40.2/go.mod h1:XsLZnULC3EJ1Gvk9GVjuCTZ8QUu9ufE4TZpOizDShko= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.16 h1:ld6NyySjx5lowVKwJvMRLnW5nxKX/xnpSiFYZ/Lxur0= +github.com/ulikunitz/xz v0.5.16/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw= +github.com/weppos/publicsuffix-go v0.50.3 h1:eT5dcjHQcVDNc0igpFEsGHKIip30feuB2zuuI9eJxiE= +github.com/weppos/publicsuffix-go v0.50.3/go.mod h1:/rOa781xBykZhHK/I3QeHo92qdDKVmKZKF7s8qAEM/4= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= +golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ= +golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0= +golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ++++++ vendor.tar.gz ++++++ ++++ 298111 lines of diff (skipped)
