Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LaunchDarkly Token Analyzer #3948

Merged
Merged
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
36e64af
initial commit
kashifkhan0771 Feb 21, 2025
1c92298
initial commit
kashifkhan0771 Feb 24, 2025
f143fa8
initial commit
kashifkhan0771 Feb 25, 2025
b840782
inital commit
kashifkhan0771 Feb 26, 2025
aab2c0a
initial working structure for launchdarkly analyzer
kashifkhan0771 Feb 27, 2025
9806026
added more apis
kashifkhan0771 Mar 4, 2025
cc2ac00
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 4, 2025
8ff00ae
added test cases
kashifkhan0771 Mar 4, 2025
745868e
removed imposter print statement
kashifkhan0771 Mar 4, 2025
6d3b8fa
updated some code
kashifkhan0771 Mar 4, 2025
6902b03
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 5, 2025
746e4fb
removed id from printResources
kashifkhan0771 Mar 5, 2025
8452bdd
added nabeel suggestion and set analysis info
kashifkhan0771 Mar 5, 2025
d445b25
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 5, 2025
25356be
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 6, 2025
4235470
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 6, 2025
143947a
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 7, 2025
8a0561a
resolved ahrav comments
kashifkhan0771 Mar 7, 2025
deccdd9
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 7, 2025
8d6265f
Merge branch 'main' into feat/oss-95-launchdarkly-analyzer
kashifkhan0771 Mar 10, 2025
08cdc2f
resolved ahrav comments
kashifkhan0771 Mar 10, 2025
47cdd0c
implemented ahrav's suggestion 🔥
kashifkhan0771 Mar 10, 2025
1c61fce
resolved linter error
kashifkhan0771 Mar 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
initial commit
  • Loading branch information
