Lint / golangci-lint (push) Waiting to run
Lint / semgrep (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
We want to move the detector types out of the Scanning team purview. So I split off detector types into its own proto file (so that file detector_type.proto can be owned by the Integrations team), regenerated the pb files with "make protos", and made the detector files use the new generated detector_type.pb.go. Included the new detector_type.proto file in CODEOWNERS and made CODEOWNERS categories that contain larger teams be towards the top so that more fine grained ownership is filtered properly.
320 lines
7.2 KiB
Go
320 lines
7.2 KiB
Go
//go:build detectors && integration
|
|
// +build detectors,integration
|
|
|
|
package ldap
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
"github.com/kylelemons/godebug/pretty"
|
|
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
|
|
|
|
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detector_typepb"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
code, err := runMain(m)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
os.Exit(code)
|
|
}
|
|
|
|
func runMain(m *testing.M) (int, error) {
|
|
if err := startOpenLDAP(); err != nil {
|
|
return 0, err
|
|
}
|
|
defer stopOpenLDAP()
|
|
return m.Run(), nil
|
|
}
|
|
|
|
func dockerLogLine(hash string, needle string) chan struct{} {
|
|
ch := make(chan struct{}, 1)
|
|
go func() {
|
|
for {
|
|
out, err := exec.Command("docker", "logs", hash).CombinedOutput()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if strings.Contains(string(out), needle) {
|
|
ch <- struct{}{}
|
|
return
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
func TestLdap_Integration_FromChunk(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
data []byte
|
|
verify bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
s Scanner
|
|
args args
|
|
want []detectors.Result
|
|
wantErr bool
|
|
wantVerificationErr bool
|
|
}{
|
|
{
|
|
name: "found with URI and separate user+password usage, verified",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`
|
|
ldap://localhost:1389
|
|
binddn="cn=admin,dc=example,dc=org"
|
|
pass="P@55w0rd"`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: true,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "found with URI and separate user+password usage, unverified",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`
|
|
ldap://localhost:1389
|
|
binddn="cn=someuser,dc=example,dc=org"
|
|
pass="P@55w0rd"`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: false,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "found with IAD lib usage, verified",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`Set ou = dso.OpenDSObject("LDAP://localhost:1389", "cn=admin,dc=example,dc=org", "P@55w0rd", 1)`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: true,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "found with IAD lib usage, verified",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`Set ou = dso.OpenDSObject("LDAP://localhost:1389", "cn=admin,dc=example,dc=org", "P@55w0rd", 1)`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: true,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "found with IAD lib usage, unverified",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`Set ou = dso.OpenDSObject("LDAP://localhost:1389", "cn=admin,dc=example,dc=org", "invalid", 1)`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: false,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "inaccessible host",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`
|
|
ldap://badhost:1389
|
|
binddn="cn=admin,dc=example,dc=org"
|
|
pass="P@55w0rd"`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: false,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
wantVerificationErr: true,
|
|
},
|
|
{
|
|
name: "not found",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte("You cannot find the secret within"),
|
|
verify: true,
|
|
},
|
|
want: nil,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := Scanner{}
|
|
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Ldap.FromData() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
for i := range got {
|
|
if len(got[i].Raw) == 0 {
|
|
t.Fatalf("no raw secret present: \n %+v", got[i])
|
|
}
|
|
if (got[i].VerificationError() != nil) != tt.wantVerificationErr {
|
|
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError())
|
|
}
|
|
}
|
|
ignoreOpts := cmpopts.IgnoreFields(detectors.Result{}, "Raw", "verificationError")
|
|
if diff := cmp.Diff(got, tt.want, ignoreOpts); diff != "" {
|
|
t.Errorf("Ldap.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
var containerID string
|
|
|
|
func startOpenLDAP() error {
|
|
cmd := exec.Command(
|
|
"docker", "run", "--rm", "-p", "1389:1389",
|
|
"-e", "LDAP_ROOT=dc=example,dc=org",
|
|
"-e", "LDAP_ADMIN_USERNAME=admin",
|
|
"-e", "LDAP_ADMIN_PASSWORD=P@55w0rd",
|
|
"-d", "bitnami/openldap:latest",
|
|
)
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
containerID = string(bytes.TrimSpace(out))
|
|
select {
|
|
case <-dockerLogLine(containerID, "slapd starting"):
|
|
return nil
|
|
case <-time.After(30 * time.Second):
|
|
stopOpenLDAP()
|
|
return errors.New("timeout waiting for ldap service to be ready")
|
|
}
|
|
}
|
|
|
|
func stopOpenLDAP() {
|
|
exec.Command("docker", "kill", containerID).Run()
|
|
}
|
|
|
|
func TestLdap_FromChunk(t *testing.T) {
|
|
type args struct {
|
|
ctx context.Context
|
|
data []byte
|
|
verify bool
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
s Scanner
|
|
args args
|
|
want []detectors.Result
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "found with IAD lib usage, unverified",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte(`Set ou = dso.OpenDSObject("LDAP://DC.business.com/OU=IT,DC=Business,DC=com", "Business\administrator", "Pa$$word01", 1)`),
|
|
verify: true,
|
|
},
|
|
want: []detectors.Result{
|
|
{
|
|
DetectorType: detector_typepb.DetectorType_LDAP,
|
|
Verified: false,
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "not found",
|
|
s: Scanner{},
|
|
args: args{
|
|
ctx: context.Background(),
|
|
data: []byte("You cannot find the secret within"),
|
|
verify: true,
|
|
},
|
|
want: nil,
|
|
wantErr: false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := Scanner{}
|
|
got, err := s.FromData(tt.args.ctx, tt.args.verify, tt.args.data)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Ldap.FromData() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
for i := range got {
|
|
if len(got[i].Raw) == 0 {
|
|
t.Fatalf("no raw secret present: \n %+v", got[i])
|
|
}
|
|
got[i].Raw = nil
|
|
}
|
|
if diff := pretty.Compare(got, tt.want); diff != "" {
|
|
t.Errorf("Ldap.FromData() %s diff: (-got +want)\n%s", tt.name, diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkFromData(benchmark *testing.B) {
|
|
ctx := context.Background()
|
|
s := Scanner{}
|
|
for name, data := range detectors.MustGetBenchmarkData() {
|
|
benchmark.Run(name, func(b *testing.B) {
|
|
b.ResetTimer()
|
|
for n := 0; n < b.N; n++ {
|
|
_, err := s.FromData(ctx, false, data)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|