Skip to content

Commit

Permalink
feat(privatekey): run checks concurrently (#2139)
Browse files Browse the repository at this point in the history
  • Loading branch information
rgmz authored Dec 10, 2023
1 parent 331336d commit 6c5fc2f
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 40 deletions.
129 changes: 90 additions & 39 deletions pkg/detectors/privatekey/privatekey.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"regexp"
"strings"
"sync"
"time"

"github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
Expand Down Expand Up @@ -37,16 +38,12 @@ func (s Scanner) Keywords() []string {
}

// 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) ([]detectors.Result, error) {
var results []detectors.Result
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)

matches := keyPat.FindAllString(dataStr, -1)

for _, match := range matches {

token := normalize(match)

if len(token) < 64 {
continue
}
Expand All @@ -55,10 +52,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]dete
DetectorType: detectorspb.DetectorType_PrivateKey,
Raw: []byte(token),
Redacted: token[0:64],
ExtraData: make(map[string]string),
}

s1.ExtraData = make(map[string]string)

var passphrase string
parsedKey, err := ssh.ParseRawPrivateKey([]byte(token))
if err != nil && strings.Contains(err.Error(), "private key is passphrase protected") {
Expand All @@ -82,43 +78,64 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) ([]dete
}

if verify {
var verificationErrors []string
data, err := lookupFingerprint(fingerprint, s.IncludeExpired)
if err == nil {
if data != nil {
s1.Verified = true
s1.ExtraData["certificate_urls"] = strings.Join(data.CertificateURLs, ", ")
var (
wg sync.WaitGroup
extraData = newExtraData()
verificationErrors = newVerificationErrors()
)

// Look up certificate information.
wg.Add(1)
go func() {
defer wg.Done()
data, err := lookupFingerprint(fingerprint, s.IncludeExpired)
if err == nil {
if data != nil {
extraData.Add("certificate_urls", strings.Join(data.CertificateURLs, ", "))
}
} else {
verificationErrors.Add(err)
}
} else {
verificationErrors = append(verificationErrors, err.Error())
}
}()

// Test SSH key against github.com
wg.Add(1)
go func() {
defer wg.Done()
user, err := verifyGitHubUser(parsedKey)
if err != nil && !errors.Is(err, errPermissionDenied) {
verificationErrors.Add(err)
}
if user != nil {
extraData.Add("github_user", *user)
}
}()

// Test SSH key against gitlab.com
wg.Add(1)
go func() {
defer wg.Done()
user, err := verifyGitLabUser(parsedKey)
if err != nil && !errors.Is(err, errPermissionDenied) {
verificationErrors.Add(err)
}
if user != nil {
extraData.Add("gitlab_user", *user)
}
}()

user, err := verifyGitHubUser(parsedKey)
if err != nil && !errors.Is(err, errPermissionDenied) {
verificationErrors = append(verificationErrors, err.Error())
}
if user != nil {
wg.Wait()
if len(extraData.data) > 0 {
s1.Verified = true
s1.ExtraData["github_user"] = *user
}

user, err = verifyGitLabUser(parsedKey)
if err != nil && !errors.Is(err, errPermissionDenied) {
verificationErrors = append(verificationErrors, err.Error())
for k, v := range extraData.data {
s1.ExtraData[k] = v
}
} else {
s1.ExtraData = nil
}
if user != nil {
s1.Verified = true
s1.ExtraData["gitlab_user"] = *user
if len(verificationErrors.errors) > 0 {
s1.SetVerificationError(fmt.Errorf("verification failures: %s", strings.Join(verificationErrors.errors, ", ")), token)
}

if !s1.Verified && len(verificationErrors) > 0 {
err = fmt.Errorf("verification failures: %s", strings.Join(verificationErrors, ", "))
s1.SetVerificationError(err, token)
}
}

if len(s1.ExtraData) == 0 {
s1.ExtraData = nil
}

results = append(results, s1)
Expand Down Expand Up @@ -179,6 +196,40 @@ type DriftwoodResult struct {
} `json:"GitHubSSHResults"`
}

type extraData struct {
mutex sync.Mutex
data map[string]string
}

func newExtraData() *extraData {
return &extraData{
data: make(map[string]string),
}
}

func (e *extraData) Add(key string, value string) {
e.mutex.Lock()
e.data[key] = value
e.mutex.Unlock()
}

type verificationErrors struct {
mutex sync.Mutex
errors []string
}

func newVerificationErrors() *verificationErrors {
return &verificationErrors{
errors: make([]string, 0, 3),
}
}

func (e *verificationErrors) Add(err error) {
e.mutex.Lock()
e.errors = append(e.errors, err.Error())
e.mutex.Unlock()
}

func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_PrivateKey
}
2 changes: 1 addition & 1 deletion pkg/detectors/privatekey/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func firstResponseFromSSH(parsedKey any, username, hostport string) (string, err

// Verify the server fingerprint to ensure that there is no MITM replay attack
config := &ssh.ClientConfig{
Timeout: 3 * time.Second,
Timeout: 5 * time.Second,
User: username,
Auth: []ssh.AuthMethod{
ssh.PublicKeys(signer),
Expand Down

0 comments on commit 6c5fc2f

Please sign in to comment.