mirror of
https://github.com/dstotijn/hetty
synced 2024-11-22 03:33:03 +00:00
Add certificate management subcommands
This commit is contained in:
parent
ca0c085021
commit
ad26478043
5 changed files with 238 additions and 7 deletions
211
cmd/hetty/cert.go
Normal file
211
cmd/hetty/cert.go
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/peterbourgon/ff/v3/ffcli"
|
||||||
|
"github.com/smallstep/truststore"
|
||||||
|
)
|
||||||
|
|
||||||
|
var certUsage = `
|
||||||
|
Usage:
|
||||||
|
hetty cert <subcommand> [flags]
|
||||||
|
|
||||||
|
Certificate management tools.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help, -h Output this usage text.
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
- install Installs a certificate to the system trust store, and
|
||||||
|
(optionally) to the Firefox and Java trust stores.
|
||||||
|
- uninstall Uninstalls a certificate from the system trust store, and
|
||||||
|
(optionally) from the Firefox and Java trust stores.
|
||||||
|
|
||||||
|
Run ` + "`hetty cert <subcommand> --help`" + ` for subcommand specific usage instructions.
|
||||||
|
|
||||||
|
Visit https://hetty.xyz to learn more about Hetty.
|
||||||
|
`
|
||||||
|
|
||||||
|
var certInstallUsage = `
|
||||||
|
Usage:
|
||||||
|
hetty cert install [flags]
|
||||||
|
|
||||||
|
Installs a certificate to the system trust store, and (optionally) to the Firefox
|
||||||
|
and Java trust stores.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--cert Path to certificate. (Default: "~/.hetty/hetty_cert.pem")
|
||||||
|
--firefox Install certificate to Firefox trust store. (Default: false)
|
||||||
|
--java Install certificate to Java trust store. (Default: false)
|
||||||
|
--skip-system Skip installing certificate to system trust store (Default: false)
|
||||||
|
--help, -h Output this usage text.
|
||||||
|
|
||||||
|
Visit https://hetty.xyz to learn more about Hetty.
|
||||||
|
`
|
||||||
|
|
||||||
|
var certUninstallUsage = `
|
||||||
|
Usage:
|
||||||
|
hetty cert uninstall [flags]
|
||||||
|
|
||||||
|
Uninstalls a certificate from the system trust store, and (optionally) from the Firefox
|
||||||
|
and Java trust stores.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--cert Path to certificate. (Default: "~/.hetty/hetty_cert.pem")
|
||||||
|
--firefox Uninstall certificate from Firefox trust store. (Default: false)
|
||||||
|
--java Uninstall certificate from Java trust store. (Default: false)
|
||||||
|
--skip-system Skip uninstalling certificate from system trust store (Default: false)
|
||||||
|
--help, -h Output this usage text.
|
||||||
|
|
||||||
|
Visit https://hetty.xyz to learn more about Hetty.
|
||||||
|
`
|
||||||
|
|
||||||
|
type CertInstallCommand struct {
|
||||||
|
config *Config
|
||||||
|
cert string
|
||||||
|
firefox bool
|
||||||
|
java bool
|
||||||
|
skipSystem bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertUninstallCommand struct {
|
||||||
|
config *Config
|
||||||
|
cert string
|
||||||
|
firefox bool
|
||||||
|
java bool
|
||||||
|
skipSystem bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCertCommand(rootConfig *Config) *ffcli.Command {
|
||||||
|
return &ffcli.Command{
|
||||||
|
Name: "cert",
|
||||||
|
Subcommands: []*ffcli.Command{
|
||||||
|
NewCertInstallCommand(rootConfig),
|
||||||
|
NewCertUninstallCommand(rootConfig),
|
||||||
|
},
|
||||||
|
Exec: func(context.Context, []string) error {
|
||||||
|
return flag.ErrHelp
|
||||||
|
},
|
||||||
|
UsageFunc: func(*ffcli.Command) string {
|
||||||
|
return certUsage
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCertInstallCommand(rootConfig *Config) *ffcli.Command {
|
||||||
|
cmd := CertInstallCommand{
|
||||||
|
config: rootConfig,
|
||||||
|
}
|
||||||
|
fs := flag.NewFlagSet("hetty cert install", flag.ExitOnError)
|
||||||
|
|
||||||
|
fs.StringVar(&cmd.cert, "cert", "~/.hetty/hetty_cert.pem", "Path to certificate.")
|
||||||
|
fs.BoolVar(&cmd.firefox, "firefox", false, "Install certificate to Firefox trust store. (Default: false)")
|
||||||
|
fs.BoolVar(&cmd.java, "java", false, "Install certificate to Java trust store. (Default: false)")
|
||||||
|
fs.BoolVar(&cmd.skipSystem, "skip-system", false, "Skip installing certificate to system trust store (Default: false)")
|
||||||
|
|
||||||
|
cmd.config.RegisterFlags(fs)
|
||||||
|
|
||||||
|
return &ffcli.Command{
|
||||||
|
Name: "install",
|
||||||
|
FlagSet: fs,
|
||||||
|
Exec: cmd.Exec,
|
||||||
|
UsageFunc: func(*ffcli.Command) string {
|
||||||
|
return certInstallUsage
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *CertInstallCommand) Exec(_ context.Context, _ []string) error {
|
||||||
|
caCertFile, err := homedir.Expand(cmd.cert)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse certificate filepath: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []truststore.Option{}
|
||||||
|
|
||||||
|
if cmd.skipSystem {
|
||||||
|
opts = append(opts, truststore.WithNoSystem())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.firefox {
|
||||||
|
opts = append(opts, truststore.WithFirefox())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.java {
|
||||||
|
opts = append(opts, truststore.WithJava())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.skipSystem {
|
||||||
|
cmd.config.logger.Info(
|
||||||
|
"To install the certificate in the system trust store, you might be prompted for your password.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := truststore.InstallFile(caCertFile, opts...); err != nil {
|
||||||
|
return fmt.Errorf("failed to install certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.config.logger.Info("Finished installing certificate.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCertUninstallCommand(rootConfig *Config) *ffcli.Command {
|
||||||
|
cmd := CertUninstallCommand{
|
||||||
|
config: rootConfig,
|
||||||
|
}
|
||||||
|
fs := flag.NewFlagSet("hetty cert uninstall", flag.ExitOnError)
|
||||||
|
|
||||||
|
fs.StringVar(&cmd.cert, "cert", "~/.hetty/hetty_cert.pem", "Path to certificate.")
|
||||||
|
fs.BoolVar(&cmd.firefox, "firefox", false, "Uninstall certificate from Firefox trust store. (Default: false)")
|
||||||
|
fs.BoolVar(&cmd.java, "java", false, "Uninstall certificate from Java trust store. (Default: false)")
|
||||||
|
fs.BoolVar(&cmd.skipSystem, "skip-system", false, "Skip uninstalling certificate from system trust store (Default: false)")
|
||||||
|
|
||||||
|
cmd.config.RegisterFlags(fs)
|
||||||
|
|
||||||
|
return &ffcli.Command{
|
||||||
|
Name: "uninstall",
|
||||||
|
FlagSet: fs,
|
||||||
|
Exec: cmd.Exec,
|
||||||
|
UsageFunc: func(*ffcli.Command) string {
|
||||||
|
return certUninstallUsage
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *CertUninstallCommand) Exec(_ context.Context, _ []string) error {
|
||||||
|
caCertFile, err := homedir.Expand(cmd.cert)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse certificate filepath: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := []truststore.Option{}
|
||||||
|
|
||||||
|
if cmd.skipSystem {
|
||||||
|
opts = append(opts, truststore.WithNoSystem())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.firefox {
|
||||||
|
opts = append(opts, truststore.WithFirefox())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cmd.java {
|
||||||
|
opts = append(opts, truststore.WithJava())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cmd.skipSystem {
|
||||||
|
cmd.config.logger.Info(
|
||||||
|
"To uninstall the certificate from the system trust store, you might be prompted for your password.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := truststore.UninstallFile(caCertFile, opts...); err != nil {
|
||||||
|
return fmt.Errorf("failed to uninstall certificate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.config.logger.Info("Finished uninstalling certificate.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -43,7 +43,9 @@ var adminContent embed.FS
|
||||||
|
|
||||||
var hettyUsage = `
|
var hettyUsage = `
|
||||||
Usage:
|
Usage:
|
||||||
hetty [flags]
|
hetty [flags] [subcommand] [flags]
|
||||||
|
|
||||||
|
Runs an HTTP server with (MITM) proxy, GraphQL service, and a web based admin interface.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--cert Path to root CA certificate. Creates file if it doesn't exist. (Default: "~/.hetty/hetty_cert.pem")
|
--cert Path to root CA certificate. Creates file if it doesn't exist. (Default: "~/.hetty/hetty_cert.pem")
|
||||||
|
@ -56,6 +58,11 @@ Options:
|
||||||
--version, -v Output version.
|
--version, -v Output version.
|
||||||
--help, -h Output this usage text.
|
--help, -h Output this usage text.
|
||||||
|
|
||||||
|
Subcommands:
|
||||||
|
- cert Certificate management
|
||||||
|
|
||||||
|
Run ` + "`hetty <subcommand> --help`" + ` for subcommand specific usage instructions.
|
||||||
|
|
||||||
Visit https://hetty.xyz to learn more about Hetty.
|
Visit https://hetty.xyz to learn more about Hetty.
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -90,10 +97,12 @@ func NewHettyCommand() (*ffcli.Command, *Config) {
|
||||||
cmd.config.RegisterFlags(fs)
|
cmd.config.RegisterFlags(fs)
|
||||||
|
|
||||||
return &ffcli.Command{
|
return &ffcli.Command{
|
||||||
Name: "hetty",
|
Name: "hetty",
|
||||||
ShortUsage: "hetty [-v] [subcommand] [flags] [<arg>...]",
|
FlagSet: fs,
|
||||||
FlagSet: fs,
|
Subcommands: []*ffcli.Command{
|
||||||
Exec: cmd.Exec,
|
NewCertCommand(cmd.config),
|
||||||
|
},
|
||||||
|
Exec: cmd.Exec,
|
||||||
UsageFunc: func(*ffcli.Command) string {
|
UsageFunc: func(*ffcli.Command) string {
|
||||||
return hettyUsage
|
return hettyUsage
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,6 +2,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
llog "log"
|
llog "log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -25,7 +27,8 @@ func main() {
|
||||||
|
|
||||||
cfg.logger = logger
|
cfg.logger = logger
|
||||||
|
|
||||||
if err := hettyCmd.Run(context.Background()); err != nil {
|
err = hettyCmd.Run(context.Background())
|
||||||
logger.Fatal("Unexpected error running command.", zap.Error(err))
|
if err != nil && !errors.Is(err, flag.ErrHelp) {
|
||||||
|
logger.Fatal("Command failed.", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -12,6 +12,7 @@ require (
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/peterbourgon/ff/v3 v3.1.2
|
github.com/peterbourgon/ff/v3 v3.1.2
|
||||||
|
github.com/smallstep/truststore v0.11.0
|
||||||
github.com/vektah/gqlparser/v2 v2.2.0
|
github.com/vektah/gqlparser/v2 v2.2.0
|
||||||
go.uber.org/zap v1.21.0
|
go.uber.org/zap v1.21.0
|
||||||
)
|
)
|
||||||
|
@ -54,4 +55,5 @@ require (
|
||||||
golang.org/x/tools v0.1.5 // indirect
|
golang.org/x/tools v0.1.5 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||||
|
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
|
||||||
)
|
)
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -81,6 +81,7 @@ github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||||
|
@ -133,6 +134,8 @@ github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJ
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||||
|
github.com/smallstep/truststore v0.11.0 h1:JUTkQ4oHr40jHTS/A2t0usEhteMWG+45CDD2iJA/dIk=
|
||||||
|
github.com/smallstep/truststore v0.11.0/go.mod h1:HwHKRcBi0RUxxw1LYDpTRhYC4jZUuxPpkHdVonlkoDM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
@ -250,6 +253,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
@ -258,5 +262,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
|
||||||
|
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
|
||||||
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
|
||||||
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
|
sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k=
|
||||||
|
|
Loading…
Reference in a new issue