106 lines
3.4 KiB
Go
106 lines
3.4 KiB
Go
package scan
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"tea.karlcom.de/karl/bucketeer/internal/dns"
|
||
|
|
"tea.karlcom.de/karl/bucketeer/internal/model"
|
||
|
|
"tea.karlcom.de/karl/bucketeer/internal/probe"
|
||
|
|
)
|
||
|
|
|
||
|
|
type fakeProber struct {
|
||
|
|
last model.Candidate
|
||
|
|
state model.ExposureState
|
||
|
|
detail string
|
||
|
|
listable bool
|
||
|
|
obj bool
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fakeProber) ProbeDetailed(ctx context.Context, c model.Candidate) probe.Result {
|
||
|
|
f.last = c
|
||
|
|
return probe.Result{Exposure: f.state, Detail: f.detail, Listable: f.listable, ObjectReadable: f.obj}
|
||
|
|
}
|
||
|
|
|
||
|
|
type fakeLister struct{ entries []model.Entry }
|
||
|
|
|
||
|
|
func (f *fakeLister) List(ctx context.Context, c model.Candidate) ([]model.Entry, error) {
|
||
|
|
return f.entries, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
type fakeEnricher struct{ chain []string }
|
||
|
|
|
||
|
|
func (f *fakeEnricher) Resolve(ctx context.Context, host string) (dns.Result, bool) {
|
||
|
|
return dns.Result{Chain: append([]string{host}, f.chain...)}, true
|
||
|
|
}
|
||
|
|
|
||
|
|
type fakeStore struct {
|
||
|
|
ensured []model.Candidate
|
||
|
|
entries int
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fakeStore) EnsureAsset(ctx context.Context, c model.Candidate) (int64, error) {
|
||
|
|
f.ensured = append(f.ensured, c)
|
||
|
|
return int64(len(f.ensured)), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fakeStore) ReplaceEntries(ctx context.Context, provider, service, host string, entries []model.Entry) (int, error) {
|
||
|
|
f.entries = len(entries)
|
||
|
|
return len(entries), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fakeStore) ListProbeTargets(ctx context.Context, exposure string, limit int) ([]model.ProbeTarget, error) {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (f *fakeStore) UpdateExposure(ctx context.Context, provider, service, host string, state model.ExposureState, detail string, listable, objectReadable bool) error {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestScanDirectReadable(t *testing.T) {
|
||
|
|
pb := &fakeProber{state: model.ExposurePublicRead, detail: "s3 status=200", listable: true}
|
||
|
|
st := &fakeStore{}
|
||
|
|
s := &Scanner{Prober: pb, Indexer: &fakeLister{entries: []model.Entry{{Name: "a.txt"}, {Name: "b.txt"}}}, Store: st}
|
||
|
|
res := s.Scan(context.Background(), "bucket-01.s3-eu-west-1.amazonaws.com")
|
||
|
|
if len(res) != 1 || res[0].Exposure != "public_read" || res[0].Entries != 2 || len(res[0].Sample) != 2 {
|
||
|
|
t.Fatalf("unexpected result: %+v", res)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestScanAzureContainerPath(t *testing.T) {
|
||
|
|
pb := &fakeProber{state: model.ExposurePublicRead, detail: "azure container 'images' is publicly listable", listable: true}
|
||
|
|
s := &Scanner{Prober: pb, Indexer: &fakeLister{}, Store: &fakeStore{}}
|
||
|
|
res := s.Scan(context.Background(), "https://eroticfeeldev.blob.core.windows.net/images")
|
||
|
|
if len(res) != 1 || res[0].Service != "blob_storage" {
|
||
|
|
t.Fatalf("unexpected result: %+v", res)
|
||
|
|
}
|
||
|
|
if pb.last.Container != "images" {
|
||
|
|
t.Fatalf("container hint not passed: %+v", pb.last)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestScanViaCNAME(t *testing.T) {
|
||
|
|
pb := &fakeProber{state: model.ExposurePublicRead, listable: true}
|
||
|
|
st := &fakeStore{}
|
||
|
|
s := &Scanner{
|
||
|
|
Prober: pb,
|
||
|
|
Indexer: &fakeLister{},
|
||
|
|
Store: st,
|
||
|
|
Enricher: &fakeEnricher{chain: []string{"veteran1.nyc3.digitaloceanspaces.com"}},
|
||
|
|
}
|
||
|
|
res := s.Scan(context.Background(), "cdn.mydomain.example")
|
||
|
|
if len(res) != 1 || res[0].Provider != "digitalocean" {
|
||
|
|
t.Fatalf("expected digitalocean via CNAME, got %+v", res)
|
||
|
|
}
|
||
|
|
if len(res[0].CNAMEChain) < 2 {
|
||
|
|
t.Fatalf("expected CNAME chain, got %+v", res[0].CNAMEChain)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestScanUnknown(t *testing.T) {
|
||
|
|
s := &Scanner{Prober: &fakeProber{}, Store: &fakeStore{}}
|
||
|
|
res := s.Scan(context.Background(), "www.example.com")
|
||
|
|
if len(res) != 1 || res[0].Error == "" {
|
||
|
|
t.Fatalf("expected error result, got %+v", res)
|
||
|
|
}
|
||
|
|
}
|