Кастомный Lua-скрипт постобработки событий активности пользователя
Документация
Главная

Кастомный Lua-скрипт постобработки событий активности пользователя

Агент мониторинга поддерживает кастомные Lua-скрипты постобработки событий активности пользователя. Скрипт выполняется в дополнение к встроенной постобработке и позволяет управлять составом собираемых данных: исключать или хешировать чувствительную информацию под требования организации.

Скрипт — это файл с расширением .lua размером до 1 Мб.

Lua-окружение

Скрипт выполняется в среде Lua 5.4.7. В ней дополнительно доступна библиотека luautf8, предоставляющая функции utf8.* — в частности, utf8.len, utf8.gmatch, utf8.sub, utf8.match, используемые в примерах ниже.

Шаблон скрипта

Кастомный скрипт содержит четыре функции, которые должны присутствовать в любом скрипте: GetVersion и три функции Lua API.

function GetVersion()
    return "1.0.0"
end

function RemoveCustomUninformativeInfo(WAL)
end

function PreExtractCustomParameters(WAL)
end

function PostExtractCustomParameters(WAL)
end

Где:

  • GetVersion — должна присутствовать в скрипте и быть реализована. Возвращает строку с версией скрипта. Версия обеспечивает совместимость с кодом агента и меняется только при несовместимых изменениях в формате структуры WAL
  • RemoveCustomUninformativeInfo, PreExtractCustomParameters, PostExtractCustomParameters — должны присутствовать в скрипте. Реализуйте только те, которые нужны; тело остальных можно оставить пустым

Агент вызывает только эти четыре функции. Внутри скрипта можно объявлять любые вспомогательные функции и вызывать их из основных четырех — это стандартная практика Lua. Вспомогательные функции показаны в разделах ниже и в полном примере скрипта.

Структура WAL

WAL (WindowActivityLua) — структура с данными по событию активности пользователя. Lua-код может изменять ее поля.

ПолеТипОписание
windowHierarchyVectorWindowDataLuaИерархия окон события. Список объектов WindowDataLua
applicationInfoApplicationInfoLuaИнформация о приложении, в котором произошло событие
uiElementElementDataLuaUI-элемент, с которым взаимодействовал пользователь (может отсутствовать)
filePathStringПуть к файлу, открытому в момент события
urlStringURL, открытый в момент события

WindowDataLua

Данные одного окна в иерархии.

ПолеТипОписание
nameStringЗаголовок окна
windowTypeNumberВнутренний код типа окна
formDataWindowParametersLuaПараметры формы окна. Содержит поле formData типа VectorFormDataElementLua

FormDataElementLua

Элемент вектора formData.

ПолеТипОписание
elementElementDataLuaUI-элемент, которому соответствует поле формы
valueFormDataValueLuaЗначение поля формы

ApplicationInfoLua

Информация о приложении.

ПолеТипОписание
executablePathStringПуть к исполняемому файлу приложения
programNameStringИмя приложения
versionVersionLuaВерсия приложения
typeNumberВнутренний код типа процесса

VersionLua

Версия приложения.

ПолеТипОписание
majorNumberСтарший номер версии
minorNumberМладший номер версии
buildNumberНомер сборки

ElementDataLua

UI-элемент.

ПолеТипОписание
nameStringИмя элемента
nameTypeNumberВнутренний код типа имени
controlTypeNumberВнутренний код типа элемента управления

FormDataValueLua

Значение поля формы.

ПолеТипОписание
valueStringТекстовое значение поля
typeNumberТип значения. FormDataValueType.Extracted соответствует извлеченному параметру

Изменяемость структуры

Lua-код может изменять поля существующих объектов в WAL. Добавление новых окон в windowHierarchy через push_back не поддерживается. Удаление последнего окна (pop_back) и полная очистка иерархии (clear) работают корректно.

Функции Lua API

После сбора данных агент мониторинга выполняет встроенную постобработку события в два этапа: удаляет избыточную информацию по внутренним правилам, затем извлекает параметры из данных окон по встроенным шаблонам. Три функции скрипта — это точки кастомизации: агент вызывает их в определенные моменты этой постобработки, передавая структуру WAL с данными события.

Каждая функция принимает аргумент WAL (структура данных по событию).

