yellow.git


yellow

~ opinionated web scanning pipeline for pentesters in a hurry
git clone https://github.com/djnnvx/yellow.git
log | files | refs | archive | README | LICENSE

asn_test.go (3911B)

      1 package osint
      2 
      3 import (
      4 	"errors"
      5 	"net"
      6 	"testing"
      7 )
      8 
      9 func TestParseCymruOrigin(t *testing.T) {
     10 	got, ok := parseCymruOrigin("15169 | 8.8.8.0/24 | US | arin | 1992-12-01")
     11 	if !ok {
     12 		t.Fatal("real cymru record failed to parse")
     13 	}
     14 	want := asnOrigin{ASN: "15169", Prefix: "8.8.8.0/24", Country: "US", Registry: "arin"}
     15 	if got != want {
     16 		t.Errorf("got %+v, want %+v", got, want)
     17 	}
     18 }
     19 
     20 func TestParseCymruOriginMultiOrigin(t *testing.T) {
     21 	got, ok := parseCymruOrigin("1234 5678 | 194.204.0.0/18 | EE | ripencc | 1995-12-04")
     22 	if !ok || got.ASN != "1234" {
     23 		t.Errorf("multiply-originated prefix: got ASN %q, want 1234", got.ASN)
     24 	}
     25 }
     26 
     27 func TestParseCymruOriginRejectsJunk(t *testing.T) {
     28 	for _, txt := range []string{"", "   ", "no pipes here", " | 1.2.3.0/24"} {
     29 		if _, ok := parseCymruOrigin(txt); ok {
     30 			t.Errorf("parsed junk %q as a record", txt)
     31 		}
     32 	}
     33 }
     34 
     35 func TestParseShadowserverOriginFieldOrder(t *testing.T) {
     36 	got, ok := parseShadowserverOrigin("15169 | 8.8.8.0/24 | AS15169 | US | Google LLC")
     37 	if !ok {
     38 		t.Fatal("real shadowserver record failed to parse")
     39 	}
     40 	// field order differs from cymru: org is index 4, country index 3
     41 	want := asnOrigin{ASN: "15169", Prefix: "8.8.8.0/24", Country: "US", Org: "Google LLC"}
     42 	if got != want {
     43 		t.Errorf("got %+v, want %+v", got, want)
     44 	}
     45 }
     46 
     47 func TestParseCymruASName(t *testing.T) {
     48 	// AS name itself contains hyphens and commas
     49 	got := parseCymruASName("20473 | US | arin | 2001-05-11 | AS-VULTR - The Constant Company, LLC, US")
     50 	if got != "AS-VULTR - The Constant Company, LLC, US" {
     51 		t.Errorf("got %q", got)
     52 	}
     53 	if parseCymruASName("20473 | US | arin") != "" {
     54 		t.Error("short record should yield no name")
     55 	}
     56 }
     57 
     58 func TestReverseIPv4(t *testing.T) {
     59 	if got := reverseIPv4("1.2.3.4"); got != "4.3.2.1" {
     60 		t.Errorf("got %q, want 4.3.2.1", got)
     61 	}
     62 	if got := reverseIPv4("2001:4860::1"); got != "" {
     63 		t.Errorf("ipv6 should yield empty, got %q", got)
     64 	}
     65 }
     66 
     67 func TestIsHostingProviderCatchesVultr(t *testing.T) {
     68 	// RFC 5737 documentation range: cdncheck does not match it, so only the
     69 	// AS-name keyword can flag this. Same situation as Vultr in the wild.
     70 	doc := net.ParseIP("192.0.2.1")
     71 	if LookupCDNMatches(doc) {
     72 		t.Skip("cdncheck now covers the documentation range; keyword path untestable here")
     73 	}
     74 	if !isHostingProvider("AS-VULTR - The Constant Company, LLC, US", doc) {
     75 		t.Error("Vultr not flagged as hosting provider")
     76 	}
     77 	if isHostingProvider("EXAMPLE-CORP - Example Bank Inc", doc) {
     78 		t.Error("a self-hosted org was wrongly flagged as a provider")
     79 	}
     80 }
     81 
     82 func TestOriginFailsOverToSecondProvider(t *testing.T) {
     83 	calls := []string{}
     84 	a := &ASN{
     85 		lookupTXT: func(name string) ([]string, error) {
     86 			calls = append(calls, name)
     87 			if name == "4.3.2.1.origin.asn.cymru.com" {
     88 				return nil, errors.New("primary down")
     89 			}
     90 			return []string{"15169 | 8.8.8.0/24 | AS15169 | US | Google LLC"}, nil
     91 		},
     92 		providers: asnProviders,
     93 	}
     94 
     95 	origin, source, ok := a.origin("1.2.3.4")
     96 	if !ok {
     97 		t.Fatalf("failover produced no record; queried %v", calls)
     98 	}
     99 	if source != "shadowserver" {
    100 		t.Errorf("source = %q, want shadowserver", source)
    101 	}
    102 	if origin.ASN != "15169" || origin.Prefix != "8.8.8.0/24" || origin.Org != "Google LLC" {
    103 		t.Errorf("bad failover record: %+v", origin)
    104 	}
    105 }
    106 
    107 func TestOriginAllProvidersDown(t *testing.T) {
    108 	a := &ASN{
    109 		lookupTXT: func(string) ([]string, error) { return nil, errors.New("down") },
    110 		providers: asnProviders,
    111 	}
    112 	if _, _, ok := a.origin("1.2.3.4"); ok {
    113 		t.Error("all providers down must yield no record, not a bogus one")
    114 	}
    115 }
    116 
    117 func TestOriginEmptyAnswerIsNotARecord(t *testing.T) {
    118 	a := &ASN{
    119 		lookupTXT: func(string) ([]string, error) { return []string{""}, nil },
    120 		providers: asnProviders,
    121 	}
    122 	if _, _, ok := a.origin("192.168.1.1"); ok {
    123 		t.Error("private IP with empty TXT must yield no record")
    124 	}
    125 }
    126 
    127 func LookupCDNMatches(ip net.IP) bool {
    128 	return isHostingProvider("", ip)
    129 }