• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nixpig / syringe.sh / 13880173777

16 Mar 2025 05:10AM UTC coverage: 11.525% (-0.5%) from 11.981%
13880173777

push

github

nixpig
chore: add docker stuff

0 of 2 new or added lines in 2 files covered. (0.0%)

96 existing lines in 3 files now uncovered.

127 of 1102 relevant lines covered (11.52%)

0.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/internal/cli/cli.go
1
package cli
2

3
import (
4
        "bytes"
5
        "fmt"
6
        "io"
7
        "net/mail"
8
        "os"
9
        "os/user"
10
        "path/filepath"
11
        "strings"
12

13
        "github.com/nixpig/syringe.sh/internal/api"
14
        "github.com/nixpig/syringe.sh/pkg/ssh"
15
        "github.com/spf13/cobra"
16
        "github.com/spf13/pflag"
17
        "github.com/spf13/viper"
18
        "golang.org/x/term"
19
)
20

21
const (
22
        identityFlag = "identity"
23
        usernameFlag = "username"
24
        emailFlag    = "email"
25
        hostFlag     = "host"
26
        portFlag     = "port"
27
        configFlag   = "config"
28

29
        defaultHost = "ssh.syringe.sh"
30
        defaultPort = 2323
31
)
32

33
// TODO: how can we avoid this global variable?
34
var a *api.HostAPI
35

36
func New(v *viper.Viper) *cobra.Command {
×
37
        rootCmd := &cobra.Command{
×
38
                Use:          "syringe",
×
39
                Short:        "Encrypted key-value store",
×
40
                Version:      "",
×
41
                SilenceUsage: true,
×
UNCOV
42
                PersistentPreRunE: func(c *cobra.Command, args []string) error {
×
43
                        applyFlags(c, v)
×
44

×
45
                        configPath := v.GetString(configFlag)
×
46
                        v.SetConfigType("env")
×
47
                        v.SetConfigFile(configPath)
×
48
                        v.ReadInConfig()
×
49

×
50
                        identity := v.GetString(identityFlag)
×
51
                        if identity == "" {
×
52
                                c.Help()
×
UNCOV
53
                                return fmt.Errorf("no identity")
×
54
                        }
×
55

56
                        host := v.GetString(hostFlag)
×
57
                        if host == "" {
×
UNCOV
58
                                c.Help()
×
59
                                return fmt.Errorf("no host")
×
60
                        }
×
61

UNCOV
62
                        port := v.GetInt(portFlag)
×
UNCOV
63
                        if port < 1 || port > 65535 {
×
64
                                c.Help()
×
65
                                return fmt.Errorf("invalid port number")
×
66
                        }
×
67

68
                        username := v.GetString(usernameFlag)
×
UNCOV
69
                        if username == "" {
×
70
                                c.Help()
×
71
                                return fmt.Errorf("username is empty")
×
72
                        }
×
73

74
                        email := v.GetString(emailFlag)
×
75
                        if email == "" {
×
76
                                f, err := os.ReadFile(identity + ".pub")
×
77
                                if err == nil {
×
78
                                        parts := strings.Split(string(f), " ")
×
79
                                        if len(parts) > 0 {
×
80
                                                email = strings.TrimSpace(parts[len(parts)-1])
×
81
                                        }
×
82
                                }
83

84
                        }
85
                        if _, err := mail.ParseAddress(email); err != nil {
×
86
                                c.Help()
×
87
                                return fmt.Errorf("invalid email")
×
88
                        }
×
89

90
                        authMethod, err := ssh.AuthMethod(identity, c.OutOrStdout())
×
91
                        if err != nil {
×
92
                                return fmt.Errorf("failed to create auth method: %w", err)
×
93
                        }
×
94

UNCOV
95
                        client, err := ssh.NewSSHClient(
×
96
                                host,
×
97
                                port,
×
98
                                username,
×
99
                                authMethod,
×
100
                                filepath.Join(os.Getenv("HOME"), ".ssh", "known_hosts"),
×
101
                        )
×
102
                        if err != nil {
×
NEW
103
                                return fmt.Errorf("failed to create ssh client: %w", err)
×
UNCOV
104
                        }
×
105

UNCOV
106
                        a = api.New(client, c.OutOrStdout())
×
107

×
108
                        return nil
×
109
                },
110
                PersistentPostRun: func(c *cobra.Command, args []string) {
×
111
                        a.Close()
×
112
                },
×
113
        }
114

115
        defaultUsername := ""
×
116
        currentUser, err := user.Current()
×
117
        if err == nil {
×
118
                defaultUsername = currentUser.Username
×
UNCOV
119
        }
×
120

121
        defaultConfigPath := ""
×
122
        configDir, _ := os.UserConfigDir()
×
123
        if configDir != "" {
×
124
                defaultConfigPath = filepath.Join(configDir, "syringe.cfg")
×
125
        }
×
126

127
        rootCmd.CompletionOptions.HiddenDefaultCmd = true
×
128
        rootCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to SSH key")
×
129
        rootCmd.PersistentFlags().StringP(usernameFlag, "u", defaultUsername, "Username")
×
UNCOV
130
        rootCmd.PersistentFlags().StringP(emailFlag, "e", "", "Email")
×
131
        rootCmd.PersistentFlags().StringP(hostFlag, "d", defaultHost, "Host")
×
UNCOV
132
        rootCmd.PersistentFlags().IntP(portFlag, "p", defaultPort, "Port")
×
UNCOV
133
        rootCmd.PersistentFlags().StringP(configFlag, "c", defaultConfigPath, "Config file location")
×
UNCOV
134

×
UNCOV
135
        bindFlags(rootCmd, v)
×
136

×
137
        rootCmd.AddCommand(
×
138
                registerCmd(v),
×
139
                setCmd(v),
×
140
                getCmd(v),
×
141
                listCmd(v),
×
142
                removeCmd(v),
×
143
        )
×
144

×
145
        return rootCmd
×
146
}
147

