Skip to content

Commit 486a5e9

Browse files
committed
feat(hckms): Add HuaweiCloud KMS support
1 parent 07ded6f commit 486a5e9

File tree

10 files changed

+794
-109
lines changed

10 files changed

+794
-109
lines changed

cmd/sops/main.go

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/getsops/sops/v3/cmd/sops/subcommand/updatekeys"
3535
"github.com/getsops/sops/v3/config"
3636
"github.com/getsops/sops/v3/gcpkms"
37+
"github.com/getsops/sops/v3/hckms"
3738
"github.com/getsops/sops/v3/hcvault"
3839
"github.com/getsops/sops/v3/keys"
3940
"github.com/getsops/sops/v3/keyservice"
@@ -90,13 +91,13 @@ func main() {
9091
},
9192
}
9293
app.Name = "sops"
93-
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, Azure Key Vault, age, and GPG support"
94+
app.Usage = "sops - encrypted file editor with AWS KMS, GCP KMS, HuaweiCloud KMS, Azure Key Vault, age, and GPG support"
9495
app.ArgsUsage = "sops [options] file"
9596
app.Version = version.Version
9697
app.Authors = []cli.Author{
9798
{Name: "CNCF Maintainers"},
9899
}
99-
app.UsageText = `sops is an editor of encrypted files that supports AWS KMS, GCP, AZKV,
100+
app.UsageText = `sops is an editor of encrypted files that supports AWS KMS, GCP, HuaweiCloud KMS, AZKV,
100101
PGP, and Age
101102
102103
To encrypt or decrypt a document with AWS KMS, specify the KMS ARN
@@ -109,6 +110,12 @@ func main() {
109110
(You need to setup Google application default credentials. See
110111
https://developers.google.com/identity/protocols/application-default-credentials)
111112
113+
To encrypt or decrypt a document with HuaweiCloud KMS, specify the
114+
HuaweiCloud KMS key ID (format: region:key-uuid) in the --hckms flag or in the
115+
SOPS_HUAWEICLOUD_KMS_IDS environment variable.
116+
(You need to setup HuaweiCloud credentials via environment variables:
117+
HUAWEICLOUD_SDK_AK, HUAWEICLOUD_SDK_SK, HUAWEICLOUD_SDK_PROJECT_ID, or
118+
use credentials file at ~/.huaweicloud/credentials)
112119
113120
To encrypt or decrypt a document with HashiCorp Vault's Transit Secret
114121
Engine, specify the Vault key URI name in the --hc-vault-transit flag
@@ -135,12 +142,12 @@ func main() {
135142
To use multiple KMS or PGP keys, separate them by commas. For example:
136143
$ sops -p "10F2...0A, 85D...B3F21" file.yaml
137144
138-
The -p, -k, --gcp-kms, --hc-vault-transit, and --azure-kv flags are only
145+
The -p, -k, --gcp-kms, --hckms, --hc-vault-transit, and --azure-kv flags are only
139146
used to encrypt new documents. Editing or decrypting existing documents
140147
can be done with "sops file" or "sops decrypt file" respectively. The KMS and
141148
PGP keys listed in the encrypted documents are used then. To manage master
142-
keys in existing documents, use the "add-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}"
143-
and "rm-{kms,pgp,gcp-kms,azure-kv,hc-vault-transit}" flags with --rotate
149+
keys in existing documents, use the "add-{kms,pgp,gcp-kms,hckms,azure-kv,hc-vault-transit}"
150+
and "rm-{kms,pgp,gcp-kms,hckms,azure-kv,hc-vault-transit}" flags with --rotate
144151
or the updatekeys command.
145152
146153
To use a different GPG binary than the one in your PATH, set SOPS_GPG_EXEC.
@@ -570,6 +577,10 @@ func main() {
570577
Name: "gcp-kms",
571578
Usage: "the GCP KMS Resource ID the new group should contain. Can be specified more than once",
572579
},
580+
cli.StringSliceFlag{
581+
Name: "hckms",
582+
Usage: "the HuaweiCloud KMS key ID (format: region:key-uuid) the new group should contain. Can be specified more than once",
583+
},
573584
cli.StringSliceFlag{
574585
Name: "azure-kv",
575586
Usage: "the Azure Key Vault key URL the new group should contain. Can be specified more than once",
@@ -933,6 +944,11 @@ func main() {
933944
Usage: "comma separated list of GCP KMS resource IDs",
934945
EnvVar: "SOPS_GCP_KMS_IDS",
935946
},
947+
cli.StringFlag{
948+
Name: "hckms",
949+
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
950+
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
951+
},
936952
cli.StringFlag{
937953
Name: "azure-kv",
938954
Usage: "comma separated list of Azure Key Vault URLs",
@@ -1118,6 +1134,14 @@ func main() {
11181134
Name: "rm-gcp-kms",
11191135
Usage: "remove the provided comma-separated list of GCP KMS key resource IDs from the list of master keys on the given file",
11201136
},
1137+
cli.StringFlag{
1138+
Name: "add-hckms",
1139+
Usage: "add the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) to the list of master keys on the given file",
1140+
},
1141+
cli.StringFlag{
1142+
Name: "rm-hckms",
1143+
Usage: "remove the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) from the list of master keys on the given file",
1144+
},
11211145
cli.StringFlag{
11221146
Name: "add-azure-kv",
11231147
Usage: "add the provided comma-separated list of Azure Key Vault key URLs to the list of master keys on the given file",
@@ -1184,8 +1208,8 @@ func main() {
11841208
return toExitError(err)
11851209
}
11861210
if _, err := os.Stat(fileName); os.IsNotExist(err) {
1187-
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
1188-
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
1211+
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
1212+
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hckms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
11891213
return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use the `edit` subcommand instead.", fileName), codes.CannotChangeKeysFromNonExistentFile)
11901214
}
11911215
}
@@ -1271,6 +1295,11 @@ func main() {
12711295
Usage: "comma separated list of GCP KMS resource IDs",
12721296
EnvVar: "SOPS_GCP_KMS_IDS",
12731297
},
1298+
cli.StringFlag{
1299+
Name: "hckms",
1300+
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
1301+
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
1302+
},
12741303
cli.StringFlag{
12751304
Name: "azure-kv",
12761305
Usage: "comma separated list of Azure Key Vault URLs",
@@ -1679,6 +1708,11 @@ func main() {
16791708
Usage: "comma separated list of GCP KMS resource IDs",
16801709
EnvVar: "SOPS_GCP_KMS_IDS",
16811710
},
1711+
cli.StringFlag{
1712+
Name: "hckms",
1713+
Usage: "comma separated list of HuaweiCloud KMS key IDs (format: region:key-uuid)",
1714+
EnvVar: "SOPS_HUAWEICLOUD_KMS_IDS",
1715+
},
16821716
cli.StringFlag{
16831717
Name: "azure-kv",
16841718
Usage: "comma separated list of Azure Key Vault URLs",
@@ -1727,6 +1761,14 @@ func main() {
17271761
Name: "rm-gcp-kms",
17281762
Usage: "remove the provided comma-separated list of GCP KMS key resource IDs from the list of master keys on the given file",
17291763
},
1764+
cli.StringFlag{
1765+
Name: "add-hckms",
1766+
Usage: "add the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) to the list of master keys on the given file",
1767+
},
1768+
cli.StringFlag{
1769+
Name: "rm-hckms",
1770+
Usage: "remove the provided comma-separated list of HuaweiCloud KMS key IDs (format: region:key-uuid) from the list of master keys on the given file",
1771+
},
17301772
cli.StringFlag{
17311773
Name: "add-azure-kv",
17321774
Usage: "add the provided comma-separated list of Azure Key Vault key URLs to the list of master keys on the given file",
@@ -1861,8 +1903,8 @@ func main() {
18611903
return toExitError(err)
18621904
}
18631905
if _, err := os.Stat(fileName); os.IsNotExist(err) {
1864-
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
1865-
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
1906+
if c.String("add-kms") != "" || c.String("add-pgp") != "" || c.String("add-gcp-kms") != "" || c.String("add-hckms") != "" || c.String("add-hc-vault-transit") != "" || c.String("add-azure-kv") != "" || c.String("add-age") != "" ||
1907+
c.String("rm-kms") != "" || c.String("rm-pgp") != "" || c.String("rm-gcp-kms") != "" || c.String("rm-hckms") != "" || c.String("rm-hc-vault-transit") != "" || c.String("rm-azure-kv") != "" || c.String("rm-age") != "" {
18661908
return common.NewExitError(fmt.Sprintf("Error: cannot add or remove keys on non-existent file %q, use `--kms` and `--pgp` instead.", fileName), codes.CannotChangeKeysFromNonExistentFile)
18671909
}
18681910
if isEncryptMode || isDecryptMode || isRotateMode {
@@ -2191,7 +2233,7 @@ func getEncryptConfig(c *cli.Context, fileName string, inputStore common.Store,
21912233
}, nil
21922234
}
21932235

2194-
func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string) ([]keys.MasterKey, error) {
2236+
func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsOptionName string, pgpOptionName string, gcpKmsOptionName string, hckmsOptionName string, azureKvOptionName string, hcVaultTransitOptionName string, ageOptionName string) ([]keys.MasterKey, error) {
21952237
var masterKeys []keys.MasterKey
21962238
for _, k := range kms.MasterKeysFromArnString(c.String(kmsOptionName), kmsEncryptionContext, c.String("aws-profile")) {
21972239
masterKeys = append(masterKeys, k)
@@ -2202,6 +2244,13 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO
22022244
for _, k := range gcpkms.MasterKeysFromResourceIDString(c.String(gcpKmsOptionName)) {
22032245
masterKeys = append(masterKeys, k)
22042246
}
2247+
hckmsKeys, err := hckms.NewMasterKeyFromKeyIDString(c.String(hckmsOptionName))
2248+
if err != nil {
2249+
return nil, err
2250+
}
2251+
for _, k := range hckmsKeys {
2252+
masterKeys = append(masterKeys, k)
2253+
}
22052254
azureKeys, err := azkv.MasterKeysFromURLs(c.String(azureKvOptionName))
22062255
if err != nil {
22072256
return nil, err
@@ -2228,11 +2277,11 @@ func getMasterKeys(c *cli.Context, kmsEncryptionContext map[string]*string, kmsO
22282277

22292278
func getRotateOpts(c *cli.Context, fileName string, inputStore common.Store, outputStore common.Store, svcs []keyservice.KeyServiceClient, decryptionOrder []string) (rotateOpts, error) {
22302279
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
2231-
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-azure-kv", "add-hc-vault-transit", "add-age")
2280+
addMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "add-kms", "add-pgp", "add-gcp-kms", "add-hckms", "add-azure-kv", "add-hc-vault-transit", "add-age")
22322281
if err != nil {
22332282
return rotateOpts{}, err
22342283
}
2235-
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
2284+
rmMasterKeys, err := getMasterKeys(c, kmsEncryptionContext, "rm-kms", "rm-pgp", "rm-gcp-kms", "rm-hckms", "rm-azure-kv", "rm-hc-vault-transit", "rm-age")
22362285
if err != nil {
22372286
return rotateOpts{}, err
22382287
}
@@ -2380,6 +2429,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
23802429
var cloudKmsKeys []keys.MasterKey
23812430
var azkvKeys []keys.MasterKey
23822431
var hcVaultMkKeys []keys.MasterKey
2432+
var hckmsMkKeys []keys.MasterKey
23832433
var ageMasterKeys []keys.MasterKey
23842434
kmsEncryptionContext := kms.ParseKMSContext(c.String("encryption-context"))
23852435
if c.String("encryption-context") != "" && kmsEncryptionContext == nil {
@@ -2395,6 +2445,15 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
23952445
cloudKmsKeys = append(cloudKmsKeys, k)
23962446
}
23972447
}
2448+
if c.String("hckms") != "" {
2449+
hckmsKeys, err := hckms.NewMasterKeyFromKeyIDString(c.String("hckms"))
2450+
if err != nil {
2451+
return nil, err
2452+
}
2453+
for _, k := range hckmsKeys {
2454+
hckmsMkKeys = append(hckmsMkKeys, k)
2455+
}
2456+
}
23982457
if c.String("azure-kv") != "" {
23992458
azureKeys, err := azkv.MasterKeysFromURLs(c.String("azure-kv"))
24002459
if err != nil {
@@ -2427,7 +2486,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
24272486
ageMasterKeys = append(ageMasterKeys, k)
24282487
}
24292488
}
2430-
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
2489+
if c.String("kms") == "" && c.String("pgp") == "" && c.String("gcp-kms") == "" && c.String("hckms") == "" && c.String("azure-kv") == "" && c.String("hc-vault-transit") == "" && c.String("age") == "" {
24312490
conf := optionalConfig
24322491
var err error
24332492
if conf == nil {
@@ -2446,6 +2505,7 @@ func keyGroups(c *cli.Context, file string, optionalConfig *config.Config) ([]so
24462505
var group sops.KeyGroup
24472506
group = append(group, kmsKeys...)
24482507
group = append(group, cloudKmsKeys...)
2508+
group = append(group, hckmsMkKeys...)
24492509
group = append(group, azkvKeys...)
24502510
group = append(group, pgpKeys...)
24512511
group = append(group, hcVaultMkKeys...)

config/config.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/getsops/sops/v3/age"
1616
"github.com/getsops/sops/v3/azkv"
1717
"github.com/getsops/sops/v3/gcpkms"
18+
"github.com/getsops/sops/v3/hckms"
1819
"github.com/getsops/sops/v3/hcvault"
1920
"github.com/getsops/sops/v3/kms"
2021
"github.com/getsops/sops/v3/pgp"
@@ -132,6 +133,7 @@ type keyGroup struct {
132133
Merge []keyGroup `yaml:"merge"`
133134
KMS []kmsKey `yaml:"kms"`
134135
GCPKMS []gcpKmsKey `yaml:"gcp_kms"`
136+
HCKms []hckmsKey `yaml:"hckms"`
135137
AzureKV []azureKVKey `yaml:"azure_keyvault"`
136138
Vault []string `yaml:"hc_vault"`
137139
Age []string `yaml:"age"`
@@ -155,6 +157,10 @@ type azureKVKey struct {
155157
Version string `yaml:"version"`
156158
}
157159

160+
type hckmsKey struct {
161+
KeyID string `yaml:"key_id"`
162+
}
163+
158164
type destinationRule struct {
159165
PathRegex string `yaml:"path_regex"`
160166
S3Bucket string `yaml:"s3_bucket"`
@@ -176,6 +182,7 @@ type creationRule struct {
176182
Age interface{} `yaml:"age"` // string or []string
177183
PGP interface{} `yaml:"pgp"` // string or []string
178184
GCPKMS interface{} `yaml:"gcp_kms"` // string or []string
185+
HCKms interface{} `yaml:"hckms"` // string or []string
179186
AzureKeyVault interface{} `yaml:"azure_keyvault"` // string or []string
180187
VaultURI interface{} `yaml:"hc_vault_transit_uri"` // string or []string
181188
KeyGroups []keyGroup `yaml:"key_groups"`
@@ -214,6 +221,10 @@ func (c *creationRule) GetVaultURIs() ([]string, error) {
214221
return parseKeyField(c.VaultURI, "hc_vault_transit_uri")
215222
}
216223

224+
func (c *creationRule) GetHckmsKeys() ([]string, error) {
225+
return parseKeyField(c.HCKms, "hckms")
226+
}
227+
217228
// Utility function to handle both string and []string
218229
func parseKeyField(field interface{}, fieldName string) ([]string, error) {
219230
if field == nil {
@@ -329,6 +340,13 @@ func extractMasterKeys(group keyGroup) (sops.KeyGroup, error) {
329340
for _, k := range group.GCPKMS {
330341
keyGroup = append(keyGroup, gcpkms.NewMasterKeyFromResourceID(k.ResourceID))
331342
}
343+
for _, k := range group.HCKms {
344+
key, err := hckms.NewMasterKey(k.KeyID)
345+
if err != nil {
346+
return nil, err
347+
}
348+
keyGroup = append(keyGroup, key)
349+
}
332350
for _, k := range group.AzureKV {
333351
if key, err := azkv.NewMasterKeyWithOptionalVersion(k.VaultURL, k.Key, k.Version); err == nil {
334352
keyGroup = append(keyGroup, key)
@@ -402,6 +420,17 @@ func getKeyGroupsFromCreationRule(cRule *creationRule, kmsEncryptionContext map[
402420
for _, k := range gcpkms.MasterKeysFromResourceIDString(strings.Join(gcpkmsKeys, ",")) {
403421
keyGroup = append(keyGroup, k)
404422
}
423+
hckmsKeys, err := getKeysWithValidation(cRule.GetHckmsKeys, "hckms")
424+
if err != nil {
425+
return nil, err
426+
}
427+
hckmsMasterKeys, err := hckms.NewMasterKeyFromKeyIDString(strings.Join(hckmsKeys, ","))
428+
if err != nil {
429+
return nil, err
430+
}
431+
for _, k := range hckmsMasterKeys {
432+
keyGroup = append(keyGroup, k)
433+
}
405434
azKeys, err := getKeysWithValidation(cRule.GetAzureKeyVaultKeys, "azure_keyvault")
406435
if err != nil {
407436
return nil, err

go.mod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require (
2525
github.com/goware/prefixer v0.0.0-20160118172347-395022866408
2626
github.com/hashicorp/go-cleanhttp v0.5.2
2727
github.com/hashicorp/vault/api v1.22.0
28+
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.176
2829
github.com/lib/pq v1.10.9
2930
github.com/mitchellh/go-homedir v1.1.0
3031
github.com/mitchellh/go-wordwrap v1.0.1
@@ -97,6 +98,7 @@ require (
9798
github.com/go-logr/logr v1.4.3 // indirect
9899
github.com/go-logr/stdr v1.2.2 // indirect
99100
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
101+
github.com/goccy/go-yaml v1.9.8 // indirect
100102
github.com/gogo/protobuf v1.3.2 // indirect
101103
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
102104
github.com/google/s2a-go v0.1.9 // indirect
@@ -111,13 +113,16 @@ require (
111113
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
112114
github.com/hashicorp/go-sockaddr v1.0.7 // indirect
113115
github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
116+
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
114117
github.com/kylelemons/godebug v1.1.0 // indirect
115118
github.com/mattn/go-colorable v0.1.14 // indirect
116119
github.com/mattn/go-isatty v0.0.20 // indirect
117120
github.com/mitchellh/mapstructure v1.5.0 // indirect
118121
github.com/moby/docker-image-spec v1.3.1 // indirect
119122
github.com/moby/sys/user v0.3.0 // indirect
120123
github.com/moby/term v0.5.2 // indirect
124+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
125+
github.com/modern-go/reflect2 v1.0.2 // indirect
121126
github.com/opencontainers/go-digest v1.0.0 // indirect
122127
github.com/opencontainers/image-spec v1.1.1 // indirect
123128
github.com/opencontainers/runc v1.2.8 // indirect
@@ -127,10 +132,12 @@ require (
127132
github.com/russross/blackfriday/v2 v2.1.0 // indirect
128133
github.com/ryanuber/go-glob v1.0.0 // indirect
129134
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
135+
github.com/tjfoc/gmsm v1.4.1 // indirect
130136
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
131137
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
132138
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
133139
github.com/zeebo/errs v1.4.0 // indirect
140+
go.mongodb.org/mongo-driver v1.13.1 // indirect
134141
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
135142
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
136143
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
@@ -143,6 +150,7 @@ require (
143150
golang.org/x/sync v0.17.0 // indirect
144151
golang.org/x/text v0.30.0 // indirect
145152
golang.org/x/time v0.14.0 // indirect
153+
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
146154
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
147155
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
148156
gopkg.in/yaml.v3 v3.0.1 // indirect

0 commit comments

Comments
 (0)