Агент вызывает функции в следующем порядке:

  • RemoveCustomUninformativeInfo — агент вызывает ее после того, как удалил избыточную информацию по собственным правилам, но до начала извлечения параметров. Данные, которые функция удалила, в событии не сохраняются
  • PreExtractCustomParameters — агент вызывает ее перед тем, как начнет извлекать параметры по встроенным шаблонам
  • PostExtractCustomParameters — агент вызывает ее после того, как завершит извлечение параметров по встроенным шаблонам. В этот момент событие уже содержит извлеченные параметры

Один и тот же фрагмент данных можно обработать по-разному в зависимости от выбранной функции. Например, для ИНН:

  • Чтобы удалить ИНН полностью и не фиксировать в событии, используйте RemoveCustomUninformativeInfo
  • Чтобы извлечь ИНН, преобразовать его или заменить на символ-заглушку, используйте PostExtractCustomParameters

RemoveCustomUninformativeInfo

Агент мониторинга вызывает эту функцию в ходе удаления избыточной информации — после того как выполнил собственное удаление, но до того как начнет извлекать параметры.

Используйте, чтобы дополнительно удалить нужные подстроки из значений полей формы окна.

Фрагмент ниже иллюстрирует функцию RemoveCustomUninformativeInfo и вспомогательную функцию RemoveWordsStartingWithZero. Пример — удалить из значений полей формы окна слова, начинающиеся на «0»:

-- Удаляет из строки слова, начинающиеся на "0".
-- Слово — последовательность непробельных символов; оставшиеся слова
-- разделяются одиночным пробелом.
--
-- @param str string Исходная строка.
-- @return string Строка без слов, начинающихся на "0".
function RemoveWordsStartingWithZero(str)
    if utf8.len(str) == 0 then
        return str
    end

    local parts = {}
    for word in utf8.gmatch(str, "%S+") do
        if utf8.sub(word, 1, 1) ~= "0" then
            table.insert(parts, word)
        end
    end

    return table.concat(parts, " ")
end

-- Удаляет из значений полей формы окна слова, начинающиеся на "0".
--
-- @param WAL window activity для lua
function RemoveCustomUninformativeInfo(WAL)
    local hierarchy = WAL.windowHierarchy
    for i = 0, hierarchy:size() - 1 do
        local window = hierarchy:at(i)

        for j = 0, window.formData.formData:size() - 1 do
            window.formData.formData:at(j).value.value =
                RemoveWordsStartingWithZero(window.formData.formData:at(j).value.value)
        end
    end
end

PreExtractCustomParameters

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

Используйте, чтобы извлечь параметры до того, как их обработают встроенные шаблоны агента. Для каждого извлеченного параметра установите тип FormDataValueType.Extracted, чтобы стандартное извлечение не обработало значение повторно.

Фрагмент ниже иллюстрирует функцию PreExtractCustomParameters и вспомогательные функции IsOneNumber, ExtractOneNumbers. Пример — извлечь из значений полей формы окна числа, начинающиеся на «1», с префиксом one_number::

-- Проверяет, состоит ли строка только из цифр и начинается ли с "1".
--
-- @param word string Проверяемое слово.
-- @return boolean true, если слово — число, начинающееся на "1".
local function IsOneNumber(word)
    return utf8.match(word, "^1%d*$") ~= nil
end

-- Извлекает из значения поля формы окна числа (слова, состоящие только из цифр),
-- начинающиеся на "1", помечая их префиксом "one_number:".
--
-- Функция вызывается до стандартного извлечения параметров, поэтому работает
-- с сырыми значениями полей формы окна (стандартного префикса "number:" здесь еще нет).
--
-- @param value string Значение поля формы окна.
-- @return string Преобразованное значение.
-- @return boolean true, если был извлечен хотя бы один параметр.
function ExtractOneNumbers(value)
    local parts = {}
    local extracted = false
    for word in utf8.gmatch(value, "%S+") do
        if IsOneNumber(word) then
            table.insert(parts, "one_number:" .. word)
            extracted = true
        else
            table.insert(parts, word)
        end
    end

    return table.concat(parts, " "), extracted
end

