Skip to content

Commit

Permalink
feat: add path flag for ignore file (#1213)
Browse files Browse the repository at this point in the history
* feat: add path flag for ignore file

* fix: improve migration output

* refactor: clean up and test ignore util

refactor: move display methods to command file

* fix: improve bearer ignore show

* docs: update for bearer-ignore-file flag

* fix: prefer pointers for optional fields

* docs: new line

---------

Co-authored-by: gotbadger <[email protected]>
  • Loading branch information
elsapet and gotbadger authored Aug 28, 2023
1 parent 7d3a1b4 commit d88b1de
Show file tree
Hide file tree
Showing 11 changed files with 324 additions and 139 deletions.
3 changes: 3 additions & 0 deletions docs/_data/bearer_ignore_add.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ options:
- name: author
shorthand: a
usage: Add author information to this ignored finding.
- name: bearer-ignore-file
default_value: bearer.ignore
usage: Load bearer.ignore file from the specified path.
- name: comment
usage: Add a comment to this ignored finding.
- name: force
Expand Down
3 changes: 3 additions & 0 deletions docs/_data/bearer_ignore_migrate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ synopsis: |
Migrate ignored fingerprints from bearer.yml to bearer.ignore
usage: ' ignore migrate [flags]'
options:
- name: bearer-ignore-file
default_value: bearer.ignore
usage: Load bearer.ignore file from the specified path.
- name: config-file
default_value: bearer.yml
usage: Load configuration from the specified path.
Expand Down
3 changes: 3 additions & 0 deletions docs/_data/bearer_ignore_show.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ name: ' ignore show'
synopsis: Show an ignored fingerprint
usage: ' ignore show <fingerprint> [flags]'
options:
- name: bearer-ignore-file
default_value: bearer.ignore
usage: Load bearer.ignore file from the specified path.
- name: help
shorthand: h
default_value: "false"
Expand Down
170 changes: 130 additions & 40 deletions pkg/commands/ignore.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package commands

import (
"errors"
"encoding/json"
"fmt"
"os"

"github.com/bearer/bearer/pkg/flag"
"github.com/bearer/bearer/pkg/util/file"
"github.com/bearer/bearer/pkg/util/ignore"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var migratedIgnoreComment = "migrated from bearer.yml"

var bold = color.New(color.Bold).SprintFunc()
var morePrefix = color.HiBlackString("├─ ")
var lastPrefix = color.HiBlackString("└─ ")

func NewIgnoreCommand() *cobra.Command {
usageTemplate := `
Usage: bearer ignore <command> [flags]
Expand Down Expand Up @@ -53,39 +60,60 @@ Examples:
}

func newIgnoreShowCommand() *cobra.Command {
return &cobra.Command{
var IgnoreShowFlags = &flag.Flags{
IgnoreFlagGroup: flag.NewIgnoreFlagGroup(),
}
cmd := &cobra.Command{
Use: "show <fingerprint>",
Short: "Show an ignored fingerprint",
Example: `# Show the details of an ignored fingerprint from your bearer.ignore file
$ bearer ignore show <fingerprint>`,
RunE: func(cmd *cobra.Command, args []string) error {
if err := IgnoreShowFlags.Bind(cmd); err != nil {
return fmt.Errorf("flag bind error: %w", err)
}

if len(args) == 0 {
return cmd.Help()
}

ignoredFingerprints, err := ignore.GetIgnoredFingerprints(nil)
options, err := IgnoreShowFlags.ToOptions(args)
if err != nil {
return fmt.Errorf("flag error: %s", err)
}

ignoredFingerprints, fileExists, err := ignore.GetIgnoredFingerprints(options.IgnoreOptions.BearerIgnoreFile)
if err != nil {
cmd.Printf("Issue loading ignored fingerprints from bearer.ignore file: %s", err)
return nil
}
if !fileExists {
cmd.Printf("bearer.ignore file not found. Perhaps you need to use --bearer-ignore-file to specify the path to bearer.ignore?\n")
return nil
}
fingerprintId := args[0]
selectedIgnoredFingerprint, ok := ignoredFingerprints[fingerprintId]
if !ok {
cmd.Printf("Ignored fingerprint '%s' was not found in bearer.ignore file\n", fingerprintId)
return nil
}
cmd.Print("\n")
cmd.Print(ignore.DisplayIgnoredEntryTextString(fingerprintId, selectedIgnoredFingerprint))
cmd.Print(displayIgnoredEntryTextString(fingerprintId, selectedIgnoredFingerprint))
cmd.Print("\n")
return nil
},
SilenceErrors: false,
SilenceUsage: false,
}
IgnoreShowFlags.AddFlags(cmd)
cmd.SetUsageTemplate(fmt.Sprintf(scanTemplate, IgnoreShowFlags.Usages(cmd)))

return cmd
}

func newIgnoreAddCommand() *cobra.Command {
var IgnoreAddFlags = &flag.Flags{
IgnoreFlagGroup: flag.NewIgnoreFlagGroup(),
IgnoreAddFlagGroup: flag.NewIgnoreAddFlagGroup(),
}
cmd := &cobra.Command{
Expand All @@ -107,26 +135,39 @@ $ bearer ignore add <fingerprint> --author Mish --comment "Possible false positi
return fmt.Errorf("flag error: %s", err)
}
fingerprintId := args[0]
fingerprintEntry := ignore.IgnoredFingerprint{
Author: options.IgnoreAddOptions.Author,
Comment: options.IgnoreAddOptions.Comment,
var fingerprintEntry ignore.IgnoredFingerprint
if options.IgnoreAddOptions.Author != "" {
fingerprintEntry.Author = &options.IgnoreAddOptions.Author
}
if options.IgnoreAddOptions.Comment != "" {
fingerprintEntry.Comment = &options.IgnoreAddOptions.Comment
}

fingerprintsToIgnore := map[string]ignore.IgnoredFingerprint{
fingerprintId: fingerprintEntry,
}

if err = ignore.AddToIgnoreFile(fingerprintsToIgnore, options.IgnoreAddOptions.Force); err != nil {
target := &ignore.DuplicateIgnoredFingerprintError{}
if errors.As(err, &target) {
// handle expected error (duplicate entry in bearer.ignore)
cmd.Printf("Error: %s\n", err.Error())
return nil
}
ignoredFingerprints, fileExists, err := ignore.GetIgnoredFingerprints(options.IgnoreOptions.BearerIgnoreFile)
if err != nil {
return fmt.Errorf("error retrieving existing ignores: %s", err)
}

if !fileExists {
cmd.Printf("\nCreating bearer.ignore file...\n")
}

if mergeErr := ignore.MergeIgnoredFingerprints(fingerprintsToIgnore, ignoredFingerprints, options.IgnoreAddOptions.Force); mergeErr != nil {
// handle expected error (duplicate entry in bearer.ignore)
cmd.Printf("Error: %s\n", mergeErr.Error())
return nil
}

if err := writeIgnoreFile(ignoredFingerprints, options.IgnoreOptions.BearerIgnoreFile); err != nil {
return err
}

cmd.Print("fingerprint added to bearer.ignore:\n\n")
cmd.Print(ignore.DisplayIgnoredEntryTextString(fingerprintId, fingerprintEntry))
cmd.Print("Fingerprint added to bearer.ignore:\n\n")
cmd.Print(displayIgnoredEntryTextString(fingerprintId, ignoredFingerprints[fingerprintId]))
cmd.Print("\n")
return nil
},
Expand All @@ -141,6 +182,7 @@ $ bearer ignore add <fingerprint> --author Mish --comment "Possible false positi

func newIgnoreMigrateCommand() *cobra.Command {
IgnoreMigrateFlags := &flag.Flags{
IgnoreFlagGroup: flag.NewIgnoreFlagGroup(),
IgnoreMigrateFlagGroup: flag.NewIgnoreMigrateFlagGroup(),
}
cmd := &cobra.Command{
Expand All @@ -157,25 +199,46 @@ $ bearer ignore migrate`,
if err != nil {
return fmt.Errorf("flag error: %s", err)
}
configFilePath := viper.GetString(flag.ConfigFileFlag.ConfigName)
fingerprintsToMigrate, err := getIgnoredFingerprintsFromConfig(configFilePath)
if err != nil {
return fmt.Errorf("error reading config: %s\nPerhaps you need to use --config-file to specify the config path?", err.Error())
}

ignoredFingerprintsFromConfig, err := getIgnoredFingerprintsFromConfig(args)
ignoredFingerprints, fileExists, err := ignore.GetIgnoredFingerprints(options.IgnoreOptions.BearerIgnoreFile)
if err != nil {
// handle expected error (duplicate entry in bearer.ignore)
cmd.Printf("%s\n", err.Error())
return nil
return fmt.Errorf("error retrieving existing ignores: %s", err)
}

migratedIgnoredCount := len(fingerprintsToMigrate)
skippedIgnoresToMigrate := ""
cmd.Printf("Found %d ignores in:\n\t%s\n", migratedIgnoredCount, configFilePath)

if !fileExists {
cmd.Printf("\nCreating bearer.ignore file...\n")
}

if err = ignore.AddToIgnoreFile(ignoredFingerprintsFromConfig, options.IgnoreMigrateOptions.Force); err != nil {
target := &ignore.DuplicateIgnoredFingerprintError{}
if errors.As(err, &target) {
// handle expected error (duplicate entry in bearer.ignore)
cmd.Printf("%s\n", err.Error())
return nil
if !options.IgnoreMigrateOptions.Force {
for key := range ignoredFingerprints {
if _, ok := fingerprintsToMigrate[key]; ok {
migratedIgnoredCount--
skippedIgnoresToMigrate += fmt.Sprintf("- %s\n", key)
delete(fingerprintsToMigrate, key)
}
}
return err
}

return nil
cmd.Printf("Added %d ignores to:\n\t%s\n", migratedIgnoredCount, options.IgnoreOptions.BearerIgnoreFile)

if skippedIgnoresToMigrate != "" {
cmd.Printf("\nThe following ignores already exist in the bearer.ignore file:\n")
cmd.Printf(skippedIgnoresToMigrate)
cmd.Printf("\nTo overwrite these entries, use --force\n")
}

// either no duplicate entries at this point or --force is true so we can ignore merge error
_ = ignore.MergeIgnoredFingerprints(fingerprintsToMigrate, ignoredFingerprints, options.IgnoreMigrateOptions.Force)
return writeIgnoreFile(ignoredFingerprints, options.IgnoreOptions.BearerIgnoreFile)
},
SilenceErrors: false,
SilenceUsage: false,
Expand All @@ -186,25 +249,52 @@ $ bearer ignore migrate`,
return cmd
}

func getIgnoredFingerprintsFromConfig(args []string) (ignoredFingerprintsFromConfig map[string]ignore.IgnoredFingerprint, err error) {
ignoredFingerprintsFromConfig = make(map[string]ignore.IgnoredFingerprint)

configPath := viper.GetString(flag.ConfigFileFlag.ConfigName)
var defaultConfigPath = ""
if len(args) > 0 {
defaultConfigPath = file.GetFullFilename(args[0], configPath)
func writeIgnoreFile(ignoredFingerprints map[string]ignore.IgnoredFingerprint, bearerIgnoreFilePath string) error {
data, err := json.MarshalIndent(ignoredFingerprints, "", " ")
if err != nil {
// failed to marshall data
return err
}

return os.WriteFile(bearerIgnoreFilePath, data, 0644)
}

func getIgnoredFingerprintsFromConfig(configPath string) (ignoredFingerprintsFromConfig map[string]ignore.IgnoredFingerprint, err error) {
ignoredFingerprintsFromConfig = make(map[string]ignore.IgnoredFingerprint)

if err := readConfig(configPath); err != nil {
if err := readConfig(defaultConfigPath); err != nil {
return ignoredFingerprintsFromConfig, err
}
return ignoredFingerprintsFromConfig, err
}

for _, fingerprint := range viper.GetStringSlice("report.exclude-fingerprint") {
ignoredFingerprintsFromConfig[fingerprint] = ignore.IgnoredFingerprint{}
ignoredFingerprintsFromConfig[fingerprint] = ignore.IgnoredFingerprint{
Comment: &migratedIgnoreComment,
}
}

return ignoredFingerprintsFromConfig, nil
}

func displayIgnoredEntryTextString(fingerprintId string, entry ignore.IgnoredFingerprint) string {
prefix := morePrefix
result := fmt.Sprintf(bold(color.HiBlueString("%s \n")), fingerprintId)

if entry.Author == nil && entry.Comment == nil {
prefix = lastPrefix
}
result += fmt.Sprintf("%sIgnored At: %s\n", prefix, bold(entry.IgnoredAt))

if entry.Author != nil {
if entry.Comment == nil {
prefix = lastPrefix
}

result += fmt.Sprintf("%sAuthor: %s\n", prefix, bold(*entry.Author))
}

if entry.Comment != nil {
result += fmt.Sprintf("%sComment: %s\n", lastPrefix, bold(*entry.Comment))
}

return result
}
2 changes: 1 addition & 1 deletion pkg/commands/process/settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ func FromOptions(opts flag.Options, foundLanguages []string) (Config, error) {
}
}

ignoredFingerprints, err := ignore.GetIgnoredFingerprints(&opts.ScanOptions.Target)
ignoredFingerprints, _, err := ignore.GetIgnoredFingerprints(opts.IgnoreOptions.BearerIgnoreFile)
if err != nil {
return Config{}, err
}
Expand Down
1 change: 1 addition & 0 deletions pkg/flag/general_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var (

type GeneralFlagGroup struct {
ConfigFile *Flag
BearerIgnoreFile *Flag
APIKey *Flag
Host *Flag
DisableVersionCheck *Flag
Expand Down
41 changes: 41 additions & 0 deletions pkg/flag/ignore_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package flag

var (
BearerIgnoreFileFlag = Flag{
Name: "bearer-ignore-file",
ConfigName: "bearer-ignore-file",
Value: "bearer.ignore",
Usage: "Load bearer.ignore file from the specified path.",
DisableInConfig: true,
}
)

type IgnoreFlagGroup struct {
BearerIgnoreFileFlag *Flag
}

type IgnoreOptions struct {
BearerIgnoreFile string `mapstructure:"ignore_bearer_ignore_file" json:"ignore_bearer_ignore_file" yaml:"ignore_bearer_ignore_file"`
}

func NewIgnoreFlagGroup() *IgnoreFlagGroup {
return &IgnoreFlagGroup{
BearerIgnoreFileFlag: &BearerIgnoreFileFlag,
}
}

func (f *IgnoreFlagGroup) Name() string {
return "Ignore"
}

func (f *IgnoreFlagGroup) Flags() []*Flag {
return []*Flag{
f.BearerIgnoreFileFlag,
}
}

func (f *IgnoreFlagGroup) ToOptions() IgnoreOptions {
return IgnoreOptions{
BearerIgnoreFile: getString(f.BearerIgnoreFileFlag),
}
}
17 changes: 13 additions & 4 deletions pkg/flag/ignore_migrate_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ var (
Value: false,
Usage: "Overwrite an existing ignored finding.",
}
IgnoreMigrateBearerIgnoreFileFlag = Flag{
Name: "config-file",
ConfigName: "config-file",
Value: "bearer.yml",
Usage: "Load configuration from the specified path.",
DisableInConfig: true,
}
IgnoreMigrateConfigFileFlag = Flag{
Name: "config-file",
ConfigName: "config-file",
Expand All @@ -17,8 +24,9 @@ var (
)

type IgnoreMigrateFlagGroup struct {
IgnoreMigrateForceFlag *Flag
IgnoreMigrateConfigFileFlag *Flag
IgnoreMigrateForceFlag *Flag
IgnoreMigrateConfigFileFlag *Flag
IgnoreMigrateBearerIgnoreFileFlag *Flag
}

type IgnoreMigrateOptions struct {
Expand All @@ -28,8 +36,9 @@ type IgnoreMigrateOptions struct {

func NewIgnoreMigrateFlagGroup() *IgnoreMigrateFlagGroup {
return &IgnoreMigrateFlagGroup{
IgnoreMigrateForceFlag: &IgnoreMigrateForceFlag,
IgnoreMigrateConfigFileFlag: &IgnoreMigrateConfigFileFlag,
IgnoreMigrateForceFlag: &IgnoreMigrateForceFlag,
IgnoreMigrateBearerIgnoreFileFlag: &IgnoreMigrateBearerIgnoreFileFlag,
IgnoreMigrateConfigFileFlag: &IgnoreMigrateConfigFileFlag,
}
}

Expand Down
Loading

0 comments on commit d88b1de

Please sign in to comment.