github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/pvl/interp_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  	"fmt"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/PuerkitoBio/goquery"
    12  	libkb "github.com/keybase/client/go/libkb"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  	jsonw "github.com/keybase/go-jsonw"
    15  )
    16  
    17  type interpUnitTest struct {
    18  	name      string
    19  	proofinfo ProofInfo
    20  	// Only one of prepvl or prepvlstr should be specified.
    21  	prepvl    map[keybase1.ProofType]string
    22  	prepvlstr string
    23  	service   keybase1.ProofType
    24  
    25  	// What api call to expect (for non-DNS)
    26  	restype libkb.XAPIResType
    27  	resjson string
    28  	reshtml string
    29  	restext string
    30  	resdns  map[string]([]string)
    31  	// (Optional) Expect a different url to be fetched than proofinfo.APIURL
    32  	urloverride string
    33  	// (Optional) Don't check that the xapi was hit exactly once.
    34  	allowmanyfetches bool
    35  
    36  	// Whether the proof should validate
    37  	shouldwork bool
    38  	// (Optional) error status must equal. Must be specified for INVALID_PVL
    39  	errstatus keybase1.ProofStatus
    40  	// (Optional) error string must equal
    41  	errstr string
    42  }
    43  
    44  var interpUnitTests = []interpUnitTest{
    45  	// There are a lot of these tests in a line.
    46  	// They are organized into sections with markdown-style headings.
    47  	// All tests are run by the single runPvlTest function. If you want to run
    48  	// just one test, set the `solo` variable there.
    49  
    50  	// # Basic tests
    51  	{
    52  		name:      "basichtml",
    53  		proofinfo: info1,
    54  		prepvl: map[keybase1.ProofType]string{
    55  			keybase1.ProofType_TWITTER: `[[
    56  {"assert_regex_match": {
    57    "pattern": "^https://rooter\\.example\\.com/proofs/%{username_service}/[\\d]+\\.htjsxt$",
    58    "from": "hint_url" } },
    59  {"fetch": {
    60    "kind": "html",
    61    "from": "hint_url" } },
    62  {"selector_css": {
    63    "selectors": [".twit", 0],
    64    "into": "tmp1" } },
    65  {"whitespace_normalize": {
    66    "from": "tmp1",
    67    "into": "tmp2" } },
    68  {"assert_regex_match": {
    69    "pattern": "^.*goodproof.*$",
    70    "from": "tmp2" } }
    71  ]]`},
    72  		service:    keybase1.ProofType_TWITTER,
    73  		restype:    libkb.XAPIResHTML,
    74  		reshtml:    html1,
    75  		shouldwork: true,
    76  	},
    77  
    78  	// # Tests for individual valid instructions.
    79  	// Test match and fail of each instruction.
    80  
    81  	// ## AssertRegexMatch
    82  	{
    83  		name:      "AssertRegexMatch-url-ok",
    84  		proofinfo: info1,
    85  		prepvl: map[keybase1.ProofType]string{
    86  			keybase1.ProofType_COINBASE: `[[
    87  {"assert_regex_match": {
    88    "pattern": "^https://rooter\\.example\\.com/proofs/%{username_service}/[\\d]+\\.htjsxt$",
    89    "from": "hint_url" } },
    90  {"fetch": {
    91    "kind": "string",
    92    "from": "hint_url",
    93    "into": "tmp1" } }
    94  ]]`},
    95  		service:    keybase1.ProofType_COINBASE,
    96  		restype:    libkb.XAPIResText,
    97  		restext:    "1234",
    98  		shouldwork: true,
    99  	}, {
   100  		name:      "AssertRegexMatch-url-fail",
   101  		proofinfo: info1,
   102  		prepvl: map[keybase1.ProofType]string{
   103  			keybase1.ProofType_HACKERNEWS: `[[
   104  {"assert_regex_match": {
   105    "pattern": "^https://rooter\\.example\\.com/proofs/%{username_service}/+\\.htjsxt$",
   106    "from": "hint_url" } },
   107  {"fetch": {
   108    "kind": "string",
   109    "from": "hint_url",
   110    "into": "tmp1" } }
   111  ]]`},
   112  		service:    keybase1.ProofType_HACKERNEWS,
   113  		restype:    libkb.XAPIResText,
   114  		restext:    "1234",
   115  		shouldwork: false,
   116  	}, {
   117  		name:      "AssertRegexMatch-content-ok",
   118  		proofinfo: info1,
   119  		prepvl: map[keybase1.ProofType]string{
   120  			keybase1.ProofType_REDDIT: `[[
   121  {"fetch": {
   122    "kind": "html",
   123    "from": "hint_url" } },
   124  {"selector_css": {
   125    "selectors": [".twit", -1],
   126    "into": "tmp1" } },
   127  {"whitespace_normalize": {
   128    "from": "tmp1",
   129    "into": "tmp2" }},
   130  {"assert_regex_match": {
   131    "pattern": "^short %{sig_id_short}$",
   132    "from": "tmp2" } }
   133  ]]`},
   134  		service:    keybase1.ProofType_REDDIT,
   135  		restype:    libkb.XAPIResHTML,
   136  		reshtml:    html1,
   137  		shouldwork: true,
   138  	}, {
   139  		name:      "AssertRegexMatch-content-fail",
   140  		proofinfo: info1,
   141  		prepvl: map[keybase1.ProofType]string{
   142  			keybase1.ProofType_TWITTER: `[[
   143  {"fetch": {
   144    "kind": "html",
   145    "from": "hint_url" } },
   146  {"selector_css": {
   147    "selectors": [".twit", -1],
   148    "into": "tmp1" } },
   149  {"whitespace_normalize": {
   150    "from": "tmp1",
   151    "into": "tmp2" } },
   152  {"assert_regex_match": {
   153    "pattern": "^wrong %{sig_id_short}$",
   154    "from": "tmp2" } }
   155  ]]`},
   156  		service:    keybase1.ProofType_TWITTER,
   157  		restype:    libkb.XAPIResHTML,
   158  		reshtml:    html1,
   159  		shouldwork: false,
   160  	}, {
   161  		name:      "AssertRegexMatch-content-fail-case",
   162  		proofinfo: info1,
   163  		prepvl: map[keybase1.ProofType]string{
   164  			keybase1.ProofType_TWITTER: `[[
   165  {"fetch": {
   166    "kind": "html",
   167    "from": "hint_url" } },
   168  {"selector_css": {
   169    "selectors": [".twit", -1],
   170    "into": "tmp1" } },
   171  {"assert_regex_match": {
   172    "pattern": "^.*SHORT.*$",
   173    "from": "tmp1" } }
   174  ]]`},
   175  		service:    keybase1.ProofType_TWITTER,
   176  		restype:    libkb.XAPIResHTML,
   177  		reshtml:    html1,
   178  		shouldwork: false,
   179  	}, {
   180  		name:      "AssertRegexMatch-negate-ok",
   181  		proofinfo: info1,
   182  		prepvl: map[keybase1.ProofType]string{
   183  			keybase1.ProofType_COINBASE: `[[
   184  {"assert_regex_match": {
   185    "pattern": "^.*notacommonstringatall.*$",
   186    "from": "hint_url",
   187    "negate": true } },
   188  {"fetch": {
   189    "kind": "string",
   190    "from": "hint_url",
   191    "into": "tmp1" } }
   192  ]]`},
   193  		service:    keybase1.ProofType_COINBASE,
   194  		restype:    libkb.XAPIResText,
   195  		restext:    "1234",
   196  		shouldwork: true,
   197  	}, {
   198  		name:      "AssertRegexMatch-negate-fail",
   199  		proofinfo: info1,
   200  		prepvl: map[keybase1.ProofType]string{
   201  			keybase1.ProofType_COINBASE: `[[
   202  {"assert_regex_match": {
   203    "pattern": "^.*rooter.*$",
   204    "negate": true,
   205    "from": "hint_url" } },
   206  {"fetch": {
   207    "kind": "string",
   208    "from": "hint_url",
   209    "into": "tmp1" } }
   210  ]]`},
   211  		service:    keybase1.ProofType_COINBASE,
   212  		restype:    libkb.XAPIResText,
   213  		restext:    "1234",
   214  		shouldwork: false,
   215  	},
   216  
   217  	// ## AssertFindBase64
   218  	{
   219  		name:      "AssertFindBase64-ok",
   220  		proofinfo: info1,
   221  		prepvl: map[keybase1.ProofType]string{
   222  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   223  {"fetch": {
   224    "kind": "string",
   225    "from": "hint_url",
   226    "into": "tmp1" } },
   227  {"assert_find_base64": {
   228    "needle": "sig",
   229    "haystack": "tmp1" } }
   230  ]]`},
   231  		service: keybase1.ProofType_GENERIC_WEB_SITE,
   232  		restype: libkb.XAPIResText,
   233  		// the sig must be on a line with only spacing.
   234  		restext:    fmt.Sprintf(" asdf\n  \t\t .\n %v\t\nzad\t.\n", sig1),
   235  		shouldwork: true,
   236  	}, {
   237  		name:      "AssertFindBase64-fail",
   238  		proofinfo: info1,
   239  		prepvl: map[keybase1.ProofType]string{
   240  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   241  {"fetch": {
   242    "kind": "string",
   243    "from": "hint_url",
   244    "into": "tmp1" } },
   245  {"assert_find_base64": {
   246    "needle": "sig",
   247    "haystack": "tmp1" } }
   248  ]]`},
   249  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   250  		restype:    libkb.XAPIResText,
   251  		restext:    sig1[1:],
   252  		shouldwork: false,
   253  	},
   254  
   255  	// ## AssertCompare
   256  	{
   257  		name:      "AssertCompare-cicmp-ok",
   258  		proofinfo: info1,
   259  		prepvl: map[keybase1.ProofType]string{
   260  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   261  {"fetch": {
   262    "kind": "string",
   263    "from": "hint_url",
   264    "into": "tmp1" } },
   265  {"assert_compare": {
   266    "cmp": "cicmp",
   267    "a": "username_keybase",
   268    "b": "tmp1" } }
   269  ]]`},
   270  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   271  		restype:    libkb.XAPIResText,
   272  		restext:    "krONK",
   273  		shouldwork: true,
   274  	}, {
   275  		name:      "AssertCompare-cicmp-fail",
   276  		proofinfo: info1,
   277  		prepvl: map[keybase1.ProofType]string{
   278  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   279  {"fetch": {
   280    "kind": "string",
   281    "from": "hint_url",
   282    "into": "tmp1" } },
   283  {"assert_compare": {
   284    "cmp": "cicmp",
   285    "a": "username_keybase",
   286    "b": "tmp1" } }
   287  ]]`},
   288  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   289  		restype:    libkb.XAPIResText,
   290  		restext:    "kr0nk",
   291  		shouldwork: false,
   292  	}, {
   293  		name:      "AssertCompare-stripdots-then-cicmp-ok",
   294  		proofinfo: info1,
   295  		prepvl: map[keybase1.ProofType]string{
   296  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   297  {"fetch": {
   298    "kind": "string",
   299    "from": "hint_url",
   300    "into": "tmp1" } },
   301  {"assert_compare": {
   302    "cmp": "stripdots-then-cicmp",
   303    "a": "username_keybase",
   304    "b": "tmp1" } }
   305  ]]`},
   306  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   307  		restype:    libkb.XAPIResText,
   308  		restext:    "kr.O..NK",
   309  		shouldwork: true,
   310  	},
   311  	{
   312  		name:      "AssertCompare-stripdots-then-cicmp-fail",
   313  		proofinfo: info1,
   314  		prepvl: map[keybase1.ProofType]string{
   315  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   316  {"fetch": {
   317    "kind": "string",
   318    "from": "hint_url",
   319    "into": "tmp1" } },
   320  {"assert_compare": {
   321    "cmp": "stripdots-then-cicmp",
   322    "a": "username_keybase",
   323    "b": "tmp1" } }
   324  ]]`},
   325  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   326  		restype:    libkb.XAPIResText,
   327  		restext:    "kr0nk",
   328  		shouldwork: false,
   329  	}, {
   330  		name:      "AssertCompare-exact-ok",
   331  		proofinfo: info1,
   332  		prepvl: map[keybase1.ProofType]string{
   333  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   334  {"fetch": {
   335    "kind": "string",
   336    "from": "hint_url",
   337    "into": "tmp1" } },
   338  {"assert_compare": {
   339    "cmp": "exact",
   340    "a": "username_keybase",
   341    "b": "tmp1" } }
   342  ]]`},
   343  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   344  		restype:    libkb.XAPIResText,
   345  		restext:    "kronk",
   346  		shouldwork: true,
   347  	}, {
   348  		name:      "AssertCompare-exact-fail",
   349  		proofinfo: info1,
   350  		prepvl: map[keybase1.ProofType]string{
   351  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   352  {"fetch": {
   353    "kind": "string",
   354    "from": "hint_url",
   355    "into": "tmp1" } },
   356  {"assert_compare": {
   357    "cmp": "exact",
   358    "a": "username_keybase",
   359    "b": "tmp1" } }
   360  ]]`},
   361  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   362  		restype:    libkb.XAPIResText,
   363  		restext:    "kroNk",
   364  		shouldwork: false,
   365  	},
   366  
   367  	// ## RegexCapture and WhitespaceNormalize tested together
   368  	{
   369  		name:      "RegexCapture-ok-ignoregroup",
   370  		proofinfo: info1,
   371  		prepvl: map[keybase1.ProofType]string{
   372  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   373  {"fetch": {
   374    "kind": "html",
   375    "from": "hint_url" } },
   376  {"selector_css": {
   377    "selectors": ["div.twit", -1],
   378    "into": "tmp1" } },
   379  {"whitespace_normalize": {
   380    "from": "tmp1",
   381    "into": "tmp2" } },
   382  {"regex_capture": {
   383    "pattern": "^(?:sHoRt) ([A-Za-z0-9+/=]+)$",
   384    "case_insensitive": true,
   385    "from": "tmp2",
   386    "into": ["tmp3"] } },
   387  {"assert_regex_match": {
   388    "pattern": "^%{sig_id_short}$",
   389    "from": "tmp3" } }
   390  ]]`},
   391  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   392  		restype:    libkb.XAPIResHTML,
   393  		reshtml:    html1,
   394  		shouldwork: true,
   395  	}, {
   396  		// A repeated capture group returns its last match.
   397  		name:      "RegexCapture-ok-multimatch",
   398  		proofinfo: info1,
   399  		prepvl: map[keybase1.ProofType]string{
   400  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   401  {"fetch": {
   402    "kind": "html",
   403    "from": "hint_url" } },
   404  {"selector_css": {
   405    "selectors": ["div.twit", -1],
   406    "into": "tmp1" } },
   407  {"whitespace_normalize": {
   408    "from": "tmp1",
   409    "into": "tmp2" } },
   410  {"regex_capture": {
   411    "pattern": "^(short).*$",
   412    "from": "tmp2",
   413    "into": ["tmp3"] } },
   414  {"regex_capture": {
   415    "pattern": "^(\\w)+$",
   416    "from": "tmp3",
   417    "into": ["tmp4"] } },
   418  {"assert_regex_match": {
   419    "pattern": "^t$",
   420    "from": "tmp4" } }
   421  ]]`},
   422  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   423  		restype:    libkb.XAPIResHTML,
   424  		reshtml:    html1,
   425  		shouldwork: true,
   426  	}, {
   427  		// Capturing into multiple variables
   428  		// also more capture groups than 'into' variables
   429  		name:      "RegexCapture-ok-multi-capture",
   430  		proofinfo: info1,
   431  		prepvl: map[keybase1.ProofType]string{
   432  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   433  {"fetch": {
   434    "kind": "string",
   435    "from": "hint_url",
   436    "into": "tmp1" } },
   437  {"regex_capture": {
   438    "pattern": "^(\\w) (\\w) (\\w) (\\w) (\\w).*$",
   439    "from": "tmp1",
   440    "into": ["a", "b", "c"] } },
   441  {"assert_regex_match": {
   442    "pattern": "^%{a} %{b} %{c} d e f$",
   443    "from": "tmp1" } }
   444  ]]`},
   445  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   446  		restype:    libkb.XAPIResText,
   447  		restext:    "a b c d e f",
   448  		shouldwork: true,
   449  	}, {
   450  		// Less capture groups than `into` variables
   451  		name:      "RegexCapture-ok-multi-capture",
   452  		proofinfo: info1,
   453  		prepvl: map[keybase1.ProofType]string{
   454  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   455  {"fetch": {
   456    "kind": "string",
   457    "from": "hint_url",
   458    "into": "tmp1" } },
   459  {"regex_capture": {
   460    "pattern": "^(\\w) (\\w) .*$",
   461    "from": "tmp1",
   462    "into": ["a", "b", "c"] } }
   463  ]]`},
   464  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   465  		restype:    libkb.XAPIResText,
   466  		restext:    "a b c d e f",
   467  		shouldwork: false,
   468  	}, {
   469  		name:      "RegexCapture-fail-nomatch",
   470  		proofinfo: info1,
   471  		prepvl: map[keybase1.ProofType]string{
   472  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   473  {"fetch": {
   474    "kind": "html",
   475    "from": "hint_url" } },
   476  {"selector_css": {
   477    "selectors": ["div.twit", -1],
   478    "into": "tmp1" } },
   479  {"regex_capture": {
   480    "pattern": "^(nowhere)$",
   481    "case_insensitive": true,
   482    "from": "tmp1",
   483    "into": ["tmp2"] } }
   484  ]]`},
   485  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   486  		restype:    libkb.XAPIResHTML,
   487  		reshtml:    html1,
   488  		shouldwork: false,
   489  	}, {
   490  		name:      "RegexCapture-fail-nogroup",
   491  		proofinfo: info1,
   492  		prepvl: map[keybase1.ProofType]string{
   493  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   494  {"fetch": {
   495    "kind": "html",
   496    "from": "hint_url" } },
   497  {"selector_css": {
   498    "selectors": ["div.twit", -1],
   499    "into": "tmp1" } },
   500  {"whitespace_normalize": {
   501    "from": "tmp1",
   502    "into": "tmp2" } },
   503  {"regex_capture": {
   504    "pattern": "^sHoRt.*$",
   505    "case_insensitive": true,
   506    "from": "tmp2",
   507    "into": ["tmp3"] } },
   508  {"assert_regex_match": {
   509    "pattern": "^%{sig_id_short}$/s",
   510    "from": "tmp3" } }
   511  ]]`},
   512  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   513  		restype:    libkb.XAPIResHTML,
   514  		reshtml:    html1,
   515  		shouldwork: false,
   516  	},
   517  
   518  	// ## ReplaceAll
   519  	{
   520  		name:      "ReplaceAll-ok",
   521  		proofinfo: info1,
   522  		prepvl: map[keybase1.ProofType]string{
   523  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   524  {"fetch": {
   525    "kind": "string",
   526    "from": "hint_url",
   527    "into": "a" } },
   528  {"replace_all": {
   529    "old": "foo",
   530    "new": "pow",
   531    "from": "a",
   532    "into": "b" } },
   533  {"assert_regex_match": {
   534    "pattern": "^pow bar powz barz$",
   535    "from": "b" } }
   536  ]]`},
   537  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   538  		restype:    libkb.XAPIResText,
   539  		restext:    "foo bar fooz barz",
   540  		shouldwork: true,
   541  	}, {
   542  		name:      "ReplaceAll-ok-no-matches",
   543  		proofinfo: info1,
   544  		prepvl: map[keybase1.ProofType]string{
   545  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   546  {"fetch": {
   547    "kind": "string",
   548    "from": "hint_url",
   549    "into": "a" } },
   550  {"replace_all": {
   551    "old": "foo",
   552    "new": "pow",
   553    "from": "a",
   554    "into": "b" } },
   555  {"assert_regex_match": {
   556    "pattern": "^%{a}$",
   557    "from": "b" } }
   558  ]]`},
   559  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   560  		restype:    libkb.XAPIResText,
   561  		restext:    "glue bar gluez barz",
   562  		shouldwork: true,
   563  	}, {
   564  		// Test unescaping the weird escaping that facebook comments use.
   565  		name:      "ReplaceAll-ok-facebook",
   566  		proofinfo: info1,
   567  		prepvl: map[keybase1.ProofType]string{
   568  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
   569  {"fetch": {
   570    "kind": "string",
   571    "from": "hint_url",
   572    "into": "tmp1" } },
   573  {"replace_all": {
   574    "old": "-\\-\\",
   575    "new": "--",
   576    "from": "tmp1",
   577    "into": "tmp2" } },
   578  {"replace_all": {
   579    "old": "\\\\",
   580    "new": "\\",
   581    "from": "tmp2",
   582    "into": "tmp3" } },
   583  {"assert_regex_match": {
   584    "pattern": "^double -- back \\\\$",
   585    "from": "tmp3" } },
   586  {"fill": {
   587    "with": "double -- back \\",
   588    "into": "ref" } },
   589  {"assert_compare": {
   590    "cmp": "exact",
   591    "a": "tmp3",
   592    "b": "ref" } }
   593  ]]`},
   594  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
   595  		restype:    libkb.XAPIResText,
   596  		restext:    "double -\\-\\ back \\\\",
   597  		shouldwork: true,
   598  	},
   599  
   600  	// ## ParseURL
   601  	{
   602  		name:      "ParseURL-ok",
   603  		proofinfo: info1,
   604  		prepvl: map[keybase1.ProofType]string{
   605  			keybase1.ProofType_GITHUB: `[[
   606  {"fetch": {
   607    "kind": "string",
   608    "from": "hint_url",
   609    "into": "tmp1" } },
   610  {"parse_url": {
   611    "from": "tmp1",
   612    "path": "path",
   613    "host": "host",
   614    "scheme": "scheme" } },
   615  {"assert_regex_match": {
   616    "pattern": "^/noodle$",
   617    "from": "path" } },
   618  {"assert_regex_match": {
   619    "pattern": "^digg.example.com$",
   620    "from": "host" } },
   621  {"assert_regex_match": {
   622    "pattern": "^http$",
   623    "from": "scheme" } }
   624  ]]`},
   625  		service:    keybase1.ProofType_GITHUB,
   626  		restype:    libkb.XAPIResText,
   627  		restext:    "http://digg.example.com/noodle",
   628  		shouldwork: true,
   629  	}, {
   630  		name:      "ParseURL-fail",
   631  		proofinfo: info1,
   632  		prepvl: map[keybase1.ProofType]string{
   633  			keybase1.ProofType_GITHUB: `[[
   634  {"fetch": {
   635    "kind": "string",
   636    "from": "hint_url",
   637    "into": "tmp1" } },
   638  {"parse_url": {
   639    "from": "tmp1",
   640    "path": "path" } }
   641  ]]`},
   642  		service:    keybase1.ProofType_GITHUB,
   643  		restype:    libkb.XAPIResText,
   644  		restext:    "htj0*#)%*J)*H^dle",
   645  		shouldwork: false,
   646  	},
   647  
   648  	// ## Fetch
   649  	{
   650  		name:      "Fetch-string",
   651  		proofinfo: info1,
   652  		prepvl: map[keybase1.ProofType]string{
   653  			keybase1.ProofType_GITHUB: `[[
   654  {"fetch": {
   655    "kind": "string",
   656    "from": "hint_url",
   657    "into": "tmp1" } },
   658  {"assert_regex_match": {
   659    "pattern": "^str9\n$",
   660    "from": "tmp1" } }
   661  ]]`},
   662  		service:    keybase1.ProofType_GITHUB,
   663  		restype:    libkb.XAPIResText,
   664  		restext:    "str9\n",
   665  		shouldwork: true,
   666  	}, {
   667  		name:      "Fetch-html",
   668  		proofinfo: info1,
   669  		prepvl: map[keybase1.ProofType]string{
   670  			keybase1.ProofType_GITHUB: `[[
   671  {"fetch": {
   672    "kind": "html",
   673    "from": "hint_url" } },
   674  {"selector_css": {
   675    "selectors": ["head title"],
   676    "into": "tmp1" } },
   677  {"assert_regex_match": {
   678    "pattern": "^proofer$",
   679    "from": "tmp1" } }
   680  ]]`},
   681  		service:    keybase1.ProofType_GITHUB,
   682  		restype:    libkb.XAPIResHTML,
   683  		reshtml:    html1,
   684  		shouldwork: true,
   685  	}, {
   686  		name:      "Fetch-json",
   687  		proofinfo: info1,
   688  		prepvl: map[keybase1.ProofType]string{
   689  			keybase1.ProofType_GITHUB: `[[
   690  {"fetch": {
   691    "kind": "json",
   692    "from": "hint_url" } },
   693  {"selector_json": {
   694    "selectors": ["data", 2, "type"],
   695    "into": "tmp1" } },
   696  {"assert_regex_match": {
   697    "pattern": "^useful$",
   698    "from": "tmp1" } }
   699  ]]`},
   700  		service:    keybase1.ProofType_GITHUB,
   701  		restype:    libkb.XAPIResJSON,
   702  		resjson:    json1,
   703  		shouldwork: true,
   704  	},
   705  
   706  	// ## ParseHTML
   707  	{
   708  		name:      "ParseHTML-ok",
   709  		proofinfo: info1,
   710  		prepvl: map[keybase1.ProofType]string{
   711  			keybase1.ProofType_GITHUB: `[[
   712  {"fetch": {
   713    "kind": "string",
   714    "from": "hint_url",
   715    "into": "tmp1" } },
   716  {"parse_html": {
   717    "from": "tmp1" } },
   718  {"selector_css": {
   719    "selectors": ["p", 2],
   720    "into": "tmp2" } },
   721  {"assert_regex_match": {
   722    "pattern": "^c$",
   723    "from": "tmp2" } }
   724  ]]`},
   725  		service:    keybase1.ProofType_GITHUB,
   726  		restype:    libkb.XAPIResText,
   727  		restext:    "<p>a</p><p>b</p><p>c</p>",
   728  		shouldwork: true,
   729  	},
   730  
   731  	// ## SelectorJSON
   732  	{
   733  		name:      "SelectorJSON-simple",
   734  		proofinfo: info1,
   735  		prepvl: map[keybase1.ProofType]string{
   736  			keybase1.ProofType_GITHUB: `[[
   737  {"fetch": {
   738    "kind": "json",
   739    "from": "hint_url" } },
   740  {"selector_json": {
   741    "selectors": ["data", 2, "type"],
   742    "into": "tmp1" } },
   743  {"assert_regex_match": {
   744    "pattern": "^useful$",
   745    "from": "tmp1" } }
   746  ]]`},
   747  		service:    keybase1.ProofType_GITHUB,
   748  		restype:    libkb.XAPIResJSON,
   749  		resjson:    json1,
   750  		shouldwork: true,
   751  	}, {
   752  		name:      "SelectorJSON-index-dne",
   753  		proofinfo: info1,
   754  		prepvl: map[keybase1.ProofType]string{
   755  			keybase1.ProofType_GITHUB: `[[
   756  {"fetch": {
   757    "kind": "json",
   758    "from": "hint_url" } },
   759  {"selector_json": {
   760    "selectors": ["data", 500],
   761    "into": "tmp1" } }
   762  ]]`},
   763  		service:    keybase1.ProofType_GITHUB,
   764  		restype:    libkb.XAPIResJSON,
   765  		resjson:    json1,
   766  		shouldwork: false,
   767  	}, {
   768  		name:      "SelectorJSON-key-dne",
   769  		proofinfo: info1,
   770  		prepvl: map[keybase1.ProofType]string{
   771  			keybase1.ProofType_GITHUB: `[[
   772  {"fetch": {
   773    "kind": "json",
   774    "from": "hint_url" } },
   775  {"selector_json": {
   776    "selectors": ["data", "a500"],
   777    "into": "tmp1" } }
   778  ]]`},
   779  		service:    keybase1.ProofType_GITHUB,
   780  		restype:    libkb.XAPIResJSON,
   781  		resjson:    json1,
   782  		shouldwork: false,
   783  	}, {
   784  		name:      "SelectorJSON-index-neg",
   785  		proofinfo: info1,
   786  		prepvl: map[keybase1.ProofType]string{
   787  			keybase1.ProofType_GITHUB: `[[
   788  {"fetch": {
   789    "kind": "json",
   790    "from": "hint_url" } },
   791  {"selector_json": {
   792    "selectors": ["data", -1, "poster"],
   793    "into": "tmp1" } },
   794  {"assert_regex_match": {
   795    "pattern": "^eve$",
   796    "from": "tmp1" } }
   797  ]]`},
   798  		service:    keybase1.ProofType_GITHUB,
   799  		restype:    libkb.XAPIResJSON,
   800  		resjson:    json1,
   801  		shouldwork: true,
   802  	}, {
   803  		name:      "SelectorJSON-index-all",
   804  		proofinfo: info1,
   805  		prepvl: map[keybase1.ProofType]string{
   806  			keybase1.ProofType_GITHUB: `[[
   807  {"fetch": {
   808    "kind": "json",
   809    "from": "hint_url" } },
   810  {"selector_json": {
   811    "selectors": ["data", {"all": true }, "poster"],
   812    "into": "tmp1" } },
   813  {"assert_regex_match": {
   814    "pattern": "^kronk eve$",
   815    "from": "tmp1" } }
   816  ]]`},
   817  		service:    keybase1.ProofType_GITHUB,
   818  		restype:    libkb.XAPIResJSON,
   819  		resjson:    json1,
   820  		shouldwork: true,
   821  	}, {
   822  		// Index into a non-array
   823  		name:      "SelectorJSON-index-nonarray",
   824  		proofinfo: info1,
   825  		prepvl: map[keybase1.ProofType]string{
   826  			keybase1.ProofType_GITHUB: `[[
   827  {"fetch": {
   828    "kind": "json",
   829    "from": "hint_url" } },
   830  {"selector_json": {
   831    "selectors": ["data", {"all": true }, "extra", 0, 0],
   832    "into": "tmp1" } },
   833  {"assert_regex_match": {
   834    "pattern": "^4$",
   835    "from": "tmp1" } }
   836  ]]`},
   837  		service:    keybase1.ProofType_GITHUB,
   838  		restype:    libkb.XAPIResJSON,
   839  		resjson:    json1,
   840  		shouldwork: true,
   841  	},
   842  
   843  	// ## SelectorCSS
   844  	{
   845  		name:      "SelectorCSS-ok",
   846  		proofinfo: info1,
   847  		prepvl: map[keybase1.ProofType]string{
   848  			keybase1.ProofType_GITHUB: `[[
   849  {"fetch": {
   850    "kind": "html",
   851    "from": "hint_url" } },
   852  {"selector_css": {
   853    "selectors": ["body .twit", -1],
   854    "into": "tmp1" } },
   855  {"whitespace_normalize": {
   856    "from": "tmp1",
   857    "into": "tmp2" } },
   858  {"assert_regex_match": {
   859    "pattern": "^short %{sig_id_short}$",
   860    "from": "tmp2" } }
   861  ]]`},
   862  		service:    keybase1.ProofType_GITHUB,
   863  		restype:    libkb.XAPIResHTML,
   864  		reshtml:    html1,
   865  		shouldwork: true,
   866  	}, {
   867  		name:      "SelectorCSS-fail-dontcrash",
   868  		proofinfo: info1,
   869  		prepvl: map[keybase1.ProofType]string{
   870  			keybase1.ProofType_GITHUB: `[[
   871  {"fetch": {
   872    "kind": "html",
   873    "from": "hint_url" } },
   874  {"selector_css": {
   875    "selectors": ["body .twit:eq(0)"],
   876    "into": "tmp1" } },
   877  {"assert_regex_match": {
   878    "pattern": "^$",
   879    "from": "tmp1" } }
   880  ]]`},
   881  		service:    keybase1.ProofType_GITHUB,
   882  		restype:    libkb.XAPIResHTML,
   883  		reshtml:    html1,
   884  		shouldwork: false,
   885  	}, {
   886  		name:      "SelectorCSS-ok-multi",
   887  		proofinfo: info1,
   888  		prepvl: map[keybase1.ProofType]string{
   889  			keybase1.ProofType_GITHUB: `[[
   890  {"fetch": {
   891    "kind": "html",
   892    "from": "hint_url" } },
   893  {"selector_css": {
   894    "selectors": ["body .twit"],
   895    "multi": true,
   896    "into": "tmp1" } },
   897  {"assert_regex_match": {
   898    "pattern": "^[\\s\\S]*goodproof[\\s\\S]*evil\\.com[\\s\\S]*short[\\s\\S]*$",
   899    "from": "tmp1" } }
   900  ]]`},
   901  		service:    keybase1.ProofType_GITHUB,
   902  		restype:    libkb.XAPIResHTML,
   903  		reshtml:    html1,
   904  		shouldwork: true,
   905  	}, {
   906  		name:      "SelectorCSS-fail-nonmulti",
   907  		proofinfo: info1,
   908  		prepvl: map[keybase1.ProofType]string{
   909  			keybase1.ProofType_GITHUB: `[[
   910  {"fetch": {
   911    "kind": "html",
   912    "from": "hint_url" } },
   913  {"selector_css": {
   914    "selectors": ["body .twit"],
   915    "into": "tmp1" } },
   916  {"whitespace_normalize": {
   917    "from": "tmp1",
   918    "into": "tmp2" } },
   919  {"assert_regex_match": {
   920    "pattern": "^short %{sig_id_short}$",
   921    "from": "tmp2" } }
   922  ]]`},
   923  		service:    keybase1.ProofType_GITHUB,
   924  		restype:    libkb.XAPIResHTML,
   925  		reshtml:    html1,
   926  		shouldwork: false,
   927  	}, {
   928  		name:      "SelectorCSS-ok-attr",
   929  		proofinfo: info1,
   930  		prepvl: map[keybase1.ProofType]string{
   931  			keybase1.ProofType_GITHUB: `[[
   932  {"fetch": {
   933    "kind": "html",
   934    "from": "hint_url" } },
   935  {"selector_css": {
   936    "selectors": ["body .twit[data-x]"],
   937    "attr": "data-x",
   938    "into": "tmp1" } },
   939  {"assert_regex_match": {
   940    "pattern": "^y$",
   941    "from": "tmp1" } }
   942  ]]`},
   943  		service:    keybase1.ProofType_GITHUB,
   944  		restype:    libkb.XAPIResHTML,
   945  		reshtml:    html1,
   946  		shouldwork: true,
   947  	}, {
   948  		name:      "SelectorCSS-ok-attr-dne",
   949  		proofinfo: info1,
   950  		prepvl: map[keybase1.ProofType]string{
   951  			keybase1.ProofType_GITHUB: `[[
   952  {"fetch": {
   953    "kind": "html",
   954    "from": "hint_url" } },
   955  {"selector_css": {
   956    "selectors": ["body .twit"],
   957    "multi": true,
   958    "attr": "dne",
   959    "into": "tmp1" } },
   960  {"assert_regex_match": {
   961    "pattern": "^$",
   962    "from": "tmp1" } }
   963  ]]`},
   964  		service:    keybase1.ProofType_GITHUB,
   965  		restype:    libkb.XAPIResHTML,
   966  		reshtml:    html1,
   967  		shouldwork: true,
   968  	}, {
   969  		name:      "SelectorCSS-ok-contents",
   970  		proofinfo: info1,
   971  		prepvl: map[keybase1.ProofType]string{
   972  			keybase1.ProofType_GITHUB: `[[
   973  {"fetch": {
   974    "kind": "html",
   975    "from": "hint_url" } },
   976  {"selector_css": {
   977    "selectors": [".a .b", {"contents": true}],
   978    "multi": true,
   979    "data": true,
   980    "into": "tmp1" } },
   981  {"whitespace_normalize": {
   982    "from": "tmp1",
   983    "into": "tmp2" } },
   984  {"replace_all": {
   985    "old": " ",
   986    "new": "",
   987    "from": "tmp2",
   988    "into": "tmp3" } },
   989  {"assert_regex_match": {
   990    "pattern": "^cowabunga$",
   991    "from": "tmp3" } }
   992  ]]`},
   993  		service:    keybase1.ProofType_GITHUB,
   994  		restype:    libkb.XAPIResHTML,
   995  		reshtml:    html2,
   996  		shouldwork: true,
   997  	},
   998  
   999  	// ## Fill
  1000  	{
  1001  		name:      "Fill-ok",
  1002  		proofinfo: info1,
  1003  		prepvl: map[keybase1.ProofType]string{
  1004  			keybase1.ProofType_GITHUB: `[[
  1005  {"fetch": {
  1006    "kind": "string",
  1007    "from": "hint_url",
  1008    "into": "tmp1" } },
  1009  {"fill": {
  1010    "with": "%{tmp1}-%{username_keybase}",
  1011    "into": "tmp2" } },
  1012  {"assert_regex_match": {
  1013    "pattern": "^foozle-kronk$",
  1014    "from": "tmp2" } }
  1015  ]]`},
  1016  		service:    keybase1.ProofType_GITHUB,
  1017  		restype:    libkb.XAPIResText,
  1018  		restext:    "foozle",
  1019  		shouldwork: true,
  1020  	}, {
  1021  		name:      "Fill-ok-noregexesc",
  1022  		proofinfo: info1,
  1023  		prepvl: map[keybase1.ProofType]string{
  1024  			keybase1.ProofType_GITHUB: `[[
  1025  {"fetch": {
  1026    "kind": "string",
  1027    "from": "hint_url",
  1028    "into": "tmp1" } },
  1029  {"fill": {
  1030    "with": "%{tmp1}-%{username_keybase}",
  1031    "into": "tmp2" } },
  1032  {"assert_regex_match": {
  1033    "pattern": "^\\(x\\)-kronk$",
  1034    "from": "tmp2" } }
  1035  ]]`},
  1036  		service:    keybase1.ProofType_GITHUB,
  1037  		restype:    libkb.XAPIResText,
  1038  		restext:    "(x)",
  1039  		shouldwork: true,
  1040  	},
  1041  
  1042  	// # Tests for invalid PVL at the top level
  1043  	{
  1044  		name:      "omitted-service",
  1045  		proofinfo: info1,
  1046  		// Empty service entries should fail.
  1047  		prepvl: map[keybase1.ProofType]string{
  1048  			keybase1.ProofType_TWITTER: `[[
  1049  {"fetch": {
  1050    "kind": "html",
  1051    "from": "hint_url" } }
  1052  ]]`,
  1053  		},
  1054  		// GITHUB here isn't TWITTER
  1055  		service:    keybase1.ProofType_GITHUB,
  1056  		restype:    libkb.XAPIResHTML,
  1057  		reshtml:    html1,
  1058  		shouldwork: false,
  1059  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1060  	}, {
  1061  		name:       "omitted-version",
  1062  		proofinfo:  info1,
  1063  		prepvlstr:  `{"revision": 1, "services": {}}`,
  1064  		service:    keybase1.ProofType_GITHUB,
  1065  		restype:    libkb.XAPIResHTML,
  1066  		reshtml:    html1,
  1067  		shouldwork: false,
  1068  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1069  	}, {
  1070  		name:       "omitted-revision",
  1071  		proofinfo:  info1,
  1072  		prepvlstr:  `{"pvl_version": 1, "services": {}}`,
  1073  		service:    keybase1.ProofType_GITHUB,
  1074  		restype:    libkb.XAPIResHTML,
  1075  		reshtml:    html1,
  1076  		shouldwork: false,
  1077  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1078  	}, {
  1079  		name:       "bad-services",
  1080  		proofinfo:  info1,
  1081  		prepvlstr:  `{"pvl_version": 1, "revision": 2, "services": []}`,
  1082  		service:    keybase1.ProofType_GITHUB,
  1083  		restype:    libkb.XAPIResHTML,
  1084  		reshtml:    html1,
  1085  		shouldwork: false,
  1086  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1087  	}, {
  1088  		name:       "non-array-script",
  1089  		proofinfo:  info1,
  1090  		prepvlstr:  `{"pvl_version": 1, "revision": 2, "services": {"github": "bad"}}`,
  1091  		service:    keybase1.ProofType_GITHUB,
  1092  		restype:    libkb.XAPIResHTML,
  1093  		reshtml:    html1,
  1094  		shouldwork: false,
  1095  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1096  	}, {
  1097  		name:       "empty-script",
  1098  		proofinfo:  info1,
  1099  		prepvlstr:  `{"pvl_version": 1, "revision": 2, "services": {"github": [[]]}}`,
  1100  		service:    keybase1.ProofType_GITHUB,
  1101  		restype:    libkb.XAPIResHTML,
  1102  		reshtml:    html1,
  1103  		shouldwork: false,
  1104  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1105  	}, {
  1106  		name:       "empty-script-multi",
  1107  		proofinfo:  info1,
  1108  		prepvlstr:  `{"pvl_version": 1, "revision": 2, "services": {"github": [[], []]}}`,
  1109  		service:    keybase1.ProofType_GITHUB,
  1110  		restype:    libkb.XAPIResHTML,
  1111  		reshtml:    html1,
  1112  		shouldwork: false,
  1113  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1114  	}, {
  1115  		name:       "no-scripts",
  1116  		proofinfo:  info1,
  1117  		prepvlstr:  `{"pvl_version": 1, "revision": 2, "services": {"github": []}}`,
  1118  		service:    keybase1.ProofType_GITHUB,
  1119  		restype:    libkb.XAPIResHTML,
  1120  		reshtml:    html1,
  1121  		shouldwork: false,
  1122  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1123  	},
  1124  
  1125  	// ## Tests for bad proofinfo
  1126  	{
  1127  		name:      "bad-sig-path-in-domain-web",
  1128  		proofinfo: infoBadDomain,
  1129  		prepvl: map[keybase1.ProofType]string{
  1130  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
  1131  {"fetch": {
  1132    "kind": "string",
  1133    "from": "hint_url",
  1134    "into": "tmp1" } }
  1135  ]]`},
  1136  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
  1137  		restype:    libkb.XAPIResText,
  1138  		restext:    "fuzztroo",
  1139  		shouldwork: false,
  1140  		errstatus:  keybase1.ProofStatus_BAD_SIGNATURE,
  1141  	}, {
  1142  		name:      "bad-sig-path-in-domain-dns",
  1143  		proofinfo: infoBadDomain,
  1144  		prepvl: map[keybase1.ProofType]string{
  1145  			keybase1.ProofType_DNS: `[[
  1146  {"assert_regex_match": {
  1147    "pattern": "^foo$",
  1148    "from": "hint_url" } }
  1149  ]]`},
  1150  		service: keybase1.ProofType_DNS,
  1151  		resdns: map[string][]string{
  1152  			info1.Hostname: {"NO", "ok"},
  1153  		},
  1154  		shouldwork: false,
  1155  		errstatus:  keybase1.ProofStatus_BAD_SIGNATURE,
  1156  	}, {
  1157  		name:      "bad-sig-proto",
  1158  		proofinfo: infoBadProto,
  1159  		prepvl: map[keybase1.ProofType]string{
  1160  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
  1161  {"fetch": {
  1162    "kind": "string",
  1163    "from": "hint_url",
  1164    "into": "tmp1" } }
  1165  ]]`},
  1166  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
  1167  		restype:    libkb.XAPIResText,
  1168  		restext:    "fuzztroo",
  1169  		shouldwork: false,
  1170  		errstatus:  keybase1.ProofStatus_BAD_SIGNATURE,
  1171  	}, {
  1172  		name:      "bad-sig-sig",
  1173  		proofinfo: infoBadSig,
  1174  		prepvl: map[keybase1.ProofType]string{
  1175  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
  1176  {"fetch": {
  1177    "kind": "string",
  1178    "from": "hint_url",
  1179    "into": "tmp1" } }
  1180  ]]`},
  1181  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
  1182  		restype:    libkb.XAPIResText,
  1183  		restext:    "fuzztroo",
  1184  		shouldwork: false,
  1185  		errstatus:  keybase1.ProofStatus_BAD_SIGNATURE,
  1186  	},
  1187  
  1188  	// ## (Invalid) AssertRegexMatch
  1189  	{
  1190  		name:      "AssertRegexMatch-invalid-missing^",
  1191  		proofinfo: info1,
  1192  		prepvl: map[keybase1.ProofType]string{
  1193  			keybase1.ProofType_GITHUB: `[[
  1194  {"fetch": {
  1195    "kind": "string",
  1196    "from": "hint_url",
  1197    "into": "tmp1" } },
  1198  {"assert_regex_match": {
  1199    "pattern": "foobar$",
  1200    "from": "tmp1" } }
  1201  ]]`},
  1202  		service:    keybase1.ProofType_GITHUB,
  1203  		restype:    libkb.XAPIResText,
  1204  		restext:    "fuzztroo",
  1205  		shouldwork: false,
  1206  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1207  	}, {
  1208  		name:      "AssertRegexMatch-invalid-missing$",
  1209  		proofinfo: info1,
  1210  		prepvl: map[keybase1.ProofType]string{
  1211  			keybase1.ProofType_GITHUB: `[[
  1212  {"fetch": {
  1213    "kind": "string",
  1214    "from": "hint_url",
  1215    "into": "tmp1" } },
  1216  {"assert_regex_match": {
  1217    "pattern": "^foobar",
  1218    "from": "tmp1" } }
  1219  ]]`},
  1220  		service:    keybase1.ProofType_GITHUB,
  1221  		restype:    libkb.XAPIResText,
  1222  		restext:    "fuzztroo",
  1223  		shouldwork: false,
  1224  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1225  	}, {
  1226  		name:      "AssertRegexMatch-invalid-badvar",
  1227  		proofinfo: info1,
  1228  		prepvl: map[keybase1.ProofType]string{
  1229  			keybase1.ProofType_GITHUB: `[[
  1230  {"fetch": {
  1231    "kind": "string",
  1232    "from": "hint_url",
  1233    "into": "tmp1" } },
  1234  {"assert_regex_match": {
  1235    "pattern": "^%{hostname}$",
  1236    "from": "tmp1" } }
  1237  ]]`},
  1238  		service:    keybase1.ProofType_GITHUB,
  1239  		restype:    libkb.XAPIResText,
  1240  		restext:    "fuzztroo",
  1241  		shouldwork: false,
  1242  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1243  	}, {
  1244  		name:      "AssertRegexMatch-invalid-redesc",
  1245  		proofinfo: info1,
  1246  		prepvl: map[keybase1.ProofType]string{
  1247  			keybase1.ProofType_GITHUB: `[[
  1248  {"fetch": {
  1249    "kind": "string",
  1250    "from": "hint_url",
  1251    "into": "tmp1" } },
  1252  {"assert_regex_match": {
  1253    "pattern": ["^foobar$"],
  1254    "from": "tmp1" } }
  1255  ]]`},
  1256  		service:    keybase1.ProofType_GITHUB,
  1257  		restype:    libkb.XAPIResText,
  1258  		restext:    "fuzztroo",
  1259  		shouldwork: false,
  1260  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1261  	}, {
  1262  		name:      "AssertRegexMatch-invalid-badre",
  1263  		proofinfo: info1,
  1264  		prepvl: map[keybase1.ProofType]string{
  1265  			keybase1.ProofType_GITHUB: `[[
  1266  {"fetch": {
  1267    "kind": "string",
  1268    "from": "hint_url",
  1269    "into": "tmp1" } },
  1270  {"assert_regex_match": {
  1271    "pattern": "^foo)(bar$",
  1272    "from": "tmp1" } }
  1273  ]]`},
  1274  		service:    keybase1.ProofType_GITHUB,
  1275  		restype:    libkb.XAPIResText,
  1276  		restext:    "fuzztroo",
  1277  		shouldwork: false,
  1278  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1279  	},
  1280  
  1281  	// ## (Invalid) AssertFindBase64
  1282  	{
  1283  		name:      "AssertFindBase64-invalid-badtarget",
  1284  		proofinfo: info1,
  1285  		prepvl: map[keybase1.ProofType]string{
  1286  			keybase1.ProofType_GITHUB: `[[
  1287  {"fetch": {
  1288    "kind": "string",
  1289    "from": "hint_url",
  1290    "into": "tmp1" } },
  1291  {"assert_find_base64": {
  1292    "needle": "username_keybase",
  1293    "haystack": "tmp1" } }
  1294  ]]`},
  1295  		service:    keybase1.ProofType_GITHUB,
  1296  		restype:    libkb.XAPIResText,
  1297  		restext:    "foobar",
  1298  		shouldwork: false,
  1299  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1300  	},
  1301  
  1302  	// ## (Invalid) AssertCompare
  1303  	{
  1304  		name:      "AssertCompare-invalid-strategy",
  1305  		proofinfo: info1,
  1306  		prepvl: map[keybase1.ProofType]string{
  1307  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
  1308  {"fetch": {
  1309    "kind": "string",
  1310    "from": "hint_url",
  1311    "into": "tmp1" } },
  1312  {"assert_compare": {
  1313    "cmp": "",
  1314    "a": "username_keybase",
  1315    "b": "tmp1" } }
  1316  ]]`},
  1317  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
  1318  		restype:    libkb.XAPIResText,
  1319  		restext:    "krONK",
  1320  		shouldwork: false,
  1321  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1322  	}, {
  1323  		name:      "AssertCompare-invalid-noa",
  1324  		proofinfo: info1,
  1325  		prepvl: map[keybase1.ProofType]string{
  1326  			keybase1.ProofType_GENERIC_WEB_SITE: `[[
  1327  {"fetch": {
  1328    "kind": "string",
  1329    "from": "hint_url",
  1330    "into": "tmp1" } },
  1331  {"assert_compare": {
  1332    "cmp": "",
  1333    "a": "",
  1334    "b": "tmp1" } }
  1335  ]]`},
  1336  		service:    keybase1.ProofType_GENERIC_WEB_SITE,
  1337  		restype:    libkb.XAPIResText,
  1338  		restext:    "krONK",
  1339  		shouldwork: false,
  1340  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1341  	},
  1342  
  1343  	// ## (Invalid) WhitespaceNormalize
  1344  	// No tests
  1345  
  1346  	// ## (Invalid) RegexCapture
  1347  	// Mostly the same as AssertRegexMatch
  1348  	{
  1349  		name:      "RegexCapture-invalid-missing^",
  1350  		proofinfo: info1,
  1351  		prepvl: map[keybase1.ProofType]string{
  1352  			keybase1.ProofType_GITHUB: `[[
  1353  {"fetch": {
  1354    "kind": "string",
  1355    "from": "hint_url",
  1356    "into": "tmp1" } },
  1357  {"regex_capture": {
  1358    "pattern": "(f)oobar$",
  1359    "from": "tmp1",
  1360    "into": ["tmp2"] } }
  1361  ]]`},
  1362  		service:    keybase1.ProofType_GITHUB,
  1363  		restype:    libkb.XAPIResText,
  1364  		restext:    "fuzztroo",
  1365  		shouldwork: false,
  1366  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1367  	}, {
  1368  		name:      "RegexCapture-invalid-missing$",
  1369  		proofinfo: info1,
  1370  		prepvl: map[keybase1.ProofType]string{
  1371  			keybase1.ProofType_GITHUB: `[[
  1372  {"fetch": {
  1373    "kind": "string",
  1374    "from": "hint_url",
  1375    "into": "tmp1" } },
  1376  {"regex_capture": {
  1377    "pattern": "^(f)oobar",
  1378    "from": "tmp1",
  1379    "into": ["tmp2"] } }
  1380  ]]`},
  1381  		service:    keybase1.ProofType_GITHUB,
  1382  		restype:    libkb.XAPIResText,
  1383  		restext:    "fuzztroo",
  1384  		shouldwork: false,
  1385  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1386  	}, {
  1387  		name:      "RegexCapture-invalid-badvar",
  1388  		proofinfo: info1,
  1389  		prepvl: map[keybase1.ProofType]string{
  1390  			keybase1.ProofType_GITHUB: `[[
  1391  {"fetch": {
  1392    "kind": "string",
  1393    "from": "hint_url",
  1394    "into": "tmp1" } },
  1395  {"regex_capture": {
  1396    "pattern": "^(%{hostname})$",
  1397    "from": "tmp1",
  1398    "into": ["tmp2"] } }
  1399  ]]`},
  1400  		service:    keybase1.ProofType_GITHUB,
  1401  		restype:    libkb.XAPIResText,
  1402  		restext:    "fuzztroo",
  1403  		shouldwork: false,
  1404  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1405  	}, {
  1406  		name:      "RegexCapture-invalid-redesc",
  1407  		proofinfo: info1,
  1408  		prepvl: map[keybase1.ProofType]string{
  1409  			keybase1.ProofType_GITHUB: `[[
  1410  {"fetch": {
  1411    "kind": "string",
  1412    "from": "hint_url",
  1413    "into": "tmp1" } },
  1414  {"regex_capture": {
  1415    "pattern": ["^(f)oobar$"],
  1416    "from": "tmp1",
  1417    "into": ["tmp2"] } }
  1418  ]]`},
  1419  		service:    keybase1.ProofType_GITHUB,
  1420  		restype:    libkb.XAPIResText,
  1421  		restext:    "fuzztroo",
  1422  		shouldwork: false,
  1423  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1424  	},
  1425  
  1426  	// ## (Invalid) ParseURL
  1427  	{
  1428  		name:      "ParseURL-invalid-missing-from",
  1429  		proofinfo: info1,
  1430  		prepvl: map[keybase1.ProofType]string{
  1431  			keybase1.ProofType_GITHUB: `[[
  1432  {"fetch": {
  1433    "kind": "string",
  1434    "from": "hint_url",
  1435    "into": "tmp1" } },
  1436  {"parse_url": {
  1437    "path": "path" } }
  1438  ]]`},
  1439  		service:    keybase1.ProofType_GITHUB,
  1440  		restype:    libkb.XAPIResText,
  1441  		restext:    "http://example.com/",
  1442  		shouldwork: false,
  1443  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1444  	},
  1445  
  1446  	// ## (Invalid) ReplaceAll
  1447  	// No tests
  1448  
  1449  	// ## (Invalid) Fetch
  1450  	{
  1451  		name:      "Fetch-invalid-type",
  1452  		proofinfo: info1,
  1453  		prepvl: map[keybase1.ProofType]string{
  1454  			keybase1.ProofType_GITHUB: `[[
  1455  {"fetch": {
  1456    "kind": "whatsthis",
  1457    "from": "hint_url" } }
  1458  ]]`},
  1459  		service:    keybase1.ProofType_GITHUB,
  1460  		restype:    libkb.XAPIResText,
  1461  		restext:    "foobar",
  1462  		shouldwork: false,
  1463  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1464  	}, {
  1465  		name:      "Fetch-invalid-indns",
  1466  		proofinfo: info1,
  1467  		prepvl: map[keybase1.ProofType]string{
  1468  			keybase1.ProofType_DNS: `[[
  1469  {"fetch": {
  1470    "kind": "whatsthis",
  1471    "from": "hint_url" } }
  1472  ]]`},
  1473  		service:    keybase1.ProofType_DNS,
  1474  		restype:    libkb.XAPIResText,
  1475  		restext:    "foobar",
  1476  		shouldwork: false,
  1477  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1478  	},
  1479  	{
  1480  		name:      "Fetch-invalid-multiple",
  1481  		proofinfo: info1,
  1482  		prepvl: map[keybase1.ProofType]string{
  1483  			keybase1.ProofType_GITHUB: `[[
  1484  {"fetch": {
  1485    "kind": "string",
  1486    "from": "hint_url",
  1487    "into": "tmp2" } },
  1488  {"fetch": {
  1489    "kind": "string",
  1490    "from": "hint_url",
  1491    "into": "tmp2" } }
  1492  ]]`},
  1493  		service:    keybase1.ProofType_GITHUB,
  1494  		restype:    libkb.XAPIResText,
  1495  		restext:    "foobar",
  1496  		shouldwork: false,
  1497  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1498  	},
  1499  
  1500  	// ## (Invalid) ParseHTML
  1501  	// No tests
  1502  
  1503  	// ## (Invalid) SelectorJSON
  1504  	{
  1505  		name:      "SelectorJSON-invalid-scripttype",
  1506  		proofinfo: info1,
  1507  		prepvl: map[keybase1.ProofType]string{
  1508  			keybase1.ProofType_GITHUB: `[[
  1509  {"fetch": {
  1510    "kind": "string",
  1511    "from": "hint_url",
  1512    "into": "tmp1" } },
  1513  {"selector_json": {
  1514    "selectors": [0],
  1515    "into": "tmp1" } }
  1516  ]]`},
  1517  		service:    keybase1.ProofType_GITHUB,
  1518  		restype:    libkb.XAPIResText,
  1519  		restext:    "foobar",
  1520  		shouldwork: false,
  1521  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1522  	}, {
  1523  		name:      "SelectorJSON-invalid-selectorlist",
  1524  		proofinfo: info1,
  1525  		prepvl: map[keybase1.ProofType]string{
  1526  			keybase1.ProofType_GITHUB: `[[
  1527  {"fetch": {
  1528    "kind": "json",
  1529    "from": "hint_url" } },
  1530  {"selector_json": {
  1531    "selectors": 0 } }
  1532  ]]`},
  1533  		service:    keybase1.ProofType_GITHUB,
  1534  		restype:    libkb.XAPIResJSON,
  1535  		resjson:    json1,
  1536  		shouldwork: false,
  1537  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1538  	}, {
  1539  		name:      "SelectorJSON-invalid-empty",
  1540  		proofinfo: info1,
  1541  		prepvl: map[keybase1.ProofType]string{
  1542  			keybase1.ProofType_GITHUB: `[[
  1543  {"fetch": {
  1544    "kind": "json",
  1545    "from": "hint_url" } },
  1546  {"selector_json": {
  1547    "selectors": [],
  1548    "into": "tmp1" } }
  1549  ]]`},
  1550  		service:    keybase1.ProofType_GITHUB,
  1551  		restype:    libkb.XAPIResJSON,
  1552  		resjson:    json1,
  1553  		shouldwork: false,
  1554  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1555  	}, {
  1556  		name:      "SelectorJSON-invalid-contents",
  1557  		proofinfo: info1,
  1558  		prepvl: map[keybase1.ProofType]string{
  1559  			keybase1.ProofType_GITHUB: `[[
  1560  {"fetch": {
  1561    "kind": "json",
  1562    "from": "hint_url" } },
  1563  {"selector_json": {
  1564    "selectors": [{"contents": true}],
  1565    "into": "tmp1" } }
  1566  ]]`},
  1567  		service:    keybase1.ProofType_GITHUB,
  1568  		restype:    libkb.XAPIResJSON,
  1569  		resjson:    json1,
  1570  		shouldwork: false,
  1571  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1572  	}, {
  1573  		name:      "SelectorJSON-invalid-spec",
  1574  		proofinfo: info1,
  1575  		prepvl: map[keybase1.ProofType]string{
  1576  			keybase1.ProofType_GITHUB: `[[
  1577  {"fetch": {
  1578    "kind": "json",
  1579    "from": "hint_url" } },
  1580  {"selector_json": {
  1581    "selectors": [{"foo": "bar" }],
  1582    "into": "tmp1" } }
  1583  ]]`},
  1584  		service:    keybase1.ProofType_GITHUB,
  1585  		restype:    libkb.XAPIResJSON,
  1586  		resjson:    json1,
  1587  		shouldwork: false,
  1588  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1589  	},
  1590  
  1591  	// ## (Invalid) SelectorCSS
  1592  	{
  1593  		name:      "SelectorCSS-invalid-scripttype",
  1594  		proofinfo: info1,
  1595  		prepvl: map[keybase1.ProofType]string{
  1596  			keybase1.ProofType_GITHUB: `[[
  1597  {"fetch": {
  1598    "kind": "json",
  1599    "from": "hint_url" } },
  1600  {"selector_css": {
  1601    "selectors": [0],
  1602    "into": "tmp1" } }
  1603  ]]`},
  1604  		service:    keybase1.ProofType_GITHUB,
  1605  		restype:    libkb.XAPIResJSON,
  1606  		resjson:    json1,
  1607  		shouldwork: false,
  1608  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1609  	}, {
  1610  		name:      "SelectorCSS-invalid-selectorlist",
  1611  		proofinfo: info1,
  1612  		prepvl: map[keybase1.ProofType]string{
  1613  			keybase1.ProofType_GITHUB: `[[
  1614  {"fetch": {
  1615    "kind": "html",
  1616    "from": "hint_url" } },
  1617  {"selector_css": {
  1618    "selectors": 0 },
  1619    "into": "tmp1" }
  1620  ]]`},
  1621  		service:    keybase1.ProofType_GITHUB,
  1622  		restype:    libkb.XAPIResHTML,
  1623  		reshtml:    html1,
  1624  		shouldwork: false,
  1625  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1626  	}, {
  1627  		name:      "SelectorCSS-invalid-empty",
  1628  		proofinfo: info1,
  1629  		prepvl: map[keybase1.ProofType]string{
  1630  			keybase1.ProofType_GITHUB: `[[
  1631  {"fetch": {
  1632    "kind": "html",
  1633    "from": "hint_url" } },
  1634  {"selector_css": {
  1635    "selectors": [],
  1636    "into": "tmp1" } }
  1637  ]]`},
  1638  		service:    keybase1.ProofType_GITHUB,
  1639  		restype:    libkb.XAPIResHTML,
  1640  		reshtml:    html1,
  1641  		shouldwork: false,
  1642  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1643  	}, {
  1644  		name:      "SelectorCSS-invalid-spec",
  1645  		proofinfo: info1,
  1646  		prepvl: map[keybase1.ProofType]string{
  1647  			keybase1.ProofType_GITHUB: `[[
  1648  {"fetch": {
  1649    "kind": "html" } },
  1650  {"selector_css": {
  1651    "selectors": [{"foo": "bar" }],
  1652    "into": "tmp1" } }
  1653  ]]`},
  1654  		service:    keybase1.ProofType_GITHUB,
  1655  		restype:    libkb.XAPIResHTML,
  1656  		reshtml:    html1,
  1657  		shouldwork: false,
  1658  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1659  	},
  1660  
  1661  	// ## (Invalid) Fill
  1662  	{
  1663  		name:      "Fill-invalid-badreg",
  1664  		proofinfo: info1,
  1665  		prepvl: map[keybase1.ProofType]string{
  1666  			keybase1.ProofType_GITHUB: `[[
  1667  {"fetch": {
  1668    "kind": "string",
  1669    "from": "hint_url",
  1670    "into": "tmp1" } },
  1671  {"fill": {
  1672    "with": "%{hostname}",
  1673    "into": "tmp2" } }
  1674  ]]`},
  1675  		service:    keybase1.ProofType_GITHUB,
  1676  		restype:    libkb.XAPIResText,
  1677  		restext:    "foozle",
  1678  		shouldwork: false,
  1679  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1680  	}, {
  1681  		name:      "Fill-invalid-overwrite",
  1682  		proofinfo: info1,
  1683  		prepvl: map[keybase1.ProofType]string{
  1684  			keybase1.ProofType_GITHUB: `[[
  1685  {"fetch": {
  1686    "kind": "string",
  1687    "from": "hint_url",
  1688    "into": "tmp1" } },
  1689  {"fill": {
  1690    "with": "yeck",
  1691    "into": "tmp1" } }
  1692  ]]`},
  1693  		service:    keybase1.ProofType_GITHUB,
  1694  		restype:    libkb.XAPIResText,
  1695  		restext:    "foozle",
  1696  		shouldwork: false,
  1697  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1698  	},
  1699  
  1700  	// # Multiple Scripts
  1701  	{
  1702  		name:      "MultipleScripts-first-ok",
  1703  		proofinfo: info1,
  1704  		prepvl: map[keybase1.ProofType]string{
  1705  			keybase1.ProofType_FACEBOOK: `[[
  1706  {"fetch": {
  1707    "kind": "string",
  1708    "from": "hint_url",
  1709    "into": "tmp1" } },
  1710  {"assert_regex_match": {
  1711    "pattern": "^ok$",
  1712    "from": "tmp1" } }
  1713  ], [
  1714  {"fetch": {
  1715    "kind": "string",
  1716    "from": "hint_url",
  1717    "into": "tmp1" } },
  1718  {"assert_regex_match": {
  1719    "pattern": "^NO$",
  1720    "from": "tmp1" } }
  1721  ]]`},
  1722  		service:    keybase1.ProofType_FACEBOOK,
  1723  		restype:    libkb.XAPIResText,
  1724  		restext:    "ok",
  1725  		shouldwork: true,
  1726  	}, {
  1727  		name:      "MultipleScripts-second-ok",
  1728  		proofinfo: info1,
  1729  		prepvl: map[keybase1.ProofType]string{
  1730  			keybase1.ProofType_FACEBOOK: `[[
  1731  {"fetch": {
  1732    "kind": "string",
  1733    "from": "hint_url",
  1734    "into": "tmp1" } },
  1735  {"assert_regex_match": {
  1736    "pattern": "^NO$",
  1737    "from": "tmp1" } }
  1738  ], [
  1739  {"fetch": {
  1740    "kind": "string",
  1741    "from": "hint_url",
  1742    "into": "tmp1" } },
  1743  {"assert_regex_match": {
  1744    "pattern": "^ok$",
  1745    "from": "tmp1" } }
  1746  ]]`},
  1747  		service:          keybase1.ProofType_FACEBOOK,
  1748  		restype:          libkb.XAPIResText,
  1749  		restext:          "ok",
  1750  		allowmanyfetches: true,
  1751  		shouldwork:       true,
  1752  	}, {
  1753  		name:      "MultipleScripts-both-ok",
  1754  		proofinfo: info1,
  1755  		prepvl: map[keybase1.ProofType]string{
  1756  			keybase1.ProofType_FACEBOOK: `[[
  1757  {"fetch": {
  1758    "kind": "string",
  1759    "from": "hint_url",
  1760    "into": "tmp1" } },
  1761  {"assert_regex_match": {
  1762    "pattern": "^ok$",
  1763    "from": "tmp1" } }
  1764  ], [
  1765  {"fetch": {
  1766    "kind": "string",
  1767    "from": "hint_url",
  1768    "into": "tmp1" } },
  1769  {"assert_regex_match": {
  1770    "pattern": "^ok",
  1771    "from": "tmp1" } }
  1772  ]]`},
  1773  		service:    keybase1.ProofType_FACEBOOK,
  1774  		restype:    libkb.XAPIResText,
  1775  		restext:    "ok",
  1776  		shouldwork: true,
  1777  	}, {
  1778  		name:      "MultipleScripts-both-fail",
  1779  		proofinfo: info1,
  1780  		prepvl: map[keybase1.ProofType]string{
  1781  			keybase1.ProofType_FACEBOOK: `[[
  1782  {"fetch": {
  1783    "kind": "string",
  1784    "from": "hint_url",
  1785    "into": "tmp1" } },
  1786  {"assert_regex_match": {
  1787    "pattern": "^NO$",
  1788    "error": ["HOST_UNREACHABLE", "x"],
  1789    "from": "tmp1" } }
  1790  ], [
  1791  {"fetch": {
  1792    "kind": "string",
  1793    "from": "hint_url",
  1794    "into": "tmp1" } },
  1795  {"assert_regex_match": {
  1796    "pattern": "^NO$",
  1797    "error": ["HOST_UNREACHABLE", "x"],
  1798    "from": "tmp1" } }
  1799  ]]`},
  1800  		service:    keybase1.ProofType_FACEBOOK,
  1801  		restype:    libkb.XAPIResText,
  1802  		restext:    "ok",
  1803  		shouldwork: false,
  1804  		errstatus:  keybase1.ProofStatus_HOST_UNREACHABLE,
  1805  	},
  1806  
  1807  	// # DNS tests
  1808  	// Checking a DNS proof is different from checking other proofs.
  1809  	// It runs each script against each TXT record of 2 domains.
  1810  	// That mechanism is tested here.
  1811  	{
  1812  		name:      "DNS-second-record-ok",
  1813  		proofinfo: info1,
  1814  		prepvl: map[keybase1.ProofType]string{
  1815  			keybase1.ProofType_DNS: `[[
  1816  {"assert_regex_match": {
  1817    "pattern": "^ok$",
  1818    "from": "txt" } }
  1819  ]]`},
  1820  		service: keybase1.ProofType_DNS,
  1821  		resdns: map[string][]string{
  1822  			info1.Hostname: {"NO", "ok"},
  1823  		},
  1824  		shouldwork: true,
  1825  	}, {
  1826  		name:      "DNS-_keybase-ok",
  1827  		proofinfo: info1,
  1828  		prepvl: map[keybase1.ProofType]string{
  1829  			keybase1.ProofType_DNS: `[[
  1830  {"assert_regex_match": {
  1831    "pattern": "^ok$",
  1832    "from": "txt" } }
  1833  ]]`},
  1834  		service: keybase1.ProofType_DNS,
  1835  		resdns: map[string][]string{
  1836  			info1.Hostname:               {"NO_1", "NO_2"},
  1837  			"_keybase." + info1.Hostname: {"NO_3", "ok"},
  1838  		},
  1839  		shouldwork: true,
  1840  	}, {
  1841  		name:      "DNS-no-records",
  1842  		proofinfo: info1,
  1843  		prepvl: map[keybase1.ProofType]string{
  1844  			keybase1.ProofType_DNS: `[[
  1845  {"assert_regex_match": {
  1846    "pattern": "^ok$",
  1847    "from": "txt" } }
  1848  ]]`},
  1849  		service: keybase1.ProofType_DNS,
  1850  		resdns: map[string][]string{
  1851  			info1.Hostname:               {},
  1852  			"_keybase." + info1.Hostname: {},
  1853  		},
  1854  		shouldwork: false,
  1855  	}, {
  1856  		name:      "DNS-no-matching-records",
  1857  		proofinfo: info1,
  1858  		prepvl: map[keybase1.ProofType]string{
  1859  			keybase1.ProofType_DNS: `[[
  1860  {"assert_regex_match": {
  1861    "pattern": "^ok$",
  1862    "from": "txt" } }
  1863  ]]`},
  1864  		service: keybase1.ProofType_DNS,
  1865  		resdns: map[string][]string{
  1866  			info1.Hostname:               {"NO_1"},
  1867  			"_keybase." + info1.Hostname: {"NO_2", "NO_3"},
  1868  		},
  1869  		shouldwork: false,
  1870  	}, {
  1871  		name:      "DNS-error-then-succeed",
  1872  		proofinfo: info1,
  1873  		prepvl: map[keybase1.ProofType]string{
  1874  			keybase1.ProofType_DNS: `[[
  1875  {"assert_regex_match": {
  1876    "pattern": "^ok$",
  1877    "from": "txt" } }
  1878  ]]`},
  1879  		service: keybase1.ProofType_DNS,
  1880  		resdns: map[string][]string{
  1881  			info1.Hostname:               {"ERROR"},
  1882  			"_keybase." + info1.Hostname: {"ok"},
  1883  		},
  1884  		shouldwork: true,
  1885  	}, {
  1886  		name:      "DNS-error-then-succeed-2",
  1887  		proofinfo: info1,
  1888  		prepvl: map[keybase1.ProofType]string{
  1889  			keybase1.ProofType_DNS: `[[
  1890  {"assert_regex_match": {
  1891    "pattern": "^ok1$",
  1892    "from": "txt" } }
  1893  ], [
  1894  {"assert_regex_match": {
  1895    "pattern": "^ok2$",
  1896    "from": "txt" } }
  1897  ]]`},
  1898  		service: keybase1.ProofType_DNS,
  1899  		resdns: map[string][]string{
  1900  			info1.Hostname: {"NO_1", "ok2"},
  1901  		},
  1902  		shouldwork: true,
  1903  	},
  1904  
  1905  	// # Custom Errors
  1906  	{
  1907  		name:      "CustomError-ok",
  1908  		proofinfo: info1,
  1909  		prepvl: map[keybase1.ProofType]string{
  1910  			keybase1.ProofType_GITHUB: `[[
  1911  { "fetch": {
  1912    "kind": "string",
  1913    "from": "hint_url",
  1914    "into": "tmp1"  } },
  1915  { "assert_regex_match": {
  1916    "pattern": "^foo$",
  1917    "error": ["PERMISSION_DENIED", "whoops!"],
  1918    "from": "tmp1" } }
  1919  ]]`},
  1920  		service:    keybase1.ProofType_GITHUB,
  1921  		restype:    libkb.XAPIResText,
  1922  		restext:    "bar",
  1923  		shouldwork: false,
  1924  		errstatus:  keybase1.ProofStatus_PERMISSION_DENIED,
  1925  		errstr:     "whoops!",
  1926  	}, {
  1927  		name:      "CustomError-wrong-short-array",
  1928  		proofinfo: info1,
  1929  		prepvl: map[keybase1.ProofType]string{
  1930  			keybase1.ProofType_GITHUB: `[[
  1931  { "fetch": {
  1932    "kind": "string",
  1933    "from": "hint_url",
  1934    "into": "tmp1"  } },
  1935  { "assert_regex_match": {
  1936    "pattern": "^foo$",
  1937    "error": ["PERMISSION_DENIED"],
  1938    "from": "tmp1" } }
  1939  ]]`},
  1940  		service:    keybase1.ProofType_GITHUB,
  1941  		restype:    libkb.XAPIResText,
  1942  		restext:    "bar",
  1943  		shouldwork: false,
  1944  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1945  	}, {
  1946  		name:      "CustomError-wrong-type1",
  1947  		proofinfo: info1,
  1948  		prepvl: map[keybase1.ProofType]string{
  1949  			keybase1.ProofType_GITHUB: `[[
  1950  { "fetch": {
  1951    "kind": "string",
  1952    "from": "hint_url",
  1953    "into": "tmp1"  } },
  1954  { "assert_regex_match": {
  1955    "pattern": "^foo$",
  1956    "error": 108,
  1957    "from": "tmp1" } }
  1958  ]]`},
  1959  		service:    keybase1.ProofType_GITHUB,
  1960  		restype:    libkb.XAPIResText,
  1961  		restext:    "bar",
  1962  		shouldwork: false,
  1963  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1964  	}, {
  1965  		name:      "CustomError-wrong-type2",
  1966  		proofinfo: info1,
  1967  		prepvl: map[keybase1.ProofType]string{
  1968  			keybase1.ProofType_GITHUB: `[[
  1969  { "fetch": {
  1970    "kind": "string",
  1971    "from": "hint_url",
  1972    "into": "tmp1"  } },
  1973  { "assert_regex_match": {
  1974    "pattern": "^foo$",
  1975    "error": ["TIMEOUT", []],
  1976    "from": "tmp1" } }
  1977  ]]`},
  1978  		service:    keybase1.ProofType_GITHUB,
  1979  		restype:    libkb.XAPIResText,
  1980  		restext:    "bar",
  1981  		shouldwork: false,
  1982  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  1983  	}, {
  1984  		// A custom error should not affect invalidity.
  1985  		// Here the regex contains invalid var name.
  1986  		name:      "CustomError-and-invalid",
  1987  		proofinfo: info1,
  1988  		prepvl: map[keybase1.ProofType]string{
  1989  			keybase1.ProofType_GITHUB: `[[
  1990  { "fetch": {
  1991    "kind": "string",
  1992    "from": "hint_url",
  1993    "into": "tmp1"  } },
  1994  { "assert_regex_match": {
  1995    "pattern": "^%{invalid}$",
  1996    "error": ["PERMISSION_DENIED", "whoops"],
  1997    "from": "tmp1" } }
  1998  ]]`},
  1999  		service:    keybase1.ProofType_GITHUB,
  2000  		restype:    libkb.XAPIResText,
  2001  		restext:    "bar",
  2002  		shouldwork: false,
  2003  		errstatus:  keybase1.ProofStatus_INVALID_PVL,
  2004  	}, {
  2005  		// Without a custom error, a default will appear.
  2006  		name:      "CustomError-none",
  2007  		proofinfo: info1,
  2008  		prepvl: map[keybase1.ProofType]string{
  2009  			keybase1.ProofType_GITHUB: `[[
  2010  { "fetch": {
  2011    "kind": "string",
  2012    "from": "hint_url",
  2013    "into": "tmp1" } },
  2014  { "assert_regex_match": {
  2015    "pattern": "^foo$",
  2016    "from": "tmp1" } }
  2017  ]]`},
  2018  		service:    keybase1.ProofType_GITHUB,
  2019  		restype:    libkb.XAPIResText,
  2020  		restext:    "bar",
  2021  		shouldwork: false,
  2022  		errstatus:  keybase1.ProofStatus_CONTENT_FAILURE,
  2023  		errstr:     "Regex did not match (^foo$)",
  2024  	},
  2025  }
  2026  
  2027  func TestUnits(t *testing.T) {
  2028  	solo := ""
  2029  	for _, unit := range interpUnitTests {
  2030  		if len(solo) == 0 || unit.name == solo {
  2031  			t.Logf("Testing: %v", unit.name)
  2032  			runPvlTest(t, &unit)
  2033  		}
  2034  	}
  2035  	if len(solo) > 0 {
  2036  		// Soloing a test shall never be checked in.
  2037  		t.Fatalf("soloed a test that passed\n\n\n*\n*\n*\n*\n*\n*\n*")
  2038  	}
  2039  }
  2040  
  2041  type failer func(string, ...interface{})
  2042  
  2043  func runPvlTest(t *testing.T, unit *interpUnitTest) {
  2044  	tc := libkb.SetupTest(t, unit.name, 1)
  2045  	defer tc.Cleanup()
  2046  
  2047  	fail := func(f string, arg ...interface{}) {
  2048  		f2 := fmt.Sprintf("[%v] ", unit.name) + f
  2049  		t.Fatalf(f2, arg...)
  2050  	}
  2051  
  2052  	g := tc.G
  2053  	xapi := newStubAPIEngine()
  2054  	g.XAPI = xapi
  2055  
  2056  	var pvl string
  2057  	var err error
  2058  	switch {
  2059  	case (unit.prepvl == nil) == (unit.prepvlstr == ""):
  2060  		fail("one of prepvl or prepvlstr must be specified")
  2061  	case unit.prepvlstr == "":
  2062  		pvl, err = makeTestPvl(unit.prepvl)
  2063  		if err != nil {
  2064  			fail("Error in prepvl: %v", err)
  2065  		}
  2066  	default:
  2067  		pvl = unit.prepvlstr
  2068  	}
  2069  
  2070  	var url = unit.proofinfo.APIURL
  2071  	if unit.urloverride != "" {
  2072  		url = unit.urloverride
  2073  	}
  2074  
  2075  	isdns := unit.service == keybase1.ProofType_DNS
  2076  
  2077  	if isdns {
  2078  		unit.proofinfo.stubDNS = newStubDNSEngine(unit.resdns)
  2079  	} else {
  2080  		switch unit.restype {
  2081  		case libkb.XAPIResJSON:
  2082  			xapi.Set(url, newExternalJSONRes(unit.resjson, fail))
  2083  		case libkb.XAPIResHTML:
  2084  			xapi.SetHTML(url, newExternalHTMLRes(unit.reshtml, fail))
  2085  		case libkb.XAPIResText:
  2086  			xapi.SetText(url, newExternalTextRes(unit.restext, fail))
  2087  		default:
  2088  			fail("unsupported restype: %v", unit.restype)
  2089  		}
  2090  	}
  2091  
  2092  	m := libkb.NewMetaContextForTest(tc)
  2093  	perr := CheckProof(m, pvl, unit.service, unit.proofinfo)
  2094  	if perr == nil {
  2095  		if !unit.shouldwork {
  2096  			fail("proof should have failed")
  2097  		}
  2098  
  2099  		// err is definitely nil here.
  2100  		if isdns {
  2101  			if !unit.proofinfo.stubDNS.IsOk() {
  2102  				fail("DNS stub ran out of bounds")
  2103  			}
  2104  		} else {
  2105  			if unit.allowmanyfetches {
  2106  				err = xapi.AssertCalledWith(unit.restype, url)
  2107  			} else {
  2108  				err = xapi.AssertCalledOnceWith(unit.restype, url)
  2109  			}
  2110  			if err != nil {
  2111  				fail("%v", err)
  2112  			}
  2113  		}
  2114  	} else {
  2115  		if unit.shouldwork {
  2116  			fail("proof failed: %v", perr)
  2117  		}
  2118  		none := keybase1.ProofStatus_NONE
  2119  		if unit.errstatus != none && perr.GetProofStatus() != unit.errstatus {
  2120  			fail("status mismatch:\n  expected: %v\n  got: %v", unit.errstatus, perr.GetProofStatus())
  2121  		}
  2122  		if len(unit.errstr) > 0 && unit.errstr != perr.GetDesc() {
  2123  			fail("status mismatch:\n  expected: %v\n  got: %v", unit.errstr, perr.GetDesc())
  2124  		}
  2125  		if unit.errstatus == none && perr.GetProofStatus() == keybase1.ProofStatus_INVALID_PVL {
  2126  			fail("unexpected INVALID_PVL:\n  expected: %v\n  got: %v", unit.errstatus, perr.Error())
  2127  		}
  2128  	}
  2129  }
  2130  
  2131  func makeTestPvl(rules map[keybase1.ProofType]string) (string, error) {
  2132  	var pvl string
  2133  	services := jsonw.NewDictionary()
  2134  	for ptype, v1 := range rules {
  2135  		var err error
  2136  		ptypestr, err := serviceToString(ptype)
  2137  		if err != nil {
  2138  			return pvl, err
  2139  		}
  2140  		v2, err := jsonw.Unmarshal([]byte(v1))
  2141  		if err != nil {
  2142  			return pvl, err
  2143  		}
  2144  		err = services.SetKey(ptypestr, v2)
  2145  		if err != nil {
  2146  			return pvl, err
  2147  		}
  2148  	}
  2149  	pvlStr := jsonw.NewDictionary()
  2150  	err := pvlStr.SetKey("pvl_version", jsonw.NewInt(SupportedVersion))
  2151  	if err != nil {
  2152  		return pvl, err
  2153  	}
  2154  	err = pvlStr.SetKey("revision", jsonw.NewInt(1))
  2155  	if err != nil {
  2156  		return pvl, err
  2157  	}
  2158  	err = pvlStr.SetKey("services", services)
  2159  	if err != nil {
  2160  		return pvl, err
  2161  	}
  2162  	pvlB, err := pvlStr.Marshal()
  2163  	if err != nil {
  2164  		return pvl, err
  2165  	}
  2166  	pvl = string(pvlB)
  2167  	return pvl, nil
  2168  }
  2169  
  2170  func newExternalJSONRes(json string, fail failer) *libkb.ExternalAPIRes {
  2171  	if json == "" {
  2172  		fail("empty json string")
  2173  	}
  2174  	w, err := jsonw.Unmarshal([]byte(json))
  2175  	if err != nil {
  2176  		fail("invalid json: %v", err)
  2177  	}
  2178  	return &libkb.ExternalAPIRes{HTTPStatus: 200, Body: w}
  2179  }
  2180  
  2181  func newExternalHTMLRes(html string, fail failer) *libkb.ExternalHTMLRes {
  2182  	if html == "" {
  2183  		fail("empty html string")
  2184  	}
  2185  	reader := strings.NewReader(html)
  2186  	doc, err := goquery.NewDocumentFromReader(reader)
  2187  	if err != nil {
  2188  		fail("invalid html: %v", err)
  2189  	}
  2190  	return &libkb.ExternalHTMLRes{HTTPStatus: 200, GoQuery: doc}
  2191  }
  2192  
  2193  func newExternalTextRes(text string, fail failer) *libkb.ExternalTextRes {
  2194  	if text == "" {
  2195  		fail("empty text response")
  2196  	}
  2197  	return &libkb.ExternalTextRes{HTTPStatus: 200, Body: text}
  2198  }