github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/pvl/parse_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  	"testing"
     8  
     9  	"github.com/keybase/client/go/protocol/keybase1"
    10  )
    11  
    12  // TestParse parses the hardcoded string
    13  func TestParse(t *testing.T) {
    14  	p, err := parse(testPvlString)
    15  	if err != nil {
    16  		t.Fatalf("parse failed: %v", err)
    17  	}
    18  	if p.PvlVersion < 0 {
    19  		t.Fatalf("version should be >=0: %v", p.PvlVersion)
    20  	}
    21  }
    22  
    23  // TestParse2 checks a few of the parse output's details.
    24  func TestParse2(t *testing.T) {
    25  	p, err := parse(testPvlString)
    26  	if err != nil {
    27  		t.Fatalf("parse failed: %v", err)
    28  	}
    29  	if p.PvlVersion != 1 {
    30  		t.Fatalf("version should be 1 got %v", p.PvlVersion)
    31  	}
    32  	if p.Revision != 1 {
    33  		t.Fatalf("revision should be 1")
    34  	}
    35  	cbss, ok := p.Services.Map[keybase1.ProofType_TWITTER]
    36  	if !ok {
    37  		t.Fatalf("no twittter service entry")
    38  	}
    39  	if len(cbss) < 1 {
    40  		t.Fatalf("no scripts")
    41  	}
    42  	cbs := cbss[0]
    43  	if len(cbs.Instructions) < 1 {
    44  		t.Fatalf("empty script")
    45  	}
    46  	if cbs.Instructions[0].RegexCapture == nil {
    47  		t.Fatalf("first instruction is not a regex capture")
    48  	}
    49  }
    50  
    51  var testPvlString = `
    52    {
    53    "pvl_version": 1,
    54    "revision": 1,
    55    "services": {
    56      "coinbase": [
    57        [
    58          {
    59            "fill": {
    60              "into": "our_url",
    61              "with": "https://coinbase.com/%{username_service}/public-key"
    62            }
    63          },
    64          {
    65            "fetch": {
    66              "from": "our_url",
    67              "kind": "html"
    68            }
    69          },
    70          {
    71            "selector_css": {
    72              "error": [
    73                "FAILED_PARSE",
    74                "Couldn't find a div $(pre.statement)"
    75              ],
    76              "into": "haystack",
    77              "selectors": [
    78                "pre.statement",
    79                0
    80              ]
    81            }
    82          },
    83          {
    84            "assert_find_base64": {
    85              "haystack": "haystack",
    86              "needle": "sig"
    87            },
    88            "error": [
    89              "TEXT_NOT_FOUND",
    90              "signature not found in body"
    91            ]
    92          }
    93        ]
    94      ],
    95      "dns": [
    96        [
    97          {
    98            "assert_regex_match": {
    99              "error": [
   100                "NOT_FOUND",
   101                "matching DNS entry not found"
   102              ],
   103              "from": "txt",
   104              "pattern": "^keybase-site-verification=%{sig_id_medium}$"
   105            }
   106          }
   107        ]
   108      ],
   109      "facebook": [
   110        [
   111          {
   112            "assert_regex_match": {
   113              "error": [
   114                "BAD_USERNAME",
   115                "Invalid characters in username '%{username_service}'"
   116              ],
   117              "from": "username_service",
   118              "pattern": "^[a-zA-Z0-9\\.]+$"
   119            }
   120          },
   121          {
   122            "regex_capture": {
   123              "error": [
   124                "BAD_API_URL",
   125                "Bad hint from server; URL should start with 'https://m.facebook.com/%{username_service}/posts/', got '%{hint_url}'"
   126              ],
   127              "from": "hint_url",
   128              "into": [
   129                "unused1",
   130                "username_from_url",
   131                "post_id"
   132              ],
   133              "pattern": "^https://(m|www)\\.facebook\\.com/([^/]*)/posts/([0-9]+)$"
   134            }
   135          },
   136          {
   137            "assert_compare": {
   138              "a": "username_from_url",
   139              "b": "username_service",
   140              "cmp": "stripdots-then-cicmp",
   141              "error": [
   142                "BAD_API_URL",
   143                "Bad hint from server; username in URL should match '%{username_service}', received '%{username_from_url}'"
   144              ]
   145            }
   146          },
   147          {
   148            "fill": {
   149              "into": "our_url",
   150              "with": "https://www.facebook.com/%{username_from_url}/posts/%{post_id}"
   151            }
   152          },
   153          {
   154            "fetch": {
   155              "from": "our_url",
   156              "kind": "html"
   157            }
   158          },
   159          {
   160            "selector_css": {
   161              "data": true,
   162              "error": [
   163                "FAILED_PARSE",
   164                "Could not find proof markup comment in Facebook's response"
   165              ],
   166              "into": "first_code_comment",
   167              "selectors": [
   168                "code",
   169                0,
   170                {
   171                  "contents": true
   172                },
   173                0
   174              ]
   175            }
   176          },
   177          {
   178            "replace_all": {
   179              "from": "first_code_comment",
   180              "into": "fcc2",
   181              "new": "--",
   182              "old": "-\\-\\"
   183            }
   184          },
   185          {
   186            "replace_all": {
   187              "from": "fcc2",
   188              "into": "fcc3",
   189              "new": "\\",
   190              "old": "\\\\"
   191            }
   192          },
   193          {
   194            "parse_html": {
   195              "error": [
   196                "FAILED_PARSE",
   197                "Failed to parse proof markup comment in Facebook post: %{fcc3}"
   198              ],
   199              "from": "fcc3"
   200            }
   201          },
   202          {
   203            "selector_css": {
   204              "error": [
   205                "FAILED_PARSE",
   206                "Could not find link text in Facebook's response"
   207              ],
   208              "into": "link_text",
   209              "selectors": [
   210                "div.userContent+div a",
   211                1
   212              ]
   213            }
   214          },
   215          {
   216            "whitespace_normalize": {
   217              "from": "link_text",
   218              "into": "link_text_nw"
   219            }
   220          },
   221          {
   222            "regex_capture": {
   223              "error": [
   224                "TEXT_NOT_FOUND",
   225                "Could not find Verifying myself: I am %{username_keybase} on Keybase.io. (%{sig_id_medium})"
   226              ],
   227              "from": "link_text_nw",
   228              "into": [
   229                "username_from_link",
   230                "sig_from_link"
   231              ],
   232              "pattern": "^Verifying myself: I am (\\S+) on Keybase.io. (\\S+)$"
   233            }
   234          },
   235          {
   236            "assert_compare": {
   237              "a": "username_from_link",
   238              "b": "username_keybase",
   239              "cmp": "cicmp",
   240              "error": [
   241                "BAD_USERNAME",
   242                "Wrong Keybase username in post '%{username_from_link}' should be '%{username_keybase}'"
   243              ]
   244            }
   245          },
   246          {
   247            "assert_compare": {
   248              "a": "sig_id_medium",
   249              "b": "sig_from_link",
   250              "cmp": "exact",
   251              "error": [
   252                "BAD_SIGNATURE",
   253                "Could not find sig; '%{sig_from_link}' != '%{sig_id_medium}'"
   254              ]
   255            }
   256          }
   257        ]
   258      ],
   259      "generic_web_site": [
   260        [
   261          {
   262            "assert_regex_match": {
   263              "error": [
   264                "BAD_API_URL",
   265                "Bad hint from server; didn't recognize API url: \"%{hint_url}\""
   266              ],
   267              "from": "hint_url",
   268              "pattern": "^%{protocol}://%{hostname}/(?:\\.well-known/keybase\\.txt|keybase\\.txt)$"
   269            }
   270          },
   271          {
   272            "fetch": {
   273              "from": "hint_url",
   274              "into": "blob",
   275              "kind": "string"
   276            }
   277          },
   278          {
   279            "assert_find_base64": {
   280              "error": [
   281                "TEXT_NOT_FOUND",
   282                "signature not found in body"
   283              ],
   284              "haystack": "blob",
   285              "needle": "sig"
   286            }
   287          }
   288        ]
   289      ],
   290      "github": [
   291        [
   292          {
   293            "regex_capture": {
   294              "error": [
   295                "BAD_API_URL",
   296                "Bad hint from server; URL should start with either https://gist.github.com OR https://gist.githubusercontent.com"
   297              ],
   298              "from": "hint_url",
   299              "into": [
   300                "username_from_url"
   301              ],
   302              "pattern": "^https://gist\\.github(?:usercontent)?\\.com/([^/]*)/.*$"
   303            }
   304          },
   305          {
   306            "assert_compare": {
   307              "a": "username_from_url",
   308              "b": "username_service",
   309              "cmp": "cicmp",
   310              "error": [
   311                "BAD_API_URL",
   312                "Bad hint from server; URL should contain username matching %{username_service}; got %{username_from_url}"
   313              ]
   314            }
   315          },
   316          {
   317            "fetch": {
   318              "from": "hint_url",
   319              "into": "haystack",
   320              "kind": "string"
   321            }
   322          },
   323          {
   324            "assert_find_base64": {
   325              "haystack": "haystack",
   326              "needle": "sig"
   327            },
   328            "error": [
   329              "TEXT_NOT_FOUND",
   330              "signature not found in body"
   331            ]
   332          }
   333        ]
   334      ],
   335      "hackernews": [
   336        [
   337          {
   338            "regex_capture": {
   339              "error": [
   340                "BAD_API_URL",
   341                "Bad hint from server; URL should match https://hacker-news.firebaseio.com/v0/user/%{username_service}/about.json"
   342              ],
   343              "from": "hint_url",
   344              "into": [
   345                "username_from_url"
   346              ],
   347              "pattern": "^https://hacker-news\\.firebaseio\\.com/v0/user/([^/]+)/about.json$"
   348            }
   349          },
   350          {
   351            "assert_compare": {
   352              "a": "username_from_url",
   353              "b": "username_service",
   354              "cmp": "cicmp",
   355              "error": [
   356                "BAD_API_URL",
   357                "Bad hint from server; URL should contain username matching %{username_service}; got %{username_from_url}"
   358              ]
   359            }
   360          },
   361          {
   362            "fetch": {
   363              "from": "hint_url",
   364              "into": "profile",
   365              "kind": "string"
   366            }
   367          },
   368          {
   369            "assert_regex_match": {
   370              "error": [
   371                "TEXT_NOT_FOUND",
   372                "Posted text does not include signature '%{sig_id_medium}'"
   373              ],
   374              "from": "profile",
   375              "pattern": "^.*%{sig_id_medium}.*$"
   376            }
   377          }
   378        ]
   379      ],
   380      "reddit": [
   381        [
   382          {
   383            "regex_capture": {
   384              "error": [
   385                "BAD_API_URL",
   386                "URL should start with 'https://www.reddit.com/r/keybaseproofs'"
   387              ],
   388              "from": "hint_url",
   389              "into": [
   390                "subreddit_from_url",
   391                "path_remainder"
   392              ],
   393              "pattern": "^https://www.reddit.com/r/([^/]+)/(.*)$"
   394            }
   395          },
   396          {
   397            "assert_regex_match": {
   398              "case_insensitive": true,
   399              "error": [
   400                "BAD_API_URL",
   401                "URL contained wrong subreddit '%{subreddit_from_url}' !+ 'keybaseproofs'"
   402              ],
   403              "from": "subreddit_from_url",
   404              "pattern": "^keybaseproofs$"
   405            }
   406          },
   407          {
   408            "fetch": {
   409              "from": "hint_url",
   410              "kind": "json"
   411            }
   412          },
   413          {
   414            "selector_json": {
   415              "error": [
   416                "CONTENT_MISSING",
   417                "Could not find 'kind' in json"
   418              ],
   419              "into": "kind",
   420              "selectors": [
   421                0,
   422                "kind"
   423              ]
   424            }
   425          },
   426          {
   427            "assert_regex_match": {
   428              "error": [
   429                "CONTENT_FAILURE",
   430                "Wanted a post of type 'Listing', but got '%{kind}'"
   431              ],
   432              "from": "kind",
   433              "pattern": "^Listing$"
   434            }
   435          },
   436          {
   437            "selector_json": {
   438              "error": [
   439                "CONTENT_MISSING",
   440                "Could not find inner 'kind' in json"
   441              ],
   442              "into": "inner_kind",
   443              "selectors": [
   444                0,
   445                "data",
   446                "children",
   447                0,
   448                "kind"
   449              ]
   450            }
   451          },
   452          {
   453            "assert_regex_match": {
   454              "error": [
   455                "CONTENT_FAILURE",
   456                "Wanted a child of type 't3' but got '%{inner_kind}'"
   457              ],
   458              "from": "inner_kind",
   459              "pattern": "^t3$"
   460            }
   461          },
   462          {
   463            "selector_json": {
   464              "error": [
   465                "CONTENT_MISSING",
   466                "Could not find 'subreddit' in json"
   467              ],
   468              "into": "subreddit_from_json",
   469              "selectors": [
   470                0,
   471                "data",
   472                "children",
   473                0,
   474                "data",
   475                "subreddit"
   476              ]
   477            }
   478          },
   479          {
   480            "assert_regex_match": {
   481              "case_insensitive": true,
   482              "error": [
   483                "CONTENT_FAILURE",
   484                "Wrong subreddit %{subreddit_from_json}"
   485              ],
   486              "from": "subreddit_from_json",
   487              "pattern": "^keybaseproofs$"
   488            }
   489          },
   490          {
   491            "selector_json": {
   492              "error": [
   493                "CONTENT_MISSING",
   494                "Could not find author in json"
   495              ],
   496              "into": "author",
   497              "selectors": [
   498                0,
   499                "data",
   500                "children",
   501                0,
   502                "data",
   503                "author"
   504              ]
   505            }
   506          },
   507          {
   508            "assert_compare": {
   509              "a": "author",
   510              "b": "username_service",
   511              "cmp": "cicmp",
   512              "error": [
   513                "BAD_USERNAME",
   514                "Bad post author; wanted '%{username_service}' but got '%{author}'"
   515              ]
   516            }
   517          },
   518          {
   519            "selector_json": {
   520              "error": [
   521                "CONTENT_MISSING",
   522                "Could not find title in json"
   523              ],
   524              "into": "title",
   525              "selectors": [
   526                0,
   527                "data",
   528                "children",
   529                0,
   530                "data",
   531                "title"
   532              ]
   533            }
   534          },
   535          {
   536            "assert_regex_match": {
   537              "error": [
   538                "TITLE_NOT_FOUND",
   539                "Missing signature ID (%{sig_id_medium})) in post title '%{title}'"
   540              ],
   541              "from": "title",
   542              "pattern": "^.*%{sig_id_medium}.*$"
   543            }
   544          },
   545          {
   546            "selector_json": {
   547              "error": [
   548                "CONTENT_MISSING",
   549                "Could not find selftext in json"
   550              ],
   551              "into": "selftext",
   552              "selectors": [
   553                0,
   554                "data",
   555                "children",
   556                0,
   557                "data",
   558                "selftext"
   559              ]
   560            }
   561          },
   562          {
   563            "assert_find_base64": {
   564              "error": [
   565                "TEXT_NOT_FOUND",
   566                "signature not found in body"
   567              ],
   568              "haystack": "selftext",
   569              "needle": "sig"
   570            }
   571          }
   572        ]
   573      ],
   574      "rooter": [
   575        [
   576          {
   577            "assert_regex_match": {
   578              "case_insensitive": true,
   579              "from": "hint_url",
   580              "pattern": "^https?://[\\w:_\\-\\.]+/_/api/1\\.0/rooter/%{username_service}/.*$"
   581            }
   582          },
   583          {
   584            "fetch": {
   585              "from": "hint_url",
   586              "kind": "json"
   587            }
   588          },
   589          {
   590            "selector_json": {
   591              "into": "name",
   592              "selectors": [
   593                "status",
   594                "name"
   595              ]
   596            }
   597          },
   598          {
   599            "assert_regex_match": {
   600              "case_insensitive": true,
   601              "from": "name",
   602              "pattern": "^ok$"
   603            }
   604          },
   605          {
   606            "selector_json": {
   607              "into": "post",
   608              "selectors": [
   609                "toot",
   610                "post"
   611              ]
   612            }
   613          },
   614          {
   615            "assert_regex_match": {
   616              "from": "post",
   617              "pattern": "^.*%{sig_id_medium}.*$"
   618            }
   619          }
   620        ]
   621      ],
   622      "twitter": [
   623        [
   624          {
   625            "regex_capture": {
   626              "error": [
   627                "BAD_API_URL",
   628                "Bad hint from server; URL should start with 'https://twitter.com/%{username_service}/'"
   629              ],
   630              "from": "hint_url",
   631              "into": [
   632                "username_from_url"
   633              ],
   634              "pattern": "^https://twitter\\.com/([^/]+)/.*$"
   635            }
   636          },
   637          {
   638            "assert_compare": {
   639              "a": "username_from_url",
   640              "b": "username_service",
   641              "cmp": "cicmp",
   642              "error": [
   643                "BAD_API_URL",
   644                "Bad hint from server; URL should contain username matching %{username_service}; got %{username_from_url}"
   645              ]
   646            }
   647          },
   648          {
   649            "fetch": {
   650              "from": "hint_url",
   651              "kind": "html"
   652            }
   653          },
   654          {
   655            "selector_css": {
   656              "attr": "data-screen-name",
   657              "error": [
   658                "FAILED_PARSE",
   659                "Couldn't find a div $(div.permalink-tweet-container div.permalink-tweet).eq(0)"
   660              ],
   661              "into": "data_screen_name",
   662              "selectors": [
   663                "div.permalink-tweet-container div.permalink-tweet",
   664                0
   665              ]
   666            }
   667          },
   668          {
   669            "assert_compare": {
   670              "a": "data_screen_name",
   671              "b": "username_service",
   672              "cmp": "cicmp",
   673              "error": [
   674                "BAD_USERNAME",
   675                "Bad post authored: wanted '%{username_service}' but got '%{data_screen_name}'"
   676              ]
   677            }
   678          },
   679          {
   680            "selector_css": {
   681              "error": [
   682                "CONTENT_MISSING",
   683                "Missing <div class='tweet-text'> container for tweet"
   684              ],
   685              "into": "tweet_contents",
   686              "selectors": [
   687                "div.permalink-tweet-container div.permalink-tweet",
   688                0,
   689                "p.tweet-text",
   690                0
   691              ]
   692            }
   693          },
   694          {
   695            "whitespace_normalize": {
   696              "from": "tweet_contents",
   697              "into": "tweet_contents_nw"
   698            }
   699          },
   700          {
   701            "regex_capture": {
   702              "error": [
   703                "DELETED",
   704                "Could not find 'Verifying myself: I am %{username_keybase} on Keybase.io. %{sig_id_short}'"
   705              ],
   706              "from": "tweet_contents_nw",
   707              "into": [
   708                "username_from_tweet_contents",
   709                "sig_from_tweet_contents"
   710              ],
   711              "pattern": "^ *(?:@[a-zA-Z0-9_-]+\\s*)* *Verifying myself: I am ([A-Za-z0-9_]+) on Keybase\\.io\\. (\\S+) */.*$"
   712            }
   713          },
   714          {
   715            "assert_compare": {
   716              "a": "username_from_tweet_contents",
   717              "b": "username_keybase",
   718              "cmp": "cicmp",
   719              "error": [
   720                "BAD_USERNAME",
   721                "Wrong username in tweet '%{username_from_tweet_contents}' should be '%{username_keybase}'"
   722              ]
   723            }
   724          },
   725          {
   726            "assert_regex_match": {
   727              "error": [
   728                "TEXT_NOT_FOUND",
   729                "Could not find sig '%{sig_from_tweet_contents}' != '%{sig_id_short}'"
   730              ],
   731              "from": "sig_from_tweet_contents",
   732              "pattern": "^%{sig_id_short}$"
   733            }
   734          }
   735        ]
   736      ]
   737    }
   738  }
   739  `