package aws import ( "crypto/hmac" "crypto/sha256" "encoding/base32" "encoding/binary" "encoding/hex" "fmt" "strings" regexp "github.com/wasilibs/go-re2" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" ) // ResourceTypes derived from: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-unique-ids var ResourceTypes = map[string]string{ "ABIA": "AWS STS service bearer token", "ACCA": "Context-specific credential", "AGPA": "User group", "AIDA": "IAM user", "AIPA": "Amazon EC2 instance profile", "AKIA": "Access key", "ANPA": "Managed policy", "ANVA": "Version in a managed policy", "APKA": "Public key", "AROA": "Role", "ASCA": "Certificate", "ASIA": "Temporary (AWS STS) access key IDs", } // UrlEncodedReplacer helps capture base64-encoded results that may be url-encoded. // TODO: Add this as a decoder, or make it a more generic. var UrlEncodedReplacer = strings.NewReplacer( "%2B", "+", "%2b", "+", "%2F", "/", "%2f", "/", "%3d", "=", "%3D", "=", ) // Hashes, like those for git, do technically match the secret pattern. // But they are extremely unlikely to be generated as an actual AWS secret. // So when we find them, if they're not verified, we should ignore the result. var FalsePositiveSecretPat = regexp.MustCompile(`[a-f0-9]{40}`) func GetAccountNumFromID(id string) (string, error) { // Function to get the account number from an AWS ID (no verification required) // Source: https://medium.com/@TalBeerySec/a-short-note-on-aws-key-id-f88cc4317489 if len(id) < 4 { return "", fmt.Errorf("AWSID is too short") } if id[4] == 'I' || id[4] == 'J' { return "", fmt.Errorf("can't get account number from AKIAJ/ASIAJ or AKIAI/ASIAI keys") } trimmedAWSID := id[4:] decodedBytes, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(strings.ToUpper(trimmedAWSID)) if err != nil { return "", err } if len(decodedBytes) < 6 { return "", fmt.Errorf("decoded AWSID is too short") } data := make([]byte, 8) copy(data[2:], decodedBytes[0:6]) z := binary.BigEndian.Uint64(data) const mask uint64 = 0x7fffffffff80 accountNum := (z & mask) >> 7 return fmt.Sprintf("%012d", accountNum), nil } func GetHash(input string) string { data := []byte(input) hasher := sha256.New() hasher.Write(data) return hex.EncodeToString(hasher.Sum(nil)) } func GetHMAC(key []byte, data []byte) []byte { hasher := hmac.New(sha256.New, key) hasher.Write(data) return hasher.Sum(nil) } func CleanResults(results []detectors.Result) []detectors.Result { if len(results) == 0 { return results } // For every ID, we want at most one result, preferably verified. idResults := map[string]detectors.Result{} for _, result := range results { // Always accept the verified result as the result for the given ID. if result.Verified { idResults[result.Redacted] = result continue } // Only include an unverified result if we don't already have a result for a given ID. if _, exist := idResults[result.Redacted]; !exist { idResults[result.Redacted] = result } } var out []detectors.Result for _, r := range idResults { out = append(out, r) } return out }