Enigma detector enhance verification to prevent false verified findings (#5011)

* enhance the verification in the enigma detector

* feat(enigma): gate detector behind feature flag
This commit is contained in:
Shahzad Haider
2026-06-03 19:52:58 +05:00
committed by GitHub
parent d411fff7b8
commit 5a0f877b8a
6 changed files with 60 additions and 17 deletions
+1
View File
@@ -532,6 +532,7 @@ func run(state overseer.State, logSync func() error) {
feature.PineconeDetectorEnabled.Store(true)
feature.CloudinaryDetectorEnabled.Store(true)
feature.GitLabOAuthDetectorEnabled.Store(true)
feature.EnigmaDetectorEnabled.Store(true)
conf := &config.Config{}
if *configFilename != "" {
+48 -16
View File
@@ -2,6 +2,8 @@ package enigma
import (
"context"
"fmt"
"io"
"net/http"
"strings"
@@ -33,7 +35,6 @@ func (s Scanner) Keywords() []string {
// FromData will find and optionally verify Enigma 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)
matches := keyPat.FindAllStringSubmatch(dataStr, -1)
for _, match := range matches {
@@ -46,20 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
payload := strings.NewReader(`{"name":"Enigma Technologies, Inc.","person":{"first_name":"","last_name":""},"address":{"street_address1":"245 5th Ave","street_address2":"","city":"New York","state":"NY","postal_code":"10016"}}`)
req, err := http.NewRequestWithContext(ctx, "POST", "https://api.enigma.com/businesses/match", 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 func() { _ = res.Body.Close() }()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
}
}
isVerified, verificationErr := verifyKey(ctx, client, resMatch)
s1.Verified = isVerified
s1.SetVerificationError(verificationErr, resMatch)
}
results = append(results, s1)
@@ -68,10 +58,52 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
// verifyKey checks an Enigma API key against the GraphQL endpoint.
//
// The query selects only `__typename`, which the GraphQL spec guarantees on
// every root type, so verification does not depend on Enigma's evolving schema
// or any specific entity id. Authentication is driven by the HTTP status code:
// 200 means the key is valid, 401 mean it is not.
//
// A 200 is additionally guarded by a JSON Content-Type check: server could return
// a 200 HTML block page, which we must not mistake for a live key.
func verifyKey(ctx context.Context, client *http.Client, key string) (bool, error) {
payload := strings.NewReader(`{"query":"{ __typename }"}`)
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.enigma.com/graphql", payload)
if err != nil {
return false, err
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("x-api-key", key)
res, err := client.Do(req)
if err != nil {
return false, err
}
defer func() {
_, _ = io.Copy(io.Discard, res.Body)
_ = res.Body.Close()
}()
switch res.StatusCode {
case http.StatusOK:
if contentType := res.Header.Get("Content-Type"); !strings.Contains(contentType, "application/json") {
return false, fmt.Errorf("got HTTP 200 with unexpected Content-Type %q", contentType)
}
return true, nil
case http.StatusUnauthorized:
return false, nil
default:
return false, fmt.Errorf("unexpected HTTP response status %d", res.StatusCode)
}
}
func (s Scanner) Type() detector_typepb.DetectorType {
return detector_typepb.DetectorType_Enigma
}
func (s Scanner) Description() string {
return "Enigma is a data intelligence company that provides comprehensive data about businesses. Enigma API keys can be used to access and interact with this data."
return "Enigma is a business data intelligence platform. Enigma API keys grant access to comprehensive firmographic, legal, and identity datasets."
}
@@ -19,7 +19,7 @@ import (
func TestEnigma_FromChunk(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors1")
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors6")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
@@ -50,6 +50,9 @@ func TestEnigma_FromChunk(t *testing.T) {
{
DetectorType: detector_typepb.DetectorType_Enigma,
Verified: true,
SecretParts: map[string]string{
"key": secret,
},
},
},
wantErr: false,
@@ -66,6 +69,9 @@ func TestEnigma_FromChunk(t *testing.T) {
{
DetectorType: detector_typepb.DetectorType_Enigma,
Verified: false,
SecretParts: map[string]string{
"key": inactiveSecret,
},
},
},
wantErr: false,
+2
View File
@@ -1778,6 +1778,8 @@ func buildDetectorList() []detectors.Detector {
return !feature.CloudinaryDetectorEnabled.Load()
case *gitlaboauth2.Scanner:
return !feature.GitLabOAuthDetectorEnabled.Load()
case *enigma.Scanner:
return !feature.EnigmaDetectorEnabled.Load()
default:
return false
}
+1
View File
@@ -119,6 +119,7 @@ var excludedFromDefaultList = map[detector_typepb.DetectorType]struct{}{
// Remove each entry once its detector has been carefully added.
detector_typepb.DetectorType_Cloudinary: {},
detector_typepb.DetectorType_DatadogApikey: {},
detector_typepb.DetectorType_Enigma: {},
detector_typepb.DetectorType_GitLabOauth2: {},
detector_typepb.DetectorType_Guru: {},
detector_typepb.DetectorType_IPInfo: {},
+1
View File
@@ -19,6 +19,7 @@ var (
PineconeDetectorEnabled atomic.Bool
CloudinaryDetectorEnabled atomic.Bool
GitLabOAuthDetectorEnabled atomic.Bool
EnigmaDetectorEnabled atomic.Bool
)
type AtomicString struct {