Files
trufflehog/pkg/engine/defaults/defaults_test.go
e58aec687e feat(detectors): add Resend detector (#5103)
Adds a TruffleHog detector for Resend (https://resend.com), an email API
for transactional and marketing email.

Key format: re_[1-9A-HJ-NP-Za-km-z]{8}_[1-9A-HJ-NP-Za-km-z]{24}
(base58 alphabet, excluding the ambiguous characters 0, O, I, and l).

Co-authored-by: Shahzad Haider <[email protected]>
Co-authored-by: meredith <[email protected]>
2026-09-09 10:33:58 -05:00

371 lines
20 KiB
Go

package defaults
import (
"testing"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detector_typepb"
)
func TestDefaultDetectorsHaveUniqueVersions(t *testing.T) {
detectorTypeToVersions := make(map[detector_typepb.DetectorType]map[int]struct{})
addVersion := func(versions map[int]struct{}, version int) map[int]struct{} {
if versions == nil {
versions = make(map[int]struct{})
}
versions[version] = struct{}{}
return versions
}
// Loop through all our default detectors and find the ones that
// implement Versioner. Of those, check each version number is unique.
for _, detector := range DefaultDetectors() {
v, ok := detector.(detectors.Versioner)
if !ok {
continue
}
version := v.Version()
key := detector.Type()
if set, ok := detectorTypeToVersions[key]; ok && set != nil {
if _, ok := set[version]; ok {
t.Errorf("detector %q has duplicate version: %d", detector_typepb.DetectorType_name[int32(key)], version)
}
}
detectorTypeToVersions[key] = addVersion(detectorTypeToVersions[key], version)
}
}
func TestDefaultDetectorTypesImplementing(t *testing.T) {
isVersioner := DefaultDetectorTypesImplementing[detectors.Versioner]()
for _, detector := range DefaultDetectors() {
_, expectedOk := detector.(detectors.Versioner)
_, gotOk := isVersioner[detector.Type()]
if expectedOk == gotOk {
continue
}
t.Errorf(
"detector %q doesn't match expected",
detector_typepb.DetectorType_name[int32(detector.Type())],
)
}
}
func TestDefaultVersionerDetectorsHaveNonZeroVersions(t *testing.T) {
// Loop through all our default detectors and find the ones that
// implement Versioner. Of those, check each version is not zero.
// This is required due to an implementation detail of filtering detectors.
// See: https://github.com/trufflesecurity/trufflehog/blob/v3.63.7/main.go#L624-L638
for _, detector := range DefaultDetectors() {
v, ok := detector.(detectors.Versioner)
if !ok || v.Version() != 0 {
continue
}
t.Errorf(
"detector %q implements Versioner that returns a zero version",
detector_typepb.DetectorType_name[int32(detector.Type())],
)
}
}
// TestAllDetectorTypesAreInDefaultList ensures every proto-defined DetectorType
// is either present in buildDetectorList() or explicitly excluded below.
//
// If you add a new DetectorType to the proto:
// - Add its scanner to buildDetectorList() in defaults.go, OR
// - Add it to excludedFromDefaultList in this test.
func TestAllDetectorTypesAreInDefaultList(t *testing.T) {
activeTypes := make(map[detector_typepb.DetectorType]struct{})
for _, d := range DefaultDetectors() {
activeTypes[d.Type()] = struct{}{}
}
for typeInt32, typeName := range detector_typepb.DetectorType_name {
dt := detector_typepb.DetectorType(typeInt32)
if _, ok := activeTypes[dt]; ok {
continue
}
if _, ok := excludedFromDefaultList[dt]; ok {
continue
}
t.Errorf(
"DetectorType %q (value %d) is missing from buildDetectorList(); "+
"add it to defaults.go or to excludedFromDefaultList in this test",
typeName, typeInt32,
)
}
// Reverse check: no excluded type should appear in the active list.
// This catches types that were added to the exclude list by mistake.
for dt := range excludedFromDefaultList {
if _, ok := activeTypes[dt]; ok {
t.Errorf(
"DetectorType %q is in excludedFromDefaultList but is also present in buildDetectorList(); "+
"remove it from excludedFromDefaultList",
detector_typepb.DetectorType_name[int32(dt)],
)
}
}
}
// excludedFromDefaultList contains detector types that are intentionally absent
// from buildDetectorList(). Keep entries grouped and sorted.
//
// TODO: audit this list periodically — entries in the "mistakenly missed" group
// should be removed once the corresponding detector is added to defaults.go.
//
//nolint:staticcheck // SA1019: intentionally references deprecated DetectorType values to keep them excluded.
var excludedFromDefaultList = map[detector_typepb.DetectorType]struct{}{
// TODO: these detectors have implementations but were mistakenly never added
// to buildDetectorList() — discovered by TestAllDetectorTypesAreInDefaultList.
// They are not added immediately out of caution for the impact on customers/users.
// Remove each entry once its detector has been carefully added.
detector_typepb.DetectorType_Guru: {},
detector_typepb.DetectorType_Tru: {},
// Feature flag gated detectors
// These should be removed from this list when we remove the feature flag
detector_typepb.DetectorType_Cloudinary: {},
detector_typepb.DetectorType_DatadogApikey: {},
detector_typepb.DetectorType_Enigma: {},
detector_typepb.DetectorType_GitLabOauth2: {},
detector_typepb.DetectorType_Pinecone: {},
detector_typepb.DetectorType_TLy: {},
detector_typepb.DetectorType_Wit: {},
detector_typepb.DetectorType_Rev: {},
detector_typepb.DetectorType_User: {},
detector_typepb.DetectorType_BrainTrustApiKey: {},
detector_typepb.DetectorType_PgAnalyzeReadKey: {},
detector_typepb.DetectorType_RedHatPyxis: {},
detector_typepb.DetectorType_OctopusDeploy: {},
detector_typepb.DetectorType_OpenRouter: {},
detector_typepb.DetectorType_NewRelicInsightsInsertKey: {},
detector_typepb.DetectorType_DuffelToken: {},
detector_typepb.DetectorType_Shippo: {},
detector_typepb.DetectorType_IPInfo: {},
detector_typepb.DetectorType_Lob: {},
detector_typepb.DetectorType_HashiCorpVaultBatchToken: {},
detector_typepb.DetectorType_HashiCorpVaultToken: {},
detector_typepb.DetectorType_Duo: {},
detector_typepb.DetectorType_NewRelicLicenseKey: {},
detector_typepb.DetectorType_NewRelicBrowserKey: {},
detector_typepb.DetectorType_NewRelicUserKey: {},
detector_typepb.DetectorType_NewRelicInsightsQueryKey: {},
detector_typepb.DetectorType_NewRelicMobileAppToken: {},
detector_typepb.DetectorType_SolarWindsObservability: {},
detector_typepb.DetectorType_HumioAPIToken: {},
detector_typepb.DetectorType_Resend: {},
// Reserved / special types.
detector_typepb.DetectorType_CustomRegex: {}, // added dynamically via engine config, not via buildDetectorList()
detector_typepb.DetectorType_Test: {},
// Deprecated — the proto field is marked deprecated=true and the service
// no longer exists or has been superseded by a different type.
detector_typepb.DetectorType_AirtableApiKey: {},
detector_typepb.DetectorType_ApiScience: {},
detector_typepb.DetectorType_AppOptics: {},
detector_typepb.DetectorType_BingSubscriptionKey: {},
detector_typepb.DetectorType_Blablabus: {},
detector_typepb.DetectorType_CoinbaseWaaS: {},
detector_typepb.DetectorType_CoinMarketCap: {},
detector_typepb.DetectorType_CrossBrowserTesting: {},
detector_typepb.DetectorType_DataFire: {},
detector_typepb.DetectorType_EtsyApiKey: {},
detector_typepb.DetectorType_FakeJSON: {},
detector_typepb.DetectorType_Flowdash: {},
detector_typepb.DetectorType_Flowdock: {},
detector_typepb.DetectorType_Fusebill: {},
detector_typepb.DetectorType_GlitterlyAPI: {},
detector_typepb.DetectorType_GoogleApiKey: {},
detector_typepb.DetectorType_Happi: {},
detector_typepb.DetectorType_Heatmapapi: {},
detector_typepb.DetectorType_Integromat: {},
detector_typepb.DetectorType_Ipify: {},
detector_typepb.DetectorType_Lastfm: {},
detector_typepb.DetectorType_Macaddress: {},
detector_typepb.DetectorType_Nitro: {},
detector_typepb.DetectorType_Nytimes: {},
detector_typepb.DetectorType_OnWaterIO: {},
detector_typepb.DetectorType_Opengraphr: {},
detector_typepb.DetectorType_Passbase: {},
detector_typepb.DetectorType_ProspectIO: {},
detector_typepb.DetectorType_QuickMetrics: {},
detector_typepb.DetectorType_Restpack: {},
detector_typepb.DetectorType_Rockset: {},
detector_typepb.DetectorType_ScraperSite: {},
detector_typepb.DetectorType_Sentiment: {},
detector_typepb.DetectorType_SportRadar: {},
detector_typepb.DetectorType_Squareup: {},
detector_typepb.DetectorType_Text2Data: {},
// Intentionally commented out in buildDetectorList() — implementation
// exists but the detector is disabled (API issues, false positives, etc.).
detector_typepb.DetectorType_Abstract: {},
detector_typepb.DetectorType_AdobeIO: {},
detector_typepb.DetectorType_Alconost: {},
detector_typepb.DetectorType_Apollo: {},
detector_typepb.DetectorType_AzureFunctionKey: {},
detector_typepb.DetectorType_Besnappy: {},
detector_typepb.DetectorType_BlockNative: {},
detector_typepb.DetectorType_DailyCO: {},
detector_typepb.DetectorType_Debounce: {},
detector_typepb.DetectorType_Generic: {},
detector_typepb.DetectorType_GetEmail: {},
detector_typepb.DetectorType_GetEmails: {},
detector_typepb.DetectorType_Hive: {},
detector_typepb.DetectorType_IbmCloudUserKey: {},
detector_typepb.DetectorType_LineMessaging: {},
detector_typepb.DetectorType_M3o: {},
detector_typepb.DetectorType_Magnetic: {},
detector_typepb.DetectorType_Manifest: {},
detector_typepb.DetectorType_Mixpanel: {},
detector_typepb.DetectorType_NasdaqDataLink: {},
detector_typepb.DetectorType_Raven: {},
detector_typepb.DetectorType_Sparkpost: {},
detector_typepb.DetectorType_SpotifyKey: {},
detector_typepb.DetectorType_TogglTrack: {},
detector_typepb.DetectorType_WePay: {},
detector_typepb.DetectorType_ZapierWebhook: {},
// Not yet implemented — proto enum entry exists but no detector has been
// written for this service yet.
detector_typepb.DetectorType_Aerisweather: {},
detector_typepb.DetectorType_Aftership: {},
detector_typepb.DetectorType_Convert: {},
detector_typepb.DetectorType_Honey: {},
detector_typepb.DetectorType_HubSpot: {},
detector_typepb.DetectorType_Paymo: {},
detector_typepb.DetectorType_Sellfy: {},
detector_typepb.DetectorType_AirtableMetadataApiKey: {},
detector_typepb.DetectorType_AkamaiToken: {},
detector_typepb.DetectorType_Alphavantage: {},
detector_typepb.DetectorType_AmazonMWS: {},
detector_typepb.DetectorType_AMQP: {},
detector_typepb.DetectorType_Api2Convert: {},
detector_typepb.DetectorType_Authorize: {},
detector_typepb.DetectorType_Avalara: {},
detector_typepb.DetectorType_BaseApiIO: {},
detector_typepb.DetectorType_BasisTheory: {},
detector_typepb.DetectorType_BitGo: {},
detector_typepb.DetectorType_Bored: {},
detector_typepb.DetectorType_Brightlocal: {},
detector_typepb.DetectorType_Bubble: {},
detector_typepb.DetectorType_Checkmarket: {},
detector_typepb.DetectorType_CircleCI: {},
detector_typepb.DetectorType_Cloudant: {},
detector_typepb.DetectorType_CloudsightKey: {},
detector_typepb.DetectorType_Cloudways: {},
detector_typepb.DetectorType_CoinGecko: {},
detector_typepb.DetectorType_Cometchat: {},
detector_typepb.DetectorType_ContentfulDelivery: {},
detector_typepb.DetectorType_ContentStack: {},
detector_typepb.DetectorType_Copyscape: {},
detector_typepb.DetectorType_Createsend: {},
detector_typepb.DetectorType_Cricket: {},
detector_typepb.DetectorType_DigitalOceanSpaces: {},
detector_typepb.DetectorType_Distribusion: {}, //nolint:misspell // proto enum name is intentionally spelled this way
detector_typepb.DetectorType_Duda: {},
detector_typepb.DetectorType_Duffel: {},
detector_typepb.DetectorType_Dynadot: {},
detector_typepb.DetectorType_Dynatrace: {},
detector_typepb.DetectorType_Edusign: {},
detector_typepb.DetectorType_ElasticPath: {},
detector_typepb.DetectorType_Emailoctopus: {},
detector_typepb.DetectorType_EquinixOauth: {},
detector_typepb.DetectorType_Eversign: {},
detector_typepb.DetectorType_Fastspring: {},
detector_typepb.DetectorType_Feedly: {},
detector_typepb.DetectorType_Filestack: {},
detector_typepb.DetectorType_Firebase: {},
detector_typepb.DetectorType_FirebaseCloudMessaging: {},
detector_typepb.DetectorType_FlagsmithEnvironmentKey: {},
detector_typepb.DetectorType_FlagsmithToken: {},
detector_typepb.DetectorType_Formstack: {},
detector_typepb.DetectorType_Fountain: {},
detector_typepb.DetectorType_FullContact: {},
detector_typepb.DetectorType_GitHubOld: {},
detector_typepb.DetectorType_Goshippo: {},
detector_typepb.DetectorType_Gosquared: {},
detector_typepb.DetectorType_Hotwire: {},
detector_typepb.DetectorType_HubSpotOauth: {},
detector_typepb.DetectorType_HypeAuditor: {},
detector_typepb.DetectorType_Image4: {},
detector_typepb.DetectorType_ImageToText: {},
detector_typepb.DetectorType_Imgix: {},
detector_typepb.DetectorType_Imgur: {},
detector_typepb.DetectorType_Infobip: {},
detector_typepb.DetectorType_JSONbin: {},
detector_typepb.DetectorType_Jumpseller: {},
detector_typepb.DetectorType_Kairos: {},
detector_typepb.DetectorType_KakaoTalk: {},
detector_typepb.DetectorType_Kaleyra: {},
detector_typepb.DetectorType_KalturaAppToken: {},
detector_typepb.DetectorType_KalturaSession: {},
detector_typepb.DetectorType_Keygen: {},
detector_typepb.DetectorType_KiteConnect: {},
detector_typepb.DetectorType_KubeConfig: {},
detector_typepb.DetectorType_LinkedIn: {},
detector_typepb.DetectorType_Linode: {},
detector_typepb.DetectorType_Messari: {},
detector_typepb.DetectorType_Midise: {},
detector_typepb.DetectorType_Mixcloud: {},
detector_typepb.DetectorType_Mojohelpdesk: {},
detector_typepb.DetectorType_MollieAccessToken: {},
detector_typepb.DetectorType_MollieAPIKey: {},
detector_typepb.DetectorType_Myexperiment: {},
detector_typepb.DetectorType_NetCore: {},
detector_typepb.DetectorType_NiceHash: {},
detector_typepb.DetectorType_Nordigen: {},
detector_typepb.DetectorType_Nubela: {},
detector_typepb.DetectorType_OcrSpace: {},
detector_typepb.DetectorType_Onbuka: {},
detector_typepb.DetectorType_Opendatasoft: {},
detector_typepb.DetectorType_Optidash: {},
detector_typepb.DetectorType_Paddle: {},
detector_typepb.DetectorType_Page2Images: {},
detector_typepb.DetectorType_Papyrs: {},
detector_typepb.DetectorType_PDFmyURL: {},
detector_typepb.DetectorType_PendoIntegrationKey: {},
detector_typepb.DetectorType_PlaidToken: {},
detector_typepb.DetectorType_Printfection: {},
detector_typepb.DetectorType_Processst: {},
detector_typepb.DetectorType_Quickbase: {},
detector_typepb.DetectorType_ReCAPTCHA: {},
detector_typepb.DetectorType_Redbooth: {},
detector_typepb.DetectorType_Riotgames: {},
detector_typepb.DetectorType_Rosette: {},
detector_typepb.DetectorType_Samsara: {},
detector_typepb.DetectorType_ScrapingDog: {},
detector_typepb.DetectorType_Sendoso: {},
detector_typepb.DetectorType_ShopeeOpenPlatform: {},
detector_typepb.DetectorType_Simplybook: {},
detector_typepb.DetectorType_SMSApi: {},
detector_typepb.DetectorType_StreamChatMessaging: {},
detector_typepb.DetectorType_Supportbee: {},
detector_typepb.DetectorType_Surge: {},
detector_typepb.DetectorType_Teamup: {},
detector_typepb.DetectorType_TeamViewer: {},
detector_typepb.DetectorType_Telesign: {},
detector_typepb.DetectorType_TencentCloudKey: {},
detector_typepb.DetectorType_Timekit: {},
detector_typepb.DetectorType_Trimble: {},
detector_typepb.DetectorType_TwitterApiSecret: {},
detector_typepb.DetectorType_UberServerToken: {},
detector_typepb.DetectorType_Uproc: {},
detector_typepb.DetectorType_Veevavault: {},
detector_typepb.DetectorType_Vonage: {},
detector_typepb.DetectorType_Wakatime: {},
detector_typepb.DetectorType_Webengage: {},
detector_typepb.DetectorType_WeChatAppKey: {},
detector_typepb.DetectorType_Woopra: {},
detector_typepb.DetectorType_WordsApi: {},
detector_typepb.DetectorType_Workday: {},
detector_typepb.DetectorType_WpEngine: {},
detector_typepb.DetectorType_Yext: {},
detector_typepb.DetectorType_AzureActiveDirectoryApplicationSecret: {},
detector_typepb.DetectorType_AzureCacheForRedisAccessKey: {},
detector_typepb.DetectorType_AzureManagementCertificate: {},
detector_typepb.DetectorType_AzureMLWebServiceClassicIdentifiableKey: {},
detector_typepb.DetectorType_AzureSQL: {},
detector_typepb.DetectorType_BuiltWith: {},
}