-- Извлекает из значений полей формы окна числа, начинающиеся на "1",
-- с префиксом "one_number:". После успешного извлечения помечает значение
-- как Extracted, чтобы стандартный пайплайн не извлекал его повторно.
--
-- @param WAL window activity для lua
function PreExtractCustomParameters(WAL)
    local hierarchy = WAL.windowHierarchy
    for i = 0, hierarchy:size() - 1 do
        local window = hierarchy:at(i)

        for j = 0, window.formData.formData:size() - 1 do
            local element = window.formData.formData:at(j)
            local newValue, extracted = ExtractOneNumbers(element.value.value)
            element.value.value = newValue
            if extracted then
                element.value.type = FormDataValueType.Extracted
            end
        end
    end
end

PostExtractCustomParameters

Агент мониторинга вызывает эту функцию после того, как завершит извлечение параметров по встроенным шаблонам.

Используйте, чтобы извлечь дополнительные параметры, которые встроенные шаблоны агента не извлекают. Для каждого извлеченного параметра установите тип FormDataValueType.Extracted.

Фрагмент ниже иллюстрирует функцию PostExtractCustomParameters и вспомогательные функции IsSpecialWord, ExtractSpecialWords. Пример — извлечь из значений полей формы окна слова, начинающиеся на «абв», с префиксом special_word::

-- Проверяет, начинается ли слово с "абв".
--
-- @param word string Проверяемое слово.
-- @return boolean true, если слово начинается на "абв".
local function IsSpecialWord(word)
    local prefix = "абв"
    return utf8.sub(word, 1, utf8.len(prefix)) == prefix
end

-- Извлекает из значения поля формы окна слова, начинающиеся на "абв", помечая их
-- префиксом "special_word:".
--
-- Функция вызывается после стандартного извлечения параметров.
--
-- @param value string Значение поля формы окна.
-- @return string Преобразованное значение.
-- @return boolean true, если был извлечен хотя бы один параметр.
function ExtractSpecialWords(value)
    local parts = {}
    local extracted = false
    for word in utf8.gmatch(value, "%S+") do
        if IsSpecialWord(word) then
            table.insert(parts, "special_word:" .. word)
            extracted = true
        else
            table.insert(parts, word)
        end
    end

    return table.concat(parts, " "), extracted
end

-- Извлекает из значений полей формы окна слова, начинающиеся на "абв",
-- с префиксом "special_word:". После успешного извлечения помечает значение
-- как Extracted.
--
-- @param WAL window activity для lua
function PostExtractCustomParameters(WAL)
    local hierarchy = WAL.windowHierarchy
    for i = 0, hierarchy:size() - 1 do
        local window = hierarchy:at(i)

        for j = 0, window.formData.formData:size() - 1 do
            local element = window.formData.formData:at(j)
            local newValue, extracted = ExtractSpecialWords(element.value.value)
            element.value.value = newValue
            if extracted then
                element.value.type = FormDataValueType.Extracted
            end
        end
    end
end

Пример скрипта

Полный скрипт со всеми четырьмя функциями, которые должны присутствовать в скрипте. Реализована логика из фрагментов выше: удаление слов, начинающихся на «0», извлечение чисел с «1» и слов с «абв».

function GetVersion()
    return "1.0.0"
end

-- Удаляет из строки слова, начинающиеся на "0".
-- Слово — последовательность непробельных символов; оставшиеся слова
-- разделяются одиночным пробелом.
--
-- @param str string Исходная строка.
-- @return string Строка без слов, начинающихся на "0".
function RemoveWordsStartingWithZero(str)
    if utf8.len(str) == 0 then
        return str
    end

    local parts = {}
    for word in utf8.gmatch(str, "%S+") do
        if utf8.sub(word, 1, 1) ~= "0" then
            table.insert(parts, word)
        end
    end

    return table.concat(parts, " ")
end

-- Удаляет из значений полей формы окна слова, начинающиеся на "0".
--
-- @param WAL window activity для lua
function RemoveCustomUninformativeInfo(WAL)
    local hierarchy = WAL.windowHierarchy
    for i = 0, hierarchy:size() - 1 do
        local window = hierarchy:at(i)

        for j = 0, window.formData.formData:size() - 1 do
            window.formData.formData:at(j).value.value =
                RemoveWordsStartingWithZero(window.formData.formData:at(j).value.value)
        end
    end
end

-- Проверяет, состоит ли строка только из цифр и начинается ли с "1".
--
-- @param word string Проверяемое слово.
-- @return boolean true, если слово — число, начинающееся на "1".
local function IsOneNumber(word)
    return utf8.match(word, "^1%d*$") ~= nil
