Added support for indeterminate verification for letter B detectors (#4049)
* first 10 detectors * more detectors --------- Co-authored-by: Nabeel Alam <[email protected]>
This commit is contained in:
co-authored by
Nabeel Alam
parent
b73fa5f818
commit
8e5ef0f0ec
@@ -3,6 +3,7 @@ package bannerbear
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -46,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bannerbear.com/v2/auth", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBannerBear(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -73,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bannerbear is an API for generating dynamic images, videos, and GIFs. Bannerbear API keys can be used to access and manipulate these resources."
|
||||
}
|
||||
|
||||
// docs: https://developers.bannerbear.com/
|
||||
func verifyBannerBear(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bannerbear.com/v2/auth", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package baremetrics
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -52,18 +53,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.baremetrics.com/v1/account", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBaremetrics(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -79,3 +71,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Baremetrics is a subscription analytics and insights tool. Baremetrics API keys can be used to access and analyze subscription data."
|
||||
}
|
||||
|
||||
// docs: https://developers.baremetrics.com/reference/authentication
|
||||
func verifyBaremetrics(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.baremetrics.com/v1/account", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package beamer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getbeamer.com/v0/url", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Beamer-Api-Key", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBeamer(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -72,3 +65,31 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Beamer is a user engagement platform that helps you communicate product updates and other important information to your users. Beamer API keys can be used to access and manage this information."
|
||||
}
|
||||
|
||||
func verifyBeamer(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getbeamer.com/v0/url", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Beamer-Api-Key", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package beebole
|
||||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -47,22 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
data := fmt.Sprintf("%s:X", resMatch)
|
||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
||||
payload := strings.NewReader(`{"service": "custom_field.list"}`)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://beebole-apps.com/api/v2", payload)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBeebole(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -78,3 +65,35 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Beebole is a time tracking and business management tool. Beebole API keys can be used to access and manage time tracking data and other business-related information."
|
||||
}
|
||||
|
||||
// docs: https://beebole.com/help/api/
|
||||
func verifyBeebole(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
payload := strings.NewReader(`{"service": "custom_field.list"}`)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://beebole-apps.com/api/v2", payload)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.SetBasicAuth(key, "x")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package besnappy
|
||||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -46,20 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
Raw: []byte(resMatch),
|
||||
}
|
||||
if verify {
|
||||
data := fmt.Sprintf("%s:x", resMatch)
|
||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://app.besnappy.com/api/v1/accounts", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBesnappy(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -75,3 +64,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Besnappy is a customer service platform. The detected key can be used to access Besnappy's API, potentially exposing sensitive customer service data."
|
||||
}
|
||||
|
||||
// docs: https://github.com/BeSnappy/api-docs
|
||||
func verifyBesnappy(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://app.besnappy.com/api/v1/accounts", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.SetBasicAuth(key, "x")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package besttime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -46,23 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://besttime.app/api/v1/keys/"+resMatch, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
|
||||
if strings.Contains(body, `"status": "OK"`) {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBesttime(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -78,3 +65,40 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Besttime is a service used to predict the best time to visit a place. Besttime API keys can be used to access and utilize this service."
|
||||
}
|
||||
|
||||
// docs: https://documentation.besttime.app/#api-reference
|
||||
func verifyBesttime(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://besttime.app/api/v1/keys/"+key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
|
||||
if strings.Contains(body, `"status": "OK"`) {
|
||||
return true, nil
|
||||
} else if strings.Contains(body, `"message": "Invalid api_key_private`) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("unexpected response body: %s", body)
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package betterstack
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -51,25 +52,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
if client == nil {
|
||||
client = defaultClient
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://uptime.betterstack.com/api/v2/monitors", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
} else if res.StatusCode == 401 {
|
||||
// The secret is determinately not verified (nothing to do)
|
||||
} else {
|
||||
err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
}
|
||||
} else {
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
}
|
||||
|
||||
isVerified, verificationErr := verifyBetterStack(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -85,3 +71,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Betterstack is a monitoring service for uptime and performance of websites and APIs. Betterstack API keys can be used to access and manage these monitoring services."
|
||||
}
|
||||
|
||||
// docs: https://betterstack.com/docs/uptime/api/list-all-existing-monitors/
|
||||
func verifyBetterStack(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://uptime.betterstack.com/api/v2/monitors", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package billomat
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -53,19 +54,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.billomat.net/api/v2/clients/myself", resId), nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("X-BillomatApiKey", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBillomat(ctx, client, resId, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -82,3 +73,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Billomat is an online invoicing software. Billomat API keys can be used to access and manage invoices, clients, and other related data."
|
||||
}
|
||||
|
||||
// docs: https://www.billomat.com/en/api/basics/authentication/
|
||||
func verifyBillomat(ctx context.Context, client *http.Client, id, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.billomat.net/api/v2/clients/myself", id), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("X-BillomatApiKey", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package bitbar
|
||||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
client = common.SaneHttpClient()
|
||||
|
||||
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitbar"}) + `\b([0-9a-z]{32})\b`)
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"bitbar"}) + `\b([0-9a-zA-Z]{32})\b`)
|
||||
)
|
||||
|
||||
// Keywords are used for efficiently pre-filtering chunks.
|
||||
@@ -47,21 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
data := fmt.Sprintf("%s:", resMatch)
|
||||
baseToken := b64.StdEncoding.EncodeToString([]byte(data))
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.bitbar.com/api/me", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Basic %s", baseToken))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBitBar(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -77,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bitbar provides a cloud-based mobile app testing platform. Bitbar API keys can be used to access and manage testing resources and data."
|
||||
}
|
||||
|
||||
// docs: https://support.smartbear.com/bitbar/docs/en/use-rest-apis-with-bitbar.html
|
||||
func verifyBitBar(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.bitbar.com/api/me", http.NoBody)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.SetBasicAuth(key, "")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package bitcoinaverage
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -49,26 +51,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
DetectorType: detectorspb.DetectorType_BitcoinAverage,
|
||||
Raw: []byte(resMatch),
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://apiv2.bitcoinaverage.com/websocket/v3/get_ticket", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("x-ba-key", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
resp := &response{}
|
||||
if err = json.NewDecoder(res.Body).Decode(resp); err != nil {
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
continue
|
||||
}
|
||||
if resp.Success {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBitcoinAverage(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr, resMatch)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -84,3 +71,41 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BitcoinAverage is a service that provides cryptocurrency market data. BitcoinAverage API keys can be used to access and retrieve this market data."
|
||||
}
|
||||
|
||||
// docs: https://apiv2.bitcoinaverage.com/#authentication
|
||||
func verifyBitcoinAverage(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://apiv2.bitcoinaverage.com/websocket/v3/get_ticket", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("x-ba-key", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
apiResponse := &response{}
|
||||
if err = json.NewDecoder(resp.Body).Decode(apiResponse); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if apiResponse.Success {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,50 +43,34 @@ func (s Scanner) Keywords() []string {
|
||||
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
dataStr := string(data)
|
||||
|
||||
apiKeyMatches := apiKeyPat.FindAllStringSubmatch(dataStr, -1)
|
||||
apiSecretMatches := apiSecretPat.FindAllStringSubmatch(dataStr, -1)
|
||||
var uniqueAPIKeys, uniqueAPISecrets = make(map[string]struct{}), make(map[string]struct{})
|
||||
|
||||
for _, apiKeyMatch := range apiKeyMatches {
|
||||
apiKeyRes := strings.TrimSpace(apiKeyMatch[1])
|
||||
for _, apiKey := range apiKeyPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
uniqueAPIKeys[apiKey[1]] = struct{}{}
|
||||
}
|
||||
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Bitfinex,
|
||||
Raw: []byte(apiKeyRes),
|
||||
}
|
||||
for _, apiSecret := range apiSecretPat.FindAllStringSubmatch(dataStr, -1) {
|
||||
uniqueAPISecrets[apiSecret[1]] = struct{}{}
|
||||
}
|
||||
|
||||
for _, apiSecretMatch := range apiSecretMatches {
|
||||
apiSecretRes := strings.TrimSpace(apiSecretMatch[1])
|
||||
|
||||
if apiKeyRes == apiSecretRes {
|
||||
for apiKey := range uniqueAPIKeys {
|
||||
for apiSecret := range uniqueAPISecrets {
|
||||
// as both patterns are same, avoid verifying same string for both
|
||||
if apiKey == apiSecret {
|
||||
continue
|
||||
}
|
||||
|
||||
s1 := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Bitfinex,
|
||||
Raw: []byte(apiKey),
|
||||
}
|
||||
|
||||
if verify {
|
||||
// thankfully official golang examples exist but you just need to dig their many repos https://github.com/bitfinexcom/bitfinex-api-go/blob/master/examples/v2/rest-orders/main.go
|
||||
key := apiKeyRes
|
||||
secret := apiSecretRes
|
||||
http.DefaultClient = client // filed https://github.com/bitfinexcom/bitfinex-api-go/issues/238 to improve this
|
||||
c := rest.NewClientWithURL(*api).Credentials(key, secret)
|
||||
|
||||
isValid := true // assume valid
|
||||
_, err = c.Orders.AllHistory()
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "POST https://") { // eg POST https://api-pub.bitfinex.com/v2/auth/r/orders/hist: 500 apikey: digest invalid (10100)
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
s1.Verified = isValid
|
||||
// If there is a valid one, we need to stop iterating now and return the valid result
|
||||
if isValid {
|
||||
break
|
||||
}
|
||||
isVerified, verificationErr := verifyBitfinex(apiKey, apiSecret)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
}
|
||||
|
||||
// By appending results in the outer loop we can reduce false positives if there are multiple
|
||||
// combinations of secrets and IDs found.
|
||||
if len(apiSecretMatches) > 0 {
|
||||
results = append(results, s1)
|
||||
}
|
||||
}
|
||||
@@ -101,3 +85,19 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bitfinex is a cryptocurrency exchange offering various trading options. Bitfinex API keys can be used to access and manage trading accounts."
|
||||
}
|
||||
|
||||
// docs: https://docs.bitfinex.com/docs/introduction
|
||||
func verifyBitfinex(key, secret string) (bool, error) {
|
||||
// thankfully official golang examples exist but you just need to dig their many repos https://github.com/bitfinexcom/bitfinex-api-go/blob/master/examples/v2/rest-orders/main.go
|
||||
http.DefaultClient = client
|
||||
c := rest.NewClientWithURL(*api).Credentials(key, secret)
|
||||
|
||||
_, err := c.Orders.AllHistory()
|
||||
if err != nil {
|
||||
if strings.HasPrefix(err.Error(), "POST https://") { // eg POST https://api-pub.bitfinex.com/v2/auth/r/orders/hist: 500 apikey: digest invalid (10100)
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
@@ -59,28 +61,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
|
||||
timestamp := strconv.FormatInt(time.Now().Unix()+5, 10)
|
||||
action := "GET"
|
||||
path := "/api/v1/user"
|
||||
payload := url.Values{}
|
||||
|
||||
signature := getBitmexSignature(timestamp, resSecretMatch, action, path, payload.Encode())
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, action, "https://www.bitmex.com"+path, strings.NewReader(payload.Encode()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("api-expires", timestamp)
|
||||
req.Header.Add("api-key", resMatch)
|
||||
req.Header.Add("api-signature", signature)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBitmex(ctx, client, resMatch, resSecretMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -90,14 +73,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func getBitmexSignature(timeStamp string, secret string, action string, path string, payload string) string {
|
||||
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
mac.Write([]byte(action + path + timeStamp + payload))
|
||||
macsum := mac.Sum(nil)
|
||||
return hex.EncodeToString(macsum)
|
||||
}
|
||||
|
||||
func (s Scanner) Type() detectorspb.DetectorType {
|
||||
return detectorspb.DetectorType_Bitmex
|
||||
}
|
||||
@@ -105,3 +80,47 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bitmex is a cryptocurrency exchange and derivative trading platform. Bitmex API keys can be used to access and trade on the platform programmatically."
|
||||
}
|
||||
|
||||
// docs: https://www.bitmex.com/app/apiKeysUsage
|
||||
func verifyBitmex(ctx context.Context, client *http.Client, key, secret string) (bool, error) {
|
||||
timestamp := strconv.FormatInt(time.Now().Unix()+5, 10)
|
||||
action := "GET"
|
||||
path := "/api/v1/user"
|
||||
payload := url.Values{}
|
||||
|
||||
signature := getBitmexSignature(timestamp, secret, action, path, payload.Encode())
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, action, "https://www.bitmex.com"+path, strings.NewReader(payload.Encode()))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("api-expires", timestamp)
|
||||
req.Header.Add("api-key", key)
|
||||
req.Header.Add("api-signature", signature)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func getBitmexSignature(timeStamp string, secret string, action string, path string, payload string) string {
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
mac.Write([]byte(action + path + timeStamp + payload))
|
||||
macsum := mac.Sum(nil)
|
||||
return hex.EncodeToString(macsum)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package blazemeter
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -46,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.runscope.com/account", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBlazeMeter(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -74,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Blazemeter is a continuous testing platform for DevOps. The keys can be used to access and manage performance tests and other resources."
|
||||
}
|
||||
|
||||
// docs: https://help.blazemeter.com/apidocs/api-monitoring/account.htm?tocpath=API%20Monitoring%7C_____12
|
||||
func verifyBlazeMeter(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.runscope.com/account", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized, http.StatusForbidden:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package blitapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://blitapp.com/api/apps/all", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("API-Key", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBlitApp(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -72,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BlitApp is a service used for managing applications. BlitApp API keys can be used to access and modify application data."
|
||||
}
|
||||
|
||||
// docs: https://blitapp.com/api/#/App/get_apps_all
|
||||
func verifyBlitApp(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://blitapp.com/api/apps/all", nil)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
req.Header.Add("API-Key", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package blocknative
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -46,23 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.blocknative.com/gasprices/blockprices", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
if !strings.Contains(body, "valid") {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBlocknative(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -78,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Blocknative is a platform that provides real-time blockchain transaction monitoring and notification services. Blocknative API keys can be used to access and interact with these services."
|
||||
}
|
||||
|
||||
// docs: https://docs.blocknative.com/gas-prediction/gas-platform#api-endpoint
|
||||
func verifyBlocknative(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.blocknative.com/gasprices/blockprices", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
// Right now the blocknative API logic is broken and return 200 for invalid key as well
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized, http.StatusTooManyRequests:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package blogger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -44,17 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.googleapis.com/blogger/v3/blogs/2399953?key="+resMatch, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBlogger(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -70,3 +64,30 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Blogger API keys can be used to access and manage blogs on the Blogger platform."
|
||||
}
|
||||
|
||||
// docs: https://developers.google.com/blogger/docs/3.0/using
|
||||
func verifyBlogger(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.googleapis.com/blogger/v3/blogs/2399953?key="+key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusBadRequest, http.StatusForbidden:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package bombbomb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -45,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
// Reference : https://developer.bombbomb.com/api#operations-Users-UserInfo
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bombbomb.com/v2/user/", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBombBomb(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -73,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BombBomb is a video messaging platform that allows users to create and send video emails. BombBomb API keys can be used to access and manage video email campaigns and contacts."
|
||||
}
|
||||
|
||||
// docs: https://developer.bombbomb.com/api#operations-Users-UserInfo
|
||||
func verifyBombBomb(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bombbomb.com/v2/user/", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Bearer "+key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package boostnote
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -46,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://boostnote.io/api/docs", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBoostnote(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -73,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BoostNote is a note-taking application. The secret detected here is likely an API key or token used to access BoostNote services."
|
||||
}
|
||||
|
||||
// docs: https://boostnote.io/features/public-api
|
||||
func verifyBoostnote(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://boostnote.io/api/docs", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,31 +48,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
timeout := 10 * time.Second
|
||||
client.Timeout = timeout
|
||||
payload := strings.NewReader(`{"query":"{ sshList {id, name}}"}`)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.borgbase.com/graphql", payload)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err == nil {
|
||||
bodyString := string(bodyBytes)
|
||||
validResponse := strings.Contains(bodyString, `"sshList":[]`)
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
if validResponse {
|
||||
s1.Verified = true
|
||||
} else {
|
||||
s1.Verified = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBorgbase(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -88,3 +66,49 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Borgbase is a service for hosting Borg repositories. Borgbase API keys can be used to manage and access these repositories."
|
||||
}
|
||||
|
||||
// docs: https://docs.borgbase.com/api
|
||||
func verifyBorgbase(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
timeout := 10 * time.Second
|
||||
client.Timeout = timeout
|
||||
|
||||
payload := strings.NewReader(`{"query":"{ sshList {id, name}}"}`)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.borgbase.com/graphql", payload)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
bodyString := string(bodyBytes)
|
||||
validResponse := strings.Contains(bodyString, `"sshList":[]`)
|
||||
if validResponse {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package braintreepayments
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
@@ -105,7 +106,10 @@ func verifyBraintree(ctx context.Context, client *http.Client, url, pubKey, priv
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
bodyString := string(bodyBytes)
|
||||
if !(res.StatusCode == http.StatusOK) {
|
||||
|
||||
@@ -2,6 +2,8 @@ package brandfetch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -45,22 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
payload := strings.NewReader(`{
|
||||
"domain": "www.example.com"
|
||||
}`)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.brandfetch.io/v1/color", payload)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("x-api-key", resMatch)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBrandFetch(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -76,3 +65,37 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Brandfetch is a service that provides brand data, including logos, colors, fonts, and more. Brandfetch API keys can be used to access this data."
|
||||
}
|
||||
|
||||
// docs: https://docs.brandfetch.com/docs/brand-api#overview
|
||||
func verifyBrandFetch(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
payload := strings.NewReader(`{
|
||||
"domain": "www.example.com"
|
||||
}`)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.brandfetch.io/v1/color", payload)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("x-api-key", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized, http.StatusForbidden:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package browshot
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -45,17 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.browshot.com/api/v1/instance/list?key="+resMatch, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBrowshot(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -71,3 +65,30 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Browshot is a service that allows you to take screenshots of web pages from different browsers and devices. Browshot API keys can be used to automate and manage these screenshots."
|
||||
}
|
||||
|
||||
// docs: https://browshot.com/api/documentation#instance_list
|
||||
func verifyBrowshot(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.browshot.com/api/v1/instance/list?key="+key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusBadRequest, http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package bscscan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -46,23 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bscscan.com/api?module=account&action=balance&address=0x70F657164e5b75689b64B7fd1fA275F334f28e18&apikey="+resMatch, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
body := string(bodyBytes)
|
||||
|
||||
if !strings.Contains(body, "NOTOK") {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBscScan(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -78,3 +65,41 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BscScan is a block explorer and analytics platform for Binance Smart Chain. BscScan API keys can be used to access data from the Binance Smart Chain blockchain."
|
||||
}
|
||||
|
||||
// docs: https://docs.bscscan.com/api-endpoints/accounts
|
||||
func verifyBscScan(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bscscan.com/api?module=account&action=balance&address=0x70F657164e5b75689b64B7fd1fA275F334f28e18&apikey="+key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
body := string(bodyBytes)
|
||||
|
||||
if !strings.Contains(body, "NOTOK") {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package buddyns
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -46,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.buddyns.com/api/v2/zone/", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Token %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBuddyns(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -74,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BuddyNS is a DNS hosting service. BuddyNS API keys can be used to manage DNS zones and records."
|
||||
}
|
||||
|
||||
// docs: https://www.buddyns.com/support/api/v2/
|
||||
func verifyBuddyns(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.buddyns.com/api/v2/zone/", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Token %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package budibase
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -52,31 +53,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
client = defaultClient
|
||||
}
|
||||
|
||||
// URL: https://docs.budibase.com/reference/appsearch
|
||||
// API searches for the app with given name, since we only need to check api key, sending any appname will work.
|
||||
payload := strings.NewReader(`{"name":"qwerty"}`)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://budibase.app/api/public/v1/applications/search", payload)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("x-budibase-api-key", resMatch)
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
} else if res.StatusCode == 401 {
|
||||
// The secret is determinately not verified (nothing to do)
|
||||
} else {
|
||||
err = fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
}
|
||||
} else {
|
||||
s1.SetVerificationError(err, resMatch)
|
||||
}
|
||||
isVerified, verificationErr := verifyBudibase(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -92,3 +71,37 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Budibase is a low-code platform for creating internal tools. Budibase API keys can be used to access and modify applications and data within the platform."
|
||||
}
|
||||
|
||||
// docs: https://docs.budibase.com/docs/rest
|
||||
func verifyBudibase(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
// URL: https://docs.budibase.com/reference/appsearch
|
||||
// API searches for the app with given name, since we only need to check api key, sending any appname will work.
|
||||
payload := strings.NewReader(`{"name":"qwerty"}`)
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", "https://budibase.app/api/public/v1/applications/search", payload)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("x-budibase-api-key", key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func TestBudibase_FromChunk(t *testing.T) {
|
||||
want: func() []detectors.Result {
|
||||
r := detectors.Result{
|
||||
DetectorType: detectorspb.DetectorType_Budibase,
|
||||
Verified: true,
|
||||
Verified: false,
|
||||
}
|
||||
r.SetVerificationError(fmt.Errorf("unexpected HTTP response status 403"))
|
||||
return []detectors.Result{r}
|
||||
|
||||
@@ -2,12 +2,13 @@ package bugherd
|
||||
|
||||
import (
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
@@ -46,21 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
data := fmt.Sprintf("%s:x", resMatch)
|
||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.bugherd.com/api_v2/projects.json", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Basic %s", sEnc))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBugherd(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -76,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bugherd is a visual feedback and bug tracking tool for websites. Bugherd API keys can be used to access and manage projects, tasks, and feedback data."
|
||||
}
|
||||
|
||||
// docs: https://www.bugherd.com/api_v2
|
||||
func verifyBugherd(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.bugherd.com/api_v2/projects.json", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.SetBasicAuth(key, "x")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package bugsnag
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -46,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bugsnag.com/user/organizations?admin", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("token %s", resMatch))
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBugsnag(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -74,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bugsnag is an error monitoring service for web and mobile applications. Bugsnag API keys can be used to report and manage errors."
|
||||
}
|
||||
|
||||
// docs: https://docs.bugsnag.com/api/
|
||||
func verifyBugsnag(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bugsnag.com/user/organizations?admin", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", fmt.Sprintf("token %s", key))
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@ package bulbul
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
@@ -46,30 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://prod-api.bulbul.io/view_all_users?api_key=%s", resMatch), nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
bodyString := string(bodyBytes)
|
||||
validResponse := strings.Contains(bodyString, `"message":"Successful",`)
|
||||
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
if validResponse {
|
||||
s1.Verified = true
|
||||
} else {
|
||||
s1.Verified = false
|
||||
}
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyBulbul(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -85,3 +65,41 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "Bulbul is an API service. Bulbul API keys can be used to access and modify data within the service."
|
||||
}
|
||||
|
||||
// docs: https://docs.jungleworks.com/bulbul/bulbul-api-details
|
||||
func verifyBulbul(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://prod-api.bulbul.io/view_all_users?api_key=%s", key), nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
bodyString := string(bodyBytes)
|
||||
|
||||
if strings.Contains(bodyString, `"message":"Successful",`) {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, nil
|
||||
}
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package bulksms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
@@ -56,27 +57,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bulksms.com/v1/messages", nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
req.SetBasicAuth(id, key)
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, res.Body)
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
|
||||
if res.StatusCode == http.StatusOK {
|
||||
s1.Verified = true
|
||||
results = append(results, s1)
|
||||
// move to next id, by skipping remaining key's
|
||||
break
|
||||
}
|
||||
} else {
|
||||
s1.SetVerificationError(err, key)
|
||||
}
|
||||
isVerified, verificationErr := verifyBulksms(ctx, client, id, key)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -93,3 +76,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "BulkSMS is a service used for sending SMS messages in bulk. BulkSMS credentials can be used to access and send messages through the BulkSMS API."
|
||||
}
|
||||
|
||||
// docs: https://www.bulksms.com/developer/json/v1/
|
||||
func verifyBulksms(ctx context.Context, client *http.Client, id, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bulksms.com/v1/messages", nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
req.SetBasicAuth(id, key)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,13 @@ package buttercms
|
||||
|
||||
import (
|
||||
"context"
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
regexp "github.com/wasilibs/go-re2"
|
||||
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
||||
@@ -44,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
||||
}
|
||||
|
||||
if verify {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.buttercms.com/v2/posts/?auth_token="+resMatch, nil)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err == nil {
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
||||
s1.Verified = true
|
||||
}
|
||||
}
|
||||
isVerified, verificationErr := verifyButterCMS(ctx, client, resMatch)
|
||||
s1.Verified = isVerified
|
||||
s1.SetVerificationError(verificationErr)
|
||||
}
|
||||
|
||||
results = append(results, s1)
|
||||
@@ -71,3 +65,30 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
||||
func (s Scanner) Description() string {
|
||||
return "ButterCMS is a headless CMS that enables developers to build websites and applications with a content management system. The API keys can be used to access and modify content stored in ButterCMS."
|
||||
}
|
||||
|
||||
// docs: https://buttercms.com/docs/api/#introduction
|
||||
func verifyButterCMS(ctx context.Context, client *http.Client, key string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.buttercms.com/v2/posts/?auth_token="+key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
return true, nil
|
||||
case http.StatusUnauthorized:
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user