148
func registerCmd(v *viper.Viper) *cobra.Command {
×
149
        return &cobra.Command{
×
UNCOV
150
                Use:   "register [flags]",
×
151
                Short: "Register a user and key",
×
152
                Args:  cobra.ExactArgs(0),
×
153
                RunE: func(c *cobra.Command, args []string) error {
×
154
                        return a.Register()
×
155
                },
×
156
        }
157
}
158

159
func setCmd(v *viper.Viper) *cobra.Command {
×
UNCOV
160
        return &cobra.Command{
×
161
                Use:     "set [flags] KEY VALUE",
×
162
                Short:   "Set a key-value",
×
163
                Args:    cobra.ExactArgs(2),
×
164
                Example: "  syringe set username nixpig",
×
UNCOV
165
                RunE: func(c *cobra.Command, args []string) error {
×
166
                        identity := v.GetString(identityFlag)
×
167
                        publicKey, err := ssh.GetPublicKey(identity + ".pub")
×
168
                        if err != nil {
×
169
                                return fmt.Errorf("get public key: %w", err)
×
UNCOV
170
                        }
×
171

172
                        encrypt := ssh.NewEncryptor(publicKey)
×
173

×
UNCOV
174
                        encryptedValue, err := encrypt(args[1])
×
UNCOV
175
                        if err != nil {
×
UNCOV
176
                                return fmt.Errorf("encrypt: %w", err)
×
UNCOV
177
                        }
×
178

179
                        if err := a.Set(args[0], encryptedValue); err != nil {
×
180
                                return fmt.Errorf("set '%s' in store: %w", args[0], err)
×
181
                        }
×
182

183
                        return nil
×
184
                },
185
        }
186
}
187

UNCOV
188
func getCmd(v *viper.Viper) *cobra.Command {
×
UNCOV
189
        return &cobra.Command{
×
190
                Use:     "get [flags] KEY",
×
191
                Short:   "Get a value from the store",
×
192
                Args:    cobra.ExactArgs(1),
×
193
                Example: "  syringe get username",
×
194
                RunE: func(c *cobra.Command, args []string) error {
×
195
                        identity := v.GetString(identityFlag)
×
196

×
197
                        privateKey, err := ssh.GetPrivateKey(
×
198
                                identity, c.OutOrStderr(), term.ReadPassword,
×
UNCOV
199
                        )
×
UNCOV
200
                        if err != nil {
×
UNCOV
201
                                return fmt.Errorf("failed to get private key from identity: %w", err)
×
202
                        }
×
203

204
                        decrypt := ssh.NewDecryptor(privateKey)
×
205

×
UNCOV
206
                        var b bytes.Buffer
×
UNCOV
207
                        a.SetOut(io.Writer(&b))
×
208

×
209
                        err = a.Get(args[0])
×
210
                        if err != nil {
×
211
                                return err
×
212
                        }
×
213

UNCOV
214
                        d, err := io.ReadAll(&b)
×
UNCOV
215
                        if err != nil {
×
UNCOV
216
                                return err
×
UNCOV
217
                        }
×
218

UNCOV
219
                        decryptedValue, err := decrypt(string(d))
×
UNCOV
220
                        if err != nil {
×
UNCOV
221
                                return err
×
UNCOV
222
                        }
×
223

UNCOV
224
                        c.OutOrStdout().Write([]byte(decryptedValue))
×
UNCOV
225

×
UNCOV
226
                        return nil
×
227
                },
228
        }
229
}
230

UNCOV
231
func removeCmd(v *viper.Viper) *cobra.Command {
×
UNCOV
232
        return &cobra.Command{
×
UNCOV
233
                Use:     "remove [flags] KEY",
×
UNCOV
234
                Short:   "Remove a record from the store",
×
UNCOV
235
                Args:    cobra.ExactArgs(1),
×
UNCOV
236
                Example: "  syringe remove username",
×
UNCOV
237
                RunE: func(c *cobra.Command, args []string) error {
×
UNCOV
238
                        return a.Remove(args[0])
×
UNCOV
239
                },
×
240
        }
241
}
242

UNCOV
243
func listCmd(v *viper.Viper) *cobra.Command {
×
UNCOV
244
        return &cobra.Command{
×
UNCOV
245
                Use:     "list [flags]",
×
UNCOV
246
                Short:   "List all records in store",
×
UNCOV
247
                Args:    cobra.ExactArgs(0),
×
UNCOV
248
                Example: "  syringe list",
×
UNCOV
249
                RunE: func(c *cobra.Command, args []string) error {
×
UNCOV
250
                        return a.List()
×
UNCOV
251
                },
×
252
        }
253
}
254

UNCOV
255
func bindFlags(c *cobra.Command, v *viper.Viper) {
×
UNCOV
256
        c.PersistentFlags().VisitAll(func(f *pflag.Flag) {
×
UNCOV
257
                v.BindPFlag(f.Name, f)
×
UNCOV
258
        })
×
259
}
260

UNCOV
261
func applyFlags(c *cobra.Command, v *viper.Viper) {
×
UNCOV
262
        c.Flags().VisitAll(func(f *pflag.Flag) {
×
UNCOV
263
                if v.IsSet(f.Name) {
×
UNCOV
264
                        c.Flags().Set(f.Name, v.GetString(f.Name))
×
UNCOV
265
                }
×
266
        })
267
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc