package engine import ( "fmt" "strconv" "strings" ) // StatusFilter решает, проходит ли статус-код фильтр. type StatusFilter func(code int) bool // ParseStatusFilter разбирает спецификацию фильтра по статус-коду: // пустая строка — фильтр не задан (nil, всё проходит); список через // запятую вида "200,301,404"; диапазоны вида "200-299"; можно смешивать // в одном списке, напр. "200-299,404". func ParseStatusFilter(spec string) (StatusFilter, error) { spec = strings.TrimSpace(spec) if spec == "" { return nil, nil } var preds []func(int) bool for _, tok := range strings.Split(spec, ",") { tok = strings.TrimSpace(tok) if tok == "" { continue } if strings.Contains(tok, "-") { parts := strings.SplitN(tok, "-", 2) lo, err1 := strconv.Atoi(strings.TrimSpace(parts[0])) hi, err2 := strconv.Atoi(strings.TrimSpace(parts[1])) if err1 != nil || err2 != nil { return nil, fmt.Errorf("некорректный диапазон статусов %q", tok) } preds = append(preds, func(code int) bool { return code >= lo && code <= hi }) continue } n, err := strconv.Atoi(tok) if err != nil { return nil, fmt.Errorf("некорректный статус %q", tok) } preds = append(preds, func(code int) bool { return code == n }) } if len(preds) == 0 { return nil, nil } return func(code int) bool { for _, p := range preds { if p(code) { return true } } return false }, nil } // LengthFilter решает, проходит ли длина тела ответа фильтр. type LengthFilter func(length int64) bool // ParseLengthFilter разбирает спецификацию фильтра по длине тела: // пустая строка — фильтр не задан; ">1000", "<500", ">=1000", "<=500" — // операторы сравнения; "100-500" — диапазон; просто число — точное // совпадение. func ParseLengthFilter(spec string) (LengthFilter, error) { return parseIntRangeFilter(spec) } // WordFilter решает, проходит ли число слов в теле ответа фильтр — // аналог -mw/-fw у ffuf. "Слово" здесь — то же, что возвращает // strings.Fields (последовательность без пробельных символов). type WordFilter func(count int64) bool // ParseWordFilter — тот же синтаксис, что и у ParseLengthFilter, просто // сравнивается число слов, а не байт. func ParseWordFilter(spec string) (WordFilter, error) { return parseIntRangeFilter(spec) } // LineFilter решает, проходит ли число строк в теле ответа фильтр — // аналог -ml/-fl у ffuf. type LineFilter func(count int64) bool // ParseLineFilter — тот же синтаксис, что и у остальных числовых фильтров. func ParseLineFilter(spec string) (LineFilter, error) { return parseIntRangeFilter(spec) } // parseIntRangeFilter — общая операторная грамматика, на которой держатся // ParseLengthFilter/ParseWordFilter/ParseLineFilter: пустая строка — // фильтр не задан; ">N", "=N", "<=N" — операторы сравнения; // "N-M" — диапазон; просто число — точное совпадение. Разница между // тремя фильтрами только в том, что именно считается на стороне // вызывающего кода (байты/слова/строки) — сам синтаксис один и тот же, // поэтому парсер один, а не три копии одной и той же логики. func parseIntRangeFilter(spec string) (func(int64) bool, error) { spec = strings.TrimSpace(spec) if spec == "" { return nil, nil } parseNum := func(s string) (int64, error) { n, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64) if err != nil { return 0, fmt.Errorf("некорректное число в %q", spec) } return n, nil } switch { case strings.HasPrefix(spec, ">="): n, err := parseNum(spec[2:]) if err != nil { return nil, err } return func(l int64) bool { return l >= n }, nil case strings.HasPrefix(spec, "<="): n, err := parseNum(spec[2:]) if err != nil { return nil, err } return func(l int64) bool { return l <= n }, nil case strings.HasPrefix(spec, ">"): n, err := parseNum(spec[1:]) if err != nil { return nil, err } return func(l int64) bool { return l > n }, nil case strings.HasPrefix(spec, "<"): n, err := parseNum(spec[1:]) if err != nil { return nil, err } return func(l int64) bool { return l < n }, nil case strings.Contains(spec, "-"): parts := strings.SplitN(spec, "-", 2) lo, err1 := parseNum(parts[0]) hi, err2 := parseNum(parts[1]) if err1 != nil || err2 != nil { return nil, fmt.Errorf("некорректный диапазон %q", spec) } return func(l int64) bool { return l >= lo && l <= hi }, nil default: n, err := parseNum(spec) if err != nil { return nil, err } return func(l int64) bool { return l == n }, nil } }