github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/file/cataloger/secrets/cataloger.go (about) 1 package secrets 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "regexp" 8 "sort" 9 10 "github.com/wagoodman/go-partybus" 11 "github.com/wagoodman/go-progress" 12 13 "github.com/anchore/syft/internal" 14 "github.com/anchore/syft/internal/bus" 15 "github.com/anchore/syft/internal/log" 16 "github.com/anchore/syft/syft/event" 17 "github.com/anchore/syft/syft/file" 18 internal2 "github.com/anchore/syft/syft/file/cataloger/internal" 19 ) 20 21 var DefaultSecretsPatterns = map[string]string{ 22 "aws-access-key": `(?i)aws_access_key_id["'=:\s]*?(?P<value>(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16})`, 23 "aws-secret-key": `(?i)aws_secret_access_key["'=:\s]*?(?P<value>[0-9a-zA-Z/+]{40})`, 24 "pem-private-key": `-----BEGIN (\S+ )?PRIVATE KEY(\sBLOCK)?-----((?P<value>(\n.*?)+)-----END (\S+ )?PRIVATE KEY(\sBLOCK)?-----)?`, 25 "docker-config-auth": `"auths"((.*\n)*.*?"auth"\s*:\s*"(?P<value>[^"]+)")?`, 26 "generic-api-key": `(?i)api(-|_)?key["'=:\s]*?(?P<value>[A-Z0-9]{20,60})["']?(\s|$)`, 27 } 28 29 // Deprecated: will be removed in syft v1.0.0 30 type Cataloger struct { 31 patterns map[string]*regexp.Regexp 32 revealValues bool 33 skipFilesAboveSize int64 34 } 35 36 // Deprecated: will be removed in syft v1.0.0 37 func NewCataloger(patterns map[string]*regexp.Regexp, revealValues bool, maxFileSize int64) (*Cataloger, error) { 38 return &Cataloger{ 39 patterns: patterns, 40 revealValues: revealValues, 41 skipFilesAboveSize: maxFileSize, 42 }, nil 43 } 44 45 func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates][]file.SearchResult, error) { 46 results := make(map[file.Coordinates][]file.SearchResult) 47 locations := internal2.AllRegularFiles(resolver) 48 stage, prog, secretsDiscovered := secretsCatalogingProgress(int64(len(locations))) 49 for _, location := range locations { 50 stage.Current = location.RealPath 51 result, err := i.catalogLocation(resolver, location) 52 if internal.IsErrPathPermission(err) { 53 log.Debugf("secrets cataloger skipping - %+v", err) 54 continue 55 } 56 57 if err != nil { 58 return nil, err 59 } 60 if len(result) > 0 { 61 secretsDiscovered.Add(int64(len(result))) 62 results[location.Coordinates] = result 63 } 64 prog.Increment() 65 } 66 log.Debugf("secrets cataloger discovered %d secrets", secretsDiscovered.Current()) 67 prog.SetCompleted() 68 return results, nil 69 } 70 71 func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) ([]file.SearchResult, error) { 72 metadata, err := resolver.FileMetadataByLocation(location) 73 if err != nil { 74 return nil, err 75 } 76 77 if metadata.Size() == 0 { 78 return nil, nil 79 } 80 81 if i.skipFilesAboveSize > 0 && metadata.Size() > i.skipFilesAboveSize { 82 return nil, nil 83 } 84 85 // TODO: in the future we can swap out search strategies here 86 secrets, err := catalogLocationByLine(resolver, location, i.patterns) 87 if err != nil { 88 return nil, internal.ErrPath{Context: "secrets-cataloger", Path: location.RealPath, Err: err} 89 } 90 91 if i.revealValues { 92 for idx, secret := range secrets { 93 value, err := extractValue(resolver, location, secret.SeekPosition, secret.Length) 94 if err != nil { 95 return nil, err 96 } 97 secrets[idx].Value = value 98 } 99 } 100 101 // sort by the start location of each secret as it appears in the location 102 sort.SliceStable(secrets, func(i, j int) bool { 103 return secrets[i].SeekPosition < secrets[j].SeekPosition 104 }) 105 106 return secrets, nil 107 } 108 109 func extractValue(resolver file.Resolver, location file.Location, start, length int64) (string, error) { 110 readCloser, err := resolver.FileContentsByLocation(location) 111 if err != nil { 112 return "", fmt.Errorf("unable to fetch reader for location=%q : %w", location, err) 113 } 114 defer internal.CloseAndLogError(readCloser, location.VirtualPath) 115 116 n, err := io.CopyN(io.Discard, readCloser, start) 117 if err != nil { 118 return "", fmt.Errorf("unable to read contents for location=%q : %w", location, err) 119 } 120 if n != start { 121 return "", fmt.Errorf("unexpected seek location for location=%q : %d != %d", location, n, start) 122 } 123 124 var buf bytes.Buffer 125 n, err = io.CopyN(&buf, readCloser, length) 126 if err != nil { 127 return "", fmt.Errorf("unable to read secret value for location=%q : %w", location, err) 128 } 129 if n != length { 130 return "", fmt.Errorf("unexpected secret length for location=%q : %d != %d", location, n, length) 131 } 132 133 return buf.String(), nil 134 } 135 136 type Monitor struct { 137 progress.Stager 138 SecretsDiscovered progress.Monitorable 139 progress.Progressable 140 } 141 142 func secretsCatalogingProgress(locations int64) (*progress.Stage, *progress.Manual, *progress.Manual) { 143 stage := &progress.Stage{} 144 secretsDiscovered := &progress.Manual{} 145 prog := progress.NewManual(locations) 146 147 bus.Publish(partybus.Event{ 148 Type: event.SecretsCatalogerStarted, 149 Source: secretsDiscovered, 150 Value: Monitor{ 151 Stager: progress.Stager(stage), 152 SecretsDiscovered: secretsDiscovered, 153 Progressable: prog, 154 }, 155 }) 156 157 return stage, prog, secretsDiscovered 158 }