Новые операторы в правилах: wildcard, starts_with и ends_with
Восемь строковых операторов в движке правил: что покрывает символ *, чем строгий wildcard отличается от обычного, как ведут себя not_-проверки без поля и какой оператор выбирать.
Раньше сравнение строк в правилах сводилось к точному совпадению и поиску подстроки. Условия вида «все пути под /api/», «любой поддомен example.com» или «Content-Type, оканчивающийся на /json» приходилось собирать из нескольких правил или приближать через contains, захватывая лишнее. Теперь каждое из них записывается одним оператором.
Что добавили
Восемь операторов, четыре пары «прямой / отрицательный»:
| Оператор | Что проверяет | Регистр |
|---|---|---|
wildcard / not_wildcard |
вся строка по шаблону с * |
не учитывается |
strict_wildcard / not_strict_wildcard |
вся строка по шаблону с * |
учитывается |
starts_with / not_starts_with |
строка начинается со значения | учитывается |
ends_with / not_ends_with |
строка заканчивается значением | учитывается |
Операторы применяются к полям hostname, uri_path, uri, ua, ja4, method, scheme, а также к любому заголовку и cookie через header.<name> и cookie.<name>. Условие описывается тремя ключами — поле, оператор, значение:
{
"field": "uri_path",
"op": "wildcard",
"value": "/api/*"
}
Как работает wildcard
Правил у шаблона три, и все они важны на практике.
Сравнивается вся строка. Шаблон не ищется внутри значения, а прикладывается к нему целиком. /api/* совпадёт с /api/users, но не с /v2/api/users: строка должна начинаться так, как начинается шаблон. Если нужно «где-то внутри», ставьте * с обеих сторон — */api/*.
* — ноль или больше любых символов, включая /. Один * покрывает и пустой хвост, и вложенные пути любой глубины: /api/* подходит к /api/, /api/users и /api/v2/users/42/orders. Звёздочек в шаблоне может быть несколько.
wildcard не различает регистр, strict_wildcard различает. Для /api/* обычный wildcard пропустит и /API/USERS. Если бэкенд чувствителен к регистру и /Admin/ для него не то же самое, что /admin/, — берите строгий вариант.
Несколько типичных шаблонов:
| Шаблон | Совпадает | Не совпадает |
|---|---|---|
/api/* |
/api/, /api/v1/users |
/v2/api/, /apix |
*/admin/* |
/admin/, /ru/admin/users |
/administrator |
*.php |
/index.php, /a/b/c.PHP |
/index.php5 |
*.example.com для hostname |
cdn.example.com, a.b.example.com |
example.com, example.com.evil.net |
* |
любая строка | — |
Шаблон с несколькими * удобен для User-Agent, где между постоянными частями стоят версии:
{
"field": "ua",
"op": "wildcard",
"value": "Mozilla/5.0 (Linux; Android *) AppleWebKit/*"
}
Специальный символ в шаблоне один — *. Знак ? никакой роли не играет и сравнивается как обычный символ. Если звёздочка нужна буквально, экранируйте её обратным слешем: \*; сам обратный слеш записывается как \\. В JSON обратный слеш тоже экранируется, поэтому в файле правила это выглядит так:
{
"field": "uri_path",
"op": "strict_wildcard",
"value": "/files/\\*/*"
}
Такой шаблон совпадёт с /files/*/report.pdf, но не с /files/2024/report.pdf.
Три варианта шаблона движок отклонит как невалидные: пустое значение, незакрытый escape (обратный слеш в конце строки) и две звёздочки подряд без экранирования — **. Одна * уже покрывает любую последовательность, поэтому ** избыточна и считается ошибкой.
starts_with и ends_with
Это частные случаи wildcard, которые читаются однозначно и не требуют думать о звёздочках. Оба чувствительны к регистру — как strict_wildcard, а не как wildcard.
starts_with закрывает разделы сайта и группы маршрутов:
{
"field": "uri_path",
"op": "starts_with",
"value": "/admin/"
}
ends_with удобен для расширений файлов, суффиксов доменов и типов содержимого. Условие ниже покрывает application/json, application/problem+json и любой другой тип с этим окончанием без перечисления:
{
"field": "header.Content-Type",
"op": "ends_with",
"value": "/json"
}
Оба оператора работают и с отпечатком ja4: общий префикс отпечатка позволяет отобрать группу похожих клиентов одним условием, не перечисляя полные значения.
Отрицательные проверки и отсутствующие поля
У каждого оператора есть вариант с префиксом not_. Главная тонкость — поведение, когда поля в запросе нет: отсутствующий заголовок или cookie делает отрицательную проверку истинной. Условие ниже совпадёт и с запросом, где session начинается не с prod_, и с запросом вообще без этой cookie:
{
"field": "cookie.session",
"op": "not_starts_with",
"value": "prod_"
}
Обычно это то, что нужно: «пропустить всех, у кого нет продовой сессии». Если же поле должно присутствовать обязательно, объедините exists и not_* в одной группе all — запросы без cookie перестанут проходить:
{
"all": [
{ "field": "cookie.session", "op": "exists" },
{ "field": "cookie.session", "op": "not_starts_with", "value": "prod_" }
]
}
Для полей, которые есть в каждом запросе, — uri_path, hostname, method, scheme — этой оговорки нет, отрицание там работает буквально.
Какой оператор выбрать
- Нужен раздел сайта или префикс маршрута —
starts_with. Если регистр не важен —wildcardс*в конце. - Нужны расширение, суффикс домена или типа содержимого —
ends_with. - Нужен фрагмент в середине или несколько переменных частей —
wildcardс несколькими*. - Регистр имеет значение —
strict_wildcard,starts_withилиends_with; обычныйwildcardрегистр игнорирует. - Заголовок или cookie не должны иметь определённого вида, но при этом обязаны присутствовать —
not_*вместе сexistsв группеall.
Если раньше вы использовали contains для условий вида «путь начинается с…», стоит перевести их на starts_with: contains для /admin/ срабатывает и на /blog/admin/guide, а starts_with — нет.
Что дальше
Новые операторы уже доступны, существующие правила менять не нужно. Правила настраиваются в рабочем пространстве, вопросы — в нашем Telegram-канале.