Skip to content

Commit 856ed1c

Browse files
authored
feat: switch to api key auth instead of username and password (#23)
1 parent 94ade42 commit 856ed1c

12 files changed

Lines changed: 44 additions & 131 deletions

File tree

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,15 @@ go install github.com/saferwall/cli@latest
1212

1313
## Getting Started
1414

15-
To use the CLI you need a [Saferwall](https://saferwall.com) account. Run the `init` command to interactively set up your credentials:
15+
To use the CLI you need a [Saferwall](https://saferwall.com) API key. Run the `init` command to interactively set up your credentials:
1616

1717
```sh
1818
saferwall-cli init
1919
```
2020

2121
This launches an interactive prompt that asks for:
2222
- **URL** — the Saferwall API endpoint (defaults to `https://api.saferwall.com`)
23-
- **Username** — your Saferwall account username
24-
- **Password** — your Saferwall account password
23+
- **API Key** — your Saferwall API key
2524

2625
The credentials are saved to `~/.config/saferwall/config.toml`. To reconfigure, delete that file and run `init` again.
2726

cmd/download.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,19 +42,13 @@ var downloadCmd = &cobra.Command{
4242
Run: func(cmd *cobra.Command, args []string) {
4343
arg := args[0]
4444

45-
// Login to saferwall web service.
4645
webSvc := webapi.New(cfg.Credentials.URL)
47-
token, err := webSvc.Login(cfg.Credentials.Username, cfg.Credentials.Password)
48-
if err != nil {
49-
log.Fatalf("failed to authenticate: %v", err)
50-
}
51-
5246
hashes := collectHashes(arg)
5347
if len(hashes) == 0 {
5448
log.Fatalf("no valid SHA256 hashes found in %q", arg)
5549
}
5650

57-
downloadFiles(webSvc, token, hashes)
51+
downloadFiles(webSvc, cfg.Credentials.APIKey, hashes)
5852
},
5953
}
6054

cmd/init.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@ import (
1818
const configTemplate = `[credentials]
1919
# The URL used to interact with saferwall APIs.
2020
url = %q
21-
# The user name you choose when you sign-up for saferwall.com.
22-
username = %q
23-
# The password you choose when you sign-up for saferwall.com.
24-
password = %q
21+
# Your Saferwall API key.
22+
api_key = %q
2523
`
2624

2725
func configDir() string {
@@ -57,16 +55,16 @@ var initCmd = &cobra.Command{
5755
return
5856
}
5957

60-
url, username, password := m.values()
61-
if username == "" || password == "" {
62-
log.Fatalf("username and password are required")
58+
url, apiKey := m.values()
59+
if apiKey == "" {
60+
log.Fatalf("api key is required")
6361
}
6462

6563
if err := os.MkdirAll(configDir(), 0o700); err != nil {
6664
log.Fatalf("failed to create config directory: %v", err)
6765
}
6866

69-
content := fmt.Sprintf(configTemplate, url, username, password)
67+
content := fmt.Sprintf(configTemplate, url, apiKey)
7068
if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
7169
log.Fatalf("failed to write config file: %v", err)
7270
}

cmd/initui.go

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import (
1414

1515
const (
1616
fieldURL = iota
17-
fieldUsername
18-
fieldPassword
17+
fieldAPIKey
1918
fieldCount
2019
)
2120

@@ -35,16 +34,10 @@ func newInitModel() initModel {
3534
url.Prompt = " "
3635
inputs[fieldURL] = url
3736

38-
username := textinput.New()
39-
username.Placeholder = "your username"
40-
username.Prompt = " "
41-
inputs[fieldUsername] = username
42-
43-
password := textinput.New()
44-
password.Placeholder = "your password"
45-
password.EchoMode = textinput.EchoPassword
46-
password.Prompt = " "
47-
inputs[fieldPassword] = password
37+
apiKey := textinput.New()
38+
apiKey.Placeholder = "your api key"
39+
apiKey.Prompt = " "
40+
inputs[fieldAPIKey] = apiKey
4841

4942
return initModel{inputs: inputs}
5043
}
@@ -66,7 +59,7 @@ func (m initModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
6659
m.focused = (m.focused - 1 + fieldCount) % fieldCount
6760
return m, m.updateFocus()
6861
case "enter":
69-
if m.focused == fieldPassword {
62+
if m.focused == fieldAPIKey {
7063
m.submitted = true
7164
return m, tea.Quit
7265
}
@@ -102,7 +95,7 @@ func (m initModel) View() string {
10295
b.WriteString("\n")
10396
b.WriteString(styleLabel.Render(" Configure Saferwall CLI") + "\n\n")
10497

105-
labels := []string{"URL", "Username", "Password"}
98+
labels := []string{"URL", "API Key"}
10699
for i, label := range labels {
107100
if i == m.focused {
108101
b.WriteString(styleSuccess.Render(" > "))
@@ -120,8 +113,7 @@ func (m initModel) View() string {
120113
return b.String()
121114
}
122115

123-
func (m initModel) values() (url, username, password string) {
116+
func (m initModel) values() (url, apiKey string) {
124117
return m.inputs[fieldURL].Value(),
125-
m.inputs[fieldUsername].Value(),
126-
m.inputs[fieldPassword].Value()
118+
m.inputs[fieldAPIKey].Value()
127119
}

cmd/rescan.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,7 @@ var reScanCmd = &cobra.Command{
4646
Args: cobra.ExactArgs(1),
4747
Run: func(cmd *cobra.Command, args []string) {
4848

49-
// Login to saferwall web service
5049
webSvc := webapi.New(cfg.Credentials.URL)
51-
token, err := webSvc.Login(cfg.Credentials.Username, cfg.Credentials.Password)
52-
if err != nil {
53-
log.Fatalf("failed to authenticate: %v", err)
54-
}
55-
5650
arg := args[0]
5751

5852
var sha256List []string
@@ -71,6 +65,6 @@ var reScanCmd = &cobra.Command{
7165
}
7266
}
7367

74-
reScanFile(webSvc, sha256List, token)
68+
reScanFile(webSvc, sha256List, cfg.Credentials.APIKey)
7569
},
7670
}

cmd/scan.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,7 @@ var scanCmd = &cobra.Command{
125125
Args: cobra.ExactArgs(1),
126126
Run: func(cmd *cobra.Command, args []string) {
127127

128-
// login to saferwall web service
129128
webSvc := webapi.New(cfg.Credentials.URL)
130-
token, err := webSvc.Login(cfg.Credentials.Username, cfg.Credentials.Password)
131-
if err != nil {
132-
log.Fatalf("failed to authenticate: %v", err)
133-
}
134-
135-
scanFile(webSvc, args[0], token)
129+
scanFile(webSvc, args[0], cfg.Credentials.APIKey)
136130
},
137131
}

cmd/view.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ var viewCmd = &cobra.Command{
2626
sha256 := strings.ToLower(args[0])
2727

2828
webSvc := webapi.New(cfg.Credentials.URL)
29-
_, err := webSvc.Login(cfg.Credentials.Username, cfg.Credentials.Password)
30-
if err != nil {
31-
log.Fatalf("failed to authenticate: %v", err)
32-
}
33-
3429
var file entity.File
3530
if err := webSvc.GetFile(sha256, &file); err != nil {
3631
log.Fatalf("failed to get file: %v", err)

internal/config/config.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import (
1010

1111
// CredentialsCfg represents saferwall credentials.
1212
type CredentialsCfg struct {
13-
URL string `mapstructure:"url"`
14-
Username string `mapstructure:"username"`
15-
Password string `mapstructure:"password"`
13+
URL string `mapstructure:"url"`
14+
APIKey string `mapstructure:"api_key"`
1615
}
1716

1817
// AWSS3Cfg represents AWS S3 credentials.

internal/webapi/auth.go

Lines changed: 14 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,14 @@
1-
// Copyright 2018 Saferwall. All rights reserved.
2-
// Use of this source code is governed by Apache v2 license
3-
// license that can be found in the LICENSE file.
4-
5-
package webapi
6-
7-
import (
8-
"bytes"
9-
"encoding/json"
10-
"errors"
11-
"io"
12-
"net/http"
13-
)
14-
15-
// Pages represents a paginated list of data items.
16-
type Pages struct {
17-
Page int `json:"page"`
18-
PerPage int `json:"per_page"`
19-
PageCount int `json:"page_count"`
20-
TotalCount int `json:"total_count"`
21-
Items any `json:"items"`
22-
}
23-
24-
func (s Service) Login(username, password string) (string, error) {
25-
26-
requestBody, err := json.Marshal(map[string]string{
27-
"username": username,
28-
"password": password,
29-
})
30-
if err != nil {
31-
return "", err
32-
}
33-
34-
body := bytes.NewBuffer(requestBody)
35-
request, err := http.NewRequest(http.MethodPost, s.authURL, body)
36-
if err != nil {
37-
return "", err
38-
}
39-
40-
request.Header.Set("Content-Type", "application/json; charset=utf-8")
41-
resp, err := s.client.Do(request)
42-
if err != nil {
43-
return "", err
44-
}
45-
46-
defer resp.Body.Close()
47-
d, err := io.ReadAll(resp.Body)
48-
if err != nil {
49-
return "", err
50-
}
51-
52-
var res map[string]string
53-
err = json.Unmarshal(d, &res)
54-
if err != nil {
55-
return "", err
56-
}
57-
58-
if resp.StatusCode != http.StatusOK {
59-
return "", errors.New("failed login")
60-
}
61-
62-
return res["token"], nil
63-
}
1+
// Copyright 2018 Saferwall. All rights reserved.
2+
// Use of this source code is governed by Apache v2 license
3+
// license that can be found in the LICENSE file.
4+
5+
package webapi
6+
7+
// Pages represents a paginated list of data items.
8+
type Pages struct {
9+
Page int `json:"page"`
10+
PerPage int `json:"per_page"`
11+
PageCount int `json:"page_count"`
12+
TotalCount int `json:"total_count"`
13+
Items any `json:"items"`
14+
}

internal/webapi/files.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (s Service) ListFiles(authToken string, page int) (*Pages, error) {
8282
return nil, err
8383
}
8484

85-
request.Header.Set("Cookie", "JWTCookie="+authToken)
85+
request.Header.Set("X-Api-Key", authToken)
8686

8787
// Perform the http request.
8888
resp, err := s.client.Do(request)
@@ -131,7 +131,7 @@ func (s Service) Scan(filepath string, authToken, preferredOS string, enableDeto
131131
}
132132

133133
// Add our auth token.
134-
request.Header.Set("Cookie", "JWTCookie="+authToken)
134+
request.Header.Set("X-Api-Key", authToken)
135135

136136
// Perform the http request.
137137
resp, err := s.client.Do(request)
@@ -178,7 +178,7 @@ func (s Service) Rescan(sha256, authToken, preferredOS string, enableDetonation
178178
}
179179

180180
request.Header.Set("Content-Type", "application/json")
181-
request.Header.Set("Cookie", "JWTCookie="+authToken)
181+
request.Header.Set("X-Api-Key", authToken)
182182

183183
// Perform the http request.
184184
resp, err := s.client.Do(request)
@@ -254,7 +254,7 @@ func (s Service) Download(sha256, authToken string) (*bytes.Buffer, error) {
254254
}
255255

256256
request.Header.Set("Content-Type", "application/json")
257-
request.Header.Set("Cookie", "JWTCookie="+authToken)
257+
request.Header.Set("X-Api-Key", authToken)
258258

259259
// Perform the http request.
260260
resp, err := s.client.Do(request)
@@ -282,7 +282,7 @@ func (s Service) Delete(sha256, authToken string) error {
282282
}
283283

284284
request.Header.Set("Content-Type", "application/json")
285-
request.Header.Set("Cookie", "JWTCookie="+authToken)
285+
request.Header.Set("X-Api-Key", authToken)
286286

287287
// Perform the http request.
288288
resp, err := s.client.Do(request)

0 commit comments

Comments
 (0)