[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:
@@ -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
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
@@ -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"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user