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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -46,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bannerbear.com/v2/auth", nil)
|
isVerified, verificationErr := verifyBannerBear(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -73,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -52,18 +53,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.baremetrics.com/v1/account", nil)
|
isVerified, verificationErr := verifyBaremetrics(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -79,3 +71,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.getbeamer.com/v0/url", nil)
|
isVerified, verificationErr := verifyBeamer(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -72,3 +65,31 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
b64 "encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -47,22 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
data := fmt.Sprintf("%s:X", resMatch)
|
isVerified, verificationErr := verifyBeebole(ctx, client, resMatch)
|
||||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
s1.Verified = isVerified
|
||||||
payload := strings.NewReader(`{"service": "custom_field.list"}`)
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -78,3 +65,35 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
b64 "encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -46,20 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
Raw: []byte(resMatch),
|
Raw: []byte(resMatch),
|
||||||
}
|
}
|
||||||
if verify {
|
if verify {
|
||||||
data := fmt.Sprintf("%s:x", resMatch)
|
isVerified, verificationErr := verifyBesnappy(ctx, client, resMatch)
|
||||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
s1.Verified = isVerified
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://app.besnappy.com/api/v1/accounts", nil)
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -75,3 +64,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -46,23 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://besttime.app/api/v1/keys/"+resMatch, nil)
|
isVerified, verificationErr := verifyBesttime(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr, 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, `"status": "OK"`) {
|
|
||||||
s1.Verified = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -78,3 +65,40 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -51,25 +52,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
if client == nil {
|
if client == nil {
|
||||||
client = defaultClient
|
client = defaultClient
|
||||||
}
|
}
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://uptime.betterstack.com/api/v2/monitors", nil)
|
|
||||||
if err != nil {
|
isVerified, verificationErr := verifyBetterStack(ctx, client, resMatch)
|
||||||
continue
|
s1.Verified = isVerified
|
||||||
}
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -85,3 +71,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -53,19 +54,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s.billomat.net/api/v2/clients/myself", resId), nil)
|
isVerified, verificationErr := verifyBillomat(ctx, client, resId, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -82,3 +73,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
b64 "encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ var (
|
|||||||
client = common.SaneHttpClient()
|
client = common.SaneHttpClient()
|
||||||
|
|
||||||
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
|
// 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.
|
// 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 {
|
if verify {
|
||||||
data := fmt.Sprintf("%s:", resMatch)
|
isVerified, verificationErr := verifyBitBar(ctx, client, resMatch)
|
||||||
baseToken := b64.StdEncoding.EncodeToString([]byte(data))
|
s1.Verified = isVerified
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://cloud.bitbar.com/api/me", nil)
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -77,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -49,26 +51,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
DetectorType: detectorspb.DetectorType_BitcoinAverage,
|
DetectorType: detectorspb.DetectorType_BitcoinAverage,
|
||||||
Raw: []byte(resMatch),
|
Raw: []byte(resMatch),
|
||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://apiv2.bitcoinaverage.com/websocket/v3/get_ticket", nil)
|
isVerified, verificationErr := verifyBitcoinAverage(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr, resMatch)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -84,3 +71,41 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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) {
|
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||||
dataStr := string(data)
|
dataStr := string(data)
|
||||||
|
|
||||||
apiKeyMatches := apiKeyPat.FindAllStringSubmatch(dataStr, -1)
|
var uniqueAPIKeys, uniqueAPISecrets = make(map[string]struct{}), make(map[string]struct{})
|
||||||
apiSecretMatches := apiSecretPat.FindAllStringSubmatch(dataStr, -1)
|
|
||||||
|
|
||||||
for _, apiKeyMatch := range apiKeyMatches {
|
for _, apiKey := range apiKeyPat.FindAllStringSubmatch(dataStr, -1) {
|
||||||
apiKeyRes := strings.TrimSpace(apiKeyMatch[1])
|
uniqueAPIKeys[apiKey[1]] = struct{}{}
|
||||||
|
|
||||||
s1 := detectors.Result{
|
|
||||||
DetectorType: detectorspb.DetectorType_Bitfinex,
|
|
||||||
Raw: []byte(apiKeyRes),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, apiSecretMatch := range apiSecretMatches {
|
for _, apiSecret := range apiSecretPat.FindAllStringSubmatch(dataStr, -1) {
|
||||||
apiSecretRes := strings.TrimSpace(apiSecretMatch[1])
|
uniqueAPISecrets[apiSecret[1]] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s1 := detectors.Result{
|
||||||
|
DetectorType: detectorspb.DetectorType_Bitfinex,
|
||||||
|
Raw: []byte(apiKey),
|
||||||
|
}
|
||||||
|
|
||||||
if verify {
|
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
|
isVerified, verificationErr := verifyBitfinex(apiKey, apiSecret)
|
||||||
key := apiKeyRes
|
s1.Verified = isVerified
|
||||||
secret := apiSecretRes
|
s1.SetVerificationError(verificationErr)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
results = append(results, s1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,3 +85,19 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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/hmac"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -59,28 +61,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
|
isVerified, verificationErr := verifyBitmex(ctx, client, resMatch, resSecretMatch)
|
||||||
timestamp := strconv.FormatInt(time.Now().Unix()+5, 10)
|
s1.Verified = isVerified
|
||||||
action := "GET"
|
s1.SetVerificationError(verificationErr)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -90,14 +73,6 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
return results, nil
|
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 {
|
func (s Scanner) Type() detectorspb.DetectorType {
|
||||||
return detectorspb.DetectorType_Bitmex
|
return detectorspb.DetectorType_Bitmex
|
||||||
}
|
}
|
||||||
@@ -105,3 +80,47 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -46,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.runscope.com/account", nil)
|
isVerified, verificationErr := verifyBlazeMeter(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -74,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://blitapp.com/api/apps/all", nil)
|
isVerified, verificationErr := verifyBlitApp(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -72,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -46,23 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.blocknative.com/gasprices/blockprices", nil)
|
isVerified, verificationErr := verifyBlocknative(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -78,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -44,17 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.googleapis.com/blogger/v3/blogs/2399953?key="+resMatch, nil)
|
isVerified, verificationErr := verifyBlogger(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err == nil {
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
|
||||||
s1.Verified = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -70,3 +64,30 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
func (s Scanner) Description() string {
|
||||||
return "Blogger API keys can be used to access and manage blogs on the Blogger platform."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
// Reference : https://developer.bombbomb.com/api#operations-Users-UserInfo
|
isVerified, verificationErr := verifyBombBomb(ctx, client, resMatch)
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bombbomb.com/v2/user/", nil)
|
s1.Verified = isVerified
|
||||||
if err != nil {
|
s1.SetVerificationError(verificationErr)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -73,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -46,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://boostnote.io/api/docs", nil)
|
isVerified, verificationErr := verifyBoostnote(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -73,3 +65,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 {
|
if verify {
|
||||||
timeout := 10 * time.Second
|
isVerified, verificationErr := verifyBorgbase(ctx, client, resMatch)
|
||||||
client.Timeout = timeout
|
s1.Verified = isVerified
|
||||||
payload := strings.NewReader(`{"query":"{ sshList {id, name}}"}`)
|
s1.SetVerificationError(verificationErr)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -88,3 +66,49 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
regexp "github.com/wasilibs/go-re2"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
regexp "github.com/wasilibs/go-re2"
|
||||||
|
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
"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 {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
defer res.Body.Close()
|
defer func() {
|
||||||
|
_, _ = io.Copy(io.Discard, res.Body)
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
bodyString := string(bodyBytes)
|
bodyString := string(bodyBytes)
|
||||||
if !(res.StatusCode == http.StatusOK) {
|
if !(res.StatusCode == http.StatusOK) {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package brandfetch
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,22 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
payload := strings.NewReader(`{
|
isVerified, verificationErr := verifyBrandFetch(ctx, client, resMatch)
|
||||||
"domain": "www.example.com"
|
s1.Verified = isVerified
|
||||||
}`)
|
s1.SetVerificationError(verificationErr)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -76,3 +65,37 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,17 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.browshot.com/api/v1/instance/list?key="+resMatch, nil)
|
isVerified, verificationErr := verifyBrowshot(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err == nil {
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
|
||||||
s1.Verified = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -71,3 +65,30 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -46,23 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bscscan.com/api?module=account&action=balance&address=0x70F657164e5b75689b64B7fd1fA275F334f28e18&apikey="+resMatch, nil)
|
isVerified, verificationErr := verifyBscScan(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -78,3 +65,41 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -46,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.buddyns.com/api/v2/zone/", nil)
|
isVerified, verificationErr := verifyBuddyns(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -74,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
func (s Scanner) Description() string {
|
||||||
return "BuddyNS is a DNS hosting service. BuddyNS API keys can be used to manage DNS zones and records."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -52,31 +53,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
client = defaultClient
|
client = defaultClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL: https://docs.budibase.com/reference/appsearch
|
isVerified, verificationErr := verifyBudibase(ctx, client, resMatch)
|
||||||
// API searches for the app with given name, since we only need to check api key, sending any appname will work.
|
s1.Verified = isVerified
|
||||||
payload := strings.NewReader(`{"name":"qwerty"}`)
|
s1.SetVerificationError(verificationErr)
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -92,3 +71,37 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 {
|
want: func() []detectors.Result {
|
||||||
r := detectors.Result{
|
r := detectors.Result{
|
||||||
DetectorType: detectorspb.DetectorType_Budibase,
|
DetectorType: detectorspb.DetectorType_Budibase,
|
||||||
Verified: true,
|
Verified: false,
|
||||||
}
|
}
|
||||||
r.SetVerificationError(fmt.Errorf("unexpected HTTP response status 403"))
|
r.SetVerificationError(fmt.Errorf("unexpected HTTP response status 403"))
|
||||||
return []detectors.Result{r}
|
return []detectors.Result{r}
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ package bugherd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
b64 "encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
regexp "github.com/wasilibs/go-re2"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
regexp "github.com/wasilibs/go-re2"
|
||||||
|
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
"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 {
|
if verify {
|
||||||
data := fmt.Sprintf("%s:x", resMatch)
|
isVerified, verificationErr := verifyBugherd(ctx, client, resMatch)
|
||||||
sEnc := b64.StdEncoding.EncodeToString([]byte(data))
|
s1.Verified = isVerified
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://www.bugherd.com/api_v2/projects.json", nil)
|
s1.SetVerificationError(verificationErr)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -76,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -46,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bugsnag.com/user/organizations?admin", nil)
|
isVerified, verificationErr := verifyBugsnag(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -74,3 +65,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
regexp "github.com/wasilibs/go-re2"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
regexp "github.com/wasilibs/go-re2"
|
||||||
|
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
"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 {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://prod-api.bulbul.io/view_all_users?api_key=%s", resMatch), nil)
|
isVerified, verificationErr := verifyBulbul(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -85,3 +65,41 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
@@ -56,27 +57,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
|
|||||||
}
|
}
|
||||||
|
|
||||||
if verify {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.bulksms.com/v1/messages", nil)
|
isVerified, verificationErr := verifyBulksms(ctx, client, id, key)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -93,3 +76,32 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
regexp "github.com/wasilibs/go-re2"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
regexp "github.com/wasilibs/go-re2"
|
||||||
|
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
||||||
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
|
"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 {
|
if verify {
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", "https://api.buttercms.com/v2/posts/?auth_token="+resMatch, nil)
|
isVerified, verificationErr := verifyButterCMS(ctx, client, resMatch)
|
||||||
if err != nil {
|
s1.Verified = isVerified
|
||||||
continue
|
s1.SetVerificationError(verificationErr)
|
||||||
}
|
|
||||||
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err == nil {
|
|
||||||
defer res.Body.Close()
|
|
||||||
if res.StatusCode >= 200 && res.StatusCode < 300 {
|
|
||||||
s1.Verified = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, s1)
|
results = append(results, s1)
|
||||||
@@ -71,3 +65,30 @@ func (s Scanner) Type() detectorspb.DetectorType {
|
|||||||
func (s Scanner) Description() string {
|
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."
|
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