diff --git a/plugins/confluence.go b/plugins/confluence.go index de0d14b3..29bf4e91 100644 --- a/plugins/confluence.go +++ b/plugins/confluence.go @@ -262,7 +262,8 @@ func (p *ConfluencePlugin) getItem(page ConfluencePage, space ConfluenceSpaceRes content := &Item{ Content: pageContent.Body.Storage.Value, - ID: pageContent.Links["base"] + pageContent.Links["webui"], + ID: fmt.Sprintf("%s-%s-%s-%s", p.GetName(), p.URL, space.Key, page.ID), + Source: pageContent.Links["base"] + pageContent.Links["webui"], } return content, pageContent.History.PreviousVersion.Number, nil } diff --git a/plugins/discord.go b/plugins/discord.go index 22ba964d..6f6e5e12 100644 --- a/plugins/discord.go +++ b/plugins/discord.go @@ -216,7 +216,7 @@ func (p *DiscordPlugin) readChannelMessages(channel *discordgo.Channel) { } channelLogger.Info().Msgf("Found %d messages", len(messages)) - items := convertMessagesToItems(channel.GuildID, &messages) + items := convertMessagesToItems(p.GetName(), channel.GuildID, &messages) for _, item := range *items { p.itemChan <- item } @@ -273,12 +273,13 @@ func (p *DiscordPlugin) getMessages(channelID string, logger zerolog.Logger) ([] return append(messages, threadMessages...), nil } -func convertMessagesToItems(guildId string, messages *[]*discordgo.Message) *[]Item { +func convertMessagesToItems(pluginName, guildId string, messages *[]*discordgo.Message) *[]Item { items := []Item{} for _, message := range *messages { items = append(items, Item{ Content: message.Content, - ID: fmt.Sprintf("https://discord.com/channels/%s/%s/%s", guildId, message.ChannelID, message.ID), + ID: fmt.Sprintf("%s-%s-%s-%s", pluginName, guildId, message.ChannelID, message.ID), + Source: fmt.Sprintf("https://discord.com/channels/%s/%s/%s", guildId, message.ChannelID, message.ID), }) } return &items diff --git a/plugins/filesystem.go b/plugins/filesystem.go index a5b29a5c..3f4a15ad 100644 --- a/plugins/filesystem.go +++ b/plugins/filesystem.go @@ -10,15 +10,19 @@ import ( "github.com/spf13/cobra" ) -const flagFolder = "path" -const flagIgnored = "ignore" +const ( + flagFolder = "path" + flagProjectName = "project-name" + flagIgnored = "ignore" +) var ignoredFolders = []string{".git"} type FileSystemPlugin struct { Plugin - Path string - Ignored []string + Path string + ProjectName string + Ignored []string } func (p *FileSystemPlugin) GetName() string { @@ -46,6 +50,7 @@ func (p *FileSystemPlugin) DefineCommand(channels Channels) (*cobra.Command, err } flags.StringSliceVar(&p.Ignored, flagIgnored, []string{}, "Patterns to ignore") + flags.StringVar(&p.ProjectName, flagProjectName, "", "Project name to differentiate between filesystem scans") return cmd, nil } @@ -112,7 +117,8 @@ func (p *FileSystemPlugin) getItem(wg *sync.WaitGroup, filePath string) (*Item, content := &Item{ Content: string(b), - ID: filePath, + ID: fmt.Sprintf("%s-%s-%s", p.GetName(), p.ProjectName, filePath), + Source: filePath, } return content, nil } diff --git a/plugins/git.go b/plugins/git.go index 608b8380..7dcd3adf 100644 --- a/plugins/git.go +++ b/plugins/git.go @@ -14,6 +14,7 @@ import ( const ( argDepth = "depth" argScanAllBranches = "all-branches" + argProjectName = "project-name" ) type GitPlugin struct { @@ -21,6 +22,7 @@ type GitPlugin struct { Channels depth int scanAllBranches bool + projectName string } func (p *GitPlugin) GetName() string { @@ -37,12 +39,13 @@ func (p *GitPlugin) DefineCommand(channels Channels) (*cobra.Command, error) { Args: cobra.MatchAll(cobra.ExactArgs(1), validGitRepoArgs), Run: func(cmd *cobra.Command, args []string) { log.Info().Msg("Git plugin started") - scanGit(args[0], p.buildScanOptions(), channels.Items, channels.Errors) + p.scanGit(args[0], p.buildScanOptions(), channels.Items, channels.Errors) }, } flags := command.Flags() flags.BoolVar(&p.scanAllBranches, argScanAllBranches, false, "scan all branches [default: false]") flags.IntVar(&p.depth, argDepth, 0, "number of commits to scan from HEAD") + flags.StringVar(&p.projectName, argProjectName, "", "Project name to differentiate between filesystem scans") return command, nil } @@ -57,7 +60,7 @@ func (p *GitPlugin) buildScanOptions() string { return strings.Join(options, " ") } -func scanGit(path string, scanOptions string, itemsChan chan Item, errChan chan error) { +func (p *GitPlugin) scanGit(path string, scanOptions string, itemsChan chan Item, errChan chan error) { fileChan, err := git.GitLog(path, scanOptions) if err != nil { errChan <- fmt.Errorf("error while scanning git repository: %w", err) @@ -80,7 +83,8 @@ func scanGit(path string, scanOptions string, itemsChan chan Item, errChan chan if fileChanges != "" { itemsChan <- Item{ Content: fileChanges, - ID: fmt.Sprintf("git show %s:%s", file.PatchHeader.SHA, file.NewName), + ID: fmt.Sprintf("%s-%s-%s-%s", p.GetName(), p.projectName, file.PatchHeader.SHA, file.NewName), + Source: fmt.Sprintf("git show %s:%s", file.PatchHeader.SHA, file.NewName), } } } diff --git a/plugins/paligo.go b/plugins/paligo.go index 8f8eb0d8..9503a80e 100644 --- a/plugins/paligo.go +++ b/plugins/paligo.go @@ -181,7 +181,8 @@ func (p *PaligoPlugin) handleComponent(item PaligoItem) { p.Items <- Item{ Content: document.Content, - ID: url, + ID: fmt.Sprintf("%s-%s-%d", p.GetName(), p.paligoApi.Instance, document.ID), + Source: url, } } diff --git a/plugins/plugins.go b/plugins/plugins.go index 992d58b1..a22e2f67 100644 --- a/plugins/plugins.go +++ b/plugins/plugins.go @@ -8,8 +8,10 @@ import ( type Item struct { Content string - // Unique identifier of the item (page, document, file) with user-friendly content (e.g. URL, file path) + // Unique identifier of the item ID string + // User friendly description and/or link to the item + Source string } type Plugin struct { diff --git a/plugins/slack.go b/plugins/slack.go index cf660908..d3862bd5 100644 --- a/plugins/slack.go +++ b/plugins/slack.go @@ -125,7 +125,8 @@ func (p *SlackPlugin) getItemsFromChannel(slackApi *slack.Client, channel slack. } p.Items <- Item{ Content: message.Text, - ID: url, + ID: fmt.Sprintf("%s-%s-%s", p.GetName(), channel.ID, message.Timestamp), + Source: url, } } counter++ diff --git a/reporting/report.go b/reporting/report.go index 1d4771c2..8fa0bf55 100644 --- a/reporting/report.go +++ b/reporting/report.go @@ -2,10 +2,11 @@ package reporting import ( "fmt" - "github.com/checkmarx/2ms/config" "os" "path/filepath" "strings" + + "github.com/checkmarx/2ms/config" ) const ( @@ -23,7 +24,7 @@ type Report struct { type Secret struct { ID string `json:"id"` Source string `json:"source"` - Description string `json:"description"` + RuleID string `json:"ruleId"` StartLine int `json:"startLine"` EndLine int `json:"endLine"` StartColumn int `json:"startColumn"` diff --git a/reporting/report_test.go b/reporting/report_test.go index c05cdbe4..44a08423 100644 --- a/reporting/report_test.go +++ b/reporting/report_test.go @@ -25,7 +25,7 @@ JPcHeO7M6FohKgcEHX84koQDN98J/L7pFlSoU7WOl6f8BKavIdeSTPS9qQYWdQuT results := map[string][]Secret{} report := Report{len(results), 1, results} - secret := Secret{Description: "bla", StartLine: 0, StartColumn: 0, EndLine: 0, EndColumn: 0, Value: secretValue} + secret := Secret{Source: "bla", StartLine: 0, StartColumn: 0, EndLine: 0, EndColumn: 0, Value: secretValue} source := "directory\\rawStringAsFile.txt" report.Results[source] = append(report.Results[source], secret) diff --git a/reporting/sarif.go b/reporting/sarif.go index e2357a91..742a9e4d 100644 --- a/reporting/sarif.go +++ b/reporting/sarif.go @@ -3,8 +3,9 @@ package reporting import ( "encoding/json" "fmt" - "github.com/checkmarx/2ms/config" "log" + + "github.com/checkmarx/2ms/config" ) func writeSarif(report Report, cfg *config.Config) string { @@ -47,7 +48,7 @@ func hasNoResults(report Report) bool { } func messageText(secret Secret) string { - return fmt.Sprintf("%s has detected secret for file %s.", secret.Description, secret.ID) + return fmt.Sprintf("%s has detected secret for file %s.", secret.Source, secret.ID) } func getResults(report Report) []Results { @@ -65,7 +66,7 @@ func getResults(report Report) []Results { Message: Message{ Text: messageText(secret), }, - RuleId: secret.Description, + RuleId: secret.Source, Locations: getLocation(secret), } results = append(results, r) diff --git a/secrets/secrets.go b/secrets/secrets.go index 399fa57f..d5b0e452 100644 --- a/secrets/secrets.go +++ b/secrets/secrets.go @@ -1,9 +1,9 @@ package secrets import ( + "crypto/sha1" "fmt" "os" - "path/filepath" "regexp" "strings" "sync" @@ -15,6 +15,7 @@ import ( "github.com/zricethezav/gitleaks/v8/cmd/generate/config/rules" "github.com/zricethezav/gitleaks/v8/config" "github.com/zricethezav/gitleaks/v8/detect" + "github.com/zricethezav/gitleaks/v8/report" ) type Secrets struct { @@ -90,8 +91,17 @@ func (s *Secrets) Detect(secretsChannel chan reporting.Secret, item plugins.Item Raw: item.Content, } for _, value := range s.detector.Detect(fragment) { - itemId := getItemId(item.ID) - secretsChannel <- reporting.Secret{ID: itemId, Source: item.ID, Description: value.Description, StartLine: value.StartLine, StartColumn: value.StartColumn, EndLine: value.EndLine, EndColumn: value.EndColumn, Value: value.Secret} + itemId := getFindingId(item, value) + secretsChannel <- reporting.Secret{ + ID: itemId, + Source: item.Source, + RuleID: value.RuleID, + StartLine: value.StartLine, + StartColumn: value.StartColumn, + EndLine: value.EndLine, + EndColumn: value.EndColumn, + Value: value.Secret, + } } } @@ -112,16 +122,10 @@ func (s *Secrets) AddRegexRules(patterns []string) error { return nil } -func getItemId(fullPath string) string { - var itemId string - if strings.Contains(fullPath, "/") { - itemLinkStrings := strings.Split(fullPath, "/") - itemId = itemLinkStrings[len(itemLinkStrings)-1] - } - if strings.Contains(fullPath, "\\") { - itemId = filepath.Base(fullPath) - } - return itemId +func getFindingId(item plugins.Item, finding report.Finding) string { + idParts := []string{item.ID, finding.RuleID, finding.Secret} + sha := sha1.Sum([]byte(strings.Join(idParts, "-"))) + return fmt.Sprintf("%x", sha) } func selectRules(allRules []Rule, tags []string) map[string]config.Rule {