[Feat] Implementation of Posthog Analyzer (#4103)

* initial implementation (unbuildable)

* posthog analyzer done

* cleanup the code and redesign the structure of analyzer info.

* removed unused function.
This commit is contained in:
Abdul Basit
2025-05-16 17:11:44 +05:00
committed by GitHub
parent 8da58156fd
commit 3e87718555
9 changed files with 1744 additions and 1 deletions
+2
View File
@@ -99,6 +99,7 @@ const (
AnalyzerTypeMonday
AnalyzerTypeNgrok
AnalyzerTypeMux
AnalyzerTypePosthog
// Add new items here with AnalyzerType prefix
)
@@ -143,6 +144,7 @@ var analyzerTypeStrings = map[AnalyzerType]string{
AnalyzerTypeMonday: "Monday",
AnalyzerTypeNgrok: "Ngrok",
AnalyzerTypeMux: "Mux",
AnalyzerTypePosthog: "Posthog",
// Add new mappings here
}
@@ -0,0 +1 @@
{"AnalyzerType":39,"Bindings":[{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"action:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"activity_log:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"annotation:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"dashboard:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"event_definition:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"event_definition:write","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"export:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"group:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"group:write","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"insight:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"person:read","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"person:write","Parent":null}},{"Resource":{"Name":"Default project","FullyQualifiedName":"150774","Type":"project","Metadata":null,"Parent":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null}},"Permission":{"Value":"query:read","Parent":null}},{"Resource":{"Name":"Truffle Security","FullyQualifiedName":"019666bb-9f8e-0000-8bc2-4ea34ec57752","Type":"user","Metadata":null,"Parent":null},"Permission":{"Value":"user:read","Parent":null}},{"Resource":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null},"Permission":{"Value":"organization:read","Parent":null}},{"Resource":{"Name":"TruffleSecurity","FullyQualifiedName":"019666bb-9f89-0000-0820-312e5f974324","Type":"organization","Metadata":null,"Parent":null},"Permission":{"Value":"project:read","Parent":null}}],"UnboundedResources":null,"Metadata":null}
@@ -0,0 +1,356 @@
// Code generated by go generate; DO NOT EDIT.
package posthog
import "errors"
type Permission int
const (
Invalid Permission = iota
ActionRead Permission = iota
ActionWrite Permission = iota
ActivityLogRead Permission = iota
ActivityLogWrite Permission = iota
AnnotationRead Permission = iota
AnnotationWrite Permission = iota
BatchExportRead Permission = iota
BatchExportWrite Permission = iota
CohortRead Permission = iota
CohortWrite Permission = iota
DashboardRead Permission = iota
DashboardWrite Permission = iota
DashboardTemplateRead Permission = iota
DashboardTemplateWrite Permission = iota
EarlyAccessFeatureRead Permission = iota
EarlyAccessFeatureWrite Permission = iota
EventDefinitionRead Permission = iota
EventDefinitionWrite Permission = iota
ErrorTrackingRead Permission = iota
ErrorTrackingWrite Permission = iota
ExperimentRead Permission = iota
ExperimentWrite Permission = iota
ExportRead Permission = iota
ExportWrite Permission = iota
FeatureFlagRead Permission = iota
FeatureFlagWrite Permission = iota
GroupRead Permission = iota
GroupWrite Permission = iota
HogFunctionRead Permission = iota
HogFunctionWrite Permission = iota
InsightRead Permission = iota
InsightWrite Permission = iota
NotebookRead Permission = iota
NotebookWrite Permission = iota
OrganizationRead Permission = iota
OrganizationWrite Permission = iota
OrganizationMemberRead Permission = iota
OrganizationMemberWrite Permission = iota
PersonRead Permission = iota
PersonWrite Permission = iota
PluginRead Permission = iota
PluginWrite Permission = iota
ProjectRead Permission = iota
ProjectWrite Permission = iota
PropertyDefinitionRead Permission = iota
PropertyDefinitionWrite Permission = iota
QueryRead Permission = iota
SessionRecordingRead Permission = iota
SessionRecordingWrite Permission = iota
SessionRecordingPlaylistRead Permission = iota
SessionRecordingPlaylistWrite Permission = iota
SharingConfigurationRead Permission = iota
SharingConfigurationWrite Permission = iota
SubscriptionRead Permission = iota
SubscriptionWrite Permission = iota
SurveyRead Permission = iota
SurveyWrite Permission = iota
UserRead Permission = iota
WebhookRead Permission = iota
WebhookWrite Permission = iota
)
var (
PermissionStrings = map[Permission]string{
ActionRead: "action:read",
ActionWrite: "action:write",
ActivityLogRead: "activity_log:read",
ActivityLogWrite: "activity_log:write",
AnnotationRead: "annotation:read",
AnnotationWrite: "annotation:write",
BatchExportRead: "batch_export:read",
BatchExportWrite: "batch_export:write",
CohortRead: "cohort:read",
CohortWrite: "cohort:write",
DashboardRead: "dashboard:read",
DashboardWrite: "dashboard:write",
DashboardTemplateRead: "dashboard_template:read",
DashboardTemplateWrite: "dashboard_template:write",
EarlyAccessFeatureRead: "early_access_feature:read",
EarlyAccessFeatureWrite: "early_access_feature:write",
EventDefinitionRead: "event_definition:read",
EventDefinitionWrite: "event_definition:write",
ErrorTrackingRead: "error_tracking:read",
ErrorTrackingWrite: "error_tracking:write",
ExperimentRead: "experiment:read",
ExperimentWrite: "experiment:write",
ExportRead: "export:read",
ExportWrite: "export:write",
FeatureFlagRead: "feature_flag:read",
FeatureFlagWrite: "feature_flag:write",
GroupRead: "group:read",
GroupWrite: "group:write",
HogFunctionRead: "hog_function:read",
HogFunctionWrite: "hog_function:write",
InsightRead: "insight:read",
InsightWrite: "insight:write",
NotebookRead: "notebook:read",
NotebookWrite: "notebook:write",
OrganizationRead: "organization:read",
OrganizationWrite: "organization:write",
OrganizationMemberRead: "organization_member:read",
OrganizationMemberWrite: "organization_member:write",
PersonRead: "person:read",
PersonWrite: "person:write",
PluginRead: "plugin:read",
PluginWrite: "plugin:write",
ProjectRead: "project:read",
ProjectWrite: "project:write",
PropertyDefinitionRead: "property_definition:read",
PropertyDefinitionWrite: "property_definition:write",
QueryRead: "query:read",
SessionRecordingRead: "session_recording:read",
SessionRecordingWrite: "session_recording:write",
SessionRecordingPlaylistRead: "session_recording_playlist:read",
SessionRecordingPlaylistWrite: "session_recording_playlist:write",
SharingConfigurationRead: "sharing_configuration:read",
SharingConfigurationWrite: "sharing_configuration:write",
SubscriptionRead: "subscription:read",
SubscriptionWrite: "subscription:write",
SurveyRead: "survey:read",
SurveyWrite: "survey:write",
UserRead: "user:read",
WebhookRead: "webhook:read",
WebhookWrite: "webhook:write",
}
StringToPermission = map[string]Permission{
"action:read": ActionRead,
"action:write": ActionWrite,
"activity_log:read": ActivityLogRead,
"activity_log:write": ActivityLogWrite,
"annotation:read": AnnotationRead,
"annotation:write": AnnotationWrite,
"batch_export:read": BatchExportRead,
"batch_export:write": BatchExportWrite,
"cohort:read": CohortRead,
"cohort:write": CohortWrite,
"dashboard:read": DashboardRead,
"dashboard:write": DashboardWrite,
"dashboard_template:read": DashboardTemplateRead,
"dashboard_template:write": DashboardTemplateWrite,
"early_access_feature:read": EarlyAccessFeatureRead,
"early_access_feature:write": EarlyAccessFeatureWrite,
"event_definition:read": EventDefinitionRead,
"event_definition:write": EventDefinitionWrite,
"error_tracking:read": ErrorTrackingRead,
"error_tracking:write": ErrorTrackingWrite,
"experiment:read": ExperimentRead,
"experiment:write": ExperimentWrite,
"export:read": ExportRead,
"export:write": ExportWrite,
"feature_flag:read": FeatureFlagRead,
"feature_flag:write": FeatureFlagWrite,
"group:read": GroupRead,
"group:write": GroupWrite,
"hog_function:read": HogFunctionRead,
"hog_function:write": HogFunctionWrite,
"insight:read": InsightRead,
"insight:write": InsightWrite,
"notebook:read": NotebookRead,
"notebook:write": NotebookWrite,
"organization:read": OrganizationRead,
"organization:write": OrganizationWrite,
"organization_member:read": OrganizationMemberRead,
"organization_member:write": OrganizationMemberWrite,
"person:read": PersonRead,
"person:write": PersonWrite,
"plugin:read": PluginRead,
"plugin:write": PluginWrite,
"project:read": ProjectRead,
"project:write": ProjectWrite,
"property_definition:read": PropertyDefinitionRead,
"property_definition:write": PropertyDefinitionWrite,
"query:read": QueryRead,
"session_recording:read": SessionRecordingRead,
"session_recording:write": SessionRecordingWrite,
"session_recording_playlist:read": SessionRecordingPlaylistRead,
"session_recording_playlist:write": SessionRecordingPlaylistWrite,
"sharing_configuration:read": SharingConfigurationRead,
"sharing_configuration:write": SharingConfigurationWrite,
"subscription:read": SubscriptionRead,
"subscription:write": SubscriptionWrite,
"survey:read": SurveyRead,
"survey:write": SurveyWrite,
"user:read": UserRead,
"webhook:read": WebhookRead,
"webhook:write": WebhookWrite,
}
PermissionIDs = map[Permission]int{
ActionRead: 1,
ActionWrite: 2,
ActivityLogRead: 3,
ActivityLogWrite: 4,
AnnotationRead: 5,
AnnotationWrite: 6,
BatchExportRead: 7,
BatchExportWrite: 8,
CohortRead: 9,
CohortWrite: 10,
DashboardRead: 11,
DashboardWrite: 12,
DashboardTemplateRead: 13,
DashboardTemplateWrite: 14,
EarlyAccessFeatureRead: 15,
EarlyAccessFeatureWrite: 16,
EventDefinitionRead: 17,
EventDefinitionWrite: 18,
ErrorTrackingRead: 19,
ErrorTrackingWrite: 20,
ExperimentRead: 21,
ExperimentWrite: 22,
ExportRead: 23,
ExportWrite: 24,
FeatureFlagRead: 25,
FeatureFlagWrite: 26,
GroupRead: 27,
GroupWrite: 28,
HogFunctionRead: 29,
HogFunctionWrite: 30,
InsightRead: 31,
InsightWrite: 32,
NotebookRead: 33,
NotebookWrite: 34,
OrganizationRead: 35,
OrganizationWrite: 36,
OrganizationMemberRead: 37,
OrganizationMemberWrite: 38,
PersonRead: 39,
PersonWrite: 40,
PluginRead: 41,
PluginWrite: 42,
ProjectRead: 43,
ProjectWrite: 44,
PropertyDefinitionRead: 45,
PropertyDefinitionWrite: 46,
QueryRead: 47,
SessionRecordingRead: 48,
SessionRecordingWrite: 49,
SessionRecordingPlaylistRead: 50,
SessionRecordingPlaylistWrite: 51,
SharingConfigurationRead: 52,
SharingConfigurationWrite: 53,
SubscriptionRead: 54,
SubscriptionWrite: 55,
SurveyRead: 56,
SurveyWrite: 57,
UserRead: 58,
WebhookRead: 59,
WebhookWrite: 60,
}
IdToPermission = map[int]Permission{
1: ActionRead,
2: ActionWrite,
3: ActivityLogRead,
4: ActivityLogWrite,
5: AnnotationRead,
6: AnnotationWrite,
7: BatchExportRead,
8: BatchExportWrite,
9: CohortRead,
10: CohortWrite,
11: DashboardRead,
12: DashboardWrite,
13: DashboardTemplateRead,
14: DashboardTemplateWrite,
15: EarlyAccessFeatureRead,
16: EarlyAccessFeatureWrite,
17: EventDefinitionRead,
18: EventDefinitionWrite,
19: ErrorTrackingRead,
20: ErrorTrackingWrite,
21: ExperimentRead,
22: ExperimentWrite,
23: ExportRead,
24: ExportWrite,
25: FeatureFlagRead,
26: FeatureFlagWrite,
27: GroupRead,
28: GroupWrite,
29: HogFunctionRead,
30: HogFunctionWrite,
31: InsightRead,
32: InsightWrite,
33: NotebookRead,
34: NotebookWrite,
35: OrganizationRead,
36: OrganizationWrite,
37: OrganizationMemberRead,
38: OrganizationMemberWrite,
39: PersonRead,
40: PersonWrite,
41: PluginRead,
42: PluginWrite,
43: ProjectRead,
44: ProjectWrite,
45: PropertyDefinitionRead,
46: PropertyDefinitionWrite,
47: QueryRead,
48: SessionRecordingRead,
49: SessionRecordingWrite,
50: SessionRecordingPlaylistRead,
51: SessionRecordingPlaylistWrite,
52: SharingConfigurationRead,
53: SharingConfigurationWrite,
54: SubscriptionRead,
55: SubscriptionWrite,
56: SurveyRead,
57: SurveyWrite,
58: UserRead,
59: WebhookRead,
60: WebhookWrite,
}
)
// ToString converts a Permission enum to its string representation
func (p Permission) ToString() (string, error) {
if str, ok := PermissionStrings[p]; ok {
return str, nil
}
return "", errors.New("invalid permission")
}
// ToID converts a Permission enum to its ID
func (p Permission) ToID() (int, error) {
if id, ok := PermissionIDs[p]; ok {
return id, nil
}
return 0, errors.New("invalid permission")
}
// PermissionFromString converts a string representation to its Permission enum
func PermissionFromString(s string) (Permission, error) {
if p, ok := StringToPermission[s]; ok {
return p, nil
}
return 0, errors.New("invalid permission string")
}
// PermissionFromID converts an ID to its Permission enum
func PermissionFromID(id int) (Permission, error) {
if p, ok := IdToPermission[id]; ok {
return p, nil
}
return 0, errors.New("invalid permission ID")
}
@@ -0,0 +1,61 @@
permissions:
- action:read
- action:write
- activity_log:read
- activity_log:write
- annotation:read
- annotation:write
- batch_export:read
- batch_export:write
- cohort:read
- cohort:write
- dashboard:read
- dashboard:write
- dashboard_template:read
- dashboard_template:write
- early_access_feature:read
- early_access_feature:write
- event_definition:read
- event_definition:write
- error_tracking:read
- error_tracking:write
- experiment:read
- experiment:write
- export:read
- export:write
- feature_flag:read
- feature_flag:write
- group:read
- group:write
- hog_function:read
- hog_function:write
- insight:read
- insight:write
- notebook:read
- notebook:write
- organization:read
- organization:write
- organization_member:read
- organization_member:write
- person:read
- person:write
- plugin:read
- plugin:write
- project:read
- project:write
- property_definition:read
- property_definition:write
- query:read
- session_recording:read
- session_recording:write
- session_recording_playlist:read
- session_recording_playlist:write
- sharing_configuration:read
- sharing_configuration:write
- subscription:read
- subscription:write
- survey:read
- survey:write
- user:read
- webhook:read
- webhook:write
+545
View File
@@ -0,0 +1,545 @@
//go:generate generate_permissions permissions.yaml permissions.go posthog
package posthog
import (
"bytes"
_ "embed"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/config"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
var _ analyzers.Analyzer = (*Analyzer)(nil)
const (
USDomain = "https://us.posthog.com"
EUDomain = "https://eu.posthog.com"
)
type Analyzer struct {
Cfg *config.Config
}
func (Analyzer) Type() analyzers.AnalyzerType { return analyzers.AnalyzerTypePosthog }
func (a Analyzer) Analyze(_ context.Context, credInfo map[string]string) (*analyzers.AnalyzerResult, error) {
key, ok := credInfo["key"]
if !ok {
return nil, errors.New("missing key in credInfo")
}
info, err := AnalyzePermissions(a.Cfg, key)
if err != nil {
return nil, err
}
return secretInfoToAnalyzerResult(info), nil
}
func secretInfoToAnalyzerResult(info *SecretInfo) *analyzers.AnalyzerResult {
if info == nil {
return nil
}
result := analyzers.AnalyzerResult{
AnalyzerType: analyzers.AnalyzerTypePosthog,
Metadata: nil,
Bindings: make([]analyzers.Binding, 0),
}
if info.orgPermissions == nil {
// no permissions to check
return &result
}
if info.user != nil {
// for user resource
userResource := analyzers.Resource{
Name: info.user.FirstName + " " + info.user.LastName,
FullyQualifiedName: info.user.UUID,
Type: "user",
}
analyzerPermission := analyzers.Permission{
Value: PermissionStrings[UserRead],
}
result.Bindings = append(result.Bindings, analyzers.Binding{
Resource: userResource,
Permission: analyzerPermission,
})
}
// for organization permissions, we need to bind the permissions to the organization resource
organizationResource := analyzers.Resource{
Name: info.organization.Name,
FullyQualifiedName: info.organization.ID,
Type: "organization",
}
for _, permission := range info.orgPermissions {
if value, ok := PermissionStrings[permission]; ok {
analyzerPermission := analyzers.Permission{
Value: value,
}
result.Bindings = append(result.Bindings, analyzers.Binding{
Resource: organizationResource,
Permission: analyzerPermission,
})
}
}
// for project permissions, we need to bind the permissions to the project resource and organization as the parent resource
for _, projectPermission := range info.projectPermissions {
projectResource := analyzers.Resource{
Name: projectPermission.Project.Name,
FullyQualifiedName: strconv.FormatInt(projectPermission.Project.ID, 10),
Type: "project",
Parent: &organizationResource,
}
for _, permission := range projectPermission.Permissions {
permissionStr, _ := permission.ToString()
analyzerPermission := analyzers.Permission{
Value: permissionStr,
}
result.Bindings = append(result.Bindings, analyzers.Binding{
Resource: projectResource,
Permission: analyzerPermission,
})
}
}
return &result
}
//go:embed scopes.json
var scopesConfigBytes []byte
type HttpStatusTest struct {
Endpoint string `json:"endpoint"`
Method string `json:"method"`
Payload interface{} `json:"payload"`
ValidStatuses []int `json:"valid_status_code"`
InvalidStatuses []int `json:"invalid_status_code"`
}
func StatusContains(status int, vals []int) bool {
for _, v := range vals {
if status == v {
return true
}
}
return false
}
func (h *HttpStatusTest) RunTest(cfg *config.Config, client *http.Client, domain string, headers map[string]string, args ...any) (bool, error) {
// If body data, marshal to JSON
var data io.Reader
if h.Payload != nil {
jsonData, err := json.Marshal(h.Payload)
if err != nil {
return false, err
}
data = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequest(h.Method, fmt.Sprintf(domain+h.Endpoint, args...), data)
if err != nil {
return false, err
}
// Add custom headers if provided
for key, value := range headers {
req.Header.Set(key, value)
}
// Execute HTTP Request
resp, err := client.Do(req)
if err != nil {
return false, err
}
defer resp.Body.Close()
// Check response status code
switch {
case StatusContains(resp.StatusCode, h.ValidStatuses):
return true, nil
case StatusContains(resp.StatusCode, h.InvalidStatuses):
return false, nil
default:
fmt.Println(h.Method, h.Endpoint)
return false, errors.New("error checking response status code")
}
}
type ScopesConfig struct {
GeneralScopes []Scope `json:"general_scopes"`
OrganizationScopes []Scope `json:"organization_scopes"`
ProjectScopes []Scope `json:"project_scopes"`
}
type Scope struct {
Name string `json:"name"`
Test ScopeTest `json:"test"`
}
type ScopeTest struct {
Read *HttpStatusTest `json:"read"`
Write *HttpStatusTest `json:"write"`
}
func readInScopesConfig() (*ScopesConfig, error) {
var scopesConfig ScopesConfig
if err := json.Unmarshal(scopesConfigBytes, &scopesConfig); err != nil {
return nil, err
}
return &scopesConfig, nil
}
func checkPermissions(cfg *config.Config, client *http.Client, domain string, key string, scopes []Scope, args ...any) ([]Permission, error) {
permissions := make([]Permission, 0)
headers := map[string]string{"Authorization": "Bearer " + key}
for _, scope := range scopes {
var status bool
var err error
if scope.Test.Write != nil {
status, err = scope.Test.Write.RunTest(cfg, client, domain, headers, args...)
if err != nil {
return nil, fmt.Errorf("running test: %w", err)
}
}
if status {
if permission, ok := StringToPermission[scope.Name+":write"]; ok {
permissions = append(permissions, permission)
}
// if write exists, read also exists
if permission, ok := StringToPermission[scope.Name+":read"]; ok {
permissions = append(permissions, permission)
}
} else {
status, err = scope.Test.Read.RunTest(cfg, client, domain, headers, args...)
if err != nil {
return nil, fmt.Errorf("running test: %w", err)
}
if status {
if permission, ok := StringToPermission[scope.Name+":read"]; ok {
permissions = append(permissions, permission)
}
}
}
}
return permissions, nil
}
type ProjectPermissions struct {
Project *Project
Permissions []Permission
}
type SecretInfo struct {
user *User
organization *Organization
orgPermissions []Permission
projectPermissions []ProjectPermissions
// generalPermissions []Permission
unverifiedPermissions map[Permission]struct{}
}
func AnalyzeAndPrintPermissions(cfg *config.Config, key string) {
info, err := AnalyzePermissions(cfg, key)
if err != nil {
color.Red("[x] Error : %s", err.Error())
return
}
color.Green("[!] Valid Posthog API key")
color.Yellow("[i] Expires: Never")
if info.user != nil {
printUser(*info.user)
}
if info.organization == nil {
color.Yellow("\n[i] No permissions were verified for this key because the key does not have one of the necessary permissions (user:read or organization:read) required to verifiy other permissions.")
}
if info.orgPermissions != nil {
printOrganizationPermissions(*info.organization, info.orgPermissions)
}
if len(info.projectPermissions) > 0 {
printProjectPermissions(info.projectPermissions)
}
printUnverifiedPermissions(info.unverifiedPermissions)
}
func AnalyzePermissions(cfg *config.Config, key string) (*SecretInfo, error) {
var info = &SecretInfo{}
// These are permissions that cannot be verified due to no endpoint available
info.unverifiedPermissions = map[Permission]struct{}{
ErrorTrackingRead: {},
ErrorTrackingWrite: {},
SharingConfigurationRead: {},
SharingConfigurationWrite: {},
WebhookRead: {},
WebhookWrite: {},
}
client := analyzers.NewAnalyzeClient(cfg)
// we need to determine if the key is for US or EU domain
domain, user, err := resolveDomainAndUser(cfg, client, key)
if err != nil {
return nil, fmt.Errorf("Invalid API Key: %w", err)
}
info.user = user
// Most posthog API scopes are bound to projects and organization, so to determine the scopes we need to first get the organization and projects.
// If the key has user:read scope, we will get the user above which contains the organizations and projects.
// If the key does not have user:read scope, we can call the /organizations/@current endpoint to get the
// organization and projects. If the key does not have organization:read scope as well, we cannot determine any scope.
var org *Organization
if user == nil {
org, err = getOrganization(cfg, client, domain, key)
if err != nil {
return nil, err
}
if org == nil {
// can't determine any scopes
for permission := range PermissionStrings {
info.unverifiedPermissions[permission] = struct{}{}
}
return info, nil
}
} else {
org = &user.Organization
}
// set the organization in the info struct
info.organization = org
// read in scopes
scopesConfig, err := readInScopesConfig()
if err != nil {
return nil, err
}
// check organization permissions
organizationPermissions, err := checkOrganizationPermissions(cfg, client, domain, key, scopesConfig, org)
if err != nil {
return nil, err
}
// check general permissions
generalOrganizationPermissions, err := checkGeneralPermissions(cfg, client, domain, key, scopesConfig)
if err != nil {
return nil, err
}
// merge general permissions with organization permissions
info.orgPermissions = organizationPermissions
info.orgPermissions = append(info.orgPermissions, generalOrganizationPermissions...)
// check project permissions
projectPermissions, err := checkProjectPermissions(cfg, client, domain, key, scopesConfig, org)
if err != nil {
return nil, err
}
info.projectPermissions = projectPermissions
return info, nil
}
func checkGeneralPermissions(cfg *config.Config, client *http.Client, domain, key string, scopesConfig *ScopesConfig) ([]Permission, error) {
return checkPermissions(cfg, client, domain, key, scopesConfig.GeneralScopes)
}
func checkOrganizationPermissions(
cfg *config.Config,
client *http.Client,
domain,
key string,
scopesConfig *ScopesConfig,
org *Organization,
) ([]Permission, error) {
return checkPermissions(cfg, client, domain, key, scopesConfig.OrganizationScopes, org.ID)
}
func checkProjectPermissions(
cfg *config.Config,
client *http.Client,
domain,
key string,
scopesConfig *ScopesConfig,
org *Organization,
) ([]ProjectPermissions, error) {
projectPermissions := make([]ProjectPermissions, 0)
for _, project := range org.Projects {
projectPermission := ProjectPermissions{
Project: &project,
}
permissions, err := checkPermissions(cfg, client, domain, key, scopesConfig.ProjectScopes, project.ID)
if err != nil {
return nil, err
}
projectPermission.Permissions = permissions
projectPermissions = append(projectPermissions, projectPermission)
}
return projectPermissions, nil
}
type User struct {
UUID string `json:"uuid"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
Organization Organization `json:"organization"`
}
type Organization struct {
ID string `json:"id"`
Name string `json:"name"`
Projects []Project `json:"projects"`
}
type Project struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
// resolves the domain and user (if permission exists) by calling the /users/@me method for both US and EU domains
// if the response is 200 OK, it means the domain is valid and user:read permission is also there
// if the response is 403 Forbidden, it means the domain is valid but user:read permission is not there
// if the response is 401 Unauthorized, it means the domain is invalid
func resolveDomainAndUser(cfg *config.Config, client *http.Client, key string) (string, *User, error) {
domains := []string{USDomain, EUDomain}
for _, domain := range domains {
req, err := http.NewRequest(http.MethodGet, domain+"/api/users/@me/", nil)
if err != nil {
return "", nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
// Execute HTTP Request
resp, err := client.Do(req)
if err != nil {
return "", nil, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
// domain is valid and user permission also exists
var userInfo User
if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
return "", nil, err
}
return domain, &userInfo, nil
case http.StatusForbidden:
// domain is valid but user permission does not exist
return domain, nil, nil
case http.StatusUnauthorized:
// Key might not be valid of this domain
// Try the other domain
continue
default:
// unexpected status code
return "", nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
}
return "", nil, fmt.Errorf("invalid Posthog API key")
}
func getOrganization(cfg *config.Config, client *http.Client, domain string, key string) (*Organization, error) {
req, err := http.NewRequest(http.MethodGet, domain+"/api/organizations/@current/", nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
// Execute HTTP Request
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
var org Organization
if err := json.NewDecoder(resp.Body).Decode(&org); err != nil {
return nil, err
}
return &org, nil
case http.StatusForbidden:
return nil, nil
default:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
}
func printUser(user User) {
color.Yellow("\n[i] User Info:")
color.Green("[i] Name: %s %s", user.FirstName, user.LastName)
color.Green("[i] Email: %s", user.Email)
color.Green("[i] ID: %s", user.UUID)
}
func printOrganizationPermissions(organization Organization, permissions []Permission) {
color.Yellow("\n[i] Organization Permissions:")
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Organization", "Permission"})
permissionsString := make([]string, len(permissions))
for i, permission := range permissions {
permissionsString[i], _ = permission.ToString()
}
t.AppendRow(table.Row{
color.GreenString(organization.Name),
color.GreenString(strings.Join(permissionsString, "\n")),
})
t.Render()
}
func printProjectPermissions(projectPermissions []ProjectPermissions) {
color.Yellow("\n[i] Project Permissions:")
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Project", "Permission"})
for _, projectPermission := range projectPermissions {
permissionsString := make([]string, len(projectPermission.Permissions))
for i, permission := range projectPermission.Permissions {
permissionsString[i], _ = permission.ToString()
}
t.AppendRow(table.Row{
color.GreenString(projectPermission.Project.Name),
color.GreenString(strings.Join(permissionsString, "\n")),
})
}
t.Render()
}
func printUnverifiedPermissions(permissions map[Permission]struct{}) {
color.Yellow("\n[i] Unverified Permissions:")
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Permission"})
for permission := range permissions {
permissionStr, _ := permission.ToString()
t.AppendRow(table.Row{color.YellowString(permissionStr)})
}
t.Render()
}
@@ -0,0 +1,100 @@
package posthog
import (
_ "embed"
"encoding/json"
"sort"
"testing"
"time"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/config"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
//go:embed expected_output.json
var expectedOutput []byte
func TestAnalyzer_Analyze(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
tests := []struct {
name string
key string
want string // JSON string
wantErr bool
}{
{
name: "valid posthog api key",
key: testSecrets.MustGetField("POSTHOG_API_KEY"),
want: string(expectedOutput),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := Analyzer{Cfg: &config.Config{}}
got, err := a.Analyze(ctx, map[string]string{"key": tt.key})
if (err != nil) != tt.wantErr {
t.Errorf("Analyzer.Analyze() error = %v, wantErr %v", err, tt.wantErr)
return
}
// bindings need to be in the same order to be comparable
sortBindings(got.Bindings)
// Marshal the actual result to JSON
gotJSON, err := json.Marshal(got)
if err != nil {
t.Fatalf("could not marshal got to JSON: %s", err)
}
// Parse the expected JSON string
var wantObj analyzers.AnalyzerResult
if err := json.Unmarshal([]byte(tt.want), &wantObj); err != nil {
t.Fatalf("could not unmarshal want JSON string: %s", err)
}
// bindings need to be in the same order to be comparable
sortBindings(wantObj.Bindings)
// Marshal the expected result to JSON (to normalize)
wantJSON, err := json.Marshal(wantObj)
if err != nil {
t.Fatalf("could not marshal want to JSON: %s", err)
}
// Compare the JSON strings
if string(gotJSON) != string(wantJSON) {
// Pretty-print both JSON strings for easier comparison
var gotIndented, wantIndented []byte
gotIndented, err = json.MarshalIndent(got, "", " ")
if err != nil {
t.Fatalf("could not marshal got to indented JSON: %s", err)
}
wantIndented, err = json.MarshalIndent(wantObj, "", " ")
if err != nil {
t.Fatalf("could not marshal want to indented JSON: %s", err)
}
t.Errorf("Analyzer.Analyze() = %s, want %s", gotIndented, wantIndented)
}
})
}
}
// Helper function to sort bindings
func sortBindings(bindings []analyzers.Binding) {
sort.SliceStable(bindings, func(i, j int) bool {
if bindings[i].Resource.Name == bindings[j].Resource.Name {
return bindings[i].Permission.Value < bindings[j].Permission.Value
}
return bindings[i].Resource.Name < bindings[j].Resource.Name
})
}
+668
View File
@@ -0,0 +1,668 @@
{
"general_scopes": [
{
"name": "organization",
"test": {
"read": {
"endpoint": "/api/organizations",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/organizations",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
}
],
"organization_scopes": [
{
"name": "batch_export",
"test": {
"read": {
"endpoint": "/api/organizations/%s/batch_exports",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/organizations/%s/batch_exports",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "organization_member",
"test": {
"read": {
"endpoint": "/api/organizations/%s/members",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/organizations/%s/members/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
500
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "project",
"test": {
"read": {
"endpoint": "/api/organizations/%s/projects",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/organizations/%s/projects/`nowaythiscanexist",
"method": "DELETE",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
}
],
"project_scopes": [
{
"name": "action",
"test": {
"read": {
"endpoint": "/api/projects/%d/actions",
"method": "GET",
"valid_status_code": [200],
"invalid_status_code": [403]
},
"write": {
"endpoint": "/api/projects/%d/actions",
"method": "POST",
"valid_status_code": [500],
"invalid_status_code": [403]
}
}
},
{
"name": "activity_log",
"test": {
"read": {
"endpoint": "/api/projects/%d/activity_log",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/activity_log",
"method": "POST",
"valid_status_code": [
500
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "annotation",
"test": {
"read": {
"endpoint": "/api/projects/%d/annotations",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/annotations/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "cohort",
"test": {
"read": {
"endpoint": "/api/projects/%d/cohorts",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/cohorts/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "dashboard",
"test": {
"read": {
"endpoint": "/api/projects/%d/dashboards",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/dashboards/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
500
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "dashboard_template",
"test": {
"read": {
"endpoint": "/api/projects/%d/dashboard_templates",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/dashboard_templates/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "early_access_feature",
"test": {
"read": {
"endpoint": "/api/projects/%d/early_access_feature",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/early_access_feature",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "event_definition",
"test": {
"read": {
"endpoint": "/api/projects/%d/event_definitions",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/event_definitions/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
500
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "experiment",
"test": {
"read": {
"endpoint": "/api/projects/%d/experiments",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/experiments",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "export",
"test": {
"read": {
"endpoint": "/api/projects/%d/exports",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/exports",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "feature_flag",
"test": {
"read": {
"endpoint": "/api/projects/%d/feature_flags",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/feature_flags",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "group",
"test": {
"read": {
"endpoint": "/api/projects/%d/groups",
"method": "GET",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/groups/update_property",
"method": "POST",
"valid_status_code": [
500
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "hog_function",
"test": {
"read": {
"endpoint": "/api/projects/%d/hog_functions",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/hog_functions/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "insight",
"test": {
"read": {
"endpoint": "/api/projects/%d/insights",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/insights/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "notebook",
"test": {
"read": {
"endpoint": "/api/projects/%d/notebooks",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/notebooks/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "person",
"test": {
"read": {
"endpoint": "/api/projects/%d/persons",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/persons/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "plugin",
"test": {
"read": {
"endpoint": "/api/projects/%d/plugin_configs",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/plugin_configs",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "property_definition",
"test": {
"read": {
"endpoint": "/api/projects/%d/property_definitions",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/property_definitions/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
500
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "query",
"test": {
"read": {
"endpoint": "/api/projects/%d/query/`nowaythiscanexist",
"method": "GET",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "session_recording",
"test": {
"read": {
"endpoint": "/api/projects/%d/session_recordings",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/session_recordings/`nowaythisexists",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "session_recording_playlist",
"test": {
"read": {
"endpoint": "/api/projects/%d/session_recording_playlists",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/session_recording_playlists/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "subscription",
"test": {
"read": {
"endpoint": "/api/projects/%d/subscriptions",
"method": "GET",
"valid_status_code": [
200,
402
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/subscriptions/`nowaythiscanexist",
"method": "PATCH",
"valid_status_code": [
402,
404
],
"invalid_status_code": [
403
]
}
}
},
{
"name": "survey",
"test": {
"read": {
"endpoint": "/api/projects/%d/surveys",
"method": "GET",
"valid_status_code": [
200
],
"invalid_status_code": [
403
]
},
"write": {
"endpoint": "/api/projects/%d/surveys",
"method": "POST",
"valid_status_code": [
400
],
"invalid_status_code": [
403
]
}
}
}
]
}
+3
View File
@@ -34,6 +34,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/plaid"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/planetscale"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/postgres"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/posthog"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/postman"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/privatekey"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/sendgrid"
@@ -136,5 +137,7 @@ func Run(keyType string, secretInfo SecretInfo) {
ngrok.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"])
case "mux":
mux.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"], secretInfo.Parts["secret"])
case "posthog":
posthog.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"])
}
}
+8 -1
View File
@@ -2,10 +2,11 @@ package posthog
import (
"context"
regexp "github.com/wasilibs/go-re2"
"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"
@@ -58,6 +59,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
defer res.Body.Close()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
s1.AnalysisInfo = map[string]string{
"key": resMatch,
}
} else if res.StatusCode == 401 {
// Try EU Endpoint only if other one fails.
res, err := client.Do(reqEU)
@@ -65,6 +69,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
defer res.Body.Close()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
s1.AnalysisInfo = map[string]string{
"key": resMatch,
}
}
}
}