Skip to content

Commit

Permalink
Merge branch 'main' into update-postman-metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
casey-tran authored Jan 29, 2025
2 parents a1d1c90 + b6b00bb commit 5718f09
Show file tree
Hide file tree
Showing 15 changed files with 521 additions and 204 deletions.
4 changes: 2 additions & 2 deletions pkg/analyzer/analyzers/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ type AnalyzerRoundTripper struct {

func (r AnalyzerRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := r.parent.RoundTrip(req)
if err != nil || methodIsSafe(req.Method) {
if err != nil || IsMethodSafe(req.Method) {
return resp, err
}
// Check that unsafe methods did NOT return a valid status code.
Expand All @@ -126,7 +126,7 @@ func (r AnalyzerRoundTripper) RoundTrip(req *http.Request) (*http.Response, erro

// methodIsSafe is a helper method to check whether the HTTP method is safe according to MDN Web Docs.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods#safe_idempotent_and_cacheable_request_methods
func methodIsSafe(method string) bool {
func IsMethodSafe(method string) bool {
switch strings.ToUpper(method) {
case http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodTrace:
return true
Expand Down
2 changes: 1 addition & 1 deletion pkg/analyzer/analyzers/huggingface/scopes.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var user_scopes = map[string]map[string]string{
"user.billing.read": "Read access to user's billing usage",
},
"Collections": {
"collection.read": "Read access to all ollections under user's namespace",
"collection.read": "Read access to all collections under user's namespace",
"collection.write": "Write access to all collections under user's namespace",
},
"Discussions & Posts": {
Expand Down
11 changes: 10 additions & 1 deletion pkg/analyzer/analyzers/opsgenie/opsgenie.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,16 @@ func (h *HttpStatusTest) RunTest(cfg *config.Config, headers map[string]string)
}

// Create new HTTP request
client := analyzers.NewAnalyzeClientUnrestricted(cfg)
var client *http.Client

// Non-safe Opsgenie APIs are asynchronous and always return 202 if credential has the permission.
// For Safe API Methods, use the restricted client
if analyzers.IsMethodSafe(h.Method) {
client = analyzers.NewAnalyzeClient(cfg)
} else {
client = analyzers.NewAnalyzeClientUnrestricted(cfg)
}

req, err := http.NewRequest(h.Method, h.Endpoint, data)
if err != nil {
return false, err
Expand Down
6 changes: 5 additions & 1 deletion pkg/custom_detectors/custom_detectors.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ func (c *CustomRegexWebhook) createResults(ctx context.Context, match map[string
var raw string
for _, values := range match {
// values[0] contains the entire regex match.
raw += values[0]
secret := values[0]
if len(values) > 1 {
secret = values[1]
}
raw += secret
}
result := detectors.Result{
DetectorType: detectorspb.DetectorType_CustomRegex,
Expand Down
4 changes: 2 additions & 2 deletions pkg/custom_detectors/custom_detectors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,13 @@ func TestDetector(t *testing.T) {
// "password" is normally flagged as a false positive, but CustomRegex
// should allow the user to decide and report it as a result.
Keywords: []string{"password"},
Regex: map[string]string{"regex": "password=.*"},
Regex: map[string]string{"regex": "password=\"(.*)\""},
})
assert.NoError(t, err)
results, err := detector.FromData(context.Background(), false, []byte(`password="123456"`))
assert.NoError(t, err)
assert.Equal(t, 1, len(results))
assert.Equal(t, results[0].Raw, []byte(`password="123456"`))
assert.Equal(t, results[0].Raw, []byte(`123456`))
}

func BenchmarkProductIndices(b *testing.B) {
Expand Down
132 changes: 0 additions & 132 deletions pkg/detectors/sentrytoken/sentrytoken.go

This file was deleted.

137 changes: 137 additions & 0 deletions pkg/detectors/sentrytoken/v1/sentrytoken.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package sentrytoken

import (
"context"
"encoding/json"
"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"
)

type Scanner struct {
client *http.Client
}

type Organization struct {
ID string `json:"id"`
Name string `json:"name"`
}

// Ensure the Scanner satisfies the interface at compile time.
var _ detectors.Detector = (*Scanner)(nil)
var _ detectors.Versioner = (*Scanner)(nil)

var (
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"sentry"}) + `\b([a-f0-9]{64})\b`)

forbiddenError = "You do not have permission to perform this action."
)

func (s Scanner) Version() int {
return 1
}

// Keywords are used for efficiently pre-filtering chunks.
// Use identifiers in the secret preferably, or the provider name.
func (s Scanner) Keywords() []string {
return []string{"sentry"}
}

// FromData will find and optionally verify SentryToken secrets in a given set of bytes.
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)

// find all unique auth tokens
var uniqueAuthTokens = make(map[string]struct{})

for _, authToken := range keyPat.FindAllStringSubmatch(dataStr, -1) {
uniqueAuthTokens[authToken[1]] = struct{}{}
}

for authToken := range uniqueAuthTokens {
s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_SentryToken,
Raw: []byte(authToken),
}

if verify {
if s.client == nil {
s.client = common.SaneHttpClient()
}
extraData, isVerified, verificationErr := VerifyToken(ctx, s.client, authToken)
s1.Verified = isVerified
s1.SetVerificationError(verificationErr, authToken)
s1.ExtraData = extraData
}

results = append(results, s1)
}

return results, nil
}

func VerifyToken(ctx context.Context, client *http.Client, token string) (map[string]string, bool, error) {
// api docs: https://docs.sentry.io/api/organizations/
// this api will return 200 for user auth tokens with scope of org:<>
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://sentry.io/api/0/organizations/", nil)
if err != nil {
return nil, false, err
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))

resp, err := client.Do(req)
if err != nil {
return nil, false, err
}
defer func() {
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
}()

switch resp.StatusCode {
case http.StatusOK:
var organizations []Organization
if err = json.NewDecoder(resp.Body).Decode(&organizations); err != nil {
return nil, false, err
}

var extraData = make(map[string]string)
for _, org := range organizations {
extraData[fmt.Sprintf("orginzation_%s", org.ID)] = org.Name
}

return extraData, true, nil
case http.StatusForbidden:
var APIResp interface{}
if err = json.NewDecoder(resp.Body).Decode(&APIResp); err != nil {
return nil, false, err
}

// if response contain the forbiddenError message it means the token is active but does not have the right scope for this API call
if strings.Contains(fmt.Sprintf("%v", APIResp), forbiddenError) {
return nil, true, nil
}

return nil, false, nil
case http.StatusUnauthorized:
return nil, false, nil
default:
return nil, false, fmt.Errorf("unexpected HTTP response status %d", resp.StatusCode)
}
}

func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_SentryToken
}

func (s Scanner) Description() string {
return "Sentry is an error tracking service that helps developers monitor and fix crashes in real time. Sentry tokens can be used to access and manage projects and organizations within Sentry."
}
Loading

0 comments on commit 5718f09

Please sign in to comment.