github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/pvl/helpers_test.go (about)

     1  // Copyright 2016 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package pvl
     5  
     6  import (
     7  	b64 "encoding/base64"
     8  	"log"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/PuerkitoBio/goquery"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  )
    15  
    16  func check(err error) {
    17  	if err != nil {
    18  		log.Panicf("checked err: %v", err)
    19  	}
    20  }
    21  
    22  func sampleState() scriptState {
    23  	sigBody := []byte{1, 2, 3, 4, 5}
    24  
    25  	var sampleState = scriptState{
    26  		WhichScript: 0,
    27  		PC:          0,
    28  		Service:     keybase1.ProofType_TWITTER,
    29  		Regs:        *newNamedRegsStore(),
    30  		Sig:         sigBody,
    31  		HasFetched:  false,
    32  		FetchResult: nil,
    33  	}
    34  
    35  	check(sampleState.Regs.Set("username_service", "kronk"))
    36  	check(sampleState.Regs.Set("username_keybase", "kronk_on_kb"))
    37  	check(sampleState.Regs.Set("sig", b64.StdEncoding.EncodeToString(sigBody)))
    38  	check(sampleState.Regs.Set("sig_id_medium", "sig%{sig_id_medium}.*$(^)\\/"))
    39  	check(sampleState.Regs.Set("sig_id_short", "000"))
    40  	check(sampleState.Regs.Set("hostname", "%{sig_id_medium}"))
    41  	check(sampleState.Regs.Set("protocol", "http"))
    42  	check(sampleState.Regs.Ban("banned"))
    43  	check(sampleState.Regs.Set("restuff", "[(x)]"))
    44  
    45  	return sampleState
    46  }
    47  
    48  type serviceToStringTest struct {
    49  	service    keybase1.ProofType
    50  	name       string
    51  	shouldwork bool
    52  	status     keybase1.ProofStatus
    53  }
    54  
    55  var serviceToStringTests = []serviceToStringTest{
    56  	{keybase1.ProofType_DNS, "dns", true, keybase1.ProofStatus_NONE},
    57  	{keybase1.ProofType_GENERIC_WEB_SITE, "generic_web_site", true, 0},
    58  	{keybase1.ProofType(-12), "", false, keybase1.ProofStatus_INVALID_PVL},
    59  }
    60  
    61  func TestServiceToString(t *testing.T) {
    62  	for i, test := range serviceToStringTests {
    63  		name, err := serviceToString(test.service)
    64  		switch {
    65  		case (err == nil) != test.shouldwork:
    66  			t.Fatalf("%v err %v", i, err)
    67  		case !test.shouldwork && (err.GetProofStatus() != test.status):
    68  			t.Fatalf("%v status %v", i, err.GetProofStatus())
    69  		case test.name != name:
    70  			t.Fatalf("%v name %v %v", i, test.name, name)
    71  		}
    72  	}
    73  }
    74  
    75  var substituteTests = []struct {
    76  	shouldwork  bool
    77  	regexEscape bool
    78  	service     keybase1.ProofType
    79  	a, b        string
    80  }{
    81  	{true, true, keybase1.ProofType_TWITTER,
    82  		"%{username_service}", "kronk"},
    83  	{true, true, keybase1.ProofType_GENERIC_WEB_SITE,
    84  		"%{hostname}", "%\\{sig_id_medium\\}"},
    85  	{true, true, keybase1.ProofType_TWITTER,
    86  		"x%{username_service}y%{sig_id_short}z", "xkronky000z"},
    87  	{true, true, keybase1.ProofType_TWITTER,
    88  		"http://git(?:hub)?%{username_service}/%20%%{sig_id_short}}{}", "http://git(?:hub)?kronk/%20%000}{}"},
    89  	{true, true, keybase1.ProofType_DNS,
    90  		"^%{hostname}/(?:.well-known/keybase.txt|keybase.txt)$", "^%\\{sig_id_medium\\}/(?:.well-known/keybase.txt|keybase.txt)$"},
    91  	{true, true, keybase1.ProofType_TWITTER,
    92  		"^.*%{sig_id_short}.*$", "^.*000.*$"},
    93  	{true, true, keybase1.ProofType_TWITTER,
    94  		"^keybase-site-verification=%{sig_id_short}$", "^keybase-site-verification=000$"},
    95  	{true, true, keybase1.ProofType_TWITTER,
    96  		"^%{sig_id_medium}$", "^sig%\\{sig_id_medium\\}\\.\\*\\$\\(\\^\\)\\\\/$"},
    97  	{true, true, keybase1.ProofType_TWITTER,
    98  		"%{username_keybase}:%{sig}", "kronk_on_kb:AQIDBAU="},
    99  
   100  	{false, true, keybase1.ProofType_TWITTER,
   101  		"%{}", "%{}"},
   102  	{false, true, keybase1.ProofType_TWITTER,
   103  		"%{unset}", ""},
   104  
   105  	{false, true, keybase1.ProofType_TWITTER,
   106  		"%{banned}", ""},
   107  
   108  	// regex escape
   109  	{true, true, keybase1.ProofType_TWITTER,
   110  		"%{restuff}", "\\[\\(x\\)\\]"},
   111  	{true, false, keybase1.ProofType_TWITTER,
   112  		"%{restuff}", "[(x)]"},
   113  }
   114  
   115  func TestSubstitute(t *testing.T) {
   116  	for i, test := range substituteTests {
   117  		state := sampleState()
   118  		state.Service = test.service
   119  		var res string
   120  		var err error
   121  		if test.regexEscape {
   122  			res, err = substituteReEscape(test.a, state)
   123  		} else {
   124  			res, err = substituteExact(test.a, state)
   125  		}
   126  		if (err == nil) != test.shouldwork {
   127  			t.Fatalf("%v error mismatch: %v ; %v ; '%v'", i, test.shouldwork, err, res)
   128  		}
   129  		if err == nil && res != test.b {
   130  			t.Logf("%v lens: %v %v", i, len(res), len(test.b))
   131  			t.Fatalf("%v wrong substitute result\n%v\n%v\n%v",
   132  				i, test.a, res, test.b)
   133  		}
   134  	}
   135  }
   136  
   137  var selectionContentsDocument = makeHTMLDangerous(`
   138  <html>
   139  <head></head><div>a<span class="x" data-foo="y">b</span></div><div data-foo="z">c</div>
   140  <p>hea<!--vy han-->ded</p>
   141  </html>
   142  `)
   143  
   144  type selectionContentsTest struct {
   145  	html     *goquery.Document
   146  	selector string
   147  	contents bool
   148  	attr     string
   149  	data     bool
   150  	out      string
   151  }
   152  
   153  var selectionContentsTests = []selectionContentsTest{
   154  	{selectionContentsDocument, "div", false, "", false, "ab c"},
   155  	{selectionContentsDocument, "span", false, "", false, "b"},
   156  	{selectionContentsDocument, "span", false, "data-foo", false, "y"},
   157  	{selectionContentsDocument, "div", false, "data-foo", false, "z"},
   158  	{selectionContentsDocument, "span", false, "data-bar", false, ""},
   159  	{selectionContentsDocument, "div", false, "data-baz", false, ""},
   160  	{selectionContentsDocument, "p", false, "", false, "headed"},
   161  	{selectionContentsDocument, "p", true, "", true, "hea vy han ded"},
   162  }
   163  
   164  func TestSelectionContents(t *testing.T) {
   165  	for i, test := range selectionContentsTests {
   166  		sel := test.html.Find(test.selector)
   167  		if test.contents {
   168  			sel = sel.Contents()
   169  		}
   170  		var out string
   171  		switch {
   172  		case test.attr != "":
   173  			out = selectionAttr(sel, test.attr)
   174  		case test.data:
   175  			out = selectionData(sel)
   176  		default:
   177  			out = selectionText(sel)
   178  		}
   179  		if out != test.out {
   180  			t.Fatalf("%v mismatch\n'%v'\n'%v'", i, out, test.out)
   181  		}
   182  	}
   183  }
   184  
   185  func TestValidateDomain(t *testing.T) {
   186  	tests := []struct {
   187  		s  string
   188  		ok bool
   189  	}{
   190  		// allow domains
   191  		{"example.com", true},
   192  		{"www.example.com", true},
   193  		{"com.", true},
   194  		{"0x0f.example.com", true},
   195  
   196  		// disallow ports, paths, protocols, and other junk
   197  		{"example.com:8080", false},
   198  		{"www.example.com:8080", false},
   199  		{"http://example.com", false},
   200  		{"example.com/", false},
   201  		{"example.com/123", false},
   202  		{"example.com?a=b", false},
   203  		{"example.com#2", false},
   204  		{"http://http://", false},
   205  		{"http://http://example.com", false},
   206  		{"http://http:/example.com", false},
   207  		{"http://ht$%$&$tp:/example.com", false},
   208  
   209  		// disallow ips, even when weirdly formatted
   210  		{"8.8.8.8", false},
   211  		{"8.8.8.8.", false},
   212  		{"8.8.8.00008", false},
   213  		{"8.8.8.", false},
   214  		{"8.8.8.8/24", false},
   215  		{"8.8.8/24", false},
   216  		{"8.", false},
   217  		{"8", false},
   218  		{"8.8.8", false},
   219  		{"2001:db8:a0b:12f0::1", false},
   220  		{"::21", false},
   221  		{":21:", false},
   222  		{":21:", false},
   223  		{"2001:db8:a0b:12f0::1%eth0", false},
   224  		{"[2001:db8:a0b:12f0::1]:21", false},
   225  	}
   226  
   227  	for i, test := range tests {
   228  		ans := validateDomain(test.s)
   229  		if ans != test.ok {
   230  			t.Fatalf("%v mismatch: %v\ngot      : %v\nexpected : %v\n", i, test.s, ans, test.ok)
   231  		}
   232  	}
   233  }
   234  
   235  func TestValidateProtocol(t *testing.T) {
   236  	tests := []struct {
   237  		s        string
   238  		allowed  []string
   239  		expected string
   240  		ok       bool
   241  	}{
   242  		{"http", []string{"http", "https"}, "http", true},
   243  		{"http:", []string{"http", "https"}, "http", true},
   244  		{"https:", []string{"http", "https"}, "https", true},
   245  
   246  		{"http", []string{"https"}, "http", false},
   247  		{"dns", []string{"http", "https"}, "dns", false},
   248  
   249  		{"spdy", []string{"http", "https"}, "", false},
   250  	}
   251  
   252  	for i, test := range tests {
   253  		a, b := validateProtocol(test.s, test.allowed)
   254  		if !(a == test.expected && b == test.ok) {
   255  			t.Fatalf("%v mismatch: %v\ngot      : %v %v\nexpected : %v %v\n",
   256  				i, test.s, test.expected, test.ok, a, b)
   257  		}
   258  	}
   259  }
   260  
   261  // Test helpers
   262  
   263  func makeHTMLDangerous(html string) *goquery.Document {
   264  	reader := strings.NewReader(html)
   265  	d, err := goquery.NewDocumentFromReader(reader)
   266  	if err != nil {
   267  		log.Panic(err)
   268  	}
   269  	return d
   270  }