kashifkhan0771 committed Feb 24, 2025
commit 36e64afc3e8c0bf31a733da914ee89799ce08c8b
54 changes: 28 additions & 26 deletions pkg/analyzer/analyzers/analyzers.go
Original file line number Diff line number Diff line change
@@ -68,6 +68,7 @@ const (
AnalyzerTypeGitHub
AnalyzerTypeGitLab
AnalyzerTypeHuggingFace
AnalyzerTypeLaunchDarkly
AnalyzerTypeMailchimp
AnalyzerTypeMailgun
AnalyzerTypeMySQL
@@ -90,32 +91,33 @@ const (

// analyzerTypeStrings maps the enum to its string representation.
var analyzerTypeStrings = map[AnalyzerType]string{
AnalyzerTypeInvalid: "Invalid",
AnalyzerTypeAirbrake: "Airbrake",
AnalyzerTypeAirtable: "Airtable",
AnalyzerAnthropic: "Anthropic",
AnalyzerTypeAsana: "Asana",
AnalyzerTypeBitbucket: "Bitbucket",
AnalyzerTypeDockerHub: "DockerHub",
AnalyzerTypeGitHub: "GitHub",
AnalyzerTypeGitLab: "GitLab",
AnalyzerTypeHuggingFace: "HuggingFace",
AnalyzerTypeMailchimp: "Mailchimp",
AnalyzerTypeMailgun: "Mailgun",
AnalyzerTypeMySQL: "MySQL",
AnalyzerTypeOpenAI: "OpenAI",
AnalyzerTypeOpsgenie: "Opsgenie",
AnalyzerTypePostgres: "Postgres",
AnalyzerTypePostman: "Postman",
AnalyzerTypeSendgrid: "Sendgrid",
AnalyzerTypeShopify: "Shopify",
AnalyzerTypeSlack: "Slack",
AnalyzerTypeSourcegraph: "Sourcegraph",
AnalyzerTypeSquare: "Square",
AnalyzerTypeStripe: "Stripe",
AnalyzerTypeTwilio: "Twilio",
AnalyzerTypePrivateKey: "PrivateKey",
AnalyzerTypeNotion: "Notion",
AnalyzerTypeInvalid: "Invalid",
AnalyzerTypeAirbrake: "Airbrake",
AnalyzerTypeAirtable: "Airtable",
AnalyzerAnthropic: "Anthropic",
AnalyzerTypeAsana: "Asana",
AnalyzerTypeBitbucket: "Bitbucket",
AnalyzerTypeDockerHub: "DockerHub",
AnalyzerTypeGitHub: "GitHub",
AnalyzerTypeGitLab: "GitLab",
AnalyzerTypeHuggingFace: "HuggingFace",
AnalyzerTypeLaunchDarkly: "LaunchDarkly",
AnalyzerTypeMailchimp: "Mailchimp",
AnalyzerTypeMailgun: "Mailgun",
AnalyzerTypeMySQL: "MySQL",
AnalyzerTypeOpenAI: "OpenAI",
AnalyzerTypeOpsgenie: "Opsgenie",
AnalyzerTypePostgres: "Postgres",
AnalyzerTypePostman: "Postman",
AnalyzerTypeSendgrid: "Sendgrid",
AnalyzerTypeShopify: "Shopify",
AnalyzerTypeSlack: "Slack",
AnalyzerTypeSourcegraph: "Sourcegraph",
AnalyzerTypeSquare: "Square",
AnalyzerTypeStripe: "Stripe",
AnalyzerTypeTwilio: "Twilio",
AnalyzerTypePrivateKey: "PrivateKey",
AnalyzerTypeNotion: "Notion",
// Add new mappings here
}

22 changes: 22 additions & 0 deletions pkg/analyzer/analyzers/launchdarkly/api_endpoints.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
{
"name": "caller-identity",
"endpoint": "/v2/caller-identity",
"valid_status_code": 200,
"invalid_status_codes": [
401,
429
],
"permission": ""
},
{
"name": "token",
"endpoint": "/v2/token/%s",
"valid_status_code": 200,
"invalid_status_codes": [
401,
429
],
"permission": ""
}
]
31 changes: 31 additions & 0 deletions pkg/analyzer/analyzers/launchdarkly/launchdarkly.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package launchdarkly

import (
"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)

type Analyzer struct {
Cfg *config.Config
}

func (a Analyzer) Type() analyzers.AnalyzerType {
return analyzers.AnalyzerTypeLaunchDarkly
}

func (a Analyzer) Analyze(_ context.Context, credInfo map[string]string) (*analyzers.AnalyzerResult, error) {
return nil, nil
}

// AnalyzePermissions will collect all the scopes assigned to token along with resource it can access
func AnalyzePermissions(cfg *config.Config, token string) (*SecretInfo, error) {
// create the http client
client := analyzers.NewAnalyzeClient(cfg)

var secretInfo = &SecretInfo{}

return secretInfo, nil
}
117 changes: 117 additions & 0 deletions pkg/analyzer/analyzers/launchdarkly/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package launchdarkly

import "sync"

type SecretInfo struct {
CallerIdentity CallerIdentity
Permissions []string
Resources []Resource
// to concurrently read and write
mu sync.RWMutex
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I've found it helpful to use mu almost like a "hat" for the resource(s) it protects by placing it directly above them. This makes it visually clear what it's guarding and can even replace the need for a comment.

That said, I’m not entirely sure which resources mu is protecting in this case. 🤔 This is definitely an opinionated take, so feel free to ignore it! 🤣

I forget where I first read about this, but here is a separate article about it. 😅

Ex:

type SecretInfo struct {
    ...
    mu sync.RWMutex <-- see, like a "hat" for "Resources"
    Resources []Resource
}

}

// CallerIdentity is the information about the token
type CallerIdentity struct {
AccountID string // account id. It is the owner id of token as well
MemberID string
Name string
Role string // role of caller
Email string
Token struct {
ID string // id of the token
Name string // name of the token
CustomRoles []CustomRole // custom roles assigned to the token
InlineRole []Policy // any policy statements maybe used in place of a built-in custom role
Role string // role of token
IsServiceToken bool // is a service token or not
APIVersion int // default api version assigned to the token
}
}

// CustomRole is a flexible policies providing fine-grained access control to everything in launch darkly
type CustomRole struct {
ID string
Key string
Name string
Polices []Policy
BasePermission string
AssignedToMembers int
AssignedToTeams int
}

// policy is a set of statements
type Policy struct {
Resources []string
NotResources []string
Actions []string
NotActions []string
Effect string
}

type Resource struct {
ID string
Name string
Permission string
Type string
ParentResource *Resource
MetaData map[string]string
}

// addPermission add a new permission to secret info permissions list.
func (s *SecretInfo) addPermission(perm string) {
s.mu.Lock()
defer s.mu.Unlock()

s.Permissions = append(s.Permissions, perm)
}

// hasPermission checks if a particular permission exist in secret info permissions list.
func (s *SecretInfo) hasPermission(perm string) bool {
s.mu.Lock()
defer s.mu.Unlock()

for _, permission := range s.Permissions {
if permission == perm {
return true
}
}

return false
}

// appendResources append resource to secret info resources list
func (s *SecretInfo) appendResource(resource Resource) {
s.mu.Lock()
defer s.mu.Unlock()

s.Resources = append(s.Resources, resource)
}

// hasCustomRoles check if token has any custom roles assigned
func (c CallerIdentity) hasCustomRoles() bool {
return len(c.Token.CustomRoles) > 0
}

// hasInlineRole check if token has any inline roles
func (c CallerIdentity) hasInlineRole() bool {
return len(c.Token.InlineRole) > 0
}

// isAllowed check if policy allow the statement
func (p Policy) isAllowed() bool {
return p.Effect == "allow"
}

// setParentResource set parent resource for a resource
func (r Resource) setParentResource(resource, parent *Resource) {
resource.ParentResource = parent
}

// updateResourceMetadata
func (r Resource) updateResourceMetadata(key, value string) {
if r.MetaData == nil {
r.MetaData = make(map[string]string)
}

r.MetaData[key] = value
}
Loading
Oops, something went wrong.