Afilters

From Svacer Wiki

Advanced filters

Поле "Расширенный фильтр" в пользовательских фильтрах предназначено для фильтрации маркеров (предупреждений) по сложным предикатам.

Предикат может использовать:

  • поля маркера, его трассы, разметки и комментариев
  • информацию из контекста: имя проекта, ветки, снимка и т. п.
  • атрибуты снимка, включая пользовательские атрибуты, заданные при импорте
  • статусы разметки
  • текст и атрибуты комментариев

Формат фильтра определяется следующим шаблоном:

filter(markers, <predicate expr>)

Выражение <predicate expr> определяет предикат. Объект markers содержит массив маркеров из контекста, функция filter фильтрует данный список по предикату. Таким образом, применение фильтра заключается в фильтрации списка маркеров по указанному предикату.

Список доступных полей

Поля маркера (предупреждения)

  • Comments — список комментариев маркера. Объект типа Comment (смотрите ниже)
  • Details — информация, специфичная для анализатора. Ближайший аналог — fingerprint в SARIF
  • File — имя файла
  • Function — имя функции, может быть mangled для С++
  • ID — UUID маркера
  • Invariant — инвариант маркера. Используется для автопереноса разметки между снимками
  • Lang — язык детектора
  • Line — номер строки в файле
  • LocID — порядковый номер маркера относительно изначального отчета анализатора
  • MTid — шаблон сообщения
  • Msg — текст сообщения
  • OrigFunc — 'человекочитаемое' имя функции. Может быть недоступно
  • Tags — список тегов (меток) маркера
  • Tool — модуль анализатора, ответственного за данный детектор
  • Traces — список трасс маркера. Объект типа Trace (смотрите ниже)
  • WarnClass — название детектора/класса детекторов

Имена полей также могут использоваться в формате, где первая буква в lowercase.

Помимо использования полей, можно использовать вспомогательные функции для доступа к дополнительной информации:

  • severity(item) string — серьезность детектора (чекера)

Пример:

