github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/pvl/parse.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 "encoding/json" 8 "fmt" 9 "strings" 10 11 "github.com/keybase/client/go/protocol/keybase1" 12 ) 13 14 func parse(in string) (pvlT, error) { 15 b := []byte(in) 16 p := pvlT{} 17 p.PvlVersion = -1 18 p.Revision = -1 19 20 err := json.Unmarshal(b, &p) 21 if err != nil { 22 return p, err 23 } 24 25 if p.PvlVersion == -1 { 26 return p, fmt.Errorf("pvl_version required") 27 } 28 if p.Revision == -1 { 29 return p, fmt.Errorf("revision required") 30 } 31 return p, nil 32 } 33 34 type pvlT struct { 35 PvlVersion int `json:"pvl_version"` 36 Revision int `json:"revision"` 37 // services is a map from service to a list of scripts. 38 // each script is a list of instructions. 39 Services servicesT `json:"services"` 40 } 41 42 type servicesT struct { 43 Map map[keybase1.ProofType][]scriptT 44 } 45 46 func (x *servicesT) UnmarshalJSON(b []byte) error { 47 // read as string map 48 m := make(map[string][]scriptT) 49 err := json.Unmarshal(b, &m) 50 if err != nil { 51 return err 52 } 53 // copy to ProofType map 54 x.Map = make(map[keybase1.ProofType][]scriptT) 55 for k, v := range m { 56 t, ok := keybase1.ProofTypeMap[strings.ToUpper(k)] 57 if ok { 58 x.Map[t] = v 59 } 60 // Unrecognized proof types are discarded silently 61 // So that old clients don't break if a new service is added 62 } 63 return nil 64 } 65 66 type scriptT struct { 67 Instructions []instructionT 68 } 69 70 func (x *scriptT) UnmarshalJSON(b []byte) error { 71 err := json.Unmarshal(b, &x.Instructions) 72 for i, ins := range x.Instructions { 73 n := ins.variantsFilled() 74 if n != 1 { 75 if i == 0 { 76 return fmt.Errorf("%v != 1 variants appeared in instruction %v", n, i) 77 } 78 return fmt.Errorf("%v != 1 variants appeared in instruction %v; previous: %v", n, i, x.Instructions[i-1]) 79 } 80 } 81 return err 82 } 83 84 type instructionT struct { 85 // Exactly one of these shall be non-nil 86 // This list is duplicated to: 87 // - instructionT.variantsFilled 88 // - instructionT.Name 89 // - stepInstruction 90 // - validateScript 91 // This invariant is enforced by scriptT.UnmarshalJSON 92 AssertRegexMatch *assertRegexMatchT `json:"assert_regex_match,omitempty"` 93 AssertFindBase64 *assertFindBase64T `json:"assert_find_base64,omitempty"` 94 AssertCompare *assertCompareT `json:"assert_compare,omitempty"` 95 WhitespaceNormalize *whitespaceNormalizeT `json:"whitespace_normalize,omitempty"` 96 RegexCapture *regexCaptureT `json:"regex_capture,omitempty"` 97 ReplaceAll *replaceAllT `json:"replace_all,omitempty"` 98 ParseURL *parseURLT `json:"parse_url,omitempty"` 99 Fetch *fetchT `json:"fetch,omitempty"` 100 ParseHTML *parseHTMLT `json:"parse_html,omitempty"` 101 SelectorJSON *selectorJSONT `json:"selector_json,omitempty"` 102 SelectorCSS *selectorCSST `json:"selector_css,omitempty"` 103 Fill *fillT `json:"fill,omitempty"` 104 } 105 106 func (ins *instructionT) variantsFilled() int { 107 n := 0 108 if ins.AssertRegexMatch != nil { 109 n++ 110 } 111 if ins.AssertFindBase64 != nil { 112 n++ 113 } 114 if ins.AssertCompare != nil { 115 n++ 116 } 117 if ins.WhitespaceNormalize != nil { 118 n++ 119 } 120 if ins.RegexCapture != nil { 121 n++ 122 } 123 if ins.ReplaceAll != nil { 124 n++ 125 } 126 if ins.ParseURL != nil { 127 n++ 128 } 129 if ins.Fetch != nil { 130 n++ 131 } 132 if ins.ParseHTML != nil { 133 n++ 134 } 135 if ins.SelectorJSON != nil { 136 n++ 137 } 138 if ins.SelectorCSS != nil { 139 n++ 140 } 141 if ins.Fill != nil { 142 n++ 143 } 144 return n 145 } 146 147 func (ins instructionT) Name() string { 148 switch { 149 case ins.AssertRegexMatch != nil: 150 return string(cmdAssertRegexMatch) 151 case ins.AssertFindBase64 != nil: 152 return string(cmdAssertFindBase64) 153 case ins.AssertCompare != nil: 154 return string(cmdAssertCompare) 155 case ins.WhitespaceNormalize != nil: 156 return string(cmdWhitespaceNormalize) 157 case ins.RegexCapture != nil: 158 return string(cmdRegexCapture) 159 case ins.ReplaceAll != nil: 160 return string(cmdReplaceAll) 161 case ins.ParseURL != nil: 162 return string(cmdParseURL) 163 case ins.Fetch != nil: 164 return string(cmdFetch) 165 case ins.ParseHTML != nil: 166 return string(cmdParseHTML) 167 case ins.SelectorJSON != nil: 168 return string(cmdSelectorJSON) 169 case ins.SelectorCSS != nil: 170 return string(cmdSelectorCSS) 171 case ins.Fill != nil: 172 return string(cmdFill) 173 } 174 return "<invalid instruction>" 175 } 176 177 func (ins instructionT) String() string { 178 return fmt.Sprintf("[ins %v]", ins.Name()) 179 } 180 181 type assertRegexMatchT struct { 182 Pattern string `json:"pattern"` 183 CaseInsensitive bool `json:"case_insensitive"` 184 MultiLine bool `json:"multiline"` 185 From string `json:"from"` 186 Negate bool `json:"negate"` 187 Error *errorT `json:"error"` 188 } 189 190 type assertFindBase64T struct { 191 Needle string `json:"needle"` 192 Haystack string `json:"haystack"` 193 Error *errorT `json:"error"` 194 } 195 196 type assertCompareT struct { 197 // Comparison strategy 198 Cmp string `json:"cmp"` 199 A string `json:"a"` 200 B string `json:"b"` 201 Error *errorT `json:"error"` 202 } 203 204 type whitespaceNormalizeT struct { 205 From string `json:"from"` 206 Into string `json:"into"` 207 Error *errorT `json:"error"` 208 } 209 210 type regexCaptureT struct { 211 Pattern string `json:"pattern"` 212 MultiLine bool `json:"multiline"` 213 CaseInsensitive bool `json:"case_insensitive"` 214 From string `json:"from"` 215 Into []string `json:"into"` 216 Error *errorT `json:"error"` 217 } 218 219 type replaceAllT struct { 220 Old string `json:"old"` 221 New string `json:"new"` 222 From string `json:"from"` 223 Into string `json:"into"` 224 Error *errorT `json:"error"` 225 } 226 227 type parseURLT struct { 228 From string `json:"from"` 229 Path string `json:"path"` 230 Host string `json:"host"` 231 Scheme string `json:"scheme"` 232 Error *errorT `json:"error"` 233 } 234 235 type fetchT struct { 236 Kind string `json:"kind"` 237 From string `json:"from"` 238 // Value is "" when not fetching a string 239 Into string `json:"into"` 240 Error *errorT `json:"error"` 241 } 242 243 type parseHTMLT struct { 244 From string `json:"from"` 245 Error *errorT `json:"error"` 246 } 247 248 type selectorJSONT struct { 249 Selectors []keybase1.SelectorEntry `json:"selectors"` 250 Into string `json:"into"` 251 Error *errorT `json:"error"` 252 } 253 254 type selectorCSST struct { 255 Selectors []keybase1.SelectorEntry `json:"selectors"` 256 Attr string `json:"attr"` 257 Data bool `json:"data"` 258 // Whether the final selection can contain multiple elements. 259 Multi bool `json:"multi"` 260 Into string `json:"into"` 261 Error *errorT `json:"error"` 262 } 263 264 type fillT struct { 265 With string `json:"with"` 266 Into string `json:"into"` 267 Error *errorT `json:"error"` 268 } 269 270 type errorT struct { 271 Status keybase1.ProofStatus 272 Description string 273 } 274 275 func (e *errorT) UnmarshalJSON(b []byte) error { 276 ss := []string{} 277 err := json.Unmarshal(b, &ss) 278 if err != nil { 279 return err 280 } 281 if len(ss) != 2 { 282 return fmt.Errorf("error desc must be of length 2") 283 } 284 status, ok := keybase1.ProofStatusMap[ss[0]] 285 if !ok { 286 return fmt.Errorf("unrecognized proof status '%v'", ss[0]) 287 } 288 e.Status = status 289 e.Description = ss[1] 290 return nil 291 }