Files
trufflehog/pkg/detectors/aws/utils.go
Muneeb Ullah KhanandAmaan Ullah f446421baf
Lint / golangci-lint (push) Waiting to run
Lint / man-page-staleness (push) Waiting to run
Lint / semgrep (push) Waiting to run
Lint / checksecretparts (push) Waiting to run
Scan for secrets / test (push) Waiting to run
Test / test (push) Waiting to run
Test / test-community (push) Waiting to run
Release / mark-latest (push) Canceled after 0s
Release / Release (push) Canceled after 0s
[INS-407] Fixed AWS detector producing non deterministic output (#4836)
* Fixed AWS detector producing non deterministic output

* fixed bugbot comment

---------

Co-authored-by: Amaan Ullah <[email protected]>
2026-06-29 15:17:14 +05:00

117 lines
3.2 KiB
Go

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, verificationEnabled bool) []detectors.Result {
if len(results) == 0 {
return results
}
if !verificationEnabled {
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
}