filter(markers, severity(#) == "Critical")

Внимание: использование в предикатах поля Trace может сказаться на скорости применения фильтра, т. к. загрузка большого числа трасс может занять время.

Данные о разметке

Поддерживаются следующие функции для получения информации о разметке:

  • status(item) string — статус разметки текущего маркера. Пример: filter(markers, status(#) == "Confirmed")
  • action(item) string — статус поля action текущего маркера. Пример: filter(markers, action(#) == "Ignore")
  • mseverity(item) string — статус поля severity текущего маркера (не путать с severity детектора). Пример: filter(markers, mseverity(#) == "Critical")
  • triaged(item) bool — возвращает true, если маркер имеет не дефолтную разметку. Пример: filter(markers, triaged(#))
  • get_triaged_by(item) UserInfo — возвращает структуру с информацией о пользователе, кто произвел разметку

Поля структуры UserInfo:

  • login: string — логин пользователя
  • create_ts: Time (UTC) — время разметки в UTC формате (RFC3339)

Комментарии

Объекты типа Comment содержатся в поле Comments маркера. Объект имеет следующие поля:

  • ID — UUID комментария
  • Text — текст комментария
  • CreatedBy — имя пользователя, кто создал комментарий
  • CreateTs — время создания комментария (объект типа time.Time)
  • UpdateTs — время обновления комментария (объект типа time.Time), может быть пустым
  • UpdatedBy — имя пользователя, кто обновил комментарий

Трассы

Маркер может содержать трассу маркера в поле Traces. Трасса описывается следующими структурами:

type Trace struct {
	Role      string
	Locations []TraceEntry
}
type TraceEntry struct {
	File     string	
	Line     uint32
	Col  	 uint32	
	Info 	 string
}

Доступ к полям объекта Trace может выполняться, используя правила языка go-expr, описанные ниже.

Имена полей могут использоваться в виде как описано выше, либо в формате lowercase: role, locations, file, line, col, info

Пример:

filter(markers, any(.Comments, .Text == "Test") &&
		any(.Traces, any(.Locations, .Info == "Boolean expression has constant value"))
)

Поля снимка

Для доступа к полям снимка можно использовать переменную snapshot. Переменная имеет тип структуры со следующими полями

struct {
	ID               string                
	Name             string                
	SerialID         uint32                
	BuildObject      string                
	ResultsObject    string                
	ImportTime       time.Time             
	CreatedBy        string                
	CreatedByID      string                
	Details          map[string]any
	CreateTime       time.Time             
	SourcesAvailable bool                  
}

Имена полей могут использоваться в виде, где первая буква в lowercase.

Объекты в поле Details можно просмотреть, нажав на кнопку View JSON в WebUI в поле Snapshot Information.

Пример:

filter(markers, 
   snapshot.buildObject == "5b634b75422bd554a8569cc10bfadc3aea77b73e" &&
   .warnClass matches "DEREF"
)

Описание языка предикатов

Для написания предикатов используется язык Expr. Детальное описание всех возможностей можно найти здесь: https://github.com/expr-lang/expr

Далее описываются конструкции, наиболее подходящие для написания правил фильтрации.

Литералы

Comment /* */ or //
Boolean true, false
Integer 42, 0x2A, 0o52, 0b101010
Float 0.5, .5
String "foo", 'bar'
Array [1, 2, 3]
Map {a: 1, b: 2, c: 3}
Nil nil

Строки

Строки могут быть в одинарных и двойных кавычках. Для эскейпа используйте обратный слэш.

Пример однострочного текста:

"Hello\nWorld"

Пример многострочного текста:

`Hello
World`

В многострочном тексте escape-последовательности не поддерживаются.

Операторы

Arithmetic: +, -, *, /, % (modulus), ^ or ** (exponent)
Comparison: ==, !=, <, >, <=, >=
Logical: not or !, and or &&, or or ||
Conditional: ?: (ternary), ?? (nil coalescing), if {} else {} (multiline)
Membership: [], ., ?., in
String: + (concatenation), contains, startsWith, endsWith
Regex: matches
Range: ..
Slice: [:]
Pipe: |

Операторы доступа к полю объекта

Можно использовать оператор . и оператор []:

user.Name
user["Name"]

Оператор in

Оператор in для проверки наличия объекта в массиве или мэпе:

"John" in ["John", "Jane"]
"name" in {"name": "John", "age": 30}

Optional chaining

Оператор ?. может использоваться для доступа к полю структуры или элементу отображения (map) без необходимости проверки, является ли структура или отображение равными nil. Если структура или отображение имеют значение nil, результатом выражения будет nil.

Пример вида:

author.User?.Name

значит то же самое, что:

author.User != nil ? author.User.Name : nil

Nil coalescing

Оператор ?? может использоваться для возврата левого операнда, если он не равен nil, в противном случае возвращается правый операнд.

Пример вида:

author.User?.Name ?? "Anonymous"

значит то же самое, что:

author.User != nil ? author.User.Name : "Anonymous"

Оператор конвейера

Оператор конвейера | может использоваться для передачи результата выражения левого операнда в качестве первого аргумента выражения правого операнда.

Пример вида:

user.Name | lower() | split(" ")

значит то же самое, что:

split(lower(user.Name), " ")

Предикаты

Предикат — это выражение. Предикаты можно использовать в функциях, таких как filter, all, any, one, none и других.

filter(0..9, {# % 2 == 0})
filter(tweets, {len(.Content) > 240})

Скобки { } можно опускать:

filter(tweets, len(.Content) > 240)

Операции над строками

trim(str[, chars])
trimPrefix(str, prefix)
trimSuffix(str, suffix)
upper(str)
lower(str)
indexOf(str, substring)
lastIndexOf(str, substring)
hasPrefix(str, prefix)
hasSuffix(str, suffix)

Также есть оператор matches для поиска регулярного выражения в строке:

str matches "regular expression"

Для сопоставления целой строки с регулярным выражением следует использовать вариант:

str matches "^regex$"

Функции времени

Поддерживаются Go типы для времени и интервалов: time.Time, time.Duration из time package.

Можно использовать операции сложения и вычитания над объектами с типом time.Time.

createdAt - now()
createdAt + duration("1h")
createdAt > now() - duration("1h")

date("2023-08-14")
date("15:04:05")
date("2023-08-14T00:00:00Z")
date("2023-08-14 00:00:00", "2006-01-02 15:04:05", "Europe/Zurich")

Функции над массивами

  • all(array, predicate) — все объекты должны удовлетворять предикату
  • any(array, predicate) — должен быть по крайней мере один объект, который удовлетворяет предикату
  • one(array, predicate) — должен быть только один объект, который удовлетворяет предикату
  • none(array, predicate) — ни один объект не должен удовлетворять предикату
  • find(array, predicate) — возвращает первый объект, который удовлетворяет предикату
  • findLast(array, predicate) — возвращает последний объект, который удовлетворяет предикату
  • count(array[, predicate]) — возвращает число объектов, удовлетворяющих предикату
  • first(array) — первый объект из массива
  • last(array) — последний объект из массива

Примеры:

all(tweets, {.Size < 280})
any(tweets, {.Size > 280})
none(tweets, {.Size > 280})
find([1, 2, 3, 4], # > 2) == 3
findLast([1, 2, 3, 4], # > 2) == 4
count(users, .Age > 18)
first([1, 2, 3]) == 1
last([1, 2, 3]) == 3