github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/pvl/helpers.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  	"net"
     8  	"net/url"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/PuerkitoBio/goquery"
    13  	libkb "github.com/keybase/client/go/libkb"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  )
    16  
    17  // Substitute register values for %{name} in the string.
    18  // Regex-escape variable values
    19  func substituteReEscape(template string, state scriptState) (string, libkb.ProofError) {
    20  	return substituteInner(template, state, true)
    21  }
    22  
    23  // Substitute register values for %{name} in the string.
    24  // Does not escape register values
    25  func substituteExact(template string, state scriptState) (string, libkb.ProofError) {
    26  	return substituteInner(template, state, false)
    27  }
    28  
    29  var nameRE = regexp.MustCompile(`%\{[\w]*\}`)
    30  
    31  func substituteInner(template string, state scriptState, regexEscape bool) (string, libkb.ProofError) {
    32  	var outerr libkb.ProofError
    33  	// Regex to find %{name} occurrences.
    34  	// Match broadly here so that even %{} is sent to the default case and reported as invalid.
    35  	substituteOne := func(vartag string) string {
    36  		// Strip off the %, {, and }
    37  		varname := vartag[2 : len(vartag)-1]
    38  		value, err := state.Regs.Get(varname)
    39  		if err != nil {
    40  			outerr = libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL,
    41  				"Invalid substitution: %v", err)
    42  			return ""
    43  		}
    44  		if regexEscape {
    45  			return regexp.QuoteMeta(value)
    46  		}
    47  		return value
    48  	}
    49  	res := nameRE.ReplaceAllStringFunc(template, substituteOne)
    50  	if outerr != nil {
    51  		return template, outerr
    52  	}
    53  	return res, nil
    54  }
    55  
    56  func serviceToString(service keybase1.ProofType) (string, libkb.ProofError) {
    57  	for name, stat := range keybase1.ProofTypeMap {
    58  		if service == stat {
    59  			return strings.ToLower(name), nil
    60  		}
    61  	}
    62  
    63  	return "", libkb.NewProofError(keybase1.ProofStatus_INVALID_PVL, "Unsupported service %v", service)
    64  }
    65  
    66  // selectionText gets the Text of all elements in a selection, concatenated by a space.
    67  // The result can be an empty string.
    68  func selectionText(selection *goquery.Selection) string {
    69  	var results []string
    70  	selection.Each(func(i int, element *goquery.Selection) {
    71  		results = append(results, element.Text())
    72  	})
    73  	return strings.Join(results, " ")
    74  }
    75  
    76  // selectionAttr gets the specified attr of all elements in a selection, concatenated by a space.
    77  // If getting the attr of any elements fails, that does not cause an error.
    78  // The result can be an empty string.
    79  func selectionAttr(selection *goquery.Selection, attr string) string {
    80  	var results []string
    81  	selection.Each(func(i int, element *goquery.Selection) {
    82  		res, ok := element.Attr(attr)
    83  		if ok {
    84  			results = append(results, res)
    85  		}
    86  	})
    87  	return strings.Join(results, " ")
    88  }
    89  
    90  // selectionData gets the first node's data of all elements in a selection, concatenated by a space.
    91  // The result can be an empty string.
    92  func selectionData(selection *goquery.Selection) string {
    93  	var results []string
    94  	selection.Each(func(i int, element *goquery.Selection) {
    95  		if len(element.Nodes) > 0 {
    96  			results = append(results, element.Nodes[0].Data)
    97  		}
    98  	})
    99  	return strings.Join(results, " ")
   100  }
   101  
   102  func stringsContains(xs []string, x string) bool {
   103  	for _, y := range xs {
   104  		if x == y {
   105  			return true
   106  		}
   107  	}
   108  	return false
   109  }
   110  
   111  var hasalpha = regexp.MustCompile(`\D`)
   112  
   113  // Check that a url is valid and has only a domain and is not an ip.
   114  // No port, path, protocol, user, query, or any other junk is allowed.
   115  func validateDomain(s string) bool {
   116  	// Throw a protocol in front because the parser wants one.
   117  	proto := "http"
   118  	u, err := url.Parse(proto + "://" + s)
   119  	if err != nil {
   120  		return false
   121  	}
   122  
   123  	// The final group must include a non-numeric character.
   124  	// To disallow the likes of "8.8.8.8."
   125  	dotsplit := strings.Split(strings.TrimSuffix(u.Host, "."), ".")
   126  	if len(dotsplit) > 0 {
   127  		group := dotsplit[len(dotsplit)-1]
   128  		if !hasalpha.MatchString(group) {
   129  			return false
   130  		}
   131  	}
   132  
   133  	ok := (u.IsAbs()) &&
   134  		(u.Scheme == proto) &&
   135  		(u.User == nil) &&
   136  		(u.Path == "") &&
   137  		(u.RawPath == "") &&
   138  		(u.RawQuery == "") &&
   139  		(u.Fragment == "") &&
   140  		// Disallow colons. So no port, and no ipv6.
   141  		(!strings.Contains(u.Host, ":")) &&
   142  		// Disallow any valid ip addresses.
   143  		(net.ParseIP(u.Host) == nil)
   144  	return ok
   145  }
   146  
   147  // validateProtocol takes a protocol and returns the canonicalized form and whether it is valid.
   148  func validateProtocol(s string, allowed []string) (string, bool) {
   149  	canons := map[string]string{
   150  		"http":     "http",
   151  		"https":    "https",
   152  		"dns":      "dns",
   153  		"http:":    "http",
   154  		"https:":   "https",
   155  		"dns:":     "dns",
   156  		"http://":  "http",
   157  		"https://": "https",
   158  		"dns://":   "dns",
   159  	}
   160  
   161  	canon, ok := canons[s]
   162  	if ok {
   163  		return canon, stringsContains(allowed, canon)
   164  	}
   165  	return canon, false
   166  }
   167  
   168  func rooterRewriteURL(m metaContext, s string) (string, error) {
   169  	u1, err := url.Parse(s)
   170  	if err != nil {
   171  		return "", err
   172  	}
   173  
   174  	serverURI, err := m.G().GetServerURI()
   175  	if err != nil {
   176  		return "", nil
   177  	}
   178  
   179  	u2, err := url.Parse(serverURI)
   180  	if err != nil {
   181  		return "", err
   182  	}
   183  
   184  	u3 := url.URL{
   185  		Host:     u2.Host,
   186  		Scheme:   u2.Scheme,
   187  		Path:     u1.Path,
   188  		Fragment: u1.Fragment,
   189  	}
   190  
   191  	return u3.String(), nil
   192  }