diff --git a/modules/hibp/client.go b/modules/hibp/client.go index 70b7bff7..d1505dcb 100644 --- a/modules/hibp/client.go +++ b/modules/hibp/client.go @@ -2,6 +2,7 @@ package hibp import ( "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -9,12 +10,16 @@ import ( ) const ( - apiURL = "https://haveibeenpwned.com/api/breachedaccount/" - apiVersion = "application/vnd.haveibeenpwned.v2+json" + apiURL = "https://haveibeenpwned.com/api/v3/breachedaccount/" clientTimeoutSecs = 2 userAgent = "WTFUtil" ) +type hibpError struct { + StatusCode int `json:"statusCode"` + Message string `json:"message"` +} + func (widget *Widget) fullURL(account string, truncated bool) string { truncStr := "false" if truncated == true { @@ -43,8 +48,8 @@ func (widget *Widget) fetchForAccount(account string, since string) (*Status, er return nil, err } - request.Header.Set("Accept", apiVersion) request.Header.Set("User-Agent", userAgent) + request.Header.Set("hibp-api-key", widget.settings.apiKey) response, getErr := hibpClient.Do(request) if getErr != nil { @@ -56,6 +61,11 @@ func (widget *Widget) fetchForAccount(account string, since string) (*Status, er return nil, err } + hibpErr := widget.validateHTTPResponse(response.StatusCode, body) + if hibpErr != nil { + return nil, errors.New(hibpErr.Message) + } + stat, err := widget.parseResponseBody(account, body) if err != nil { return nil, err @@ -115,3 +125,16 @@ func (widget *Widget) filterBreaches(breaches []Breach) []Breach { return latestBreaches } + +func (widget *Widget) validateHTTPResponse(responseCode int, body []byte) *hibpError { + hibpErr := &hibpError{} + + switch responseCode { + case 401, 402: + json.Unmarshal(body, hibpErr) + default: + hibpErr = nil + } + + return hibpErr +} diff --git a/modules/hibp/settings.go b/modules/hibp/settings.go index 49d1a26c..16f0005e 100644 --- a/modules/hibp/settings.go +++ b/modules/hibp/settings.go @@ -24,7 +24,8 @@ type Settings struct { common *cfg.Common accounts []string `help:"A list of the accounts to check the HIBP database for."` - since string `help:"Only check for breaches after this date. Set this if you’ve been breached in the past, have taken steps to mitigate that (changing passwords, cancelling accounts, etc.) and now only want to know about future breaches." values:"A date string in the format 'yyyy-mm-dd', ie: '2019-06-22'", optional:"true"` + apiKey string `help:"Your HIBP API v3 API key"` + since string `help:"Only check for breaches after this date. Set this if you’ve been breached in the past, have taken steps to mitigate that (changing passwords, cancelling accounts, etc.) and now only want to know about future breaches." values:"A date string in the format 'yyyy-mm-dd', ie. '2019-06-22'" optional:"true"` } // NewSettingsFromYAML creates a new settings instance from a YAML config block @@ -32,6 +33,7 @@ func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *co settings := &Settings{ common: cfg.NewCommonSettingsFromModule(name, defaultTitle, ymlConfig, globalConfig), + apiKey: ymlConfig.UString("apiKey", ""), accounts: wtf.ToStrs(ymlConfig.UList("accounts")), since: ymlConfig.UString("since", ""), }