end

-- Извлекает из значения поля формы окна числа (слова, состоящие только из цифр),
-- начинающиеся на "1", помечая их префиксом "one_number:".
--
-- Функция вызывается до стандартного извлечения параметров, поэтому работает
-- с сырыми значениями полей формы окна (стандартного префикса "number:" здесь еще нет).
--
-- @param value string Значение поля формы окна.
-- @return string Преобразованное значение.
-- @return boolean true, если был извлечен хотя бы один параметр.
function ExtractOneNumbers(value)
    local parts = {}
    local extracted = false
    for word in utf8.gmatch(value, "%S+") do
        if IsOneNumber(word) then
            table.insert(parts, "one_number:" .. word)
            extracted = true
        else
            table.insert(parts, word)
        end
    end

    return table.concat(parts, " "), extracted
end

-- Извлекает из значений полей формы окна числа, начинающиеся на "1",
-- с префиксом "one_number:". После успешного извлечения помечает значение
-- как Extracted, чтобы стандартный пайплайн не извлекал его повторно.
--
-- @param WAL window activity для lua
function PreExtractCustomParameters(WAL)
    local hierarchy = WAL.windowHierarchy
    for i = 0, hierarchy:size() - 1 do
        local window = hierarchy:at(i)

        for j = 0, window.formData.formData:size() - 1 do
            local element = window.formData.formData:at(j)
            local newValue, extracted = ExtractOneNumbers(element.value.value)
            element.value.value = newValue
            if extracted then
                element.value.type = FormDataValueType.Extracted
            end
        end
    end
end

-- Проверяет, начинается ли слово с "абв".
--
-- @param word string Проверяемое слово.
-- @return boolean true, если слово начинается на "абв".
local function IsSpecialWord(word)
    local prefix = "абв"
    return utf8.sub(word, 1, utf8.len(prefix)) == prefix
end

-- Извлекает из значения поля формы окна слова, начинающиеся на "абв", помечая их
-- префиксом "special_word:".
--
-- Функция вызывается после стандартного извлечения параметров.
--
-- @param value string Значение поля формы окна.
-- @return string Преобразованное значение.
-- @return boolean true, если был извлечен хотя бы один параметр.
function ExtractSpecialWords(value)
    local parts = {}
    local extracted = false
    for word in utf8.gmatch(value, "%S+") do
        if IsSpecialWord(word) then
            table.insert(parts, "special_word:" .. word)
            extracted = true
        else
            table.insert(parts, word)
        end
    end

    return table.concat(parts, " "), extracted
end

-- Извлекает из значений полей формы окна слова, начинающиеся на "абв",
-- с префиксом "special_word:". После успешного извлечения помечает значение
-- как Extracted.
--
-- @param WAL window activity для lua
function PostExtractCustomParameters(WAL)
    local hierarchy = WAL.windowHierarchy
    for i = 0, hierarchy:size() - 1 do
        local window = hierarchy:at(i)

        for j = 0, window.formData.formData:size() - 1 do
            local element = window.formData.formData:at(j)
            local newValue, extracted = ExtractSpecialWords(element.value.value)
            element.value.value = newValue
            if extracted then
                element.value.type = FormDataValueType.Extracted
            end
        end
    end
end

Обработка ошибок

Если скрипт содержит ошибку (синтаксическую или возникшую при выполнении), постобработка, выполняемая этим скриптом, пропускается. В логе инспектора появляется запись вида:

[LUA] <имя_функции> <сообщение>

Здесь <имя_функции> — одна из четырех функций, завершившаяся ошибкой. <сообщение> — подробности ошибки, если их удалось получить.

Загрузка скрипта в систему

Готовый .lua-файл загружается в Proceset через GraphQL API и автоматически подписывается сервером. Агент мониторинга проверяет подпись перед каждым запуском. Инструкция по загрузке, просмотру и удалению скрипта — в разделе Кастомный скрипт постобработки документации агента мониторинга.

Была ли статья полезна?

Предыдущая
Примеры реализации Python-блоков
430006, Саранск,
Северо-восточное шоссе, д. 3
ОКВЭД 62.01
ИНН 1328​909857
Код вида деятельности
в области ИТ 15.02 и 17.01
Языки программирования