github.com/TeaOSLab/EdgeNode@v1.3.8/internal/waf/rule.go (about) 1 package waf 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/binary" 7 "errors" 8 "github.com/TeaOSLab/EdgeCommon/pkg/configutils" 9 "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs/filterconfigs" 10 "github.com/TeaOSLab/EdgeNode/internal/re" 11 "github.com/TeaOSLab/EdgeNode/internal/remotelogs" 12 "github.com/TeaOSLab/EdgeNode/internal/utils/runes" 13 "github.com/TeaOSLab/EdgeNode/internal/waf/checkpoints" 14 "github.com/TeaOSLab/EdgeNode/internal/waf/injectionutils" 15 "github.com/TeaOSLab/EdgeNode/internal/waf/requests" 16 "github.com/TeaOSLab/EdgeNode/internal/waf/utils" 17 "github.com/TeaOSLab/EdgeNode/internal/waf/values" 18 "github.com/iwind/TeaGo/lists" 19 "github.com/iwind/TeaGo/maps" 20 "github.com/iwind/TeaGo/types" 21 stringutil "github.com/iwind/TeaGo/utils/string" 22 "net" 23 "reflect" 24 "regexp" 25 "sort" 26 "strings" 27 ) 28 29 var singleParamRegexp = regexp.MustCompile(`^\${[\w.-]+}$`) 30 31 // Rule waf rule under rule set 32 type Rule struct { 33 Id int64 34 35 Description string `yaml:"description" json:"description"` 36 Param string `yaml:"param" json:"param"` // such as ${arg.name} or ${args}, can be composite as ${arg.firstName}${arg.lastName} 37 ParamFilters []*ParamFilter `yaml:"paramFilters" json:"paramFilters"` 38 Operator RuleOperator `yaml:"operator" json:"operator"` // such as contains, gt, ... 39 Value string `yaml:"value" json:"value"` // compared value 40 IsCaseInsensitive bool `yaml:"isCaseInsensitive" json:"isCaseInsensitive"` 41 CheckpointOptions map[string]any `yaml:"checkpointOptions" json:"checkpointOptions"` 42 Priority int `yaml:"priority" json:"priority"` 43 44 checkpointFinder func(prefix string) checkpoints.CheckpointInterface 45 46 singleParam string // real param after prefix 47 singleCheckpoint checkpoints.CheckpointInterface // if is single check point 48 49 multipleCheckpoints map[string]checkpoints.CheckpointInterface 50 51 isIP bool 52 ipValue net.IP 53 54 ipRangeListValue *values.IPRangeList 55 stringValues []string 56 stringValueRunes [][]rune 57 ipList *values.StringList 58 59 floatValue float64 60 61 reg *re.Regexp 62 cacheLife utils.CacheLife 63 } 64 65 func NewRule() *Rule { 66 return &Rule{} 67 } 68 69 func (this *Rule) Init() error { 70 // operator 71 switch this.Operator { 72 case RuleOperatorGt: 73 this.floatValue = types.Float64(this.Value) 74 case RuleOperatorGte: 75 this.floatValue = types.Float64(this.Value) 76 case RuleOperatorLt: 77 this.floatValue = types.Float64(this.Value) 78 case RuleOperatorLte: 79 this.floatValue = types.Float64(this.Value) 80 case RuleOperatorEq: 81 this.floatValue = types.Float64(this.Value) 82 case RuleOperatorNeq: 83 this.floatValue = types.Float64(this.Value) 84 case RuleOperatorContainsAny, RuleOperatorContainsAll, RuleOperatorContainsAnyWord, RuleOperatorContainsAllWords, RuleOperatorNotContainsAnyWord: 85 this.stringValues = []string{} 86 if len(this.Value) > 0 { 87 var lines = strings.Split(this.Value, "\n") 88 for _, line := range lines { 89 line = strings.TrimSpace(line) 90 if len(line) > 0 { 91 if this.IsCaseInsensitive { 92 this.stringValues = append(this.stringValues, strings.ToLower(line)) 93 } else { 94 this.stringValues = append(this.stringValues, line) 95 } 96 } 97 } 98 if this.Operator == RuleOperatorContainsAnyWord || this.Operator == RuleOperatorContainsAllWords || this.Operator == RuleOperatorNotContainsAnyWord { 99 sort.Strings(this.stringValues) 100 } 101 102 this.stringValueRunes = [][]rune{} 103 for _, line := range this.stringValues { 104 this.stringValueRunes = append(this.stringValueRunes, []rune(line)) 105 } 106 } 107 case RuleOperatorMatch: 108 var v = this.Value 109 if this.IsCaseInsensitive && !strings.HasPrefix(v, "(?i)") { 110 v = "(?i)" + v 111 } 112 113 v = this.unescape(v) 114 115 reg, err := re.Compile(v) 116 if err != nil { 117 return err 118 } 119 this.reg = reg 120 case RuleOperatorNotMatch: 121 var v = this.Value 122 if this.IsCaseInsensitive && !strings.HasPrefix(v, "(?i)") { 123 v = "(?i)" + v 124 } 125 126 v = this.unescape(v) 127 128 reg, err := re.Compile(v) 129 if err != nil { 130 return err 131 } 132 this.reg = reg 133 case RuleOperatorEqIP, RuleOperatorGtIP, RuleOperatorGteIP, RuleOperatorLtIP, RuleOperatorLteIP: 134 this.ipValue = net.ParseIP(this.Value) 135 this.isIP = this.ipValue != nil 136 137 if !this.isIP { 138 return errors.New("value should be a valid ip") 139 } 140 case RuleOperatorInIPList: 141 this.ipList = values.ParseStringList(this.Value, true) 142 case RuleOperatorIPRange, RuleOperatorNotIPRange: 143 this.ipRangeListValue = values.ParseIPRangeList(this.Value) 144 case RuleOperatorWildcardMatch, RuleOperatorWildcardNotMatch: 145 var pieces = strings.Split(this.Value, "*") 146 for index, piece := range pieces { 147 pieces[index] = regexp.QuoteMeta(piece) 148 } 149 var pattern = strings.Join(pieces, "(.*)") 150 var expr = "^" + pattern + "$" 151 if this.IsCaseInsensitive { 152 expr = "(?i)" + expr 153 } 154 reg, err := re.Compile(expr) 155 if err != nil { 156 return err 157 } 158 this.reg = reg 159 } 160 161 if singleParamRegexp.MatchString(this.Param) { 162 var param = this.Param[2 : len(this.Param)-1] 163 var pieces = strings.SplitN(param, ".", 2) 164 var prefix = pieces[0] 165 if len(pieces) == 1 { 166 this.singleParam = "" 167 } else { 168 this.singleParam = pieces[1] 169 } 170 171 if this.checkpointFinder != nil { 172 var checkpoint = this.checkpointFinder(prefix) 173 if checkpoint == nil { 174 return errors.New("no check point '" + prefix + "' found") 175 } 176 this.singleCheckpoint = checkpoint 177 this.Priority = checkpoint.Priority() 178 179 this.cacheLife = checkpoint.CacheLife() 180 } else { 181 var checkpoint = checkpoints.FindCheckpoint(prefix) 182 if checkpoint == nil { 183 return errors.New("no check point '" + prefix + "' found") 184 } 185 checkpoint.Init() 186 this.singleCheckpoint = checkpoint 187 this.Priority = checkpoint.Priority() 188 189 this.cacheLife = checkpoint.CacheLife() 190 } 191 192 return nil 193 } 194 195 this.multipleCheckpoints = map[string]checkpoints.CheckpointInterface{} 196 var err error = nil 197 configutils.ParseVariables(this.Param, func(varName string) (value string) { 198 var pieces = strings.SplitN(varName, ".", 2) 199 var prefix = pieces[0] 200 if this.checkpointFinder != nil { 201 var checkpoint = this.checkpointFinder(prefix) 202 if checkpoint == nil { 203 err = errors.New("no check point '" + prefix + "' found") 204 } else { 205 this.multipleCheckpoints[prefix] = checkpoint 206 this.Priority = checkpoint.Priority() 207 208 if this.cacheLife <= 0 || checkpoint.CacheLife() < this.cacheLife { 209 this.cacheLife = checkpoint.CacheLife() 210 } 211 } 212 } else { 213 var checkpoint = checkpoints.FindCheckpoint(prefix) 214 if checkpoint == nil { 215 err = errors.New("no check point '" + prefix + "' found") 216 } else { 217 checkpoint.Init() 218 this.multipleCheckpoints[prefix] = checkpoint 219 this.Priority = checkpoint.Priority() 220 221 this.cacheLife = checkpoint.CacheLife() 222 } 223 } 224 225 return "" 226 }) 227 228 return err 229 } 230 231 func (this *Rule) MatchRequest(req requests.Request) (b bool, hasRequestBody bool, err error) { 232 if this.singleCheckpoint != nil { 233 value, hasCheckedRequestBody, err, _ := this.singleCheckpoint.RequestValue(req, this.singleParam, this.CheckpointOptions, this.Id) 234 if hasCheckedRequestBody { 235 hasRequestBody = true 236 } 237 if err != nil { 238 return false, hasRequestBody, err 239 } 240 241 // execute filters 242 if len(this.ParamFilters) > 0 { 243 value = this.execFilter(value) 244 } 245 246 // if is composed checkpoint, we just returns true or false 247 if this.singleCheckpoint.IsComposed() { 248 return types.Bool(value), hasRequestBody, nil 249 } 250 251 return this.Test(value), hasRequestBody, nil 252 } 253 254 var value = configutils.ParseVariables(this.Param, func(varName string) (value string) { 255 var pieces = strings.SplitN(varName, ".", 2) 256 var prefix = pieces[0] 257 point, ok := this.multipleCheckpoints[prefix] 258 if !ok { 259 return "" 260 } 261 262 if len(pieces) == 1 { 263 value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, "", this.CheckpointOptions, this.Id) 264 if hasCheckRequestBody { 265 hasRequestBody = true 266 } 267 if err1 != nil { 268 err = err1 269 } 270 return this.stringifyValue(value1) 271 } 272 273 value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, pieces[1], this.CheckpointOptions, this.Id) 274 if hasCheckRequestBody { 275 hasRequestBody = true 276 } 277 if err1 != nil { 278 err = err1 279 } 280 return this.stringifyValue(value1) 281 }) 282 283 if err != nil { 284 return false, hasRequestBody, err 285 } 286 287 return this.Test(value), hasRequestBody, nil 288 } 289 290 func (this *Rule) MatchResponse(req requests.Request, resp *requests.Response) (b bool, hasRequestBody bool, err error) { 291 if this.singleCheckpoint != nil { 292 // if is request param 293 if this.singleCheckpoint.IsRequest() { 294 value, hasCheckRequestBody, err, _ := this.singleCheckpoint.RequestValue(req, this.singleParam, this.CheckpointOptions, this.Id) 295 if hasCheckRequestBody { 296 hasRequestBody = true 297 } 298 if err != nil { 299 return false, hasRequestBody, err 300 } 301 302 // execute filters 303 if len(this.ParamFilters) > 0 { 304 value = this.execFilter(value) 305 } 306 307 return this.Test(value), hasRequestBody, nil 308 } 309 310 // response param 311 value, hasCheckRequestBody, err, _ := this.singleCheckpoint.ResponseValue(req, resp, this.singleParam, this.CheckpointOptions, this.Id) 312 if hasCheckRequestBody { 313 hasRequestBody = true 314 } 315 if err != nil { 316 return false, hasRequestBody, err 317 } 318 319 // if is composed checkpoint, we just returns true or false 320 if this.singleCheckpoint.IsComposed() { 321 return types.Bool(value), hasRequestBody, nil 322 } 323 324 return this.Test(value), hasRequestBody, nil 325 } 326 327 var value = configutils.ParseVariables(this.Param, func(varName string) (value string) { 328 var pieces = strings.SplitN(varName, ".", 2) 329 var prefix = pieces[0] 330 point, ok := this.multipleCheckpoints[prefix] 331 if !ok { 332 return "" 333 } 334 335 if len(pieces) == 1 { 336 if point.IsRequest() { 337 value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, "", this.CheckpointOptions, this.Id) 338 if hasCheckRequestBody { 339 hasRequestBody = true 340 } 341 if err1 != nil { 342 err = err1 343 } 344 return this.stringifyValue(value1) 345 } else { 346 value1, hasCheckRequestBody, err1, _ := point.ResponseValue(req, resp, "", this.CheckpointOptions, this.Id) 347 if hasCheckRequestBody { 348 hasRequestBody = true 349 } 350 if err1 != nil { 351 err = err1 352 } 353 return this.stringifyValue(value1) 354 } 355 } 356 357 if point.IsRequest() { 358 value1, hasCheckRequestBody, err1, _ := point.RequestValue(req, pieces[1], this.CheckpointOptions, this.Id) 359 if hasCheckRequestBody { 360 hasRequestBody = true 361 } 362 if err1 != nil { 363 err = err1 364 } 365 return this.stringifyValue(value1) 366 } else { 367 value1, hasCheckRequestBody, err1, _ := point.ResponseValue(req, resp, pieces[1], this.CheckpointOptions, this.Id) 368 if hasCheckRequestBody { 369 hasRequestBody = true 370 } 371 if err1 != nil { 372 err = err1 373 } 374 return this.stringifyValue(value1) 375 } 376 }) 377 378 if err != nil { 379 return false, hasRequestBody, err 380 } 381 382 return this.Test(value), hasRequestBody, nil 383 } 384 385 func (this *Rule) Test(value any) bool { 386 // operator 387 switch this.Operator { 388 case RuleOperatorGt: 389 return types.Float64(value) > this.floatValue 390 case RuleOperatorGte: 391 return types.Float64(value) >= this.floatValue 392 case RuleOperatorLt: 393 return types.Float64(value) < this.floatValue 394 case RuleOperatorLte: 395 return types.Float64(value) <= this.floatValue 396 case RuleOperatorEq: 397 return types.Float64(value) == this.floatValue 398 case RuleOperatorNeq: 399 return types.Float64(value) != this.floatValue 400 case RuleOperatorEqString: 401 if this.IsCaseInsensitive { 402 return strings.EqualFold(this.stringifyValue(value), this.Value) 403 } else { 404 return this.stringifyValue(value) == this.Value 405 } 406 case RuleOperatorNeqString: 407 if this.IsCaseInsensitive { 408 return !strings.EqualFold(this.stringifyValue(value), this.Value) 409 } else { 410 return this.stringifyValue(value) != this.Value 411 } 412 case RuleOperatorMatch, RuleOperatorWildcardMatch: 413 if value == nil { 414 value = "" 415 } 416 417 // strings 418 stringList, ok := value.([]string) 419 if ok { 420 for _, s := range stringList { 421 if utils.MatchStringCache(this.reg, s, this.cacheLife) { 422 return true 423 } 424 } 425 return false 426 } 427 428 // bytes list 429 byteSlices, ok := value.([][]byte) 430 if ok { 431 for _, byteSlice := range byteSlices { 432 if utils.MatchBytesCache(this.reg, byteSlice, this.cacheLife) { 433 return true 434 } 435 } 436 return false 437 } 438 439 // bytes 440 byteSlice, ok := value.([]byte) 441 if ok { 442 return utils.MatchBytesCache(this.reg, byteSlice, this.cacheLife) 443 } 444 445 // string 446 return utils.MatchStringCache(this.reg, this.stringifyValue(value), this.cacheLife) 447 case RuleOperatorNotMatch, RuleOperatorWildcardNotMatch: 448 if value == nil { 449 value = "" 450 } 451 stringList, ok := value.([]string) 452 if ok { 453 for _, s := range stringList { 454 if utils.MatchStringCache(this.reg, s, this.cacheLife) { 455 return false 456 } 457 } 458 return true 459 } 460 461 // bytes list 462 byteSlices, ok := value.([][]byte) 463 if ok { 464 for _, byteSlice := range byteSlices { 465 if utils.MatchBytesCache(this.reg, byteSlice, this.cacheLife) { 466 return false 467 } 468 } 469 return true 470 } 471 472 // bytes 473 byteSlice, ok := value.([]byte) 474 if ok { 475 return !utils.MatchBytesCache(this.reg, byteSlice, this.cacheLife) 476 } 477 478 return !utils.MatchStringCache(this.reg, this.stringifyValue(value), this.cacheLife) 479 case RuleOperatorContains: 480 if types.IsSlice(value) { 481 _, isBytes := value.([]byte) 482 if !isBytes { 483 var ok = false 484 lists.Each(value, func(k int, v any) { 485 if this.stringifyValue(v) == this.Value { 486 ok = true 487 } 488 }) 489 return ok 490 } 491 } 492 if types.IsMap(value) { 493 var lowerValue = "" 494 if this.IsCaseInsensitive { 495 lowerValue = strings.ToLower(this.Value) 496 } 497 for _, v := range maps.NewMap(value) { 498 if this.IsCaseInsensitive { 499 if strings.ToLower(this.stringifyValue(v)) == lowerValue { 500 return true 501 } 502 } else { 503 if this.stringifyValue(v) == this.Value { 504 return true 505 } 506 } 507 } 508 return false 509 } 510 511 if this.IsCaseInsensitive { 512 return strings.Contains(strings.ToLower(this.stringifyValue(value)), strings.ToLower(this.Value)) 513 } else { 514 return strings.Contains(this.stringifyValue(value), this.Value) 515 } 516 case RuleOperatorNotContains: 517 if this.IsCaseInsensitive { 518 return !strings.Contains(strings.ToLower(this.stringifyValue(value)), strings.ToLower(this.Value)) 519 } else { 520 return !strings.Contains(this.stringifyValue(value), this.Value) 521 } 522 case RuleOperatorPrefix: 523 if this.IsCaseInsensitive { 524 var s = this.stringifyValue(value) 525 var sl = len(s) 526 var vl = len(this.Value) 527 if sl < vl { 528 return false 529 } 530 s = s[:vl] 531 return strings.HasPrefix(strings.ToLower(s), strings.ToLower(this.Value)) 532 } else { 533 return strings.HasPrefix(this.stringifyValue(value), this.Value) 534 } 535 case RuleOperatorSuffix: 536 if this.IsCaseInsensitive { 537 var s = this.stringifyValue(value) 538 var sl = len(s) 539 var vl = len(this.Value) 540 if sl < vl { 541 return false 542 } 543 s = s[sl-vl:] 544 return strings.HasSuffix(strings.ToLower(s), strings.ToLower(this.Value)) 545 } else { 546 return strings.HasSuffix(this.stringifyValue(value), this.Value) 547 } 548 case RuleOperatorContainsAny: 549 var stringValue = this.stringifyValue(value) 550 if this.IsCaseInsensitive { 551 stringValue = strings.ToLower(stringValue) 552 } 553 if len(stringValue) > 0 && len(this.stringValues) > 0 { 554 for _, v := range this.stringValues { 555 if strings.Contains(stringValue, v) { 556 return true 557 } 558 } 559 } 560 return false 561 case RuleOperatorContainsAll: 562 var stringValue = this.stringifyValue(value) 563 if this.IsCaseInsensitive { 564 stringValue = strings.ToLower(stringValue) 565 } 566 if len(stringValue) > 0 && len(this.stringValues) > 0 { 567 for _, v := range this.stringValues { 568 if !strings.Contains(stringValue, v) { 569 return false 570 } 571 } 572 return true 573 } 574 return false 575 case RuleOperatorContainsAnyWord: 576 return runes.ContainsAnyWordRunes(this.stringifyValue(value), this.stringValueRunes, this.IsCaseInsensitive) 577 case RuleOperatorContainsAllWords: 578 return runes.ContainsAllWords(this.stringifyValue(value), this.stringValues, this.IsCaseInsensitive) 579 case RuleOperatorNotContainsAnyWord: 580 return !runes.ContainsAnyWordRunes(this.stringifyValue(value), this.stringValueRunes, this.IsCaseInsensitive) 581 case RuleOperatorContainsSQLInjection, RuleOperatorContainsSQLInjectionStrictly: 582 if value == nil { 583 return false 584 } 585 var isStrict = this.Operator == RuleOperatorContainsSQLInjectionStrictly 586 switch xValue := value.(type) { 587 case []string: 588 for _, v := range xValue { 589 if injectionutils.DetectSQLInjectionCache(v, isStrict, this.cacheLife) { 590 return true 591 } 592 } 593 return false 594 case [][]byte: 595 for _, v := range xValue { 596 if injectionutils.DetectSQLInjectionCache(string(v), isStrict, this.cacheLife) { 597 return true 598 } 599 } 600 return false 601 default: 602 return injectionutils.DetectSQLInjectionCache(this.stringifyValue(value), isStrict, this.cacheLife) 603 } 604 case RuleOperatorContainsXSS, RuleOperatorContainsXSSStrictly: 605 if value == nil { 606 return false 607 } 608 var isStrict = this.Operator == RuleOperatorContainsXSSStrictly 609 switch xValue := value.(type) { 610 case []string: 611 for _, v := range xValue { 612 if injectionutils.DetectXSSCache(v, isStrict, this.cacheLife) { 613 return true 614 } 615 } 616 return false 617 case [][]byte: 618 for _, v := range xValue { 619 if injectionutils.DetectXSSCache(string(v), isStrict, this.cacheLife) { 620 return true 621 } 622 } 623 return false 624 default: 625 return injectionutils.DetectXSSCache(this.stringifyValue(value), isStrict, this.cacheLife) 626 } 627 case RuleOperatorContainsBinary: 628 data, _ := base64.StdEncoding.DecodeString(this.stringifyValue(this.Value)) 629 if this.IsCaseInsensitive { 630 return bytes.Contains(bytes.ToUpper([]byte(this.stringifyValue(value))), bytes.ToUpper(data)) 631 } else { 632 return bytes.Contains([]byte(this.stringifyValue(value)), data) 633 } 634 case RuleOperatorNotContainsBinary: 635 data, _ := base64.StdEncoding.DecodeString(this.stringifyValue(this.Value)) 636 if this.IsCaseInsensitive { 637 return !bytes.Contains(bytes.ToUpper([]byte(this.stringifyValue(value))), bytes.ToUpper(data)) 638 } else { 639 return !bytes.Contains([]byte(this.stringifyValue(value)), data) 640 } 641 case RuleOperatorHasKey: 642 if types.IsSlice(value) { 643 var index = types.Int(this.Value) 644 if index < 0 { 645 return false 646 } 647 return reflect.ValueOf(value).Len() > index 648 } else if types.IsMap(value) { 649 var m = maps.NewMap(value) 650 if this.IsCaseInsensitive { 651 var lowerValue = strings.ToLower(this.Value) 652 for k := range m { 653 if strings.ToLower(k) == lowerValue { 654 return true 655 } 656 } 657 } else { 658 return m.Has(this.Value) 659 } 660 } else { 661 return false 662 } 663 664 case RuleOperatorVersionGt: 665 return stringutil.VersionCompare(this.Value, types.String(value)) > 0 666 case RuleOperatorVersionLt: 667 return stringutil.VersionCompare(this.Value, types.String(value)) < 0 668 case RuleOperatorVersionRange: 669 if strings.Contains(this.Value, ",") { 670 var versions = strings.SplitN(this.Value, ",", 2) 671 var version1 = strings.TrimSpace(versions[0]) 672 var version2 = strings.TrimSpace(versions[1]) 673 if len(version1) > 0 && stringutil.VersionCompare(types.String(value), version1) < 0 { 674 return false 675 } 676 if len(version2) > 0 && stringutil.VersionCompare(types.String(value), version2) > 0 { 677 return false 678 } 679 return true 680 } else { 681 return stringutil.VersionCompare(types.String(value), this.Value) >= 0 682 } 683 case RuleOperatorEqIP: 684 var ip = net.ParseIP(types.String(value)) 685 if ip == nil { 686 return false 687 } 688 return this.isIP && ip.Equal(this.ipValue) 689 case RuleOperatorGtIP: 690 var ip = net.ParseIP(types.String(value)) 691 if ip == nil { 692 return false 693 } 694 return this.isIP && bytes.Compare(ip, this.ipValue) > 0 695 case RuleOperatorGteIP: 696 var ip = net.ParseIP(types.String(value)) 697 if ip == nil { 698 return false 699 } 700 return this.isIP && bytes.Compare(ip, this.ipValue) >= 0 701 case RuleOperatorLtIP: 702 var ip = net.ParseIP(types.String(value)) 703 if ip == nil { 704 return false 705 } 706 return this.isIP && bytes.Compare(ip, this.ipValue) < 0 707 case RuleOperatorLteIP: 708 var ip = net.ParseIP(types.String(value)) 709 if ip == nil { 710 return false 711 } 712 return this.isIP && bytes.Compare(ip, this.ipValue) <= 0 713 case RuleOperatorIPRange: 714 return this.containsIP(value) 715 case RuleOperatorNotIPRange: 716 return !this.containsIP(value) 717 case RuleOperatorIPMod: 718 var pieces = strings.SplitN(this.Value, ",", 2) 719 if len(pieces) == 1 { 720 var rem = types.Int64(pieces[0]) 721 return this.ipToInt64(net.ParseIP(types.String(value)))%10 == rem 722 } 723 var div = types.Int64(pieces[0]) 724 if div == 0 { 725 return false 726 } 727 var rem = types.Int64(pieces[1]) 728 return this.ipToInt64(net.ParseIP(types.String(value)))%div == rem 729 case RuleOperatorIPMod10: 730 return this.ipToInt64(net.ParseIP(types.String(value)))%10 == types.Int64(this.Value) 731 case RuleOperatorIPMod100: 732 return this.ipToInt64(net.ParseIP(types.String(value)))%100 == types.Int64(this.Value) 733 case RuleOperatorInIPList: 734 if this.ipList != nil { 735 return this.ipList.Contains(types.String(value)) 736 } 737 return false 738 } 739 return false 740 } 741 742 func (this *Rule) IsSingleCheckpoint() bool { 743 return this.singleCheckpoint != nil 744 } 745 746 func (this *Rule) SetCheckpointFinder(finder func(prefix string) checkpoints.CheckpointInterface) { 747 this.checkpointFinder = finder 748 } 749 750 var unescapeChars = [][2]string{ 751 {`\s`, `(\s|%09|%0A|\+)`}, 752 {`\(`, `(\(|%28)`}, 753 {`=`, `(=|%3D)`}, 754 {`<`, `(<|%3C)`}, 755 {`\*`, `(\*|%2A)`}, 756 {`\\`, `(\\|%2F)`}, 757 {`!`, `(!|%21)`}, 758 {`/`, `(/|%2F)`}, 759 {`;`, `(;|%3B)`}, 760 {`\+`, `(\+|%20)`}, 761 } 762 763 func (this *Rule) unescape(v string) string { 764 // replace urlencoded characters 765 766 for _, c := range unescapeChars { 767 if !strings.Contains(v, c[0]) { 768 continue 769 } 770 var pieces = strings.Split(v, c[0]) 771 772 // 修复piece中错误的\ 773 for pieceIndex, piece := range pieces { 774 var l = len(piece) 775 if l == 0 { 776 continue 777 } 778 if piece[l-1] != '\\' { 779 continue 780 } 781 782 // 计算\的数量 783 var countBackSlashes = 0 784 for i := l - 1; i >= 0; i-- { 785 if piece[i] == '\\' { 786 countBackSlashes++ 787 } else { 788 break 789 } 790 } 791 if countBackSlashes%2 == 1 { 792 // 去掉最后一个 793 pieces[pieceIndex] = piece[:len(piece)-1] 794 } 795 } 796 797 v = strings.Join(pieces, c[1]) 798 } 799 800 return v 801 } 802 803 func (this *Rule) containsIP(value any) bool { 804 if this.ipRangeListValue == nil { 805 return false 806 } 807 return this.ipRangeListValue.Contains(types.String(value)) 808 } 809 810 func (this *Rule) ipToInt64(ip net.IP) int64 { 811 if len(ip) == 0 { 812 return 0 813 } 814 if len(ip) == 16 { 815 return int64(binary.BigEndian.Uint32(ip[12:16])) 816 } 817 return int64(binary.BigEndian.Uint32(ip)) 818 } 819 820 func (this *Rule) execFilter(value any) any { 821 var goNext bool 822 var err error 823 824 for _, filter := range this.ParamFilters { 825 filterInstance := filterconfigs.FindFilter(filter.Code) 826 if filterInstance == nil { 827 continue 828 } 829 value, goNext, err = filterInstance.Do(value, filter.Options) 830 if err != nil { 831 remotelogs.Error("WAF", "filter error: "+err.Error()) 832 break 833 } 834 if !goNext { 835 break 836 } 837 } 838 return value 839 } 840 841 func (this *Rule) stringifyValue(value any) string { 842 if value == nil { 843 return "" 844 } 845 switch v := value.(type) { 846 case string: 847 return v 848 case []string: 849 return strings.Join(v, "") 850 case []byte: 851 return string(v) 852 case [][]byte: 853 var b = &bytes.Buffer{} 854 for _, vb := range v { 855 b.Write(vb) 856 } 857 return b.String() 858 default: 859 return types.String(v) 860 } 861 }