diff --git a/pkg/detectors/privatekey/normalize.go b/pkg/detectors/privatekey/normalize.go index 8b232ab3bd60..202cb30270e6 100644 --- a/pkg/detectors/privatekey/normalize.go +++ b/pkg/detectors/privatekey/normalize.go @@ -1,31 +1,162 @@ package privatekey import ( + "bytes" + "errors" + "regexp" "strings" ) -func normalize(in string) string { - in = strings.ReplaceAll(in, `"`, "") - in = strings.ReplaceAll(in, `'`, "") - in = strings.ReplaceAll(in, "\t", "") - in = strings.ReplaceAll(in, `\t`, "") - in = strings.ReplaceAll(in, `\\t`, "") - in = strings.ReplaceAll(in, `\n`, "\n") - in = strings.ReplaceAll(in, `\\r\\n`, "\n") - in = strings.ReplaceAll(in, `\r\n`, "\n") - in = strings.ReplaceAll(in, "\r\n", "\n") - in = strings.ReplaceAll(in, `\\r`, "\n") - in = strings.ReplaceAll(in, "\r", "\n") - in = strings.ReplaceAll(in, `\r`, "\n") - in = strings.ReplaceAll(in, `\\n`, "\n") - in = strings.ReplaceAll(in, `\n\n`, "\n") - in = strings.ReplaceAll(in, "\n\n", "\n") - in = strings.ReplaceAll(in, `\\`, "\n") - - cleaned := strings.Builder{} - parts := strings.Split(in, "\n") - for _, line := range parts { - cleaned.WriteString(strings.TrimSpace(line) + "\n") - } - return cleaned.String() +var ( + // Common errors + errNoHeader = errors.New("no header line found") + errNoContent = errors.New("no content line(s) found") + errNoFooter = errors.New("no footer line found") + + // Workaround to base64-decoder malforming keys. + // https://archive.ph/qE2C5 + errBase64 = errors.New("key malformed by base64 decoder") + b64MagicString = []byte("openssh-key-v1\u0000\u0000\u0000\u0000\u0004none") +) + +// normalizeMatch attempts to extract PEM content from surrounding noise, such as quotes. +// +// It seems there are five sections: (1) header, (2) content header [optional], +// (3) content body, (4) content footer [optional], and (5) footer. +// ``` +// (1) -----BEGIN RSA PRIVATE KEY-----\n +// (2) Proc-Type: 4,ENCRYPTED\n +// \n +// (3) MIIEowIBAAKCAQEAm+4biWr5sqOihV7T5poaMteQBNj2VKzGm4g+jG0NVXe4XSjk\n +// (3) /70DuGcVG+LiRTu2mRb6mPY9bIJIvcgenXajnVanx9UCQQDRwf6oyU/EH4x+kw/X\n +// (4) L70CPtb3x/eePqw=\n +// (5) -----END RSA PRIVATE KEY-----\n +// ``` +func normalizeMatch(input []byte) (string, error) { + var ( + lines []string + + headerEndIdx int + footerStartIdx int + footerEndIdx int + ) + + // Parse the header and footer first. + // This validates that the input is valid & provides a boundary for content. + if match := headerPat.FindSubmatchIndex(input); match != nil { + headerEndIdx = match[3] + lines = append(lines, string(input[match[2]:match[3]])) + } else { + return "", errNoHeader + } + + if match := footerPat.FindIndex(input); match != nil { + footerStartIdx = match[0] + footerEndIdx = match[1] + + // Skip incomplete matches. + // i.e., a header with no content or footer. + if idx := bytes.Index(input[headerEndIdx:footerStartIdx], []byte("-----BEGIN")); idx != -1 { + return normalizeMatch(input[headerEndIdx+idx : footerEndIdx]) + } + } else { + return "", errNoFooter + } + + // Parse the content. + var ( + contentBytes = input[headerEndIdx:footerStartIdx] + lastIdx int + l []string + ) + if len(contentBytes) < 64 { + return "", errNoContent + } + // Extract the headers, if they exist. + if l, lastIdx = getContentHeaderLines(contentBytes); l != nil { + lines = append(lines, l...) + contentBytes = contentBytes[lastIdx:] + } + + // Sanity check: has the first line been mangled by bas64 decoding? + if bytes.Contains(contentBytes[:64], b64MagicString) { + return "", errBase64 + } + // Extract the body. + if l, lastIdx = getContentLines(contentBytes); l != nil { + lines = append(lines, l...) + } else { + return "", errNoContent + } + + // Extract the footer, if it exists. + if l := getContentFooterLine(contentBytes[lastIdx:]); l != "" { + lines = append(lines, l) + } + + // Finally, append the PEM footer. + lines = append(lines, string(input[footerStartIdx:footerEndIdx])+"\n") + return strings.Join(lines, "\n"), nil +} + +var ( + headerPat = regexp.MustCompile(`^(-----BEGIN[ \w-]{0,100}PRIVATE KEY(?: BLOCK)?-----).*(?:\\r|\\n|[ \t\r\n]){1,5}`) + contentHeaderPat = regexp.MustCompile(`(?:[ \t\r\n"'\x60]|\\+r|\\+n)?([A-Z][a-zA-Z]{2,10}(?:-[A-Z][a-zA-Z]{2,10})+:[ \t].+?)(?:[ \t\r\n"'\x60]|\\+r|\\+n)`) + // contentPat = regexp.MustCompile(`(?:\\r|\\n|[ \t\r\n]){0,5}.*?([a-zA-Z0-9/+]{64,}).*?(?:\\r|\\n|[ \t\r\n]){1,5}`) + contentPat = regexp.MustCompile(`(?:\A|[ \t\r\n"'\x60]|\\+r|\\+n)?([a-zA-Z0-9/+]{64,})(?:[ \t\r\n"'\x60]|\\+r|\\+n)`) + // contentFooterPat = regexp.MustCompile(`(?:\\r|\\n|[ \t\r\n]){0,5}.*?((?:[a-zA-Z0-9/+]{4})+|(?:|[a-zA-Z0-9/+]{4})*(?:[a-zA-Z0-9/+]{3}=|[a-zA-Z0-9/+]{2}==|[a-zA-Z0-9/+]===?)).*?(?:\\r|\\n|[ \t\r\n]){1,5}`) + contentFooterPat = regexp.MustCompile(`(?:\A|[ \t\r\n"'\x60]|\\+r|\\+n)((?:[a-zA-Z0-9/+]{4})*[a-zA-Z0-9][a-zA-Z0-9/+]{0,3}={0,3})(?:[ \t\r\n"'\x60]|\\+r|\\+n|\z)`) + footerPat = regexp.MustCompile(`-----[ \t]{0,5}END[ \w-]{0,100}PRIVATE KEY(?: BLOCK)??[ \t]{0,5}-----$`) +) + +// `\nProc-Type: 4,ENCRYPTED\n` +func getContentHeaderLines(data []byte) ([]string, int) { + var ( + lastIdx = 0 + match []int + lines []string + ) + for lastIdx < len(data) { + if match = contentHeaderPat.FindSubmatchIndex(data[lastIdx:]); match == nil { + break + } + + // Adjust match indices relative to the full input + start := lastIdx + match[2] + end := lastIdx + match[3] + + lines = append(lines, string(data[start:end])) + lastIdx = lastIdx + match[1] + } + return lines, lastIdx +} + +// `/70DuGcVG+LiRTu2mRb6mPY9bIJIvcgenXajnVanx9UCQQDRwf6oyU/EH4x+kw/X\n` +func getContentLines(data []byte) ([]string, int) { + var ( + lines []string + lastIdx = 0 + match []int + ) + for lastIdx < len(data) { + if match = contentPat.FindSubmatchIndex(data[lastIdx:]); match == nil { + break + } + + // Adjust match indices relative to the full input + start := lastIdx + match[2] + end := lastIdx + match[3] + + lines = append(lines, string(data[start:end])) + lastIdx = lastIdx + match[1] + } + return lines, lastIdx +} + +// `\nIc3jMIwtyuXsn4NhJNUFlgfPL70CPtb3x/eePqw=\n` +func getContentFooterLine(data []byte) string { + if loc := contentFooterPat.FindSubmatchIndex(data); loc != nil { + return string(data[loc[2]:loc[3]]) + } + return "" } diff --git a/pkg/detectors/privatekey/normalize_test.go b/pkg/detectors/privatekey/normalize_test.go new file mode 100644 index 000000000000..b123642f0286 --- /dev/null +++ b/pkg/detectors/privatekey/normalize_test.go @@ -0,0 +1,84 @@ +package privatekey + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" +) + +func TestNormalize(t *testing.T) { + tests := []struct { + name string + input string + want string + wantErr error + }{ + // Invalid + { + name: "invalid - no header", + input: ``, + wantErr: errNoHeader, + }, + { + name: "invalid - content", + input: ` "jwt-auth": { + "key": "user-key", + "public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC KEY-----", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\n……\n-----END RSA PRIVATE KEY-----", + "algorithm": "RS256" + }`, + wantErr: errNoContent, + }, + { + name: "invalid - no content", + input: `/* openssh private key file format */ +#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" +#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" +#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1) +#define MARK_END_LEN (sizeof(MARK_END) - 1)`, + wantErr: errNoContent, + }, + { + // OpenSSH private key with the first line decoded with base64. + name: "invalid - base64 mangling", + input: string([]byte{45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 79, 80, 69, 78, 83, 83, 72, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69, 89, 45, 45, 45, 45, 45, 10, 111, 112, 101, 110, 115, 115, 104, 45, 107, 101, 121, 45, 118, 49, 0, 0, 0, 0, 4, 110, 111, 110, 101, 0, 0, 0, 4, 110, 111, 110, 101, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 23, 0, 0, 0, 7, 115, 115, 104, 45, 114, 10, 78, 104, 65, 65, 65, 65, 65, 119, 69, 65, 65, 81, 65, 65, 65, 81, 69, 65, 51, 43, 101, 112, 102, 43, 86, 71, 75, 111, 71, 80, 97, 65, 90, 88, 114, 102, 54, 83, 48, 99, 121, 117, 109, 81, 110, 100, 100, 107, 71, 66, 110, 86, 70, 88, 48, 65, 53, 101, 104, 51, 55, 82, 116, 76, 117, 103, 48, 113, 89, 53, 10, 116, 104, 120, 115, 66, 85, 98, 71, 71, 86, 114, 57, 109, 84, 100, 50, 81, 88, 119, 76, 117, 106, 66, 119, 89, 103, 53, 108, 49, 77, 80, 47, 70, 109, 103, 43, 53, 51, 49, 50, 90, 103, 120, 57, 112, 72, 109, 83, 43, 113, 75, 85, 76, 98, 97, 114, 48, 104, 108, 78, 103, 112, 116, 78, 69, 98, 43, 97, 78, 85, 10, 100, 51, 111, 57, 113, 103, 51, 97, 88, 113, 88, 109, 55, 43, 90, 110, 106, 65, 86, 48, 53, 101, 102, 47, 109, 120, 78, 82, 78, 50, 90, 118, 117, 69, 107, 119, 55, 99, 82, 112, 112, 84, 74, 99, 98, 66, 73, 43, 118, 70, 51, 108, 88, 117, 67, 88, 110, 88, 50, 107, 108, 68, 73, 57, 53, 71, 108, 50, 65, 87, 10, 51, 87, 72, 82, 116, 97, 110, 113, 76, 72, 90, 88, 117, 66, 107, 106, 106, 82, 66, 68, 75, 99, 55, 77, 85, 113, 47, 71, 80, 49, 104, 109, 76, 105, 65, 100, 57, 53, 100, 118, 85, 55, 102, 90, 106, 82, 108, 73, 69, 115, 80, 56, 52, 122, 71, 69, 73, 49, 70, 98, 48, 76, 47, 107, 109, 80, 72, 99, 79, 116, 10, 105, 86, 102, 72, 102, 116, 56, 67, 116, 109, 67, 57, 118, 54, 43, 57, 52, 74, 114, 79, 105, 80, 66, 66, 78, 83, 99, 86, 43, 100, 121, 114, 103, 65, 71, 80, 115, 100, 75, 100, 114, 47, 49, 118, 73, 112, 81, 109, 67, 78, 105, 73, 56, 115, 51, 80, 67, 105, 68, 56, 74, 55, 90, 105, 66, 97, 89, 109, 48, 73, 10, 56, 102, 113, 53, 71, 47, 113, 110, 85, 119, 65, 65, 65, 55, 103, 103, 119, 50, 100, 88, 73, 77, 78, 110, 86, 119, 65, 65, 65, 65, 100, 122, 99, 50, 103, 116, 99, 110, 78, 104, 65, 65, 65, 66, 65, 81, 68, 102, 53, 54, 108, 47, 53, 85, 89, 113, 103, 89, 57, 111, 66, 108, 101, 116, 47, 112, 76, 82, 122, 75, 10, 54, 90, 67, 100, 49, 50, 81, 89, 71, 100, 85, 86, 102, 81, 68, 108, 54, 72, 102, 116, 71, 48, 117, 54, 68, 83, 112, 106, 109, 50, 72, 71, 119, 70, 82, 115, 89, 90, 87, 118, 50, 90, 78, 51, 90, 66, 102, 65, 117, 54, 77, 72, 66, 105, 68, 109, 88, 85, 119, 47, 56, 87, 97, 68, 55, 110, 102, 88, 90, 109, 10, 68, 72, 50, 107, 101, 90, 76, 54, 111, 112, 81, 116, 116, 113, 118, 83, 71, 85, 50, 67, 109, 48, 48, 82, 118, 53, 111, 49, 82, 51, 101, 106, 50, 113, 68, 100, 112, 101, 112, 101, 98, 118, 53, 109, 101, 77, 66, 88, 84, 108, 53, 47, 43, 98, 69, 49, 69, 51, 90, 109, 43, 52, 83, 84, 68, 116, 120, 71, 109, 108, 10, 77, 108, 120, 115, 69, 106, 54, 56, 88, 101, 86, 101, 52, 74, 101, 100, 102, 97, 83, 85, 77, 106, 51, 107, 97, 88, 89, 66, 98, 100, 89, 100, 71, 49, 113, 101, 111, 115, 100, 108, 101, 52, 71, 83, 79, 78, 69, 69, 77, 112, 122, 115, 120, 83, 114, 56, 89, 47, 87, 71, 89, 117, 73, 66, 51, 51, 108, 50, 57, 84, 10, 116, 57, 109, 78, 71, 85, 103, 83, 119, 47, 122, 106, 77, 89, 81, 106, 85, 86, 118, 81, 118, 43, 83, 89, 56, 100, 119, 54, 50, 74, 86, 56, 100, 43, 51, 119, 75, 50, 89, 76, 50, 47, 114, 55, 51, 103, 109, 115, 54, 73, 56, 69, 69, 49, 74, 120, 88, 53, 51, 75, 117, 65, 65, 89, 43, 120, 48, 112, 50, 118, 10, 47, 87, 56, 105, 108, 67, 89, 73, 50, 73, 106, 121, 122, 99, 56, 75, 73, 80, 119, 110, 116, 109, 73, 70, 112, 105, 98, 81, 106, 120, 43, 114, 107, 98, 43, 113, 100, 84, 65, 65, 65, 65, 65, 119, 69, 65, 65, 81, 65, 65, 65, 81, 69, 65, 114, 87, 109, 53, 66, 52, 116, 70, 97, 115, 112, 112, 106, 85, 72, 77, 10, 83, 115, 65, 117, 97, 106, 116, 67, 120, 116, 105, 122, 73, 49, 72, 99, 49, 48, 69, 87, 53, 57, 99, 90, 77, 52, 118, 118, 85, 122, 69, 50, 102, 54, 43, 113, 90, 118, 100, 103, 87, 106, 51, 85, 85, 47, 76, 55, 69, 116, 50, 51, 119, 48, 81, 86, 117, 83, 67, 110, 67, 101, 114, 111, 120, 51, 55, 57, 90, 66, 10, 100, 100, 69, 79, 70, 70, 65, 65, 105, 81, 106, 119, 66, 120, 54, 53, 104, 98, 100, 52, 82, 82, 85, 121, 109, 120, 116, 73, 81, 102, 106, 113, 49, 56, 43, 43, 76, 99, 77, 74, 87, 49, 110, 98, 86, 81, 55, 99, 54, 57, 84, 104, 81, 98, 116, 65, 76, 73, 103, 103, 109, 98, 83, 43, 90, 69, 47, 56, 71, 120, 10, 106, 107, 119, 109, 73, 114, 67, 72, 48, 87, 119, 56, 84, 108, 112, 115, 80, 101, 43, 109, 78, 72, 117, 121, 78, 107, 55, 85, 69, 90, 111, 88, 76, 109, 50, 50, 108, 78, 76, 113, 113, 53, 113, 107, 73, 76, 53, 74, 103, 84, 54, 77, 50, 105, 78, 74, 112, 77, 79, 74, 121, 57, 47, 67, 75, 105, 54, 107, 79, 52, 10, 74, 80, 117, 86, 119, 106, 100, 71, 52, 67, 53, 112, 66, 80, 97, 77, 78, 51, 75, 74, 49, 73, 118, 65, 108, 83, 108, 76, 71, 78, 97, 88, 110, 102, 88, 99, 110, 56, 53, 103, 87, 102, 115, 67, 106, 115, 90, 109, 72, 51, 108, 105, 101, 121, 50, 78, 74, 97, 109, 113, 112, 47, 119, 56, 51, 66, 114, 75, 85, 103, 10, 89, 90, 118, 77, 82, 50, 113, 101, 87, 90, 97, 75, 107, 70, 84, 97, 104, 112, 122, 78, 53, 75, 82, 75, 49, 66, 70, 101, 66, 51, 55, 79, 48, 80, 56, 52, 68, 122, 104, 49, 98, 105, 68, 88, 56, 81, 65, 65, 65, 73, 69, 65, 105, 87, 88, 87, 56, 101, 80, 89, 70, 119, 76, 112, 97, 50, 109, 70, 73, 104, 10, 86, 118, 82, 84, 100, 99, 114, 78, 55, 48, 114, 86, 75, 53, 101, 87, 86, 97, 76, 51, 112, 121, 83, 52, 118, 71, 65, 53, 54, 74, 105, 120, 113, 56, 54, 100, 72, 118, 101, 79, 110, 98, 83, 89, 43, 105, 78, 98, 49, 106, 81, 105, 100, 116, 88, 99, 56, 83, 87, 85, 116, 50, 119, 116, 72, 113, 90, 51, 50, 104, 10, 76, 106, 105, 57, 47, 104, 77, 83, 75, 113, 101, 57, 83, 69, 80, 51, 120, 118, 68, 82, 68, 109, 85, 74, 113, 115, 86, 119, 48, 121, 83, 121, 114, 70, 114, 122, 109, 52, 49, 54, 48, 81, 89, 54, 82, 75, 85, 51, 67, 73, 81, 67, 86, 70, 115, 108, 77, 90, 57, 102, 120, 109, 114, 102, 90, 47, 104, 120, 111, 85, 10, 48, 88, 51, 70, 86, 115, 120, 109, 67, 52, 43, 107, 119, 65, 65, 65, 67, 66, 65, 80, 79, 99, 49, 89, 69, 82, 112, 86, 54, 80, 106, 65, 78, 66, 114, 71, 82, 43, 49, 111, 49, 82, 67, 100, 65, 67, 98, 109, 53, 109, 121, 99, 52, 50, 81, 122, 83, 78, 73, 97, 79, 90, 109, 103, 114, 89, 115, 43, 71, 116, 10, 55, 43, 69, 99, 111, 113, 83, 100, 98, 74, 122, 72, 74, 78, 67, 78, 81, 102, 70, 43, 65, 43, 118, 106, 98, 73, 107, 70, 105, 117, 90, 113, 113, 47, 53, 119, 119, 114, 53, 57, 113, 88, 120, 53, 79, 65, 108, 105, 106, 76, 66, 47, 121, 119, 119, 75, 109, 84, 87, 113, 54, 108, 112, 47, 47, 90, 120, 110, 121, 43, 10, 107, 97, 51, 115, 73, 71, 78, 79, 49, 52, 101, 81, 118, 109, 120, 78, 68, 110, 108, 76, 76, 43, 82, 73, 90, 108, 101, 67, 84, 69, 75, 66, 88, 83, 87, 54, 67, 90, 104, 114, 43, 117, 72, 77, 90, 70, 75, 75, 77, 116, 65, 65, 65, 65, 103, 81, 68, 114, 83, 107, 109, 43, 76, 98, 73, 76, 66, 55, 72, 57, 10, 106, 120, 69, 66, 90, 76, 104, 118, 53, 51, 97, 65, 110, 52, 117, 56, 49, 107, 70, 75, 81, 79, 74, 55, 80, 122, 122, 112, 66, 71, 83, 111, 68, 49, 50, 105, 55, 111, 73, 74, 117, 53, 115, 105, 83, 68, 53, 69, 75, 68, 78, 86, 69, 114, 43, 83, 118, 67, 102, 48, 73, 83, 85, 51, 66, 117, 77, 112, 122, 108, 10, 116, 51, 89, 114, 80, 114, 72, 82, 104, 101, 79, 70, 104, 110, 53, 101, 51, 106, 48, 101, 47, 47, 122, 66, 56, 114, 66, 67, 48, 68, 71, 66, 52, 67, 116, 84, 68, 100, 101, 104, 55, 114, 79, 88, 85, 76, 52, 75, 48, 112, 122, 43, 56, 119, 69, 112, 78, 107, 86, 54, 50, 83, 87, 120, 104, 67, 54, 78, 82, 87, 10, 73, 55, 57, 74, 104, 116, 71, 107, 104, 43, 71, 116, 99, 110, 107, 69, 102, 119, 65, 65, 65, 65, 65, 66, 10, 45, 45, 45, 45, 45, 69, 78, 68, 32, 79, 80, 69, 78, 83, 83, 72, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69, 89, 45, 45, 45, 45, 45}), + wantErr: errBase64, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if len(test.input) == 0 { + t.Skip() + return + } + + match := keyPat.Find([]byte(test.input)) + if match == nil { + t.Errorf(`keyPat.FindString(%s) => "%s"`, test.input, test.name) + return + } + + actual, err := normalizeMatch(match) + if err != nil { + if test.wantErr != nil { + assert.EqualError(t, err, test.wantErr.Error()) + } else { + t.Errorf("received unexpected error: %v", err) + return + } + } + if actual == "" { + if test.want != "" { + t.Errorf("expected: %v, got no result", test.want) + } + return + } + + if diff := cmp.Diff(test.want, actual); diff != "" { + t.Errorf("%s diff: (-want +got)\n%s", test.name, diff) + } + }) + } +} diff --git a/pkg/detectors/privatekey/privatekey.go b/pkg/detectors/privatekey/privatekey.go index 8fe43c5805fa..392082af4032 100644 --- a/pkg/detectors/privatekey/privatekey.go +++ b/pkg/detectors/privatekey/privatekey.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net/http" "strings" "sync" @@ -14,24 +15,26 @@ import ( "golang.org/x/crypto/ssh" "github.com/trufflesecurity/trufflehog/v3/pkg/common" + logContext "github.com/trufflesecurity/trufflehog/v3/pkg/context" "github.com/trufflesecurity/trufflehog/v3/pkg/detectors" "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb" ) type Scanner struct { IncludeExpired bool + client *http.Client } // Ensure the Scanner satisfies the interface at compile time. -var _ detectors.Detector = (*Scanner)(nil) -var _ detectors.CustomFalsePositiveChecker = (*Scanner)(nil) -var _ detectors.MaxSecretSizeProvider = (*Scanner)(nil) - -var ( - // TODO: add base64 encoded key support - client = common.RetryableHTTPClient() - keyPat = regexp.MustCompile(`(?i)-----\s*?BEGIN[ A-Z0-9_-]*?PRIVATE KEY\s*?-----[\s\S]*?----\s*?END[ A-Z0-9_-]*? PRIVATE KEY\s*?-----`) -) +var _ interface { + detectors.Detector + detectors.MaxSecretSizeProvider + detectors.CustomFalsePositiveChecker +} = (*Scanner)(nil) + +func (s Scanner) Type() detectorspb.DetectorType { + return detectorspb.DetectorType_PrivateKey +} // Keywords are used for efficiently pre-filtering chunks. // Use identifiers in the secret preferably, or the provider name. @@ -39,63 +42,108 @@ func (s Scanner) Keywords() []string { return []string{"private key"} } -const maxPrivateKeySize = 4096 +func (s Scanner) Description() string { + return "Private keys are used for securely connecting and authenticating to various systems and services. Exposure of private keys can lead to unauthorized access and data breaches." +} + +// MaxSecretSize returns the maximum size of a secret that this detector can find. +func (s Scanner) MaxSecretSize() int64 { return 4096 } + +func (s Scanner) IsFalsePositive(_ detectors.Result) (bool, string) { + return false, "" +} -// ProvideMaxSecretSize returns the maximum size of a secret that this detector can find. -func (s Scanner) MaxSecretSize() int64 { return maxPrivateKeySize } +var keyPat = regexp.MustCompile(`(?i)-----\s*?BEGIN[ A-Z0-9_-]*?PRIVATE KEY\s*?-----[\s\S]*?----\s*?END[ A-Z0-9_-]*? PRIVATE KEY\s*?-----`) // FromData will find and optionally verify Privatekey secrets in a given set of bytes. func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) { + logCtx := logContext.AddLogger(ctx) + logger := logCtx.Logger().WithName("privatekey") dataStr := string(data) - matches := keyPat.FindAllString(dataStr, -1) - for _, match := range matches { - token := normalize(match) - if len(token) < 64 { + // Deduplicate matches. + matches := make(map[string]struct{}) + for _, match := range keyPat.FindAllString(dataStr, -1) { + if len(match) < 64 { continue } - - s1 := detectors.Result{ - DetectorType: detectorspb.DetectorType_PrivateKey, - Raw: []byte(token), - Redacted: token[0:64], - ExtraData: make(map[string]string), + if detectors.StringShannonEntropy(match) < 3.5 { + continue } + matches[match] = struct{}{} + } - var passphrase string - parsedKey, err := ssh.ParseRawPrivateKey([]byte(token)) - if err != nil && strings.Contains(err.Error(), "private key is passphrase protected") { - s1.ExtraData["encrypted"] = "true" - parsedKey, passphrase, err = crack([]byte(token)) - if err != nil { - s1.SetVerificationError(err, token) - continue - } - if passphrase != "" { - s1.ExtraData["cracked_encryption_passphrase"] = "true" + // Process matches. + normalizedKeys := make(map[string]struct{}) + for match := range matches { + key, err := normalizeMatch([]byte(match)) + if err != nil { + if !errors.Is(err, errBase64) { + logger.Error(err, "Failed to normalize private key", "match", match) } - } else if err != nil { - // couldn't parse key, probably invalid continue } + if _, ok := normalizedKeys[key]; ok { + continue + } + normalizedKeys[key] = struct{}{} + + r := detectors.Result{ + DetectorType: s.Type(), + Raw: []byte(key), + Redacted: key[0:64], + ExtraData: make(map[string]string), + } - fingerprint, err := FingerprintPEMKey(parsedKey) + var ( + passphrase string + fingerprint string + ) + parsedKey, err := ssh.ParseRawPrivateKey([]byte(key)) if err != nil { - continue + if strings.Contains(err.Error(), "private key is passphrase protected") { + r.ExtraData["encrypted"] = "true" + parsedKey, passphrase, err = crack([]byte(key)) + if err != nil { + r.SetVerificationError(err, key) + goto End + } + if passphrase != "" { + r.ExtraData["cracked_encryption_passphrase"] = "true" + } + } else if strings.Contains(err.Error(), "ssh: unsupported key type \"ENCRYPTED PRIVATE KEY\"") { + // https://github.com/golang/go/issues/41949 + r.ExtraData["encrypted"] = "true" + r.SetVerificationError(err, key) + goto End + } else { + logger.Error(err, "Failed to parse private key", "match", match, "normalized", key) + r.SetVerificationError(err, key) + goto End + } } if verify { + fingerprint, err = FingerprintPEMKey(parsedKey) + if err != nil { + r.SetVerificationError(err, key) + goto End + } + var ( wg sync.WaitGroup extraData = newExtraData() verificationErrors = newVerificationErrors() ) + if s.client == nil { + s.client = common.RetryableHTTPClient() + } // Look up certificate information. wg.Add(1) go func() { defer wg.Done() - data, err := lookupFingerprint(ctx, fingerprint, s.IncludeExpired) + data, err := lookupFingerprint(ctx, s.client, fingerprint, s.IncludeExpired) if err == nil { if data != nil { extraData.Add("certificate_urls", strings.Join(data.CertificateURLs, ", ")) @@ -133,56 +181,53 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result wg.Wait() if len(extraData.data) > 0 { - s1.Verified = true + r.Verified = true for k, v := range extraData.data { - s1.ExtraData[k] = v + r.ExtraData[k] = v } - } else { - s1.ExtraData = nil } if len(verificationErrors.errors) > 0 { - s1.SetVerificationError(fmt.Errorf("verification failures: %s", strings.Join(verificationErrors.errors, ", ")), token) + r.SetVerificationError(fmt.Errorf("verification failures: %s", strings.Join(verificationErrors.errors, ", ")), key) } } - results = append(results, s1) + End: + if len(r.ExtraData) == 0 { + r.ExtraData = nil + } + results = append(results, r) } return results, nil } -func (s Scanner) IsFalsePositive(_ detectors.Result) (bool, string) { - return false, "" -} - -func (s Scanner) Description() string { - return "Private keys are used for securely connecting and authenticating to various systems and services. Exposure of private keys can lead to unauthorized access and data breaches." -} - type result struct { CertificateURLs []string GitHubUsername string } -func lookupFingerprint(ctx context.Context, publicKeyFingerprintInHex string, includeExpired bool) (*result, error) { +func lookupFingerprint(ctx context.Context, client *http.Client, publicKeyFingerprintInHex string, includeExpired bool) (*result, error) { req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://keychecker.trufflesecurity.com/fingerprint/%s", publicKeyFingerprintInHex), nil) if err != nil { return nil, err } + res, err := client.Do(req) if err != nil { return nil, err } - defer res.Body.Close() + defer func() { + _, _ = io.Copy(io.Discard, res.Body) + _ = res.Body.Close() + }() - results := DriftwoodResult{} + var results DriftwoodResult err = json.NewDecoder(res.Body).Decode(&results) if err != nil { return nil, err } var data *result - seen := map[string]struct{}{} for _, r := range results.CertificateResults { if _, ok := seen[r.CertificateFingerprint]; ok { @@ -244,7 +289,3 @@ func (e *verificationErrors) Add(err error) { e.errors = append(e.errors, err.Error()) e.mutex.Unlock() } - -func (s Scanner) Type() detectorspb.DetectorType { - return detectorspb.DetectorType_PrivateKey -} diff --git a/pkg/detectors/privatekey/privatekey_test.go b/pkg/detectors/privatekey/privatekey_test.go index 661a8fd96f01..bbaf5af54dcb 100644 --- a/pkg/detectors/privatekey/privatekey_test.go +++ b/pkg/detectors/privatekey/privatekey_test.go @@ -2,7 +2,6 @@ package privatekey import ( "context" - "fmt" "testing" "github.com/google/go-cmp/cmp" @@ -11,30 +10,7 @@ import ( "github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick" ) -var ( - validPattern = `-----BEGIN RSA PRIVATE KEY----- -MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu -KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm -o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k -TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 -9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy -v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs -/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 ------END RSA PRIVATE KEY----- -` - invalidPattern = `-----BEGIN?RSA?PRIVATE?KEY----- -MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu -KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm -o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k -TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7 -9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy -v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs -/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00 ------END RSA PRIVATE KEY-----` - keyword = "privatekey" -) - -func TestPrivatekey_Pattern(t *testing.T) { +func TestPrivateKey_Pattern(t *testing.T) { d := Scanner{} ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d}) tests := []struct { @@ -42,15 +18,170 @@ func TestPrivatekey_Pattern(t *testing.T) { input string want []string }{ + // TODO: Test case with multiple slashes (gcp creds inside of base64-encoding) + { + name: "multi-line standard format", + input: `-----BEGIN RSA PRIVATE KEY----- +MIICWQIBAAKBgHsSuRPLMDrxcwMB9P6ubGFGmlSvHvSXq2kfwycrcEKf/TCctShz +A2HYo2IWed8n1rqazlESHnhNmCWlFWIMMFWagZyDBy9yy71MhWISvoTuQVyCx/z3 +q1v171fy+Ds5smKwZ8wK3bgwBTR7BTKfYNmearDZvPJgwK0jsYEJDZ/DAgElAoGA +MeT+7FlK53akP31VfAGF4j83pcp0VVI+kmbSk1bMpWN0e33M5uKE1KPvNZpowkCV +UpHJQ3YMWkj4ffbRUUM2L/jQmKkICf7vynIdq5cj+lF6lNXSzwq6pVR6/octdeKS +/70DuGcVG+LiRTu2mRb6mPY9bIJIvcgenXajnVanx9UCQQDRwf6oyU/EH4x+kw/X +QZi/RebtDPD1yIQuhVG8B1xkPxBsAywTwVDL7DSZ1BsbWJcl5HcXt/q0n/3NZ62X +Rr1VAkEAljSLsMOk5H7XCctEk3mCu1WgDtUvb/RRCBiBT+cic14OpVtytJMAeLeq +cAhIj54ef4hQPGKbAsQZ3E/X4EsotwJAa7alXZfPA9jZcW4c5Ciai7wcoz3/Mhrc +F+OYrKnVf5YBg5LtHua6yZT4aqswg6oIbWd7bQty5yG5rqrcmcphOQJAHGrOUd/T +FnjckyZ0wfRk11VjeG2Fg+IdKwuOFgkiMYB/T7da4+R1tfk7666KRK82M82uUJ0I +kdISuvpZRhwOnwJBAI34lnrN4bNcUVB5kAXT9huyH8tJomNdsJOufS3vCi5tKaqK +Ic3jMIwtyuXsn4NhJNUFlgfPL70CPtb3x/eePqw= +-----END RSA PRIVATE KEY-----`, + want: []string{"-----BEGIN RSA PRIVATE KEY-----\nMIICWQIBAAKBgHsSuRPLMDrxcwMB9P6ubGFGmlSvHvSXq2kfwycrcEKf/TCctShz\nA2HYo2IWed8n1rqazlESHnhNmCWlFWIMMFWagZyDBy9yy71MhWISvoTuQVyCx/z3\nq1v171fy+Ds5smKwZ8wK3bgwBTR7BTKfYNmearDZvPJgwK0jsYEJDZ/DAgElAoGA\nMeT+7FlK53akP31VfAGF4j83pcp0VVI+kmbSk1bMpWN0e33M5uKE1KPvNZpowkCV\nUpHJQ3YMWkj4ffbRUUM2L/jQmKkICf7vynIdq5cj+lF6lNXSzwq6pVR6/octdeKS\n/70DuGcVG+LiRTu2mRb6mPY9bIJIvcgenXajnVanx9UCQQDRwf6oyU/EH4x+kw/X\nQZi/RebtDPD1yIQuhVG8B1xkPxBsAywTwVDL7DSZ1BsbWJcl5HcXt/q0n/3NZ62X\nRr1VAkEAljSLsMOk5H7XCctEk3mCu1WgDtUvb/RRCBiBT+cic14OpVtytJMAeLeq\ncAhIj54ef4hQPGKbAsQZ3E/X4EsotwJAa7alXZfPA9jZcW4c5Ciai7wcoz3/Mhrc\nF+OYrKnVf5YBg5LtHua6yZT4aqswg6oIbWd7bQty5yG5rqrcmcphOQJAHGrOUd/T\nFnjckyZ0wfRk11VjeG2Fg+IdKwuOFgkiMYB/T7da4+R1tfk7666KRK82M82uUJ0I\nkdISuvpZRhwOnwJBAI34lnrN4bNcUVB5kAXT9huyH8tJomNdsJOufS3vCi5tKaqK\nIc3jMIwtyuXsn4NhJNUFlgfPL70CPtb3x/eePqw=\n-----END RSA PRIVATE KEY-----\n"}, + }, + // https://github.com/chromium/chromium/blob/051a9895e753b1097b1b44bdd851d21366dc46ee/extensions/common/file_util_unittest.cc#L527 + { + name: "multi-line with quotes", + input: `constexpr std::string_view private_key = + "-----BEGIN PRIVATE KEY-----\n" + "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKt02SR0FYaYy6fpW\n" + "MAA+kU1BgK3d+OmmWfdr+JATIjhRkyeSF4lTd/71JQsyKqPzYkQPi3EeROWM+goTv\n" + "EhJqq07q63BolpsFmlV+S4ny+sBA2B4aWwRYXlBWikdrQSA0mJMzvEHc6nKzBgXik\n" + "QSVbyyBNAsxlDB9WaCxRVOpK3AgMBAAECgYBGvSPlrVtAOAQ2V8j9FqorKZA8SLPX\n" + "IeJC/yzU3RB2nPMjI17aMOvrUHxJUhzMeh4jwabVvSzzDtKFozPGupW3xaI8sQdi2\n" + "WWMTQIk/Q9HHDWoQ9qA6SwX2qWCc5SyjCKqVp78ye+000kqTJYjBsDgXeAlzKcx2B\n" + "4GAAeWonDdkQJBANNb8wrqNWFn7DqyQTfELzcRTRnqQ/r1pdeJo6obzbnwGnlqe3t\n" + "KhLjtJNIGrQg5iC0OVLWFuvPJs0t3z62A1ckCQQDPq2JZuwTwu5Pl4DJ0r9O1FdqN\n" + "JgqPZyMptokCDQ3khLLGakIu+TqB9YtrzI69rJMSG2Egb+6McaDX+dh3XmR/AkB9t\n" + "xJf6qDnmA2td/tMtTc0NOk8Qdg/fD8xbZ/YfYMnVoYYs9pQoilBaWRePDRNURMLYZ\n" + "vHAI0Llmw7tj7jv17pAkEAz44uXRpjRKtllUIvi5pUENAHwDz+HvdpGH68jpU3hmb\n" + "uOwrmnQYxaMReFV68Z2w9DcLZn07f7/R9Wn72z89CxwJAFsDoNaDes4h48bX7plct\n" + "s9ACjmTwcCigZjN2K7AGv7ntCLF3DnV5dK0dTHNaAdD3SbY3jl29Rk2CwiURSX6Ee\n" + "g==\n" + "-----END PRIVATE KEY-----\n";`, + want: []string{"-----BEGIN PRIVATE KEY-----\nMIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKt02SR0FYaYy6fpW\nMAA+kU1BgK3d+OmmWfdr+JATIjhRkyeSF4lTd/71JQsyKqPzYkQPi3EeROWM+goTv\nEhJqq07q63BolpsFmlV+S4ny+sBA2B4aWwRYXlBWikdrQSA0mJMzvEHc6nKzBgXik\nQSVbyyBNAsxlDB9WaCxRVOpK3AgMBAAECgYBGvSPlrVtAOAQ2V8j9FqorKZA8SLPX\nIeJC/yzU3RB2nPMjI17aMOvrUHxJUhzMeh4jwabVvSzzDtKFozPGupW3xaI8sQdi2\nWWMTQIk/Q9HHDWoQ9qA6SwX2qWCc5SyjCKqVp78ye+000kqTJYjBsDgXeAlzKcx2B\n4GAAeWonDdkQJBANNb8wrqNWFn7DqyQTfELzcRTRnqQ/r1pdeJo6obzbnwGnlqe3t\nKhLjtJNIGrQg5iC0OVLWFuvPJs0t3z62A1ckCQQDPq2JZuwTwu5Pl4DJ0r9O1FdqN\nJgqPZyMptokCDQ3khLLGakIu+TqB9YtrzI69rJMSG2Egb+6McaDX+dh3XmR/AkB9t\nxJf6qDnmA2td/tMtTc0NOk8Qdg/fD8xbZ/YfYMnVoYYs9pQoilBaWRePDRNURMLYZ\nvHAI0Llmw7tj7jv17pAkEAz44uXRpjRKtllUIvi5pUENAHwDz+HvdpGH68jpU3hmb\nuOwrmnQYxaMReFV68Z2w9DcLZn07f7/R9Wn72z89CxwJAFsDoNaDes4h48bX7plct\ns9ACjmTwcCigZjN2K7AGv7ntCLF3DnV5dK0dTHNaAdD3SbY3jl29Rk2CwiURSX6Ee\ng==\n-----END PRIVATE KEY-----\n"}, + }, + { + name: "multi-line concatenation (java)", + input: `public static String cveAttackModePrivateKey = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAuvBC2RJqGAbPg6HoJaOlT6L4tMwMzGUI8TptoBlStWe+TfRc\n" + + "uPVfxI1U6g87/7B62768kuU55H8bd3Yd7nBmmdzuNthAdPDMXlrnIbOywG52iPtH\n" + + "AV1U5Vk5QGuj39aSuLjpBSC4jUJPcdJENpmECVX+EeNwZlOEDfbtnpOTMRr/24r1\n" + + "CLSMwp9gtaLnE6NJzh+ycTDgyrWK9OtNA+UqzwfNJ9BfE53u9JHJP/nWZopqlNQ2\n" + + "6fgPASu8FULa8bmJ3kc0SZFCNvXyjZn7HVCwIno/ZEq7oN9tphmAPBwdfQhb2xmD\n" + + "3gYeWrXNP/M+SKisaX1CVwaPPowjCQMbsmfC2wIDAQABAoIBAGtODOEzq8i86BMk\n" + + "NfCdHgA3iVGmq1YMTPTDWDgFMS/GLDvtH+hfmShnBC4SrpsXv34x32bmw7OArtCE\n" + + "8atzw8FgSzEaMu2tZ3Jl9bSnxNymy83XhyumWlwIOk/bOcb8EV6NbdyuqqETRi0M\n" + + "yHEa7+q3/M5h4pwqJmwpqL5U8bHGVGXNEbiA/TneNyXjSn03uPYaKTw4R9EG951A\n" + + "pCJf4Atba5VIfdZ59fx/6rxCuKjWlvZrklE3Cll/+A0dRN5vBSR+EBYgfedMPepM\n" + + "6TYDOsQnsy1bFJjy+aE/kwYGgtjuHOlvCpwq90SY3WueXClDfioaJ/1S6QT3q8hf\n" + + "UHodWxkCgYEA8X6+dybVvBgawxyYZEi1P/KNWC9tr2zdztnkDB4nn97UIJzxmjTh\n" + + "s81EsX0Mt24DJg36HoX5x1lDHNrR2RvIEPy8vfzTdNVa6KP7E7CWUUcW39nmt/z7\n" + + "ezlyZa8TVPBE/xvozdZuTAzd0rafUX3Ugqzn17MBshz07/K4Z0iy/C0CgYEAxiqm\n" + + "J7ul9CmNVvCnQ19tvcO7kY8h9AYIEtrqf9ubiq9W7Ldf9mXIhlG3wr6U3dXuAVVa\n" + + "4g9zkXr+N7BE4hlQcJpBn5ywtYfqzK1GRy+rfwPgC/JbWEnNDP8oYnZ8R6pkhyOC\n" + + "zqDqCZPtnmD9Je/ifdmgIkkxQD25ktyCYMhPuCcCgYEAh/MQCkfEfxUay8gnSh1c\n" + + "W9mSFJjuqJki7TXgmanIKMnqpUl1AZjPjsb56uk45XJ7N0sbCV/m04C+tVnCVPS8\n" + + "1kNRhar054rMmLbnu5fnp23bxL0Ik39Jm38llXTP7zsrvGnbzzTt9sYvglXorpml\n" + + "rsLj6ZwOUlTW1tXPVeWpTSkCgYBfAkGpWRlGx8lA/p5i+dTGn5pFPmeb9GxYheba\n" + + "KDMZudkmIwD6RHBwnatJzk/XT+MNdpvdOGVDQcGyd2t/L33Wjs6ZtOkwD5suSIEi\n" + + "TiOeAQChGbBb0v5hldAJ7R7GyVXrSMZFRPcQYoERZxTX5HwltHpHFepsD2vykpBb\n" + + "0I4QDwKBgDRH3RjKJduH2WvHOmQmXqWwtkY7zkLwSysWTW5KvCEUI+4VHMggaQ9Z\n" + + "YUXuHa8osFZ8ruJzSd0HTrDVuNTb8Q7XADOn4a5AGHu1Bhw996uNCP075dx8IOsl\n" + + "B6zvMHB8rRW93GfFd08REpsgqSm+AL6iLlZHowC00FFPtLs9e7ci\n" + "-----END RSA PRIVATE KEY-----";`, + want: []string{"-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAuvBC2RJqGAbPg6HoJaOlT6L4tMwMzGUI8TptoBlStWe+TfRc\nuPVfxI1U6g87/7B62768kuU55H8bd3Yd7nBmmdzuNthAdPDMXlrnIbOywG52iPtH\nAV1U5Vk5QGuj39aSuLjpBSC4jUJPcdJENpmECVX+EeNwZlOEDfbtnpOTMRr/24r1\nCLSMwp9gtaLnE6NJzh+ycTDgyrWK9OtNA+UqzwfNJ9BfE53u9JHJP/nWZopqlNQ2\n6fgPASu8FULa8bmJ3kc0SZFCNvXyjZn7HVCwIno/ZEq7oN9tphmAPBwdfQhb2xmD\n3gYeWrXNP/M+SKisaX1CVwaPPowjCQMbsmfC2wIDAQABAoIBAGtODOEzq8i86BMk\nNfCdHgA3iVGmq1YMTPTDWDgFMS/GLDvtH+hfmShnBC4SrpsXv34x32bmw7OArtCE\n8atzw8FgSzEaMu2tZ3Jl9bSnxNymy83XhyumWlwIOk/bOcb8EV6NbdyuqqETRi0M\nyHEa7+q3/M5h4pwqJmwpqL5U8bHGVGXNEbiA/TneNyXjSn03uPYaKTw4R9EG951A\npCJf4Atba5VIfdZ59fx/6rxCuKjWlvZrklE3Cll/+A0dRN5vBSR+EBYgfedMPepM\n6TYDOsQnsy1bFJjy+aE/kwYGgtjuHOlvCpwq90SY3WueXClDfioaJ/1S6QT3q8hf\nUHodWxkCgYEA8X6+dybVvBgawxyYZEi1P/KNWC9tr2zdztnkDB4nn97UIJzxmjTh\ns81EsX0Mt24DJg36HoX5x1lDHNrR2RvIEPy8vfzTdNVa6KP7E7CWUUcW39nmt/z7\nezlyZa8TVPBE/xvozdZuTAzd0rafUX3Ugqzn17MBshz07/K4Z0iy/C0CgYEAxiqm\nJ7ul9CmNVvCnQ19tvcO7kY8h9AYIEtrqf9ubiq9W7Ldf9mXIhlG3wr6U3dXuAVVa\n4g9zkXr+N7BE4hlQcJpBn5ywtYfqzK1GRy+rfwPgC/JbWEnNDP8oYnZ8R6pkhyOC\nzqDqCZPtnmD9Je/ifdmgIkkxQD25ktyCYMhPuCcCgYEAh/MQCkfEfxUay8gnSh1c\nW9mSFJjuqJki7TXgmanIKMnqpUl1AZjPjsb56uk45XJ7N0sbCV/m04C+tVnCVPS8\n1kNRhar054rMmLbnu5fnp23bxL0Ik39Jm38llXTP7zsrvGnbzzTt9sYvglXorpml\nrsLj6ZwOUlTW1tXPVeWpTSkCgYBfAkGpWRlGx8lA/p5i+dTGn5pFPmeb9GxYheba\nKDMZudkmIwD6RHBwnatJzk/XT+MNdpvdOGVDQcGyd2t/L33Wjs6ZtOkwD5suSIEi\nTiOeAQChGbBb0v5hldAJ7R7GyVXrSMZFRPcQYoERZxTX5HwltHpHFepsD2vykpBb\n0I4QDwKBgDRH3RjKJduH2WvHOmQmXqWwtkY7zkLwSysWTW5KvCEUI+4VHMggaQ9Z\nYUXuHa8osFZ8ruJzSd0HTrDVuNTb8Q7XADOn4a5AGHu1Bhw996uNCP075dx8IOsl\nB6zvMHB8rRW93GfFd08REpsgqSm+AL6iLlZHowC00FFPtLs9e7ci\n-----END RSA PRIVATE KEY-----\n"}, + }, + { + name: "multi-line concatenation (ruby)", + input: ` key_data = "-----BEGIN DSA PRIVATE KEY-----\n" + key_data += "MIIBugIBAAKBgQCUw7F/vKJT2Xsq+fIPVxNC/Dyk+dN9DWQT5RO56eIQasd+h6Fm\n" + key_data += "q1qtQrJ/DOe3VjfUrSm7NN5NoIGOrGCSuQFthFmq+9Lpt6WIykB4mau5iE5orbKM\n" + key_data += "xTfyu8LtntoikYKrlMB+UrmKDidvZ+7oWiC14imT+Px/3Q7naj0UmOrSTwIVAO25\n" + key_data += "Yf3SYNtTYv8yzaV+X9yNr/AfAoGADAcEh2bdsrDhwhXtVi1L3cFQx1KpN0B07JLr\n" + key_data += "gJzJcDLUrwmlMUmrXR2obDGfVQh46EFMeo/k3IESw2zJUS58FJW+sKZ4noSwRZPq\n" + key_data += "mpBnERKpLOTcWMxUyV8ETsz+9oz71YEMjmR1qvNYAopXf5Yy+4Zq3bgqmMMQyM+K\n" + key_data += "O1PdlCkCgYBmhSl9CVPgVMv1xO8DAHVhM1huIIK8mNFrzMJz+JXzBx81ms1kWSeQ\n" + key_data += "OC/nraaXFTBlqiQsvB8tzr4xZdbaI/QzVLKNAF5C8BJ4ScNlTIx1aZJwyMil8Nzb\n" + key_data += "+0YAsw5Ja+bEZZvEVlAYnd10qRWrPeEY1txLMmX3wDa+JvJL7fmuBgIUZoXsJnzs\n" + key_data += "+sqSEhA35Le2kC4Y1/A=\n" + key_data += "-----END DSA PRIVATE KEY-----\n"`, + want: []string{"-----BEGIN DSA PRIVATE KEY-----\nMIIBugIBAAKBgQCUw7F/vKJT2Xsq+fIPVxNC/Dyk+dN9DWQT5RO56eIQasd+h6Fm\nq1qtQrJ/DOe3VjfUrSm7NN5NoIGOrGCSuQFthFmq+9Lpt6WIykB4mau5iE5orbKM\nxTfyu8LtntoikYKrlMB+UrmKDidvZ+7oWiC14imT+Px/3Q7naj0UmOrSTwIVAO25\nYf3SYNtTYv8yzaV+X9yNr/AfAoGADAcEh2bdsrDhwhXtVi1L3cFQx1KpN0B07JLr\ngJzJcDLUrwmlMUmrXR2obDGfVQh46EFMeo/k3IESw2zJUS58FJW+sKZ4noSwRZPq\nmpBnERKpLOTcWMxUyV8ETsz+9oz71YEMjmR1qvNYAopXf5Yy+4Zq3bgqmMMQyM+K\nO1PdlCkCgYBmhSl9CVPgVMv1xO8DAHVhM1huIIK8mNFrzMJz+JXzBx81ms1kWSeQ\nOC/nraaXFTBlqiQsvB8tzr4xZdbaI/QzVLKNAF5C8BJ4ScNlTIx1aZJwyMil8Nzb\n+0YAsw5Ja+bEZZvEVlAYnd10qRWrPeEY1txLMmX3wDa+JvJL7fmuBgIUZoXsJnzs\n+sqSEhA35Le2kC4Y1/A=\n-----END DSA PRIVATE KEY-----\n"}, + }, { - name: "valid pattern - with keyword privatekey", - input: fmt.Sprintf("%s token = '%s'", keyword, validPattern), - want: []string{validPattern}, + // https://github.com/sailfishos/sailfish-secrets/blob/a066aa78078d20656068b4ab4102c57bcd13259c/plugins/exampleusbtokenplugin/exampleusbtokenplugin.cpp#L75 + name: "multi-line - encrypted header", + input: ` // The passphrase for the following RSA key.pem is "12345" which must + // be passed as the lockCode in order for the unlock operation to succeed. + const QByteArray pemData( + "-----BEGIN RSA PRIVATE KEY-----\n" + "Proc-Type: 4,ENCRYPTED\n" + "DEK-Info: AES-128-CBC,58909F0499FB07748B8159C42B84CA75\n" + "\n" + "DJyGd3AQ53uz0mwfuLZ7uQr+7W7TeS54nn7jvBfFS0MtDd5FtaKbir2FurW3fWet\n" + "HebFzg8fUCrhY+/cGN5WfKjGoCHo5hsKxuKgowoBMwsgnU0khkjQMz3Jw6h6F7KT\n" + "4SAhI02OPKQZD9g8YBzx4ui+LXpcBLS4pHf5KhY1WMq5CuPzafrqwl3jUdz1Qaiv\n" + "JBePjlCBEXlUGemDNkNR4lzk8RuCs8kZKZo1iJd3W3YHpBhs9DyErBVbTkpCT7yA\n" + "ELZ6w28pyFUbFFXm7GXhiokqjSfLFQH3MbPCUKVIbEVkHSP4FqoTDPnBdGlW+Fvq\n" + "sALyqS9/NTsJ5jXF0CV2gEum4bRMalTyqQhHVihEWkuX8CRpmAP7/eoOjhN+ydVU\n" + "ggkzxVyXRicpDBzt8r7MjmpO6zwuYmrsRwagaEh+aUokHU+Z++WelFXXai5b1uEO\n" + "wjRxsjOmPP8R+VhFyyG4VvpzPT3yU4lMav+U3Z7hsaD0UzuJAmxMOMtatl3A6Pt6\n" + "ME9p/B3ofcE0m1g9EhH7sBo6jMkrgG+pwtkIJ1xMbvYBjCPr2fTzhGgUuTkln1fp\n" + "XrwNZeYIBhYhZ95imXfzZVEQOyJc8QHS0iJciodJDkbnlwenb2TccWkhJyxtoeF9\n" + "RBmqJn5bbLjCVHRgmXj7OePAgQiYFoirQ6F/J1eKrVBSgLlR/gsPzqPisIlto9tx\n" + "GaGsstuq6TLejSa8WEq1HzPaxccjOpR6tA0f2+xc9LweLB7nEnm82EvKFukk5e0i\n" + "hVe/u6XQWw0FW11Wio2y87437BF93oytlPcHWQyB/fkS7FvMHxfnrnt2ybGbPnTL\n" + "qODt2g4IziyNQF3PiMJOzYWSJ5JG0L0A8W0FK+Pb9G2jnQBAuPIpRSqQ7yUtBGh0\n" + "slrxEGapCPZY3mccS1pLEzHBLFEUudWqhaNU8tmeBw48QBLrK2DE+kzr7bzLsdvH\n" + "b27QEkGWvF+KgPbEqBKC3d9u4z4cWNHYuLRuiaE/2MbxAQ4yGcr4acahm2gjTPZg\n" + "ajTbk50NZBj0L0AzHusODCssypnREnY/40v0VdIYNBcUf+fbSUXV5LNqblrnf6ra\n" + "B2pzk+tKqE9QOBYz7HZ3Pkq9GeIVMGKDM71jczw5dFRPY58doU22C9fQzBQuasVn\n" + "sSUusNkHOm0OM6VX2hXH/lhhZYLgvy5MSzpnSSwTv+4wFa1mzuvUJkyL4SPgZ2Nx\n" + "XQr1ss88t7qAw0bQeLNmBIbQDVtlhQ3E/5qUuhNY8/P50vt8LmiCXJ/IvGxecJgS\n" + "NtNAno1XcQ73A8Ri5d1zdu2+4GXkHUrSwVlFMZGmC3cXlO5nA8pkcVLl7vSQmib4\n" + "tzR2wfVvj1X0W/NYrcnAQ6ooymhpE8yVCKKLw94YABOtiDP89YB4hdtzbfHOsEXP\n" + "iHZB5uURv2uwE0s0f2zVRt3ryZZoF/Dgc9BD+6wcN8z/uK4ucaXuDmVJW8EX6V5U\n" + "F3LPsdi3w2rx/pauRNQpTTIFpqtIrogSkTpWmQv3kIM4+Z62Y3X9Cr/61RpTZIF5\n" + "6obcnqDfdVsOcLZIjLpXeoW3GQ7dakwe3gPwVvCEEDqNzTPosxKNCUKlzVasRECQ\n" + "-----END RSA PRIVATE KEY-----\n");`, + want: []string{"-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: AES-128-CBC,58909F0499FB07748B8159C42B84CA75\nDJyGd3AQ53uz0mwfuLZ7uQr+7W7TeS54nn7jvBfFS0MtDd5FtaKbir2FurW3fWet\nHebFzg8fUCrhY+/cGN5WfKjGoCHo5hsKxuKgowoBMwsgnU0khkjQMz3Jw6h6F7KT\n4SAhI02OPKQZD9g8YBzx4ui+LXpcBLS4pHf5KhY1WMq5CuPzafrqwl3jUdz1Qaiv\nJBePjlCBEXlUGemDNkNR4lzk8RuCs8kZKZo1iJd3W3YHpBhs9DyErBVbTkpCT7yA\nELZ6w28pyFUbFFXm7GXhiokqjSfLFQH3MbPCUKVIbEVkHSP4FqoTDPnBdGlW+Fvq\nsALyqS9/NTsJ5jXF0CV2gEum4bRMalTyqQhHVihEWkuX8CRpmAP7/eoOjhN+ydVU\nggkzxVyXRicpDBzt8r7MjmpO6zwuYmrsRwagaEh+aUokHU+Z++WelFXXai5b1uEO\nwjRxsjOmPP8R+VhFyyG4VvpzPT3yU4lMav+U3Z7hsaD0UzuJAmxMOMtatl3A6Pt6\nME9p/B3ofcE0m1g9EhH7sBo6jMkrgG+pwtkIJ1xMbvYBjCPr2fTzhGgUuTkln1fp\nXrwNZeYIBhYhZ95imXfzZVEQOyJc8QHS0iJciodJDkbnlwenb2TccWkhJyxtoeF9\nRBmqJn5bbLjCVHRgmXj7OePAgQiYFoirQ6F/J1eKrVBSgLlR/gsPzqPisIlto9tx\nGaGsstuq6TLejSa8WEq1HzPaxccjOpR6tA0f2+xc9LweLB7nEnm82EvKFukk5e0i\nhVe/u6XQWw0FW11Wio2y87437BF93oytlPcHWQyB/fkS7FvMHxfnrnt2ybGbPnTL\nqODt2g4IziyNQF3PiMJOzYWSJ5JG0L0A8W0FK+Pb9G2jnQBAuPIpRSqQ7yUtBGh0\nslrxEGapCPZY3mccS1pLEzHBLFEUudWqhaNU8tmeBw48QBLrK2DE+kzr7bzLsdvH\nb27QEkGWvF+KgPbEqBKC3d9u4z4cWNHYuLRuiaE/2MbxAQ4yGcr4acahm2gjTPZg\najTbk50NZBj0L0AzHusODCssypnREnY/40v0VdIYNBcUf+fbSUXV5LNqblrnf6ra\nB2pzk+tKqE9QOBYz7HZ3Pkq9GeIVMGKDM71jczw5dFRPY58doU22C9fQzBQuasVn\nsSUusNkHOm0OM6VX2hXH/lhhZYLgvy5MSzpnSSwTv+4wFa1mzuvUJkyL4SPgZ2Nx\nXQr1ss88t7qAw0bQeLNmBIbQDVtlhQ3E/5qUuhNY8/P50vt8LmiCXJ/IvGxecJgS\nNtNAno1XcQ73A8Ri5d1zdu2+4GXkHUrSwVlFMZGmC3cXlO5nA8pkcVLl7vSQmib4\ntzR2wfVvj1X0W/NYrcnAQ6ooymhpE8yVCKKLw94YABOtiDP89YB4hdtzbfHOsEXP\niHZB5uURv2uwE0s0f2zVRt3ryZZoF/Dgc9BD+6wcN8z/uK4ucaXuDmVJW8EX6V5U\nF3LPsdi3w2rx/pauRNQpTTIFpqtIrogSkTpWmQv3kIM4+Z62Y3X9Cr/61RpTZIF5\n6obcnqDfdVsOcLZIjLpXeoW3GQ7dakwe3gPwVvCEEDqNzTPosxKNCUKlzVasRECQ\n-----END RSA PRIVATE KEY-----\n"}, }, { - name: "invalid pattern", - input: fmt.Sprintf("%s = '%s'", keyword, invalidPattern), - want: []string{}, + name: "one line - newlines", + input: `SUPERTOKENS_APPLE_SECRET_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----`, + want: []string{"-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----\n"}, + }, + { + // https://github.com/trufflesecurity/trufflehog/issues/2338 + name: "one line - spaces", + input: `private_key=-----BEGIN RSA PRIVATE KEY----- MIICWQIBAAKBgHsSuRPLMDrxcwMB9P6ubGFGmlSvHvSXq2kfwycrcEKf/TCctShz A2HYo2IWed8n1rqazlESHnhNmCWlFWIMMFWagZyDBy9yy71MhWISvoTuQVyCx/z3 q1v171fy+Ds5smKwZ8wK3bgwBTR7BTKfYNmearDZvPJgwK0jsYEJDZ/DAgElAoGA MeT+7FlK53akP31VfAGF4j83pcp0VVI+kmbSk1bMpWN0e33M5uKE1KPvNZpowkCV UpHJQ3YMWkj4ffbRUUM2L/jQmKkICf7vynIdq5cj+lF6lNXSzwq6pVR6/octdeKS /70DuGcVG+LiRTu2mRb6mPY9bIJIvcgenXajnVanx9UCQQDRwf6oyU/EH4x+kw/X QZi/RebtDPD1yIQuhVG8B1xkPxBsAywTwVDL7DSZ1BsbWJcl5HcXt/q0n/3NZ62X Rr1VAkEAljSLsMOk5H7XCctEk3mCu1WgDtUvb/RRCBiBT+cic14OpVtytJMAeLeq cAhIj54ef4hQPGKbAsQZ3E/X4EsotwJAa7alXZfPA9jZcW4c5Ciai7wcoz3/Mhrc F+OYrKnVf5YBg5LtHua6yZT4aqswg6oIbWd7bQty5yG5rqrcmcphOQJAHGrOUd/T FnjckyZ0wfRk11VjeG2Fg+IdKwuOFgkiMYB/T7da4+R1tfk7666KRK82M82uUJ0I kdISuvpZRhwOnwJBAI34lnrN4bNcUVB5kAXT9huyH8tJomNdsJOufS3vCi5tKaqK Ic3jMIwtyuXsn4NhJNUFlgfPL70CPtb3x/eePqw= -----END RSA PRIVATE KEY-----`, + want: []string{"-----BEGIN RSA PRIVATE KEY-----\nMIICWQIBAAKBgHsSuRPLMDrxcwMB9P6ubGFGmlSvHvSXq2kfwycrcEKf/TCctShz\nA2HYo2IWed8n1rqazlESHnhNmCWlFWIMMFWagZyDBy9yy71MhWISvoTuQVyCx/z3\nq1v171fy+Ds5smKwZ8wK3bgwBTR7BTKfYNmearDZvPJgwK0jsYEJDZ/DAgElAoGA\nMeT+7FlK53akP31VfAGF4j83pcp0VVI+kmbSk1bMpWN0e33M5uKE1KPvNZpowkCV\nUpHJQ3YMWkj4ffbRUUM2L/jQmKkICf7vynIdq5cj+lF6lNXSzwq6pVR6/octdeKS\n/70DuGcVG+LiRTu2mRb6mPY9bIJIvcgenXajnVanx9UCQQDRwf6oyU/EH4x+kw/X\nQZi/RebtDPD1yIQuhVG8B1xkPxBsAywTwVDL7DSZ1BsbWJcl5HcXt/q0n/3NZ62X\nRr1VAkEAljSLsMOk5H7XCctEk3mCu1WgDtUvb/RRCBiBT+cic14OpVtytJMAeLeq\ncAhIj54ef4hQPGKbAsQZ3E/X4EsotwJAa7alXZfPA9jZcW4c5Ciai7wcoz3/Mhrc\nF+OYrKnVf5YBg5LtHua6yZT4aqswg6oIbWd7bQty5yG5rqrcmcphOQJAHGrOUd/T\nFnjckyZ0wfRk11VjeG2Fg+IdKwuOFgkiMYB/T7da4+R1tfk7666KRK82M82uUJ0I\nkdISuvpZRhwOnwJBAI34lnrN4bNcUVB5kAXT9huyH8tJomNdsJOufS3vCi5tKaqK\nIc3jMIwtyuXsn4NhJNUFlgfPL70CPtb3x/eePqw=\n-----END RSA PRIVATE KEY-----\n"}, + }, + { + name: "one line - encrypted", + input: ` # "privateKey": "-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIGepgN2Ze6asCAggA\nMBQGCCqGSIb3DQMHBAgVLnyjsNLu6ASCBMhSvz/EMaBaxfgi9Zs6RCKyEZWAQo34\nPGcdiiu1ebD7yxAQO88tV6ZJOpKJDxQDTLVB0GsPFJsmtuViSFWPxR5bHrfvL2Q/\nCcBB1HIgMByYPRf82Pg28shushae1Cn24vGmpgaLSPJ4skcF2kdLXirpKQTdUmYj\nAE1AYVPwd9r+0rsEhxaOVUr1MGBl9Af/muT/WxhIOAX/bhkeI9l6XIgRWukwjZhR\n9/CuIoOfxpsOHDcqN3lf6pB+bmTzlzVPith+WgjFEZndz63cvifha5AoErFWVZ30\nwHlWZxYgLPoyt5cI0TmTdS95DABuxu9N8aTMNYMLNx/cR+uLDpwBEnnnjNM195Li\nqur2CcP6cPg9f5AyOyIQSGrj93juN4usMDiArvaUUteBVhepUeu3Z7OrmkSf89Y9\nkMSRt+ZqXmZMIlBi3RoVD0rV4pQiH8D/NzEYH8aSrkSJwCf3fnOE39mXVg3gR9+P\n1KKuAl+oxSN916ZfOOu1Kd7LizdC7HDKDD1mnSkTr7Di+ubJ+ox4ZQcOSWUmrJu5\nMgCl1Bitgcybu5gnjO10Vo17UwE3TjzsbIyCgHw4ArMNewliVcUZRgSp2bWAvCIo\ntvZxJ/sTYiUulK5cm6VAdtYopQOo3R2N4zpV8wK6ymemx019N1OsyuqQDFmUMFlg\nAlZrI74Dkhba0JyuKe+SYQn9cqLIgYwBgUPCrQQwwoG8i/mhmBXf8T5NTyIwso03\niluof5g76f4rhJBC6/SFVR1NBS96Hsl7EjNxkR27Elx0g1tlSM0ilJhevwAQT2MM\n23Ux+CxiBOS2UzRyhx4fu91wsGLBFKdevlr43n2+PDb16baK3D8JCelxWLys/PZp\n5YzShdY35SDUM2u8/2Sc1W/RXAtUu9Az4QO2pB/xoUrFvQooM7VhlbKdWsve7u5b\nZDYhsZ/wYuAG2ixQodx6B75F+fG2TmU7LG1UWkyKtKL25FQvPGmcbvg/KExb2i5H\naPvwoDVhM6b3UNgPM9dSQKnTK8YjyVluSP86Mk2X8FYpzgHpKf+HCFEFPcLUOvE/\nUnteSGkRnebHmPFvTSna95b9ts7M6o2lW8auszt/Rc7CHlD9Ex/X6ed3ViSUKDQW\nDmmJbkMBUmcVYDWG7o2GPrJIhLJ96Jcp+YqrXZ5zuxCWw2gFqnId9WZMku1AUvsz\n7ty8smSMZarXbPgPM2Bccf1Plw4q739HKS5SrenrKo5UIuZukilyXD14Bq3mfJ55\n3Z01igk/FnaZzed+h8ciKMM6hyt5H7YszgcnHnpfF24yWMAZzO7edo0yH+RwJPT4\ndLbBVq9NbZAms4p05osnohly/BIkImKyZifayNAmdAObmW50v5MzNvssjMJrfHef\n4i9QXG/ACRYtuAAFWQdus/LdtcnxMY0TVIqm9YQXGThB9If0x2IFxsu+fH384T2C\nDPRdQ1s7+7Llb5dluoXsXNd9IJHh34/hLcgK7ftHpanETwNG3Bfd0f/juPvpPTOf\nUcgG3bDpu+a2hwUgvWlrYfqCvFCZKH+/CX2iSpmafjnQwD2EDb7EUdhd9Gb3c+dv\nfBW8dVLRFWtSfUF6gyNCnBiEbsNuyDQ7CnluIJHrDH9ilZ/d7yQZOQKeA9JFTM0x\nrYA=\n-----END ENCRYPTED PRIVATE KEY-----\n",`, + want: []string{"-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIGepgN2Ze6asCAggA\nMBQGCCqGSIb3DQMHBAgVLnyjsNLu6ASCBMhSvz/EMaBaxfgi9Zs6RCKyEZWAQo34\nPGcdiiu1ebD7yxAQO88tV6ZJOpKJDxQDTLVB0GsPFJsmtuViSFWPxR5bHrfvL2Q/\nCcBB1HIgMByYPRf82Pg28shushae1Cn24vGmpgaLSPJ4skcF2kdLXirpKQTdUmYj\nAE1AYVPwd9r+0rsEhxaOVUr1MGBl9Af/muT/WxhIOAX/bhkeI9l6XIgRWukwjZhR\n9/CuIoOfxpsOHDcqN3lf6pB+bmTzlzVPith+WgjFEZndz63cvifha5AoErFWVZ30\nwHlWZxYgLPoyt5cI0TmTdS95DABuxu9N8aTMNYMLNx/cR+uLDpwBEnnnjNM195Li\nqur2CcP6cPg9f5AyOyIQSGrj93juN4usMDiArvaUUteBVhepUeu3Z7OrmkSf89Y9\nkMSRt+ZqXmZMIlBi3RoVD0rV4pQiH8D/NzEYH8aSrkSJwCf3fnOE39mXVg3gR9+P\n1KKuAl+oxSN916ZfOOu1Kd7LizdC7HDKDD1mnSkTr7Di+ubJ+ox4ZQcOSWUmrJu5\nMgCl1Bitgcybu5gnjO10Vo17UwE3TjzsbIyCgHw4ArMNewliVcUZRgSp2bWAvCIo\ntvZxJ/sTYiUulK5cm6VAdtYopQOo3R2N4zpV8wK6ymemx019N1OsyuqQDFmUMFlg\nAlZrI74Dkhba0JyuKe+SYQn9cqLIgYwBgUPCrQQwwoG8i/mhmBXf8T5NTyIwso03\niluof5g76f4rhJBC6/SFVR1NBS96Hsl7EjNxkR27Elx0g1tlSM0ilJhevwAQT2MM\n23Ux+CxiBOS2UzRyhx4fu91wsGLBFKdevlr43n2+PDb16baK3D8JCelxWLys/PZp\n5YzShdY35SDUM2u8/2Sc1W/RXAtUu9Az4QO2pB/xoUrFvQooM7VhlbKdWsve7u5b\nZDYhsZ/wYuAG2ixQodx6B75F+fG2TmU7LG1UWkyKtKL25FQvPGmcbvg/KExb2i5H\naPvwoDVhM6b3UNgPM9dSQKnTK8YjyVluSP86Mk2X8FYpzgHpKf+HCFEFPcLUOvE/\nUnteSGkRnebHmPFvTSna95b9ts7M6o2lW8auszt/Rc7CHlD9Ex/X6ed3ViSUKDQW\nDmmJbkMBUmcVYDWG7o2GPrJIhLJ96Jcp+YqrXZ5zuxCWw2gFqnId9WZMku1AUvsz\n7ty8smSMZarXbPgPM2Bccf1Plw4q739HKS5SrenrKo5UIuZukilyXD14Bq3mfJ55\n3Z01igk/FnaZzed+h8ciKMM6hyt5H7YszgcnHnpfF24yWMAZzO7edo0yH+RwJPT4\ndLbBVq9NbZAms4p05osnohly/BIkImKyZifayNAmdAObmW50v5MzNvssjMJrfHef\n4i9QXG/ACRYtuAAFWQdus/LdtcnxMY0TVIqm9YQXGThB9If0x2IFxsu+fH384T2C\nDPRdQ1s7+7Llb5dluoXsXNd9IJHh34/hLcgK7ftHpanETwNG3Bfd0f/juPvpPTOf\nUcgG3bDpu+a2hwUgvWlrYfqCvFCZKH+/CX2iSpmafjnQwD2EDb7EUdhd9Gb3c+dv\nfBW8dVLRFWtSfUF6gyNCnBiEbsNuyDQ7CnluIJHrDH9ilZ/d7yQZOQKeA9JFTM0x\nrYA=\n-----END ENCRYPTED PRIVATE KEY-----\n"}, + }, + { + name: "one line - orphaned header", + input: `# Should Match +# Should capture private key headers +-----BEGIN PGP PRIVATE KEY----- + +# Should Match +# Should capture private key headers +-----BEGIN OPENSSH PRIVATE KEY-----\n0b3d576ba5a108c3b7374142bfd029920b3d576ba5a108c3b7374142bfd029920b3d576ba5a108c3b7374142bfd02992\n-----END OPENSSH PRIVATE KEY----- +`, + want: []string{"-----BEGIN OPENSSH PRIVATE KEY-----\n0b3d576ba5a108c3b7374142bfd029920b3d576ba5a108c3b7374142bfd029920b3d576ba5a108c3b7374142bfd02992\n-----END OPENSSH PRIVATE KEY-----\n"}, + }, + + // Invalid + { + name: "invalid - content", + input: ` "jwt-auth": { + "key": "user-key", + "public_key": "-----BEGIN PUBLIC KEY-----\n……\n-----END PUBLIC KEY-----", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\n……\n-----END RSA PRIVATE KEY-----", + "algorithm": "RS256" + }`, }, } diff --git a/pkg/detectors/privatekey/ssh_integration.go b/pkg/detectors/privatekey/ssh.go similarity index 100% rename from pkg/detectors/privatekey/ssh_integration.go rename to pkg/detectors/privatekey/ssh.go diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 15b0dafb2a37..dba1a2d64f4b 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -11,7 +11,7 @@ import ( lru "github.com/hashicorp/golang-lru/v2" "google.golang.org/protobuf/proto" - + "github.com/trufflesecurity/trufflehog/v3/pkg/common" "github.com/trufflesecurity/trufflehog/v3/pkg/config" "github.com/trufflesecurity/trufflehog/v3/pkg/context"